mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 13:02:28 +01:00
aa107ef3ab
modified: storage/connect/tabjson.cpp storage/connect/tabjson.h
1442 lines
41 KiB
C++
1442 lines
41 KiB
C++
/************* tabjson C++ Program Source Code File (.CPP) *************/
|
|
/* PROGRAM NAME: tabjson Version 1.0 */
|
|
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
|
|
/* This program are the JSON 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 "xtable.h"
|
|
//#include "mycat.h" // for FNC_COL
|
|
#include "maputil.h"
|
|
#include "filamtxt.h"
|
|
#include "tabdos.h"
|
|
//#include "resource.h" // for IDS_COLUMNS
|
|
#include "tabjson.h"
|
|
#include "filamap.h"
|
|
#if defined(ZIP_SUPPORT)
|
|
#include "filamzip.h"
|
|
#endif // ZIP_SUPPORT
|
|
#include "tabmul.h"
|
|
#include "checklvl.h"
|
|
|
|
/***********************************************************************/
|
|
/* External function. */
|
|
/***********************************************************************/
|
|
USETEMP UseTemp(void);
|
|
|
|
/* -------------------------- Class JSONDEF -------------------------- */
|
|
|
|
JSONDEF::JSONDEF(void)
|
|
{
|
|
Jmode = MODE_OBJECT;
|
|
Objname = NULL;
|
|
Xcol = NULL;
|
|
Limit = 1;
|
|
ReadMode = 0;
|
|
} // end of JSONDEF constructor
|
|
|
|
/***********************************************************************/
|
|
/* DefineAM: define specific AM block values. */
|
|
/***********************************************************************/
|
|
bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
|
|
{
|
|
Jmode = (JMODE)GetIntCatInfo("Jmode", MODE_OBJECT);
|
|
Objname = GetStringCatInfo(g, "Object", NULL);
|
|
Xcol = GetStringCatInfo(g, "Expand", NULL);
|
|
Pretty = GetIntCatInfo("Pretty", 2);
|
|
Limit = GetIntCatInfo("Limit", 10);
|
|
return DOSDEF::DefineAM(g, "DOS", poff);
|
|
} // end of DefineAM
|
|
|
|
/***********************************************************************/
|
|
/* GetTable: makes a new Table Description Block. */
|
|
/***********************************************************************/
|
|
PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
|
|
{
|
|
PTDBASE tdbp;
|
|
PTXF txfp = NULL;
|
|
|
|
// JSN not used for pretty=1 for insert or delete
|
|
if (!Pretty || (Pretty == 1 && (m == MODE_READ || m == MODE_UPDATE))) {
|
|
USETEMP tmp = UseTemp();
|
|
bool map = Mapped && m != MODE_INSERT &&
|
|
!(tmp != TMP_NO && m == MODE_UPDATE) &&
|
|
!(tmp == TMP_FORCE &&
|
|
(m == MODE_UPDATE || m == MODE_DELETE));
|
|
|
|
if (Compressed) {
|
|
#if defined(ZIP_SUPPORT)
|
|
if (Compressed == 1)
|
|
txfp = new(g) ZIPFAM(this);
|
|
else
|
|
txfp = new(g) ZLBFAM(this);
|
|
#else // !ZIP_SUPPORT
|
|
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
|
|
return NULL;
|
|
#endif // !ZIP_SUPPORT
|
|
} else if (map)
|
|
txfp = new(g) MAPFAM(this);
|
|
else
|
|
txfp = new(g) DOSFAM(this);
|
|
|
|
// Txfp must be set for TDBDOS
|
|
tdbp = new(g) TDBJSN(this, txfp);
|
|
} else {
|
|
txfp = new(g) DOSFAM(this);
|
|
tdbp = new(g) TDBJSON(this, txfp);
|
|
} // endif Pretty
|
|
|
|
if (Multiple)
|
|
tdbp = new(g) TDBMUL(tdbp);
|
|
|
|
return tdbp;
|
|
} // end of GetTable
|
|
|
|
/* --------------------------- Class TDBJSN -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Implementation of the TDBJSN class. */
|
|
/***********************************************************************/
|
|
TDBJSN::TDBJSN(PJDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
|
|
{
|
|
Row = NULL;
|
|
Colp = NULL;
|
|
Jmode = tdp->Jmode;
|
|
Xcol = tdp->Xcol;
|
|
Fpos = -1;
|
|
//Spos = 0;
|
|
N = 0;
|
|
Limit = tdp->Limit;
|
|
NextSame = 0;
|
|
SameRow = 0;
|
|
Xval = -1;
|
|
Pretty = tdp->Pretty;
|
|
Strict = tdp->Strict;
|
|
Comma = false;
|
|
} // end of TDBJSN standard constructor
|
|
|
|
TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
|
|
{
|
|
Row = tdbp->Row;
|
|
Colp = tdbp->Colp;
|
|
Jmode = tdbp->Jmode;
|
|
Xcol = tdbp->Xcol;
|
|
Fpos = tdbp->Fpos;
|
|
//Spos = tdbp->Spos;
|
|
N = tdbp->N;
|
|
Limit = tdbp->Limit;
|
|
NextSame = tdbp->NextSame;
|
|
SameRow = tdbp->SameRow;
|
|
Xval = tdbp->Xval;
|
|
Pretty = tdbp->Pretty;
|
|
Strict = tdbp->Strict;
|
|
Comma = tdbp->Comma;
|
|
} // end of TDBJSN copy constructor
|
|
|
|
// Used for update
|
|
PTDB TDBJSN::CopyOne(PTABS t)
|
|
{
|
|
PTDB tp;
|
|
PJCOL cp1, cp2;
|
|
PGLOBAL g = t->G;
|
|
|
|
tp = new(g) TDBJSN(this);
|
|
|
|
for (cp1 = (PJCOL)Columns; cp1; cp1 = (PJCOL)cp1->GetNext()) {
|
|
cp2 = new(g) JSONCOL(cp1, tp); // Make a copy
|
|
NewPointer(t, cp1, cp2);
|
|
} // endfor cp1
|
|
|
|
return tp;
|
|
} // end of CopyOne
|
|
|
|
/***********************************************************************/
|
|
/* Allocate JSN column description block. */
|
|
/***********************************************************************/
|
|
PCOL TDBJSN::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
|
|
{
|
|
PJCOL colp = new(g) JSONCOL(g, cdp, this, cprec, n);
|
|
|
|
return (colp->ParseJpath(g)) ? NULL : colp;
|
|
} // end of MakeCol
|
|
|
|
/***********************************************************************/
|
|
/* InsertSpecialColumn: Put a special column ahead of the column list.*/
|
|
/***********************************************************************/
|
|
PCOL TDBJSN::InsertSpecialColumn(PGLOBAL g, 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 TDBJSN::Cardinality(PGLOBAL g)
|
|
{
|
|
if (!g)
|
|
return 0;
|
|
else if (Cardinal < 0)
|
|
Cardinal = TDBDOS::Cardinality(g);
|
|
|
|
return Cardinal;
|
|
} // end of Cardinality
|
|
|
|
/***********************************************************************/
|
|
/* JSON GetMaxSize: returns file size estimate in number of lines. */
|
|
/***********************************************************************/
|
|
int TDBJSN::GetMaxSize(PGLOBAL g)
|
|
{
|
|
if (MaxSize < 0)
|
|
MaxSize = TDBDOS::GetMaxSize(g) * ((Xcol) ? Limit : 1);
|
|
|
|
return MaxSize;
|
|
} // end of GetMaxSize
|
|
|
|
/***********************************************************************/
|
|
/* OpenDB: Data Base open routine for JSN access method. */
|
|
/***********************************************************************/
|
|
bool TDBJSN::OpenDB(PGLOBAL g)
|
|
{
|
|
if (Use == USE_OPEN) {
|
|
/*******************************************************************/
|
|
/* Table already open replace it at its beginning. */
|
|
/*******************************************************************/
|
|
Fpos= -1;
|
|
// Spos = 0;
|
|
NextSame = 0;
|
|
SameRow = 0;
|
|
} else {
|
|
/*******************************************************************/
|
|
/* First opening. */
|
|
/*******************************************************************/
|
|
if (Mode == MODE_INSERT)
|
|
switch (Jmode) {
|
|
case MODE_OBJECT: Row = new(g) JOBJECT; break;
|
|
case MODE_ARRAY: Row = new(g) JARRAY; break;
|
|
case MODE_VALUE: Row = new(g) JVALUE; break;
|
|
default:
|
|
sprintf(g->Message, "Invalid Jmode %d", Jmode);
|
|
return true;
|
|
} // endswitch Jmode
|
|
|
|
} // endif Use
|
|
|
|
return TDBDOS::OpenDB(g);
|
|
} // end of OpenDB
|
|
|
|
/***********************************************************************/
|
|
/* SkipHeader: Physically skip first header line if applicable. */
|
|
/* This is called from TDBDOS::OpenDB and must be executed before */
|
|
/* Kindex construction if the file is accessed using an index. */
|
|
/***********************************************************************/
|
|
bool TDBJSN::SkipHeader(PGLOBAL g)
|
|
{
|
|
int len = GetFileLength(g);
|
|
bool rc = false;
|
|
|
|
#if defined(_DEBUG)
|
|
if (len < 0)
|
|
return true;
|
|
#endif // _DEBUG
|
|
|
|
#if defined(WIN32)
|
|
#define Ending 2
|
|
#else // !WIN32
|
|
#define Ending 1
|
|
#endif // !WIN32
|
|
|
|
if (Pretty == 1) {
|
|
if (Mode == MODE_INSERT || Mode == MODE_DELETE) {
|
|
// Mode Insert and delete are no more handled here
|
|
assert(false);
|
|
} else if (len) // !Insert && !Delete
|
|
rc = (Txfp->SkipRecord(g, false) == RC_FX || Txfp->RecordPos(g));
|
|
|
|
} // endif Pretty
|
|
|
|
return rc;
|
|
} // end of SkipHeader
|
|
|
|
/***********************************************************************/
|
|
/* ReadDB: Data Base read routine for JSN access method. */
|
|
/***********************************************************************/
|
|
int TDBJSN::ReadDB(PGLOBAL g)
|
|
{
|
|
int rc;
|
|
|
|
N++;
|
|
|
|
if (NextSame) {
|
|
SameRow = NextSame;
|
|
NextSame = 0;
|
|
return RC_OK;
|
|
} else if ((rc = TDBDOS::ReadDB(g)) == RC_OK)
|
|
if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK)) {
|
|
// Deferred reading failed
|
|
} else if (!(Row = ParseJson(g, To_Line,
|
|
strlen(To_Line), Pretty, &Comma))) {
|
|
rc = (Pretty == 1 && !strcmp(To_Line, "]")) ? RC_EF : RC_FX;
|
|
} else {
|
|
SameRow = 0;
|
|
Fpos++;
|
|
rc = RC_OK;
|
|
} // endif's
|
|
|
|
return rc;
|
|
} // end of ReadDB
|
|
|
|
/***********************************************************************/
|
|
/* PrepareWriting: Prepare the line for WriteDB. */
|
|
/***********************************************************************/
|
|
bool TDBJSN::PrepareWriting(PGLOBAL g)
|
|
{
|
|
PSZ s = Serialize(g, Row, NULL, Pretty);
|
|
|
|
if (s) {
|
|
if (Comma)
|
|
strcat(s, ",");
|
|
|
|
if ((signed)strlen(s) > Lrecl) {
|
|
sprintf(g->Message, "Line would be truncated (lrecl=%d)", Lrecl);
|
|
return true;
|
|
} else
|
|
strcpy(To_Line, s);
|
|
|
|
Row->Clear();
|
|
return false;
|
|
} else
|
|
return true;
|
|
|
|
} // end of PrepareWriting
|
|
|
|
/* ---------------------------- JSONCOL ------------------------------ */
|
|
|
|
/***********************************************************************/
|
|
/* JSONCOL public constructor. */
|
|
/***********************************************************************/
|
|
JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
|
|
: DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
|
|
{
|
|
Tjp = (TDBJSN *)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
|
|
Jpath = cdp->GetFmt();
|
|
MulVal = NULL;
|
|
Nodes = NULL;
|
|
Nod = 0;
|
|
Xnod = -1;
|
|
Xpd = false;
|
|
Parsed = false;
|
|
} // end of JSONCOL constructor
|
|
|
|
/***********************************************************************/
|
|
/* JSONCOL constructor used for copying columns. */
|
|
/* tdbp is the pointer to the new table descriptor. */
|
|
/***********************************************************************/
|
|
JSONCOL::JSONCOL(JSONCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
|
|
{
|
|
Tjp = col1->Tjp;
|
|
Jpath = col1->Jpath;
|
|
MulVal = col1->MulVal;
|
|
Nodes = col1->Nodes;
|
|
Nod = col1->Nod;
|
|
Xnod = col1->Xnod;
|
|
Xpd = col1->Xpd;
|
|
Parsed = col1->Parsed;
|
|
} // end of JSONCOL copy constructor
|
|
|
|
/***********************************************************************/
|
|
/* SetBuffer: prepare a column block for write operation. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
|
|
{
|
|
if (DOSCOL::SetBuffer(g, value, ok, check))
|
|
return true;
|
|
|
|
// Parse the json path
|
|
if (ParseJpath(g))
|
|
return true;
|
|
|
|
Tjp = (TDBJSN*)To_Tdb;
|
|
return false;
|
|
} // end of SetBuffer
|
|
|
|
/***********************************************************************/
|
|
/* Check whether this object is expanded. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
|
|
{
|
|
if ((Tjp->Xcol && nm && !strcmp(nm, Tjp->Xcol) &&
|
|
(Tjp->Xval < 0 || Tjp->Xval == i)) || Xpd) {
|
|
Xpd = true; // Expandable object
|
|
Nodes[i].Op = OP_EXP;
|
|
} else if (b) {
|
|
strcpy(g->Message, "Cannot expand more than one branch");
|
|
return true;
|
|
} // endif Xcol
|
|
|
|
return false;
|
|
} // end of CheckExpand
|
|
|
|
/***********************************************************************/
|
|
/* Analyse array processing options. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
|
|
{
|
|
int n = (int)strlen(p);
|
|
bool dg = true;
|
|
PJNODE jnp = &Nodes[i];
|
|
|
|
if (*p) {
|
|
if (p[--n] == ']') {
|
|
p[n--] = 0;
|
|
p++;
|
|
} else {
|
|
// Wrong array specification
|
|
sprintf(g->Message,
|
|
"Invalid array specification %s for %s", p, Name);
|
|
return true;
|
|
} // endif p
|
|
|
|
} // endif *p
|
|
|
|
// To check whether a numeric Rank was specified
|
|
for (int k = 0; dg && p[k]; k++)
|
|
dg = isdigit(p[k]) > 0;
|
|
|
|
if (!n) {
|
|
// Default specifications
|
|
if (CheckExpand(g, i, nm, false))
|
|
return true;
|
|
else if (jnp->Op != OP_EXP)
|
|
if (!Value->IsTypeNum()) {
|
|
jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING);
|
|
jnp->Op = OP_CNC;
|
|
} else
|
|
jnp->Op = OP_ADD;
|
|
|
|
} else if (dg) {
|
|
if (atoi(p) > 0) {
|
|
// Return nth value
|
|
jnp->Rank = atoi(p);
|
|
jnp->Op = OP_EQ;
|
|
} else // Ignore array
|
|
jnp->Op = OP_NULL;
|
|
|
|
} else if (n == 1) {
|
|
// Set the Op value;
|
|
switch (*p) {
|
|
case '+': jnp->Op = OP_ADD; break;
|
|
case '*': 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 'x':
|
|
case 'X': // Expand this array
|
|
if (!Tjp->Xcol && nm) {
|
|
Xpd = true;
|
|
jnp->Op = OP_EXP;
|
|
Tjp->Xval = i;
|
|
Tjp->Xcol = nm;
|
|
} else if (CheckExpand(g, i, nm, true))
|
|
return true;
|
|
|
|
break;
|
|
default:
|
|
sprintf(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 {
|
|
sprintf(g->Message, "Wrong array specification for %s", Name);
|
|
return true;
|
|
} // endif's
|
|
|
|
// For calculated arrays, a local Value must be used
|
|
switch (jnp->Op) {
|
|
case OP_NUM:
|
|
jnp->Valp = AllocateValue(g, TYPE_INT);
|
|
break;
|
|
case OP_ADD:
|
|
case OP_MULT:
|
|
case OP_SEP:
|
|
if (!IsTypeChar(Buf_Type))
|
|
jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
|
|
else
|
|
jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);
|
|
|
|
break;
|
|
case OP_MIN:
|
|
case OP_MAX:
|
|
jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
|
|
break;
|
|
case OP_CNC:
|
|
if (IsTypeChar(Buf_Type))
|
|
jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
|
|
else
|
|
jnp->Valp = AllocateValue(g, TYPE_STRING, 512);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
} // endswitch Op
|
|
|
|
if (jnp->Valp)
|
|
MulVal = AllocateValue(g, jnp->Valp);
|
|
|
|
return false;
|
|
} // end of SetArrayOptions
|
|
|
|
/***********************************************************************/
|
|
/* Parse the eventual passed Jpath information. */
|
|
/* This information can be specified in the Fieldfmt column option */
|
|
/* when creating the table. It permits to indicate the position of */
|
|
/* the node corresponding to that column. */
|
|
/***********************************************************************/
|
|
bool JSONCOL::ParseJpath(PGLOBAL g)
|
|
{
|
|
char *p, *p2 = NULL, *pbuf = NULL;
|
|
int i;
|
|
bool mul = false;
|
|
|
|
if (Parsed)
|
|
return false; // Already done
|
|
else if (InitValue(g))
|
|
return true;
|
|
else if (!Jpath)
|
|
Jpath = Name;
|
|
|
|
pbuf = (char*)PlugSubAlloc(g, NULL, strlen(Jpath) + 1);
|
|
strcpy(pbuf, Jpath);
|
|
|
|
// The Jpath must be analyzed
|
|
for (i = 0, p = pbuf; (p = strchr(p, ':')); 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; i < Nod; i++, p = (p2 ? p2 + 1 : p + strlen(p))) {
|
|
if ((p2 = strchr(p, ':')))
|
|
*p2 = 0;
|
|
|
|
// Jpath must be explicit
|
|
if (*p == 0 || *p == '[') {
|
|
// Analyse intermediate array processing
|
|
if (SetArrayOptions(g, p, i, Nodes[i-1].Key))
|
|
return true;
|
|
|
|
} 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
|
|
|
|
MulVal = AllocateValue(g, Value);
|
|
Parsed = true;
|
|
return false;
|
|
} // end of ParseJpath
|
|
|
|
/***********************************************************************/
|
|
/* MakeJson: Serialize the json item and set value to it. */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::MakeJson(PGLOBAL g, PJSON jsp)
|
|
{
|
|
if (Value->IsTypeNum()) {
|
|
strcpy(g->Message, "Cannot make Json for a numeric column");
|
|
Value->Reset();
|
|
} else
|
|
Value->SetValue_psz(Serialize(g, jsp, NULL, 0));
|
|
|
|
return Value;
|
|
} // end of MakeJson
|
|
|
|
/***********************************************************************/
|
|
/* SetValue: Set a value from a JVALUE contains. */
|
|
/***********************************************************************/
|
|
void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
|
|
{
|
|
if (val) {
|
|
switch (val->GetValType()) {
|
|
case TYPE_STRG:
|
|
case TYPE_INTG:
|
|
case TYPE_DBL:
|
|
vp->SetValue_pval(val->GetValue());
|
|
break;
|
|
case TYPE_BOOL:
|
|
if (vp->IsTypeNum())
|
|
vp->SetValue(val->GetInteger() ? 1 : 0);
|
|
else
|
|
vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));
|
|
|
|
break;
|
|
case TYPE_JAR:
|
|
SetJsonValue(g, vp, val->GetArray()->GetValue(0), n);
|
|
break;
|
|
case TYPE_JOB:
|
|
// if (!vp->IsTypeNum() || !Strict) {
|
|
vp->SetValue_psz(val->GetObject()->GetText(g, NULL));
|
|
break;
|
|
// } // endif Type
|
|
|
|
default:
|
|
vp->Reset();
|
|
} // endswitch Type
|
|
|
|
} else
|
|
vp->Reset();
|
|
|
|
} // end of SetJsonValue
|
|
|
|
/***********************************************************************/
|
|
/* ReadColumn: */
|
|
/***********************************************************************/
|
|
void JSONCOL::ReadColumn(PGLOBAL g)
|
|
{
|
|
if (!Tjp->SameRow || Xnod >= Tjp->SameRow)
|
|
Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0));
|
|
|
|
} // end of ReadColumn
|
|
|
|
/***********************************************************************/
|
|
/* GetColumnValue: */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i)
|
|
{
|
|
int n = Nod - 1;
|
|
bool expd = false;
|
|
PJAR arp;
|
|
PJVAL val = NULL;
|
|
|
|
for (; i < Nod && row; i++) {
|
|
if (Nodes[i].Op == OP_NUM) {
|
|
Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1);
|
|
return(Value);
|
|
} else if (Nodes[i].Op == OP_XX) {
|
|
return MakeJson(g, row);
|
|
} else switch (row->GetType()) {
|
|
case TYPE_JOB:
|
|
if (!Nodes[i].Key) {
|
|
// Expected Array was not there
|
|
if (i < Nod-1)
|
|
continue;
|
|
else
|
|
val = new(g) JVALUE(row);
|
|
|
|
} else
|
|
val = ((PJOB)row)->GetValue(Nodes[i].Key);
|
|
|
|
break;
|
|
case TYPE_JAR:
|
|
arp = (PJAR)row;
|
|
|
|
if (!Nodes[i].Key) {
|
|
if (Nodes[i].Op != OP_NULL) {
|
|
if (Nodes[i].Rank) {
|
|
val = arp->GetValue(Nodes[i].Rank - 1);
|
|
} else if (Nodes[i].Op == OP_EXP) {
|
|
return ExpandArray(g, arp, i);
|
|
} else
|
|
return CalculateArray(g, arp, i);
|
|
|
|
} else
|
|
val = NULL;
|
|
|
|
} else if (i < Nod-1) {
|
|
strcpy(g->Message, "Unexpected array");
|
|
val = NULL; // Not an expected array
|
|
} else
|
|
val = arp->GetValue(0);
|
|
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
|
|
val = NULL;
|
|
} // endswitch Type
|
|
|
|
if (i < Nod-1)
|
|
row = (val) ? val->GetJson() : NULL;
|
|
|
|
} // endfor i
|
|
|
|
SetJsonValue(g, Value, val, n);
|
|
return Value;
|
|
} // end of GetColumnValue
|
|
|
|
/***********************************************************************/
|
|
/* ExpandArray: */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::ExpandArray(PGLOBAL g, PJAR arp, int n)
|
|
{
|
|
int ars;
|
|
PJVAL jvp;
|
|
JVALUE jval;
|
|
|
|
ars = MY_MIN(Tjp->Limit, arp->size());
|
|
|
|
if (!(jvp = arp->GetValue(Nodes[n].Nx))) {
|
|
strcpy(g->Message, "Logical error expanding array");
|
|
longjmp(g->jumper[g->jump_level], 666);
|
|
} // endif jvp
|
|
|
|
if (n < Nod - 1 && jvp->GetJson()) {
|
|
jval.SetValue(GetColumnValue(g, jvp->GetJson(), n + 1));
|
|
jvp = &jval;
|
|
} // endif n
|
|
|
|
if (n >= Tjp->NextSame) {
|
|
if (++Nodes[n].Nx == ars) {
|
|
Nodes[n].Nx = 0;
|
|
Xnod = 0;
|
|
} else
|
|
Xnod = n;
|
|
|
|
Tjp->NextSame = Xnod;
|
|
} // endif NextSame
|
|
|
|
SetJsonValue(g, Value, jvp, n);
|
|
return Value;
|
|
} // end of ExpandArray
|
|
|
|
/***********************************************************************/
|
|
/* CalculateArray: */
|
|
/***********************************************************************/
|
|
PVAL JSONCOL::CalculateArray(PGLOBAL g, PJAR arp, int n)
|
|
{
|
|
int i, ars, nv = 0, nextsame = Tjp->NextSame;
|
|
bool err;
|
|
OPVAL op = Nodes[n].Op;
|
|
PVAL val[2], vp = Nodes[n].Valp;
|
|
PJVAL jvrp, jvp;
|
|
JVALUE jval;
|
|
|
|
vp->Reset();
|
|
ars = MY_MIN(Tjp->Limit, arp->size());
|
|
|
|
for (i = 0; i < ars; i++) {
|
|
jvrp = arp->GetValue(i);
|
|
|
|
do {
|
|
if (n < Nod - 1 && jvrp->GetJson()) {
|
|
Tjp->NextSame = nextsame;
|
|
jval.SetValue(GetColumnValue(g, jvrp->GetJson(), n + 1));
|
|
jvp = &jval;
|
|
} else
|
|
jvp = jvrp;
|
|
|
|
if (!nv++) {
|
|
SetJsonValue(g, vp, jvp, n);
|
|
continue;
|
|
} else
|
|
SetJsonValue(g, MulVal, jvp, n);
|
|
|
|
if (!MulVal->IsZero()) {
|
|
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();
|
|
|
|
} // endif Zero
|
|
|
|
} while (Tjp->NextSame > nextsame);
|
|
|
|
} // endfor i
|
|
|
|
if (op == OP_SEP) {
|
|
// Calculate average
|
|
MulVal->SetValue(nv);
|
|
val[0] = vp;
|
|
val[1] = MulVal;
|
|
|
|
if (vp->Compute(g, val, 2, OP_DIV))
|
|
vp->Reset();
|
|
|
|
} // endif Op
|
|
|
|
Tjp->NextSame = nextsame;
|
|
return vp;
|
|
} // end of CalculateArray
|
|
|
|
/***********************************************************************/
|
|
/* GetRow: Get the object containing this column. */
|
|
/***********************************************************************/
|
|
PJSON JSONCOL::GetRow(PGLOBAL g)
|
|
{
|
|
PJVAL val;
|
|
PJAR arp;
|
|
PJSON nwr, row = Tjp->Row;
|
|
|
|
for (int i = 0; i < Nod-1 && row; i++) {
|
|
if (Nodes[i+1].Op == OP_XX)
|
|
break;
|
|
else switch (row->GetType()) {
|
|
case TYPE_JOB:
|
|
if (!Nodes[i].Key)
|
|
// Expected Array was not there
|
|
continue;
|
|
|
|
val = ((PJOB)row)->GetValue(Nodes[i].Key);
|
|
break;
|
|
case TYPE_JAR:
|
|
if (!Nodes[i].Key) {
|
|
if (Nodes[i].Op != OP_NULL) {
|
|
arp = (PJAR)row;
|
|
|
|
if (Nodes[i].Rank)
|
|
val = arp->GetValue(Nodes[i].Rank - 1);
|
|
else
|
|
val = arp->GetValue(Nodes[i].Nx);
|
|
|
|
} else
|
|
val = NULL;
|
|
|
|
} else {
|
|
strcpy(g->Message, "Unexpected array");
|
|
val = NULL; // Not an expected array
|
|
} // endif Nodes
|
|
|
|
break;
|
|
case TYPE_JVAL:
|
|
val = (PJVAL)row;
|
|
break;
|
|
default:
|
|
sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
|
|
val = NULL;
|
|
} // endswitch Type
|
|
|
|
if (val) {
|
|
row = val->GetJson();
|
|
} else {
|
|
// Construct missing objects
|
|
for (i++; row && i < Nod; i++) {
|
|
if (Nodes[i].Op == OP_XX)
|
|
break;
|
|
else if (!Nodes[i].Key)
|
|
// Construct intermediate array
|
|
nwr = new(g) JARRAY;
|
|
else
|
|
nwr = new(g) JOBJECT;
|
|
|
|
if (row->GetType() == TYPE_JOB) {
|
|
((PJOB)row)->SetValue(g, new(g) JVALUE(nwr), Nodes[i-1].Key);
|
|
} else if (row->GetType() == TYPE_JAR) {
|
|
((PJAR)row)->AddValue(g, new(g) JVALUE(nwr));
|
|
((PJAR)row)->InitArray(g);
|
|
} else {
|
|
strcpy(g->Message, "Wrong type when writing new row");
|
|
nwr = NULL;
|
|
} // endif's
|
|
|
|
row = nwr;
|
|
} // endfor i
|
|
|
|
break;
|
|
} // endelse
|
|
|
|
} // endfor i
|
|
|
|
return row;
|
|
} // end of GetRow
|
|
|
|
/***********************************************************************/
|
|
/* WriteColumn: */
|
|
/***********************************************************************/
|
|
void JSONCOL::WriteColumn(PGLOBAL g)
|
|
{
|
|
/*********************************************************************/
|
|
/* Check whether this node must be written. */
|
|
/*********************************************************************/
|
|
if (Value != To_Val)
|
|
Value->SetValue_pval(To_Val, FALSE); // Convert the updated value
|
|
|
|
/*********************************************************************/
|
|
/* On INSERT Null values are represented by no node. */
|
|
/*********************************************************************/
|
|
if (Value->IsNull() && Tjp->Mode == MODE_INSERT)
|
|
return;
|
|
|
|
char *s;
|
|
PJOB objp = NULL;
|
|
PJAR arp = NULL;
|
|
PJVAL jvp = NULL;
|
|
PJSON jsp, row = GetRow(g);
|
|
JTYP type = row->GetType();
|
|
|
|
switch (row->GetType()) {
|
|
case TYPE_JOB: objp = (PJOB)row; break;
|
|
case TYPE_JAR: arp = (PJAR)row; break;
|
|
case TYPE_JVAL: jvp = (PJVAL)row; break;
|
|
default: row = NULL; // ???????????????????????????
|
|
} // endswitch Type
|
|
|
|
if (row) switch (Buf_Type) {
|
|
case TYPE_STRING:
|
|
if (Nodes[Nod-1].Op == OP_XX) {
|
|
s = Value->GetCharValue();
|
|
|
|
if (!(jsp = ParseJson(g, s, (int)strlen(s), 0))) {
|
|
strcpy(g->Message, s);
|
|
longjmp(g->jumper[g->jump_level], 666);
|
|
} // endif jsp
|
|
|
|
if (arp) {
|
|
if (Nod > 1 && Nodes[Nod-2].Rank)
|
|
arp->SetValue(g, new(g) JVALUE(jsp), Nodes[Nod-2].Rank-1);
|
|
else
|
|
arp->AddValue(g, new(g) JVALUE(jsp));
|
|
|
|
arp->InitArray(g);
|
|
} else if (objp) {
|
|
if (Nod > 1 && Nodes[Nod-2].Key)
|
|
objp->SetValue(g, new(g) JVALUE(jsp), Nodes[Nod-2].Key);
|
|
|
|
} else if (jvp)
|
|
jvp->SetValue(jsp);
|
|
|
|
break;
|
|
} // endif Op
|
|
|
|
// Passthru
|
|
case TYPE_DATE:
|
|
case TYPE_INT:
|
|
case TYPE_DOUBLE:
|
|
if (arp) {
|
|
if (Nodes[Nod-1].Rank)
|
|
arp->SetValue(g, new(g) JVALUE(g, Value), Nodes[Nod-1].Rank-1);
|
|
else
|
|
arp->AddValue(g, new(g) JVALUE(g, Value));
|
|
|
|
arp->InitArray(g);
|
|
} else if (objp) {
|
|
if (Nodes[Nod-1].Key)
|
|
objp->SetValue(g, new(g) JVALUE(g, Value), Nodes[Nod-1].Key);
|
|
|
|
} else if (jvp)
|
|
jvp->SetValue(Value);
|
|
|
|
break;
|
|
default: // ??????????
|
|
sprintf(g->Message, "Invalid column type %d", Buf_Type);
|
|
} // endswitch Type
|
|
|
|
} // end of WriteColumn
|
|
|
|
/* -------------------------- Class TDBJSON -------------------------- */
|
|
|
|
/***********************************************************************/
|
|
/* Implementation of the TDBJSON class. */
|
|
/***********************************************************************/
|
|
TDBJSON::TDBJSON(PJDEF tdp, PTXF txfp) : TDBJSN(tdp, txfp)
|
|
{
|
|
Top = NULL;
|
|
Doc = NULL;
|
|
Objname = tdp->Objname;
|
|
Multiple = tdp->Multiple;
|
|
Done = Changed = false;
|
|
} // end of TDBJSON standard constructor
|
|
|
|
TDBJSON::TDBJSON(PJTDB tdbp) : TDBJSN(tdbp)
|
|
{
|
|
Top = tdbp->Top;
|
|
Doc = tdbp->Doc;
|
|
Objname = tdbp->Objname;
|
|
Multiple = tdbp->Multiple;
|
|
Done = tdbp->Done;
|
|
Changed = tdbp->Changed;
|
|
} // end of TDBJSON copy constructor
|
|
|
|
// Used for update
|
|
PTDB TDBJSON::CopyOne(PTABS t)
|
|
{
|
|
PTDB tp;
|
|
PJCOL cp1, cp2;
|
|
PGLOBAL g = t->G;
|
|
|
|
tp = new(g) TDBJSON(this);
|
|
|
|
for (cp1 = (PJCOL)Columns; cp1; cp1 = (PJCOL)cp1->GetNext()) {
|
|
cp2 = new(g) JSONCOL(cp1, tp); // Make a copy
|
|
NewPointer(t, cp1, cp2);
|
|
} // endfor cp1
|
|
|
|
return tp;
|
|
} // end of CopyOne
|
|
|
|
/***********************************************************************/
|
|
/* Make the document tree from a file. */
|
|
/***********************************************************************/
|
|
int TDBJSON::MakeNewDoc(PGLOBAL g)
|
|
{
|
|
// Create a void table that will be populated
|
|
Doc = new(g) JARRAY;
|
|
|
|
if (Objname) {
|
|
// Parse and allocate Objname item(s)
|
|
char *p;
|
|
char *objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname)+1);
|
|
int i;
|
|
PJOB objp;
|
|
PJAR arp;
|
|
PJVAL val = NULL;
|
|
|
|
strcpy(objpath, Objname);
|
|
Top = NULL;
|
|
|
|
for (; objpath; objpath = p) {
|
|
if ((p = strchr(objpath, ':')))
|
|
*p++ = 0;
|
|
|
|
if (*objpath != '[') {
|
|
objp = new(g) JOBJECT;
|
|
|
|
if (!Top)
|
|
Top = objp;
|
|
|
|
if (val)
|
|
val->SetValue(objp);
|
|
|
|
val = new(g) JVALUE;
|
|
objp->SetValue(g, val, objpath);
|
|
} else if (objpath[strlen(objpath)-1] == ']') {
|
|
arp = new(g) JARRAY;
|
|
|
|
if (!Top)
|
|
Top = arp;
|
|
|
|
if (val)
|
|
val->SetValue(arp);
|
|
|
|
val = new(g) JVALUE;
|
|
i = atoi(objpath+1) - 1;
|
|
arp->SetValue(g, val, i);
|
|
arp->InitArray(g);
|
|
} else {
|
|
sprintf(g->Message, "Invalid Table path %s", Objname);
|
|
return RC_FX;
|
|
} // endif objpath
|
|
|
|
} // endfor p
|
|
|
|
val->SetValue(Doc);
|
|
} else
|
|
Top = Doc;
|
|
|
|
return RC_OK;
|
|
} // end of MakeNewDoc
|
|
|
|
/***********************************************************************/
|
|
/* Make the document tree from a file. */
|
|
/***********************************************************************/
|
|
int TDBJSON::MakeDocument(PGLOBAL g)
|
|
{
|
|
char *p, *memory, *objpath, *key, filename[_MAX_PATH];
|
|
int len, i = 0;
|
|
HANDLE hFile;
|
|
MEMMAP mm;
|
|
PJSON jsp;
|
|
PJOB objp = NULL;
|
|
PJAR arp = NULL;
|
|
PJVAL val = NULL;
|
|
|
|
if (Done)
|
|
return RC_OK;
|
|
else
|
|
Done = true;
|
|
|
|
// Now open the JSON file
|
|
PlugSetPath(filename, Txfp->To_File, GetPath());
|
|
|
|
/*********************************************************************/
|
|
/* Create the mapping file object. */
|
|
/*********************************************************************/
|
|
hFile = CreateFileMap(g, filename, &mm, MODE_READ, false);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
DWORD drc = GetLastError();
|
|
|
|
if (drc != ENOENT || Mode != MODE_INSERT) {
|
|
if (!(*g->Message))
|
|
sprintf(g->Message, MSG(OPEN_MODE_ERROR),
|
|
"map", (int)drc, filename);
|
|
|
|
return RC_FX;
|
|
} else
|
|
return MakeNewDoc(g);
|
|
|
|
} // endif hFile
|
|
|
|
/*********************************************************************/
|
|
/* Get the file size (assuming file is smaller than 4 GB) */
|
|
/*********************************************************************/
|
|
len = mm.lenL;
|
|
memory = (char *)mm.memory;
|
|
|
|
if (!len) { // Empty file
|
|
CloseFileHandle(hFile);
|
|
CloseMemMap(memory, len);
|
|
|
|
if (Mode == MODE_INSERT)
|
|
return MakeNewDoc(g);
|
|
|
|
} // endif len
|
|
|
|
if (!memory) {
|
|
CloseFileHandle(hFile);
|
|
sprintf(g->Message, MSG(MAP_VIEW_ERROR), filename, GetLastError());
|
|
return RC_FX;
|
|
} // endif Memory
|
|
|
|
CloseFileHandle(hFile); // Not used anymore
|
|
hFile = INVALID_HANDLE_VALUE; // For Fblock
|
|
|
|
/*********************************************************************/
|
|
/* Parse the json file and allocate its tree structure. */
|
|
/*********************************************************************/
|
|
g->Message[0] = 0;
|
|
jsp = Top = ParseJson(g, memory, len, Pretty);
|
|
CloseMemMap(memory, len);
|
|
|
|
if (!jsp && g->Message[0])
|
|
return RC_FX;
|
|
|
|
if (Objname) {
|
|
objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname) + 1);
|
|
strcpy(objpath, Objname);
|
|
} else
|
|
objpath = NULL;
|
|
|
|
/*********************************************************************/
|
|
/* Find the table in the tree structure. */
|
|
/*********************************************************************/
|
|
for (; jsp && objpath; objpath = p) {
|
|
if ((p = strchr(objpath, ':')))
|
|
*p++ = 0;
|
|
|
|
if (*objpath != '[') { // objpass is a key
|
|
if (jsp->GetType() != TYPE_JOB) {
|
|
strcpy(g->Message, "Table path does no match json file");
|
|
return RC_FX;
|
|
} // endif Type
|
|
|
|
key = objpath;
|
|
objp = jsp->GetObject();
|
|
arp = NULL;
|
|
val = objp->GetValue(key);
|
|
|
|
if (!val || !(jsp = val->GetJson())) {
|
|
sprintf(g->Message, "Cannot find object key %s", key);
|
|
return RC_FX;
|
|
} // endif val
|
|
|
|
} else if (objpath[strlen(objpath)-1] == ']') {
|
|
if (jsp->GetType() != TYPE_JAR) {
|
|
strcpy(g->Message, "Table path does no match json file");
|
|
return RC_FX;
|
|
} // endif Type
|
|
|
|
arp = jsp->GetArray();
|
|
objp = NULL;
|
|
i = atoi(objpath+1) - 1;
|
|
val = arp->GetValue(i);
|
|
|
|
if (!val) {
|
|
sprintf(g->Message, "Cannot find array value %d", i);
|
|
return RC_FX;
|
|
} // endif val
|
|
|
|
} else {
|
|
sprintf(g->Message, "Invalid Table path %s", Objname);
|
|
return RC_FX;
|
|
} // endif objpath
|
|
|
|
jsp = val->GetJson();
|
|
} // endfor objpath
|
|
|
|
if (jsp && jsp->GetType() == TYPE_JAR)
|
|
Doc = jsp->GetArray();
|
|
else {
|
|
// The table is void or is just one object or one value
|
|
Doc = new(g) JARRAY;
|
|
|
|
if (val) {
|
|
Doc->AddValue(g, val);
|
|
Doc->InitArray(g);
|
|
} else if (jsp) {
|
|
Doc->AddValue(g, new(g) JVALUE(jsp));
|
|
Doc->InitArray(g);
|
|
} // endif val
|
|
|
|
if (objp)
|
|
objp->SetValue(g, new(g) JVALUE(Doc), key);
|
|
else if (arp)
|
|
arp->SetValue(g, new(g) JVALUE(Doc), i);
|
|
else
|
|
Top = Doc;
|
|
|
|
} // endif jsp
|
|
|
|
return RC_OK;
|
|
} // end of MakeDocument
|
|
|
|
/***********************************************************************/
|
|
/* JSON Cardinality: returns table size in number of rows. */
|
|
/***********************************************************************/
|
|
int TDBJSON::Cardinality(PGLOBAL g)
|
|
{
|
|
if (!g)
|
|
return (Xcol || Multiple) ? 0 : 1;
|
|
else if (Cardinal < 0)
|
|
if (!Multiple) {
|
|
if (MakeDocument(g) == RC_OK)
|
|
Cardinal = Doc->size();
|
|
|
|
} else
|
|
return 10;
|
|
|
|
return Cardinal;
|
|
} // end of Cardinality
|
|
|
|
/***********************************************************************/
|
|
/* JSON GetMaxSize: returns table size estimate in number of rows. */
|
|
/***********************************************************************/
|
|
int TDBJSON::GetMaxSize(PGLOBAL g)
|
|
{
|
|
if (MaxSize < 0)
|
|
MaxSize = Cardinality(g) * ((Xcol) ? Limit : 1);
|
|
|
|
return MaxSize;
|
|
} // end of GetMaxSize
|
|
|
|
/***********************************************************************/
|
|
/* ResetSize: call by TDBMUL when calculating size estimate. */
|
|
/***********************************************************************/
|
|
void TDBJSON::ResetSize(void)
|
|
{
|
|
MaxSize = Cardinal = -1;
|
|
Fpos = -1;
|
|
N = 0;
|
|
Done = false;
|
|
} // end of ResetSize
|
|
|
|
/***********************************************************************/
|
|
/* TDBJSON is not indexable. */
|
|
/***********************************************************************/
|
|
int TDBJSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
|
|
{
|
|
if (pxdf) {
|
|
strcpy(g->Message, "JSON not indexable when pretty = 2");
|
|
return RC_FX;
|
|
} else
|
|
return RC_OK;
|
|
|
|
} // end of MakeIndex
|
|
|
|
/***********************************************************************/
|
|
/* JSON Access Method opening routine. */
|
|
/***********************************************************************/
|
|
bool TDBJSON::OpenDB(PGLOBAL g)
|
|
{
|
|
if (Use == USE_OPEN) {
|
|
/*******************************************************************/
|
|
/* Table already open replace it at its beginning. */
|
|
/*******************************************************************/
|
|
Fpos= -1;
|
|
NextSame = false;
|
|
SameRow = 0;
|
|
return false;
|
|
} // endif use
|
|
|
|
/*********************************************************************/
|
|
/* OpenDB: initialize the JSON file processing. */
|
|
/*********************************************************************/
|
|
if (MakeDocument(g) != RC_OK)
|
|
return true;
|
|
|
|
if (Mode == MODE_INSERT)
|
|
switch (Jmode) {
|
|
case MODE_OBJECT: Row = new(g) JOBJECT; break;
|
|
case MODE_ARRAY: Row = new(g) JARRAY; break;
|
|
case MODE_VALUE: Row = new(g) JVALUE; break;
|
|
default:
|
|
sprintf(g->Message, "Invalid Jmode %d", Jmode);
|
|
return true;
|
|
} // endswitch Jmode
|
|
|
|
Use = USE_OPEN;
|
|
return false;
|
|
} // end of OpenDB
|
|
|
|
/***********************************************************************/
|
|
/* ReadDB: Data Base read routine for JSON access method. */
|
|
/***********************************************************************/
|
|
int TDBJSON::ReadDB(PGLOBAL g)
|
|
{
|
|
int rc;
|
|
|
|
N++;
|
|
|
|
if (NextSame) {
|
|
SameRow = NextSame;
|
|
NextSame = false;
|
|
rc = RC_OK;
|
|
} else if (++Fpos < (signed)Doc->size()) {
|
|
Row = Doc->GetValue(Fpos);
|
|
|
|
if (Row->GetType() == TYPE_JVAL)
|
|
Row = ((PJVAL)Row)->GetJson();
|
|
|
|
SameRow = 0;
|
|
rc = RC_OK;
|
|
} else
|
|
rc = RC_EF;
|
|
|
|
return rc;
|
|
} // end of ReadDB
|
|
|
|
/***********************************************************************/
|
|
/* WriteDB: Data Base write routine for JSON access method. */
|
|
/***********************************************************************/
|
|
int TDBJSON::WriteDB(PGLOBAL g)
|
|
{
|
|
if (Jmode == MODE_OBJECT) {
|
|
PJVAL vp = new(g) JVALUE(Row);
|
|
|
|
if (Mode == MODE_INSERT) {
|
|
Doc->AddValue(g, vp);
|
|
Row = new(g) JOBJECT;
|
|
} else if (Doc->SetValue(g, vp, Fpos))
|
|
return RC_FX;
|
|
|
|
} else if (Jmode == MODE_ARRAY) {
|
|
PJVAL vp = new(g) JVALUE(Row);
|
|
|
|
if (Mode == MODE_INSERT) {
|
|
Doc->AddValue(g, vp);
|
|
Row = new(g) JARRAY;
|
|
} else if (Doc->SetValue(g, vp, Fpos))
|
|
return RC_FX;
|
|
|
|
} else { // if (Jmode == MODE_VALUE)
|
|
if (Mode == MODE_INSERT) {
|
|
Doc->AddValue(g, (PJVAL)Row);
|
|
Row = new(g) JVALUE;
|
|
} else if (Doc->SetValue(g, (PJVAL)Row, Fpos))
|
|
return RC_FX;
|
|
|
|
} // endif Jmode
|
|
|
|
Changed = true;
|
|
return RC_OK;
|
|
} // end of WriteDB
|
|
|
|
/***********************************************************************/
|
|
/* Data Base delete line routine for JSON access method. */
|
|
/***********************************************************************/
|
|
int TDBJSON::DeleteDB(PGLOBAL g, int irc)
|
|
{
|
|
if (irc == RC_OK) {
|
|
// Deleted current row
|
|
if (Doc->DeleteValue(Fpos)) {
|
|
sprintf(g->Message, "Value %d does not exist", Fpos + 1);
|
|
return RC_FX;
|
|
} // endif Delete
|
|
|
|
Changed = true;
|
|
} else if (irc == RC_FX)
|
|
// Delete all
|
|
for (int i = 0; i < Doc->size(); i++) {
|
|
Doc->DeleteValue(i);
|
|
Changed = true;
|
|
} // endfor i
|
|
|
|
return RC_OK;
|
|
} // end of DeleteDB
|
|
|
|
/***********************************************************************/
|
|
/* Data Base close routine for JSON access methods. */
|
|
/***********************************************************************/
|
|
void TDBJSON::CloseDB(PGLOBAL g)
|
|
{
|
|
if (!Changed)
|
|
return;
|
|
|
|
// Save the modified document
|
|
char filename[_MAX_PATH];
|
|
PSZ msg;
|
|
FILE *fop;
|
|
|
|
Doc->InitArray(g);
|
|
|
|
// We used the file name relative to recorded datapath
|
|
PlugSetPath(filename, ((PJDEF)To_Def)->Fn, GetPath());
|
|
|
|
// Serialize the modified table
|
|
if (!(fop = fopen(filename, "wb"))) {
|
|
sprintf(g->Message, MSG(OPEN_MODE_ERROR),
|
|
"w", (int)errno, filename);
|
|
strcat(strcat(g->Message, ": "), strerror(errno));
|
|
} else if ((msg = Serialize(g, Top, fop, Pretty)))
|
|
puts(msg);
|
|
|
|
} // end of CloseDB
|
|
|
|
/* -------------------------- End of json --------------------------- */
|