Commit 2d2e110b authored by Olivier Bertrand's avatar Olivier Bertrand

- Adding the JSON table type

added:
  storage/connect/json.cpp
  storage/connect/json.h
  storage/connect/mysql-test/connect/r/json.result
  storage/connect/mysql-test/connect/std_data/biblio.jsn
  storage/connect/mysql-test/connect/std_data/expense.jsn
  storage/connect/mysql-test/connect/std_data/mulexp3.jsn
  storage/connect/mysql-test/connect/std_data/mulexp4.jsn
  storage/connect/mysql-test/connect/std_data/mulexp5.jsn
  storage/connect/mysql-test/connect/t/json.test
  storage/connect/tabjson.cpp
  storage/connect/tabjson.h
modified:
  storage/connect/CMakeLists.txt
  storage/connect/engmsg.h
  storage/connect/filamtxt.h
  storage/connect/ha_connect.cc
  storage/connect/msgid.h
  storage/connect/mycat.cc
  storage/connect/plgdbsem.h
  storage/connect/tabdos.cpp
  storage/connect/value.cpp
  storage/connect/value.h
parent fb3f4696
......@@ -21,18 +21,18 @@ ha_connect.cc connect.cc user_connect.cc mycat.cc
fmdlex.c osutil.c plugutil.c rcmsg.c rcmsg.h
array.cpp blkfil.cpp colblk.cpp csort.cpp
filamap.cpp filamdbf.cpp filamfix.cpp filamtxt.cpp filamvct.cpp filamzip.cpp
filter.cpp maputil.cpp myutil.cpp plgdbutl.cpp reldef.cpp tabcol.cpp
tabdos.cpp tabfix.cpp tabfmt.cpp table.cpp tabmul.cpp taboccur.cpp
filter.cpp json.cpp maputil.cpp myutil.cpp plgdbutl.cpp reldef.cpp tabcol.cpp
tabdos.cpp tabfix.cpp tabfmt.cpp tabjson.cpp table.cpp tabmul.cpp taboccur.cpp
tabpivot.cpp tabsys.cpp tabtbl.cpp tabutil.cpp tabvct.cpp tabvir.cpp
tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp
array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h
engmsg.h filamap.h filamdbf.h filamfix.h filamtxt.h filamvct.h filamzip.h
filter.h global.h ha_connect.h inihandl.h maputil.h msgid.h mycat.h myutil.h
os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h resource.h tabcol.h
tabdos.h tabfix.h tabfmt.h tabmul.h taboccur.h tabpivot.h tabsys.h
tabtbl.h tabutil.h tabvct.h tabvir.h tabxcl.h user_connect.h valblk.h value.h
xindex.h xobject.h xtable.h)
filter.h global.h ha_connect.h inihandl.h json.h maputil.h msgid.h mycat.h
myutil.h os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h resource.h
tabcol.h tabdos.h tabfix.h tabfmt.h tabjson.h tabmul.h taboccur.h tabpivot.h
tabsys.h tabtbl.h tabutil.h tabvct.h tabvir.h tabxcl.h user_connect.h
valblk.h value.h xindex.h xobject.h xtable.h)
#
# Definitions that are shared for all OSes
......
......@@ -103,6 +103,10 @@
#define MSG_FILE_MAP_ERROR "CreateFileMapping %s error rc=%d"
#define MSG_FILE_OPEN_YET "File %s already open"
#define MSG_FILE_UNFOUND "File %s not found"
#define MSG_FIX_OVFLW_ADD "Fixed Overflow on add"
#define MSG_FIX_OVFLW_TIMES "Fixed Overflow on times"
#define MSG_FIX_UNFLW_ADD "Fixed Underflow on add"
#define MSG_FIX_UNFLW_TIMES "Fixed Underflow on times"
#define MSG_FLD_TOO_LNG_FOR "Field %d too long for %s line %d of %s"
#define MSG_FLT_BAD_RESULT "Float inexact result"
#define MSG_FLT_DENORMAL_OP "Float denormal operand"
......@@ -318,3 +322,4 @@
#define MSG_XPATH_CNTX_ERR "Unable to create new XPath context"
#define MSG_XPATH_EVAL_ERR "Unable to evaluate xpath location '%s'"
#define MSG_XPATH_NOT_SUPP "Unsupported Xpath for column %s"
#define MSG_ZERO_DIVIDE "Zero divide in expression"
......@@ -26,6 +26,7 @@ class DllExport TXTFAM : public BLOCK {
friend class TDBCSV;
friend class TDBFIX;
friend class TDBVCT;
friend class TDBJSON;
friend class DOSCOL;
friend class BINCOL;
friend class VCTCOL;
......
......@@ -170,8 +170,8 @@
#define SZWMIN 4194304 // Minimum work area size 4M
extern "C" {
char version[]= "Version 1.03.0005 January 13, 2015";
char compver[]= "Version 1.03.0005 " __DATE__ " " __TIME__;
char version[]= "Version 1.03.0006 January 13, 2015";
char compver[]= "Version 1.03.0006 " __DATE__ " " __TIME__;
#if defined(WIN32)
char slash= '\\';
......@@ -3834,7 +3834,7 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
case TAB_XML:
case TAB_INI:
case TAB_VEC:
// case TAB_JSON:
case TAB_JSON:
if (options->filename && *options->filename) {
char *s, path[FN_REFLEN], dbpath[FN_REFLEN];
#if defined(WIN32)
......
/*************** json CPP Declares Source Code File (.H) ***************/
/* Name: json.cpp Version 1.0 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
/* This file contains the JSON classes functions. */
/***********************************************************************/
/***********************************************************************/
/* 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. */
/* xjson.h is header containing the JSON classes declarations. */
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
#include "json.h"
#define ARGS MY_MIN(24,len-i),s+MY_MAX(i-3,0)
#if defined(WIN32)
#define EL "\r\n"
#else
#define EL "\n"
#endif
/***********************************************************************/
/* Parse a json string. */
/***********************************************************************/
PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
{
int i;
bool b = false;
PJSON jsp = NULL;
STRG src;
if (!s || !len) {
strcpy(g->Message, "Void JSON object");
return NULL;
} else if (comma)
*comma = false;
src.str = s;
src.len = len;
for (i = 0; i < len; i++)
switch (s[i]) {
case '[':
if (jsp) {
strcpy(g->Message, "More than one item in file");
return NULL;
} else if (!(jsp = ParseArray(g, ++i, src)))
return NULL;
break;
case '{':
if (jsp) {
strcpy(g->Message, "More than one item in file");
return NULL;
} else if (!(jsp = ParseObject(g, ++i, src)))
return NULL;
break;
case ' ':
case '\t':
case '\n':
case '\r':
break;
case ',':
if (jsp && pretty == 1) {
if (comma)
*comma = true;
break;
} // endif pretty
sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty);
return NULL;
case '(':
b = true;
break;
case ')':
if (b) {
b = false;
break;
} // endif b
default:
sprintf(g->Message, "Bad '%c' character near %.*s",
s[i], ARGS);
return NULL;
}; // endswitch s[i]
if (!jsp)
sprintf(g->Message, "Invalid Json string '%.*s'", 50, s);
return jsp;
} // end of ParseJson
/***********************************************************************/
/* Parse a JSON Array. */
/***********************************************************************/
PJAR ParseArray(PGLOBAL g, int& i, STRG& src)
{
char *s = src.str;
int len = src.len;
int level = 0;
PJAR jarp = new(g) JARRAY;
PJVAL jvp = NULL;
for (; i < len; i++)
switch (s[i]) {
case ',':
if (level < 2) {
sprintf(g->Message, "Unexpected ',' near %.*s",ARGS);
return NULL;
} else
level = 1;
break;
case ']':
if (level == 1) {
sprintf(g->Message, "Unexpected ',]' near %.*s", ARGS);
return NULL;
} // endif level
jarp->InitArray(g);
return jarp;
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
if (level == 2) {
sprintf(g->Message, "Unexpected value near %.*s", ARGS);
return NULL;
} else if ((jvp = ParseValue(g, i, src))) {
jarp->AddValue(g, jvp);
level = 2;
} else
return NULL;
level = 2;
break;
}; // endswitch s[i]
strcpy(g->Message, "Unexpected EOF in array");
return NULL;
} // end of ParseArray
/***********************************************************************/
/* Parse a JSON Object. */
/***********************************************************************/
PJOB ParseObject(PGLOBAL g, int& i, STRG& src)
{
PSZ key;
char *s = src.str;
int len = src.len;
int level = 0;
PJOB jobp = new(g) JOBJECT;
PJPR jpp = NULL;
for (; i < len; i++)
switch (s[i]) {
case '"':
if (level < 2) {
if ((key = ParseString(g, ++i, src))) {
jpp = jobp->AddPair(g, key);
level = 1;
} else
return NULL;
} else {
sprintf(g->Message, "misplaced string near %.*s", ARGS);
return NULL;
} // endif level
break;
case ':':
if (level == 1) {
if (!(jpp->Val = ParseValue(g, ++i, src)))
return NULL;
level = 2;
} else {
sprintf(g->Message, "Unexpected ':' near %.*s", ARGS);
return NULL;
} // endif level
break;
case ',':
if (level < 2) {
sprintf(g->Message, "Unexpected ',' near %.*s", ARGS);
return NULL;
} else
level = 1;
break;
case '}':
if (level == 1) {
sprintf(g->Message, "Unexpected '}' near %.*s", ARGS);
return NULL;
} // endif level
return jobp;
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
sprintf(g->Message, "Unexpected character '%c' near %.*s",
s[i], ARGS);
return NULL;
}; // endswitch s[i]
strcpy(g->Message, "Unexpected EOF in Object");
return NULL;
} // end of ParseObject
/***********************************************************************/
/* Parse a JSON Value. */
/***********************************************************************/
PJVAL ParseValue(PGLOBAL g, int& i, STRG& src)
{
char *strval, *s = src.str;
int n, len = src.len;
PJVAL jvp = new(g) JVALUE;
for (; i < len; i++)
switch (s[i]) {
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
goto suite;
} // endswitch
suite:
switch (s[i]) {
case '[':
if (!(jvp->Jsp = ParseArray(g, ++i, src)))
return NULL;
break;
case '{':
if (!(jvp->Jsp = ParseObject(g, ++i, src)))
return NULL;
break;
case '"':
if ((strval = ParseString(g, ++i, src)))
jvp->Value = AllocateValue(g, strval, TYPE_STRING);
else
return NULL;
break;
case 't':
if (!strncmp(s + i, "true", 4)) {
n = 1;
jvp->Value = AllocateValue(g, &n, TYPE_TINY);
i += 3;
} else
goto err;
break;
case 'f':
if (!strncmp(s + i, "false", 5)) {
n = 0;
jvp->Value = AllocateValue(g, &n, TYPE_TINY);
i += 4;
} else
goto err;
break;
case 'n':
if (!strncmp(s + i, "null", 4))
i += 3;
else
goto err;
break;
case '-':
default:
if (s[i] == '-' || isdigit(s[i])) {
if (!(jvp->Value = ParseNumeric(g, i, src)))
goto err;
} else
goto err;
}; // endswitch s[i]
jvp->Size = 1;
return jvp;
err:
sprintf(g->Message, "Unexpected character '%c' near %.*s",
s[i], ARGS);
return NULL;
} // end of ParseValue
/***********************************************************************/
/* Unescape and parse a JSON string. */
/***********************************************************************/
char *ParseString(PGLOBAL g, int& i, STRG& src)
{
char *p, *s = src.str;
int n = 0, len = src.len;
// The size to allocate is not known yet
p = (char*)PlugSubAlloc(g, NULL, 0);
for (; i < len; i++)
switch (s[i]) {
case '"':
p[n++] = 0;
PlugSubAlloc(g, NULL, n);
return p;
case '\\':
if (++i < len) {
if (s[i] == 'u') {
if (len - i > 5) {
// if (charset == utf8) {
char xs[5];
UINT hex;
xs[0] = s[++i];
xs[1] = s[++i];
xs[2] = s[++i];
xs[3] = s[++i];
xs[4] = 0;
hex = strtoul(xs, NULL, 16);
if (hex < 0x80) {
p[n] = (UCHAR)hex;
} else if (hex < 0x800) {
p[n++] = (UCHAR)(0xC0 | (hex >> 6));
p[n] = (UCHAR)(0x80 | (hex & 0x3F));
} else if (hex < 0x10000) {
p[n++] = (UCHAR)(0xE0 | (hex >> 12));
p[n++] = (UCHAR)(0x80 | ((hex >> 6) & 0x3f));
p[n] = (UCHAR)(0x80 | (hex & 0x3f));
} else
p[n] = '?';
#if 0
} else {
char xs[3];
UINT hex;
i += 2;
xs[0] = s[++i];
xs[1] = s[++i];
xs[2] = 0;
hex = strtoul(xs, NULL, 16);
p[n] = (char)hex;
} // endif charset
#endif // 0
} else
goto err;
} else switch(s[i]) {
case 't': p[n] = '\t'; break;
case 'n': p[n] = '\n'; break;
case 'r': p[n] = '\r'; break;
case 'b': p[n] = '\b'; break;
case 'f': p[n] = '\f'; break;
default: p[n] = s[i]; break;
} // endswitch
n++;
} else
goto err;
break;
default:
p[n++] = s[i];
break;
}; // endswitch s[i]
err:
strcpy(g->Message, "Unexpected EOF in String");
return NULL;
} // end of ParseString
/***********************************************************************/
/* Parse a JSON numeric value. */
/***********************************************************************/
PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src)
{
char *s = src.str, buf[50];
int n = 0, len = src.len;
short nd = 0;
bool has_dot = false;
bool has_e = false;
bool found_digit = false;
PVAL valp = NULL;
for (; i < len; i++) {
switch (s[i]) {
case '.':
if (!found_digit || has_dot || has_e)
goto err;
has_dot = true;
break;
case 'e':
case 'E':
if (!found_digit || has_e)
goto err;
has_e = true;
found_digit = false;
break;
case '+':
if (!has_e)
goto err;
// passthru
case '-':
if (found_digit)
goto err;
break;
default:
if (isdigit(s[i])) {
if (has_dot && !has_e)
nd++; // Number of decimals
found_digit = true;
} else
goto fin;
}; // endswitch s[i]
buf[n++] = s[i];
} // endfor i
fin:
if (found_digit) {
buf[n] = 0;
if (has_dot || has_e) {
double dv = strtod(buf, NULL);
valp = AllocateValue(g, &dv, TYPE_DOUBLE, nd);
} else {
int iv = strtol(buf, NULL, 10);
valp = AllocateValue(g, &iv, TYPE_INT);
} // endif has
i--; // Unstack following character
return valp;
} else {
strcpy(g->Message, "No digit found");
return NULL;
} // endif found_digit
err:
strcpy(g->Message, "Unexpected EOF in number");
return NULL;
} // end of ParseNumeric
/***********************************************************************/
/* Serialize a JSON tree: */
/***********************************************************************/
PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty)
{
bool b = false, err = true;
JOUT *jp;
g->Message[0] = 0;
if (!jsp) {
strcpy(g->Message, "Null json tree");
return NULL;
} else if (!fs) {
// Serialize to a string
jp = new(g) JOUTSTR(g);
b = pretty == 1;
} else if (pretty == 2) {
// Serialize to a pretty file
jp = new(g) JOUTPRT(g, fs);
} else {
// Serialize to a flat file
jp = new(g) JOUTFILE(g, fs);
b = pretty == 1;
} // endif's
switch (jsp->GetType()) {
case TYPE_JAR:
err = SerializeArray(jp, (PJAR)jsp, b);
break;
case TYPE_JOB:
err = (b && jp->Write('\t'));
err |= SerializeObject(jp, (PJOB)jsp);
break;
default:
strcpy(g->Message, "json tree is not an Array or an Object");
} // endswitch Type
if (fs) {
fputc('\n', fs);
fclose(fs);
return (err) ? g->Message : NULL;
} else if (!err) {
PSZ str = ((JOUTSTR*)jp)->Strp;
jp->Write('\0');
PlugSubAlloc(g, NULL, ((JOUTSTR*)jp)->N);
return str;
} else {
if (!g->Message[0])
strcpy(g->Message, "Error in Serialize");
return NULL;
} // endif's
} // end of Serialize
/***********************************************************************/
/* Serialize a JSON Array. */
/***********************************************************************/
bool SerializeArray(JOUT *js, PJAR jarp, bool b)
{
bool first = true;
if (js->Write('['))
return true;
else if (b && (js->Write(EL) || js->Write('\t')))
return true;
for (int i = 0; i < jarp->size(); i++) {
if (first)
first = false;
else if (js->Write(','))
return true;
else if (b && (js->Write(EL) || js->Write('\t')))
return true;
if (SerializeValue(js, jarp->GetValue(i)))
return true;
} // endfor i
if (b && js->Write(EL))
return true;
return js->Write(']');
} // end of SerializeArray
/***********************************************************************/
/* Serialize a JSON Object. */
/***********************************************************************/
bool SerializeObject(JOUT *js, PJOB jobp)
{
bool first = true;
if (js->Write('{'))
return true;
for (PJPR pair = jobp->First; pair; pair = pair->Next) {
if (first)
first = false;
else if (js->Write(','))
return true;
if (js->Write("\"") ||
js->Write(pair->Key) ||
js->Write("\"") ||
js->Write(':') ||
SerializeValue(js, pair->Val))
return true;
} // endfor i
return js->Write('}');
} // end of SerializeObject
/***********************************************************************/
/* Serialize a JSON Value. */
/***********************************************************************/
bool SerializeValue(JOUT *js, PJVAL jvp)
{
PJAR jap;
PJOB jop;
PVAL valp;
if ((jap = jvp->GetArray()))
return SerializeArray(js, jap, false);
else if ((jop = jvp->GetObject()))
return SerializeObject(js, jop);
else if (!(valp = jvp->Value) || valp->IsNull())
return js->Write("null");
else switch (valp->GetType()) {
case TYPE_TINY:
return js->Write(valp->GetTinyValue() ? "true" : "false");
case TYPE_STRING:
return js->Escape(valp->GetCharValue());
default:
if (valp->IsTypeNum()) {
char buf[32];
return js->Write(valp->GetCharString(buf));
} // endif valp
} // endswitch Type
strcpy(js->g->Message, "Unrecognized value");
return true;
} // end of SerializeValue
/* -------------------------- Class JOUTSTR -------------------------- */
/***********************************************************************/
/* JOUTSTR constructor. */
/***********************************************************************/
JOUTSTR::JOUTSTR(PGLOBAL g) : JOUT(g)
{
PPOOLHEADER pph = (PPOOLHEADER)g->Sarea;
N = 0;
Max = pph->FreeBlk;
Max = (Max > 512) ? Max - 512 : Max;
Strp = (char*)PlugSubAlloc(g, NULL, 0); // Size not know yet
} // end of JOUTSTR constructor
/***********************************************************************/
/* Concatenate a string to the Serialize string. */
/***********************************************************************/
bool JOUTSTR::Write(char *s)
{
if (s) {
size_t len = strlen(s);
if (N + len > Max)
return true;
memcpy(Strp + N, s, len);
N += len;
return false;
} else
return true;
} // end of Write
/***********************************************************************/
/* Concatenate a character to the Serialize string. */
/***********************************************************************/
bool JOUTSTR::Write(char c)
{
if (N + 1 > Max)
return true;
Strp[N++] = c;
return false;
} // end of Write
/***********************************************************************/
/* Escape and Concatenate a string to the Serialize string. */
/***********************************************************************/
bool JOUTSTR::Escape(char *s)
{
Write('"');
for (unsigned int i = 0; i < strlen(s); i++)
switch (s[i]) {
case '\t': Write("\\t"); break;
case '\n': Write("\\t"); break;
case '\r': Write("\\t"); break;
case '\b': Write("\\t"); break;
case '\f': Write("\\t"); break;
case '"': Write('\\');
// passthru
default:
Write(s[i]);
break;
} // endswitch s[i]
Write('"');
return false;
} // end of Escape
/* ------------------------- Class JOUTFILE -------------------------- */
/***********************************************************************/
/* Write a string to the Serialize file. */
/***********************************************************************/
bool JOUTFILE::Write(char *s)
{
// This is temporary
fputs(s, Stream);
return false;
} // end of Write
/***********************************************************************/
/* Write a character to the Serialize file. */
/***********************************************************************/
bool JOUTFILE::Write(char c)
{
// This is temporary
fputc(c, Stream);
return false;
} // end of Write
/***********************************************************************/
/* Escape and Concatenate a string to the Serialize string. */
/***********************************************************************/
bool JOUTFILE::Escape(char *s)
{
// This is temporary
fputc('"', Stream);
for (unsigned int i = 0; i < strlen(s); i++)
switch (s[i]) {
case '\t': fputs("\\t", Stream); break;
case '\n': fputs("\\n", Stream); break;
case '\r': fputs("\\r", Stream); break;
case '\b': fputs("\\b", Stream); break;
case '\f': fputs("\\f", Stream); break;
case '"': fputs("\\\"", Stream); break;
default:
fputc(s[i], Stream);
break;
} // endswitch s[i]
fputc('"', Stream);
return false;
} // end of Escape
/* ------------------------- Class JOUTPRT --------------------------- */
/***********************************************************************/
/* Write a string to the Serialize pretty file. */
/***********************************************************************/
bool JOUTPRT::Write(char *s)
{
// This is temporary
if (B) {
fputs(EL, Stream);
M--;
for (int i = 0; i < M; i++)
fputc('\t', Stream);
B = false;
} // endif B
fputs(s, Stream);
return false;
} // end of Write
/***********************************************************************/
/* Write a character to the Serialize pretty file. */
/***********************************************************************/
bool JOUTPRT::Write(char c)
{
switch (c) {
case ':':
fputs(": ", Stream);
break;
case '{':
case '[':
#if 0
if (M)
fputs(EL, Stream);
for (int i = 0; i < M; i++)
fputc('\t', Stream);
#endif // 0
fputc(c, Stream);
fputs(EL, Stream);
M++;
for (int i = 0; i < M; i++)
fputc('\t', Stream);
break;
case '}':
case ']':
M--;
fputs(EL, Stream);
for (int i = 0; i < M; i++)
fputc('\t', Stream);
fputc(c, Stream);
B = true;
break;
case ',':
fputc(c, Stream);
fputs(EL, Stream);
for (int i = 0; i < M; i++)
fputc('\t', Stream);
B = false;
break;
default:
fputc(c, Stream);
} // endswitch c
return false;
} // end of Write
/* -------------------------- Class JOBJECT -------------------------- */
/***********************************************************************/
/* Add a new pair to an Object. */
/***********************************************************************/
PJPR JOBJECT::AddPair(PGLOBAL g, PSZ key)
{
PJPR jpp = new(g) JPAIR(key);
if (Last)
Last->Next = jpp;
else
First = jpp;
Last = jpp;
Size++;
return jpp;
} // end of AddPair
/***********************************************************************/
/* Get the value corresponding to the given key. */
/***********************************************************************/
PJVAL JOBJECT::GetValue(const char* key)
{
for (PJPR jp = First; jp; jp = jp->Next)
if (!strcmp(jp->Key, key))
return jp->Val;
return NULL;
} // end of GetValue;
/***********************************************************************/
/* Return the text corresponding to all keys (XML like). */
/***********************************************************************/
PSZ JOBJECT::GetText(PGLOBAL g)
{
char *p, *text = (char*)PlugSubAlloc(g, NULL, 0);
bool b = true;
if (!First)
return NULL;
else for (PJPR jp = First; jp; jp = jp->Next) {
if (!(p = jp->Val->GetString()))
p = "???";
if (b) {
strcpy(text, p);
b = false;
} else
strcat(strcat(text, " "), p);
} // endfor jp
PlugSubAlloc(g, NULL, strlen(text) + 1);
return text;
} // end of GetValue;
/***********************************************************************/
/* Set or add a value corresponding to the given key. */
/***********************************************************************/
void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key)
{
PJPR jp;
for (jp = First; jp; jp = jp->Next)
if (!strcmp(jp->Key, key)) {
jp->Val = jvp;
break;
} // endif key
if (!jp) {
jp = AddPair(g, key);
jp->Val = jvp;
} // endif jp
} // end of SetValue
/* -------------------------- Class JARRAY --------------------------- */
/***********************************************************************/
/* Make the array of values from the values list. */
/***********************************************************************/
void JARRAY::InitArray(PGLOBAL g)
{
int i;
PJVAL jvp;
for (Size = 0, jvp = First; jvp; jvp = jvp->Next)
if (!jvp->Del)
Size++;
if (!Size) {
return;
} else if (Size > Alloc) {
// No need to realloc after deleting values
Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL));
Alloc = Size;
} // endif Size
for (i = 0, jvp = First; jvp; jvp = jvp->Next)
if (!jvp->Del)
Mvals[i++] = jvp;
} // end of InitArray
/***********************************************************************/
/* Get the Nth value of an Array. */
/***********************************************************************/
PJVAL JARRAY::GetValue(int i)
{
if (Mvals && i >= 0 && i < Size)
return Mvals[i];
else
return NULL;
} // end of GetValue
/***********************************************************************/
/* Add a Value to the Arrays Value list. */
/***********************************************************************/
PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp)
{
if (!jvp)
jvp = new(g) JVALUE;
if (Last)
Last->Next = jvp;
else
First = jvp;
Last = jvp;
return jvp;
} // end of AddValue
/***********************************************************************/
/* Add a Value to the Arrays Value list. */
/***********************************************************************/
bool JARRAY::SetValue(PGLOBAL g, PJVAL jvp, int n)
{
int i = 0;
PJVAL jp, *jpp = &First;
for (i = 0, jp = First; i < n; i++, jp = *(jpp = &jp->Next))
if (!jp)
*jpp = jp = new(g) JVALUE;
*jpp = jvp;
jvp->Next = (jp ? jp->Next : NULL);
return false;
} // end of SetValue
/***********************************************************************/
/* Delete a Value from the Arrays Value list. */
/***********************************************************************/
bool JARRAY::DeleteValue(int n)
{
PJVAL jvp = GetValue(n);
if (jvp) {
jvp->Del = true;
return false;
} else
return true;
} // end of DeleteValue
/* -------------------------- Class JVALUE- -------------------------- */
/***********************************************************************/
/* Constructor for a Value with a given string or numeric value. */
/***********************************************************************/
JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON()
{
Jsp = NULL;
Value = AllocateValue(g, valp);
Next = NULL;
Del = false;
} // end of JVALUE constructor
/***********************************************************************/
/* Returns the type of the Value's value. */
/***********************************************************************/
JTYP JVALUE::GetValType(void)
{
if (Jsp)
return Jsp->GetType();
else if (Value)
return (JTYP)Value->GetType();
else
return (JTYP)TYPE_VOID;
} // end of GetValType
/***********************************************************************/
/* Return the Value's Object value. */
/***********************************************************************/
PJOB JVALUE::GetObject(void)
{
if (Jsp && Jsp->GetType() == TYPE_JOB)
return (PJOB)Jsp;
return NULL;
} // end of GetObject
/***********************************************************************/
/* Return the Value's Array value. */
/***********************************************************************/
PJAR JVALUE::GetArray(void)
{
if (Jsp && Jsp->GetType() == TYPE_JAR)
return (PJAR)Jsp;
return NULL;
} // end of GetArray
/***********************************************************************/
/* Return the Value's Integer value. */
/***********************************************************************/
int JVALUE::GetInteger(void)
{
return (Value) ? Value->GetIntValue() : 0;
} // end of GetInteger
/***********************************************************************/
/* Return the Value's Double value. */
/***********************************************************************/
double JVALUE::GetFloat(void)
{
return (Value) ? Value->GetFloatValue() : 0.0;
} // end of GetFloat
/***********************************************************************/
/* Return the Value's String value. */
/***********************************************************************/
PSZ JVALUE::GetString(void)
{
char buf[32];
return (Value) ? Value->GetCharString(buf) : NULL;
} // end of GetString
/**************** json H Declares Source Code File (.H) ****************/
/* Name: json.h Version 1.0 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
/* This file contains the JSON classes declares. */
/***********************************************************************/
#include "value.h"
#if defined(_DEBUG)
#define X assert(false);
#else
#define X
#endif
enum JTYP {TYPE_JSON = 12, TYPE_JAR, TYPE_JOB, TYPE_JVAL};
class JOUT;
class JSON;
class JMAP;
class JVALUE;
class JOBJECT;
class JARRAY;
typedef class JPAIR *PJPR;
typedef class JSON *PJSON;
typedef class JVALUE *PJVAL;
typedef class JOBJECT *PJOB;
typedef class JARRAY *PJAR;
typedef struct {
char *str;
int len;
} STRG, *PSG;
PJSON ParseJson(PGLOBAL g, char *s, int n, int prty, bool *b = NULL);
PJAR ParseArray(PGLOBAL g, int& i, STRG& src);
PJOB ParseObject(PGLOBAL g, int& i, STRG& src);
PJVAL ParseValue(PGLOBAL g, int& i, STRG& src);
char *ParseString(PGLOBAL g, int& i, STRG& src);
PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src);
PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty);
bool SerializeArray(JOUT *js, PJAR jarp, bool b);
bool SerializeObject(JOUT *js, PJOB jobp);
bool SerializeValue(JOUT *js, PJVAL jvp);
/***********************************************************************/
/* Class JOUT. Used by Serialize. */
/***********************************************************************/
class JOUT : public BLOCK {
public:
JOUT(PGLOBAL gp) : BLOCK() {g = gp;}
virtual bool Write(char *s) = 0;
virtual bool Write(char c) = 0;
virtual bool Escape(char *s) = 0;
// Member
PGLOBAL g;
}; // end of class JOUT
/***********************************************************************/
/* Class JOUTSTR. Used to Serialize to a string. */
/***********************************************************************/
class JOUTSTR : public JOUT {
public:
JOUTSTR(PGLOBAL g);
virtual bool Write(char *s);
virtual bool Write(char c);
virtual bool Escape(char *s);
// Member
char *Strp; // The serialized string
size_t N; // Position of next char
size_t Max; // String max size
}; // end of class JOUTSTR
/***********************************************************************/
/* Class JOUTFILE. Used to Serialize to a file. */
/***********************************************************************/
class JOUTFILE : public JOUT {
public:
JOUTFILE(PGLOBAL g, FILE *str) : JOUT(g) {Stream = str;}
virtual bool Write(char *s);
virtual bool Write(char c);
virtual bool Escape(char *s);
// Member
FILE *Stream;
}; // end of class JOUTFILE
/***********************************************************************/
/* Class JOUTPRT. Used to Serialize to a pretty file. */
/***********************************************************************/
class JOUTPRT : public JOUTFILE {
public:
JOUTPRT(PGLOBAL g, FILE *str) : JOUTFILE(g, str) {M = 0; B = false;}
virtual bool Write(char *s);
virtual bool Write(char c);
// Member
int M;
bool B;
}; // end of class JOUTPRT
/***********************************************************************/
/* Class PAIR. The pairs of a json Object. */
/***********************************************************************/
class JPAIR : public BLOCK {
friend class JOBJECT;
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
public:
JPAIR(PSZ key) : BLOCK() {Key = key; Val = NULL; Next = NULL;}
protected:
PSZ Key; // This pair key name
PJVAL Val; // To the value of the pair
PJPR Next; // To the next pair
}; // end of class JPAIR
/***********************************************************************/
/* Class JSON. The base class for all other json classes. */
/***********************************************************************/
class JSON : public BLOCK {
public:
JSON(void) {Size = 0;}
int size(void) {return Size;}
virtual void Clear(void) {Size = 0;}
virtual JTYP GetType(void) {return TYPE_JSON;}
virtual JTYP GetValType(void) {X return TYPE_JSON;}
virtual void InitArray(PGLOBAL g) {X}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL) {X return NULL;}
virtual PJPR AddPair(PGLOBAL g, PSZ key) {X return NULL;}
virtual PJVAL GetValue(const char *key) {X return NULL;}
virtual PJOB GetObject(void) {X return NULL;}
virtual PJAR GetArray(void) {X return NULL;}
virtual PJVAL GetValue(int i) {X return NULL;}
virtual PVAL GetValue(void) {X return NULL;}
virtual PJSON GetJson(void) {X return NULL;}
virtual int GetInteger(void) {X return 0;}
virtual double GetFloat() {X return 0.0;}
virtual PSZ GetString() {X return NULL;}
virtual PSZ GetText(PGLOBAL g) {X return NULL;}
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i) {X return true;}
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key) {X}
virtual void SetValue(PVAL valp) {X}
virtual void SetValue(PJSON jsp) {X}
virtual bool DeleteValue(int i) {X return true;}
protected:
int Size;
}; // end of class JSON
/***********************************************************************/
/* Class JOBJECT: contains a list of value pairs. */
/***********************************************************************/
class JOBJECT : public JSON {
friend PJOB ParseObject(PGLOBAL, int&, STRG&);
friend bool SerializeObject(JOUT *, PJOB);
public:
JOBJECT(void) : JSON() {First = Last = NULL;}
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JOB;}
virtual PJPR AddPair(PGLOBAL g, PSZ key);
virtual PJOB GetObject(void) {return this;}
virtual PJVAL GetValue(const char* key);
virtual PSZ GetText(PGLOBAL g);
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key);
protected:
PJPR First;
PJPR Last;
}; // end of class JOBJECT
/***********************************************************************/
/* Class JARRAY. */
/***********************************************************************/
class JARRAY : public JSON {
friend PJAR ParseArray(PGLOBAL, int&, STRG&);
public:
JARRAY(void) : JSON() {Alloc = 0; First = Last = NULL; Mvals = NULL;}
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JAR;}
virtual PJAR GetArray(void) {return this;}
virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL);
virtual void InitArray(PGLOBAL g);
virtual PJVAL GetValue(int i);
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i);
virtual bool DeleteValue(int n);
protected:
// Members
int Alloc; // The Mvals allocated size
PJVAL First; // Used when constructing
PJVAL Last; // Last constructed value
PJVAL *Mvals; // Allocated when finished
}; // end of class JARRAY
/***********************************************************************/
/* Class JVALUE. */
/***********************************************************************/
class JVALUE : public JSON {
friend class JARRAY;
friend PJVAL ParseValue(PGLOBAL, int&, STRG&);
friend bool SerializeValue(JOUT *, PJVAL);
public:
JVALUE(void) : JSON()
{Jsp = NULL; Value = NULL; Next = NULL; Del = false;}
JVALUE(PJSON jsp) : JSON()
{Jsp = jsp; Value = NULL; Next = NULL; Del = false;}
JVALUE(PGLOBAL g, PVAL valp);
virtual void Clear(void)
{Jsp = NULL; Value = NULL; Next = NULL; Del = false; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JVAL;}
virtual JTYP GetValType(void);
virtual PJOB GetObject(void);
virtual PJAR GetArray(void);
virtual PVAL GetValue(void) {return Value;}
virtual PJSON GetJson(void) {return (Jsp ? Jsp : this);}
virtual int GetInteger(void);
virtual double GetFloat(void);
virtual PSZ GetString(void);
virtual void SetValue(PVAL valp) {Value = valp;}
virtual void SetValue(PJSON jsp) {Jsp = jsp;}
protected:
PJSON Jsp; // To the json value
PVAL Value; // The numeric value
PJVAL Next; // Next value in array
bool Del; // True when deleted
}; // end of class JVALUE
......@@ -318,3 +318,8 @@
#define MSG_XPATH_CNTX_ERR 517
#define MSG_XPATH_EVAL_ERR 518
#define MSG_XPATH_NOT_SUPP 519
#define MSG_ZERO_DIVIDE 520
#define MSG_FIX_OVFLW_ADD 521
#define MSG_FIX_OVFLW_TIMES 522
#define MSG_FIX_UNFLW_ADD 523
#define MSG_FIX_UNFLW_TIMES 524
......@@ -89,7 +89,7 @@
#include "tabpivot.h"
#endif // PIVOT_SUPPORT
#include "tabvir.h"
//#include "tabjson.h"
#include "tabjson.h"
#include "ha_connect.h"
#include "mycat.h"
......@@ -140,7 +140,7 @@ TABTYPE GetTypeID(const char *type)
: (!stricmp(type, "PIVOT")) ? TAB_PIVOT
#endif
: (!stricmp(type, "VIR")) ? TAB_VIR
// : (!stricmp(type, "JSON")) ? TAB_JSON
: (!stricmp(type, "JSON")) ? TAB_JSON
: (!stricmp(type, "OEM")) ? TAB_OEM : TAB_NIY;
} // end of GetTypeID
......@@ -161,7 +161,7 @@ bool IsFileType(TABTYPE type)
case TAB_XML:
case TAB_INI:
case TAB_VEC:
// case TAB_JSON:
case TAB_JSON:
isfile= true;
break;
default:
......@@ -254,6 +254,7 @@ bool IsTypeIndexable(TABTYPE type)
case TAB_BIN:
case TAB_VEC:
case TAB_DBF:
case TAB_JSON:
idx= true;
break;
default:
......@@ -279,6 +280,7 @@ int GetIndexType(TABTYPE type)
case TAB_BIN:
case TAB_VEC:
case TAB_DBF:
case TAB_JSON:
xtyp= 1;
break;
case TAB_MYSQL:
......@@ -542,7 +544,7 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, LPCSTR name, LPCSTR am)
case TAB_PIVOT: tdp= new(g) PIVOTDEF; break;
#endif // PIVOT_SUPPORT
case TAB_VIR: tdp= new(g) VIRDEF; break;
// case TAB_JSON: tdp= new(g) JSONDEF; break;
case TAB_JSON: tdp= new(g) JSONDEF; break;
default:
sprintf(g->Message, MSG(BAD_TABLE_TYPE), am, name);
} // endswitch
......
#
# Testing doc samples
#
CREATE TABLE t1
(
ISBN CHAR(15),
LANG CHAR(2),
SUBJECT CHAR(32),
AUTHOR CHAR(64),
TITLE CHAR(32),
TRANSLATION CHAR(32),
TRANSLATOR CHAR(80),
PUBLISHER CHAR(32),
DATEPUB int(4)
) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
ISBN LANG SUBJECT AUTHOR TITLE TRANSLATION TRANSLATOR PUBLISHER DATEPUB
9782212090819 fr applications Jean-Christophe Bernadac Construire une application XML Eyrolles Paris 1999
9782840825685 fr applications William J. Pardi XML en Action adapt de l'anglais par James Guerin Microsoft Press Paris 1999
DROP TABLE t1;
#
# Testing Jpath. Get the number of authors
#
CREATE TABLE t1
(
ISBN CHAR(15),
Language CHAR(2) FIELD_FORMAT='LANG',
Subject CHAR(32) FIELD_FORMAT='SUBJECT',
Authors INT(2) FIELD_FORMAT='AUTHOR:[#]',
Title CHAR(32) FIELD_FORMAT='TITLE',
Translation CHAR(32) FIELD_FORMAT='TRANSLATION',
Translator CHAR(80) FIELD_FORMAT='TRANSLATOR',
Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
Year int(4) FIELD_FORMAT='DATEPUB'
)
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
ISBN Language Subject Authors Title Translation Translator Publisher Location Year
9782212090819 fr applications 2 Construire une application XML Eyrolles Paris 1999
9782840825685 fr applications 1 XML en Action adapt de l'anglais par James Guerin Microsoft Press Paris 1999
DROP TABLE t1;
#
# Concatenates the authors
#
CREATE TABLE t1
(
ISBN CHAR(15),
Language CHAR(2) FIELD_FORMAT='LANG',
Subject CHAR(32) FIELD_FORMAT='SUBJECT',
AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[" and "]:FIRSTNAME',
AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[" and "]:LASTNAME',
Title CHAR(32) FIELD_FORMAT='TITLE',
Translation CHAR(32) FIELD_FORMAT='TRANSLATION',
Translator CHAR(80) FIELD_FORMAT='TRANSLATOR',
Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
Year int(4) FIELD_FORMAT='DATEPUB'
)
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
ISBN Language Subject AuthorFN AuthorLN Title Translation Translator Publisher Location Year
9782212090819 fr applications Jean-Christophe and Franois Bernadac and Knab Construire une application XML Eyrolles Paris 1999
9782840825685 fr applications William J. Pardi XML en Action adapt de l'anglais par James Guerin Microsoft Press Paris 1999
DROP TABLE t1;
#
# Testing expanding authors
#
CREATE TABLE t1
(
ISBN CHAR(15),
Language CHAR(2) FIELD_FORMAT='LANG',
Subject CHAR(32) FIELD_FORMAT='SUBJECT',
AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:FIRSTNAME',
AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:LASTNAME',
Title CHAR(32) FIELD_FORMAT='TITLE',
Translation CHAR(32) FIELD_FORMAT='TRANSLATION',
Translator CHAR(80) FIELD_FORMAT='TRANSLATOR',
Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
Year int(4) FIELD_FORMAT='DATEPUB'
)
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
ISBN Language Subject AuthorFN AuthorLN Title Translation Translator Publisher Location Year
9782212090819 fr applications Jean-Christophe Bernadac Construire une application XML Eyrolles Paris 1999
9782212090819 fr applications Franois Knab Construire une application XML Eyrolles Paris 1999
9782840825685 fr applications William J. Pardi XML en Action adapt de l'anglais par James Guerin Microsoft Press Paris 1999
UPDATE t1 SET AuthorFN = 'Philippe' WHERE AuthorLN = 'Knab';
SELECT * FROM t1 WHERE ISBN = '9782212090819';
ISBN Language Subject AuthorFN AuthorLN Title Translation Translator Publisher Location Year
9782212090819 fr applications Jean-Christophe Bernadac Construire une application XML Eyrolles Paris 1999
9782212090819 fr applications Philippe Knab Construire une application XML Eyrolles Paris 1999
#
# To add an author a new table must be created
#
CREATE TABLE t2 (
FIRSTNAME CHAR(32),
LASTNAME CHAR(32))
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn' OPTION_LIST='Object=[2]:AUTHOR';
SELECT * FROM t2;
FIRSTNAME LASTNAME
William J. Pardi
INSERT INTO t2 VALUES('Charles','Dickens');
SELECT * FROM t1;
ISBN Language Subject AuthorFN AuthorLN Title Translation Translator Publisher Location Year
9782212090819 fr applications Jean-Christophe Bernadac Construire une application XML Eyrolles Paris 1999
9782212090819 fr applications Philippe Knab Construire une application XML Eyrolles Paris 1999
9782840825685 fr applications William J. Pardi XML en Action adapt de l'anglais par James Guerin Microsoft Press Paris 1999
9782840825685 fr applications Charles Dickens XML en Action adapt de l'anglais par James Guerin Microsoft Press Paris 1999
DROP TABLE t1;
DROP TABLE t2;
#
# Check the biblio file has the good format
#
CREATE TABLE t1
(
line char(255)
)
ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='biblio.jsn';
SELECT * FROM t1;
line
[
{
"ISBN": "9782212090819",
"LANG": "fr",
"SUBJECT": "applications",
"AUTHOR": [
{
"FIRSTNAME": "Jean-Christophe",
"LASTNAME": "Bernadac"
},
{
"FIRSTNAME": "Philippe",
"LASTNAME": "Knab"
}
],
"TITLE": "Construire une application XML",
"PUBLISHER": {
"NAME": "Eyrolles",
"PLACE": "Paris"
},
"DATEPUB": 1999
},
{
"ISBN": "9782840825685",
"LANG": "fr",
"SUBJECT": "applications",
"AUTHOR": [
{
"FIRSTNAME": "William J.",
"LASTNAME": "Pardi"
},
{
"FIRSTNAME": "Charles",
"LASTNAME": "Dickens"
}
],
"TITLE": "XML en Action",
"TRANSLATION": "adapt de l'anglais par",
"TRANSLATOR": {
"FIRSTNAME": "James",
"LASTNAME": "Guerin"
},
"PUBLISHER": {
"NAME": "Microsoft Press",
"PLACE": "Paris"
},
"DATEPUB": 1999
}
]
DROP TABLE t1;
#
# A file with 2 arrays
#
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[X]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK::EXPENSE:["+"]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK::EXPENSE:[+]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t1;
WHO WEEK WHAT AMOUNT
Joe 3 Beer+Food+Food+Car 69.00
Joe 4 Beer+Beer+Food+Food+Beer 83.00
Joe 5 Beer+Food 26.00
Beth 3 Beer 16.00
Beth 4 Food+Beer 32.00
Beth 5 Food+Beer 32.00
Janet 3 Car+Food+Beer 55.00
Janet 4 Car 17.00
Janet 5 Beer+Car+Beer+Food 57.00
DROP TABLE t1;
#
# Cannot be fully expanded
#
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[X]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[X]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[X]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t1;
ERROR HY000: Got error 174 'Cannot expand more than one array' from CONNECT
DROP TABLE t1;
#
# Expand expense in 3 one week tables
#
CREATE TABLE t2 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[1]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[1]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[1]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t2;
WHO WEEK WHAT AMOUNT
Joe 3 Beer 18.00
Joe 3 Food 12.00
Joe 3 Food 19.00
Joe 3 Car 20.00
Beth 3 Beer 16.00
Janet 3 Car 19.00
Janet 3 Food 18.00
Janet 3 Beer 18.00
CREATE TABLE t3 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[2]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[2]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[2]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t3;
WHO WEEK WHAT AMOUNT
Joe 4 Beer 19.00
Joe 4 Beer 16.00
Joe 4 Food 17.00
Joe 4 Food 17.00
Joe 4 Beer 14.00
Beth 4 Food 17.00
Beth 4 Beer 15.00
Janet 4 Car 17.00
CREATE TABLE t4 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[3]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[3]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[3]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t4;
WHO WEEK WHAT AMOUNT
Joe 5 Beer 14.00
Joe 5 Food 12.00
Beth 5 Food 12.00
Beth 5 Beer 20.00
Janet 5 Beer 14.00
Janet 5 Car 12.00
Janet 5 Beer 19.00
Janet 5 Food 12.00
#
# The expanded table is made as a TBL table
#
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32),
AMOUNT DOUBLE(8,2))
ENGINE=CONNECT TABLE_TYPE=TBL TABLE_LIST='t2,t3,t4';
SELECT * FROM t1;
WHO WEEK WHAT AMOUNT
Joe 3 Beer 18.00
Joe 3 Food 12.00
Joe 3 Food 19.00
Joe 3 Car 20.00
Beth 3 Beer 16.00
Janet 3 Car 19.00
Janet 3 Food 18.00
Janet 3 Beer 18.00
Joe 4 Beer 19.00
Joe 4 Beer 16.00
Joe 4 Food 17.00
Joe 4 Food 17.00
Joe 4 Beer 14.00
Beth 4 Food 17.00
Beth 4 Beer 15.00
Janet 4 Car 17.00
Joe 5 Beer 14.00
Joe 5 Food 12.00
Beth 5 Food 12.00
Beth 5 Beer 20.00
Janet 5 Beer 14.00
Janet 5 Car 12.00
Janet 5 Beer 19.00
Janet 5 Food 12.00
DROP TABLE t1, t2, t3, t4;
#
# Three partial JSON tables
#
CREATE TABLE t2 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp3.jsn';
SELECT * FROM t2;
WHO WEEK WHAT AMOUNT
Joe 3 Beer 18.00
Joe 3 Food 12.00
Joe 3 Food 19.00
Joe 3 Car 20.00
Beth 3 Beer 16.00
Janet 3 Car 19.00
Janet 3 Food 18.00
Janet 3 Beer 18.00
CREATE TABLE t3 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp4.jsn';
SELECT * FROM t3;
WHO WEEK WHAT AMOUNT
Joe 4 Beer 19.00
Joe 4 Beer 16.00
Joe 4 Food 17.00
Joe 4 Food 17.00
Joe 4 Beer 14.00
Beth 4 Food 17.00
Beth 4 Beer 15.00
Janet 4 Car 17.00
CREATE TABLE t4 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp5.jsn';
SELECT * FROM t4;
WHO WEEK WHAT AMOUNT
Joe 5 Beer 14.00
Joe 5 Food 12.00
Beth 5 Food 12.00
Beth 5 Beer 20.00
Janet 5 Beer 14.00
Janet 5 Car 12.00
Janet 5 Beer 19.00
Janet 5 Food 12.00
#
# The complete table can be a multiple JSON table
#
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp*.jsn' MULTIPLE=1;
SELECT * FROM t1;
WHO WEEK WHAT AMOUNT
Joe 3 Beer 18.00
Joe 3 Food 12.00
Joe 3 Food 19.00
Joe 3 Car 20.00
Beth 3 Beer 16.00
Janet 3 Car 19.00
Janet 3 Food 18.00
Janet 3 Beer 18.00
Joe 4 Beer 19.00
Joe 4 Beer 16.00
Joe 4 Food 17.00
Joe 4 Food 17.00
Joe 4 Beer 14.00
Beth 4 Food 17.00
Beth 4 Beer 15.00
Janet 4 Car 17.00
Joe 5 Beer 14.00
Joe 5 Food 12.00
Beth 5 Food 12.00
Beth 5 Beer 20.00
Janet 5 Beer 14.00
Janet 5 Car 12.00
Janet 5 Beer 19.00
Janet 5 Food 12.00
DROP TABLE t1;
#
# Or also a partition JSON table
#
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp%s.jsn';
ALTER TABLE t1
PARTITION BY LIST COLUMNS(WEEK) (
PARTITION `3` VALUES IN(3),
PARTITION `4` VALUES IN(4),
PARTITION `5` VALUES IN(5));
Warnings:
Warning 1105 Data repartition in 3 is unchecked
Warning 1105 Data repartition in 4 is unchecked
Warning 1105 Data repartition in 5 is unchecked
SHOW WARNINGS;
Level Code Message
Warning 1105 Data repartition in 3 is unchecked
Warning 1105 Data repartition in 4 is unchecked
Warning 1105 Data repartition in 5 is unchecked
SELECT * FROM t1;
WHO WEEK WHAT AMOUNT
Joe 3 Beer 18.00
Joe 3 Food 12.00
Joe 3 Food 19.00
Joe 3 Car 20.00
Beth 3 Beer 16.00
Janet 3 Car 19.00
Janet 3 Food 18.00
Janet 3 Beer 18.00
Joe 4 Beer 19.00
Joe 4 Beer 16.00
Joe 4 Food 17.00
Joe 4 Food 17.00
Joe 4 Beer 14.00
Beth 4 Food 17.00
Beth 4 Beer 15.00
Janet 4 Car 17.00
Joe 5 Beer 14.00
Joe 5 Food 12.00
Beth 5 Food 12.00
Beth 5 Beer 20.00
Janet 5 Beer 14.00
Janet 5 Car 12.00
Janet 5 Beer 19.00
Janet 5 Food 12.00
SELECT * FROM t1 WHERE WEEK = 4;
WHO WEEK WHAT AMOUNT
Joe 4 Beer 19.00
Joe 4 Beer 16.00
Joe 4 Food 17.00
Joe 4 Food 17.00
Joe 4 Beer 14.00
Beth 4 Food 17.00
Beth 4 Beer 15.00
Janet 4 Car 17.00
DROP TABLE t1, t2, t3, t4;
[
{
"ISBN": "9782212090819",
"LANG": "fr",
"SUBJECT": "applications",
"AUTHOR": [
{
"FIRSTNAME": "Jean-Christophe",
"LASTNAME": "Bernadac"
},
{
"FIRSTNAME": "François",
"LASTNAME": "Knab"
}
],
"TITLE": "Construire une application XML",
"PUBLISHER": {
"NAME": "Eyrolles",
"PLACE": "Paris"
},
"DATEPUB": 1999
},
{
"ISBN": "9782840825685",
"LANG": "fr",
"SUBJECT": "applications",
"AUTHOR": [
{
"FIRSTNAME": "William J.",
"LASTNAME": "Pardi"
}
],
"TITLE": "XML en Action",
"TRANSLATION": "adapté de l'anglais par",
"TRANSLATOR": {
"FIRSTNAME": "James",
"LASTNAME": "Guerin"
},
"PUBLISHER": {
"NAME": "Microsoft Press",
"PLACE": "Paris"
},
"DATEPUB": 1999
}
]
[
{
"WHO": "Joe",
"WEEK": [
{
"NUMBER": 3,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 18.00
},
{
"WHAT": "Food",
"AMOUNT": 12.00
},
{
"WHAT": "Food",
"AMOUNT": 19.00
},
{
"WHAT": "Car",
"AMOUNT": 20.00
}
]
},
{
"NUMBER": 4,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 19.00
},
{
"WHAT": "Beer",
"AMOUNT": 16.00
},
{
"WHAT": "Food",
"AMOUNT": 17.00
},
{
"WHAT": "Food",
"AMOUNT": 17.00
},
{
"WHAT": "Beer",
"AMOUNT": 14.00
}
]
},
{
"NUMBER": 5,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 14.00
},
{
"WHAT": "Food",
"AMOUNT": 12.00
}
]
}
]
},
{
"WHO": "Beth",
"WEEK": [
{
"NUMBER": 3,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 16.00
}
]
},
{
"NUMBER": 4,
"EXPENSE": [
{
"WHAT": "Food",
"AMOUNT": 17.00
},
{
"WHAT": "Beer",
"AMOUNT": 15.00
}
]
},
{
"NUMBER": 5,
"EXPENSE": [
{
"WHAT": "Food",
"AMOUNT": 12.00
},
{
"WHAT": "Beer",
"AMOUNT": 20.00
}
]
}
]
},
{
"WHO": "Janet",
"WEEK": [
{
"NUMBER": 3,
"EXPENSE": [
{
"WHAT": "Car",
"AMOUNT": 19.00
},
{
"WHAT": "Food",
"AMOUNT": 18.00
},
{
"WHAT": "Beer",
"AMOUNT": 18.00
}
]
},
{
"NUMBER": 4,
"EXPENSE": [
{
"WHAT": "Car",
"AMOUNT": 17.00
}
]
},
{
"NUMBER": 5,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 14.00
},
{
"WHAT": "Car",
"AMOUNT": 12.00
},
{
"WHAT": "Beer",
"AMOUNT": 19.00
},
{
"WHAT": "Food",
"AMOUNT": 12.00
}
]
}
]
}
]
[
{
"WHO": "Joe",
"WEEK": 3,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 18.00
},
{
"WHAT": "Food",
"AMOUNT": 12.00
},
{
"WHAT": "Food",
"AMOUNT": 19.00
},
{
"WHAT": "Car",
"AMOUNT": 20.00
}
]
},
{
"WHO": "Beth",
"WEEK": 3,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 16.00
}
]
},
{
"WHO": "Janet",
"WEEK": 3,
"EXPENSE": [
{
"WHAT": "Car",
"AMOUNT": 19.00
},
{
"WHAT": "Food",
"AMOUNT": 18.00
},
{
"WHAT": "Beer",
"AMOUNT": 18.00
}
]
}
]
[
{
"WHO": "Joe",
"WEEK": 4,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 19.00
},
{
"WHAT": "Beer",
"AMOUNT": 16.00
},
{
"WHAT": "Food",
"AMOUNT": 17.00
},
{
"WHAT": "Food",
"AMOUNT": 17.00
},
{
"WHAT": "Beer",
"AMOUNT": 14.00
}
]
},
{
"WHO": "Beth",
"WEEK": 4,
"EXPENSE": [
{
"WHAT": "Food",
"AMOUNT": 17.00
},
{
"WHAT": "Beer",
"AMOUNT": 15.00
}
]
},
{
"WHO": "Janet",
"WEEK": 4,
"EXPENSE": [
{
"WHAT": "Car",
"AMOUNT": 17.00
}
]
}
]
[
{
"WHO": "Joe",
"WEEK": 5,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 14.00
},
{
"WHAT": "Food",
"AMOUNT": 12.00
}
]
},
{
"WHO": "Beth",
"WEEK": 5,
"EXPENSE": [
{
"WHAT": "Food",
"AMOUNT": 12.00
},
{
"WHAT": "Beer",
"AMOUNT": 20.00
}
]
},
{
"WHO": "Janet",
"WEEK": 5,
"EXPENSE": [
{
"WHAT": "Beer",
"AMOUNT": 14.00
},
{
"WHAT": "Car",
"AMOUNT": 12.00
},
{
"WHAT": "Beer",
"AMOUNT": 19.00
},
{
"WHAT": "Food",
"AMOUNT": 12.00
}
]
}
]
--source include/not_embedded.inc
--source include/have_partition.inc
let $MYSQLD_DATADIR= `select @@datadir`;
--copy_file $MTR_SUITE_DIR/std_data/biblio.jsn $MYSQLD_DATADIR/test/biblio.jsn
--copy_file $MTR_SUITE_DIR/std_data/expense.jsn $MYSQLD_DATADIR/test/expense.jsn
--copy_file $MTR_SUITE_DIR/std_data/mulexp3.jsn $MYSQLD_DATADIR/test/mulexp3.jsn
--copy_file $MTR_SUITE_DIR/std_data/mulexp4.jsn $MYSQLD_DATADIR/test/mulexp4.jsn
--copy_file $MTR_SUITE_DIR/std_data/mulexp5.jsn $MYSQLD_DATADIR/test/mulexp5.jsn
--echo #
--echo # Testing doc samples
--echo #
CREATE TABLE t1
(
ISBN CHAR(15),
LANG CHAR(2),
SUBJECT CHAR(32),
AUTHOR CHAR(64),
TITLE CHAR(32),
TRANSLATION CHAR(32),
TRANSLATOR CHAR(80),
PUBLISHER CHAR(32),
DATEPUB int(4)
) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # Testing Jpath. Get the number of authors
--echo #
CREATE TABLE t1
(
ISBN CHAR(15),
Language CHAR(2) FIELD_FORMAT='LANG',
Subject CHAR(32) FIELD_FORMAT='SUBJECT',
Authors INT(2) FIELD_FORMAT='AUTHOR:[#]',
Title CHAR(32) FIELD_FORMAT='TITLE',
Translation CHAR(32) FIELD_FORMAT='TRANSLATION',
Translator CHAR(80) FIELD_FORMAT='TRANSLATOR',
Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
Year int(4) FIELD_FORMAT='DATEPUB'
)
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # Concatenates the authors
--echo #
CREATE TABLE t1
(
ISBN CHAR(15),
Language CHAR(2) FIELD_FORMAT='LANG',
Subject CHAR(32) FIELD_FORMAT='SUBJECT',
AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[" and "]:FIRSTNAME',
AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[" and "]:LASTNAME',
Title CHAR(32) FIELD_FORMAT='TITLE',
Translation CHAR(32) FIELD_FORMAT='TRANSLATION',
Translator CHAR(80) FIELD_FORMAT='TRANSLATOR',
Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
Year int(4) FIELD_FORMAT='DATEPUB'
)
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # Testing expanding authors
--echo #
CREATE TABLE t1
(
ISBN CHAR(15),
Language CHAR(2) FIELD_FORMAT='LANG',
Subject CHAR(32) FIELD_FORMAT='SUBJECT',
AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:FIRSTNAME',
AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:LASTNAME',
Title CHAR(32) FIELD_FORMAT='TITLE',
Translation CHAR(32) FIELD_FORMAT='TRANSLATION',
Translator CHAR(80) FIELD_FORMAT='TRANSLATOR',
Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
Year int(4) FIELD_FORMAT='DATEPUB'
)
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn';
SELECT * FROM t1;
UPDATE t1 SET AuthorFN = 'Philippe' WHERE AuthorLN = 'Knab';
SELECT * FROM t1 WHERE ISBN = '9782212090819';
--echo #
--echo # To add an author a new table must be created
--echo #
CREATE TABLE t2 (
FIRSTNAME CHAR(32),
LASTNAME CHAR(32))
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.jsn' OPTION_LIST='Object=[2]:AUTHOR';
SELECT * FROM t2;
INSERT INTO t2 VALUES('Charles','Dickens');
SELECT * FROM t1;
DROP TABLE t1;
DROP TABLE t2;
--echo #
--echo # Check the biblio file has the good format
--echo #
CREATE TABLE t1
(
line char(255)
)
ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='biblio.jsn';
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # A file with 2 arrays
--echo #
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[X]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK::EXPENSE:["+"]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK::EXPENSE:[+]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # Cannot be fully expanded
--echo #
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[X]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[X]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[X]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
--error ER_GET_ERRMSG
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # Expand expense in 3 one week tables
--echo #
CREATE TABLE t2 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[1]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[1]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[1]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t2;
CREATE TABLE t3 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[2]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[2]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[2]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t3;
CREATE TABLE t4 (
WHO CHAR(12),
WEEK INT(2) FIELD_FORMAT='WEEK:[3]:NUMBER',
WHAT CHAR(32) FIELD_FORMAT='WEEK:[3]:EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='WEEK:[3]:EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='expense.jsn';
SELECT * FROM t4;
--echo #
--echo # The expanded table is made as a TBL table
--echo #
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32),
AMOUNT DOUBLE(8,2))
ENGINE=CONNECT TABLE_TYPE=TBL TABLE_LIST='t2,t3,t4';
SELECT * FROM t1;
DROP TABLE t1, t2, t3, t4;
--echo #
--echo # Three partial JSON tables
--echo #
CREATE TABLE t2 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp3.jsn';
SELECT * FROM t2;
CREATE TABLE t3 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp4.jsn';
SELECT * FROM t3;
CREATE TABLE t4 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp5.jsn';
SELECT * FROM t4;
--echo #
--echo # The complete table can be a multiple JSON table
--echo #
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp*.jsn' MULTIPLE=1;
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # Or also a partition JSON table
--echo #
CREATE TABLE t1 (
WHO CHAR(12),
WEEK INT(2),
WHAT CHAR(32) FIELD_FORMAT='EXPENSE:[X]:WHAT',
AMOUNT DOUBLE(8,2) FIELD_FORMAT='EXPENSE:[X]:AMOUNT')
ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='mulexp%s.jsn';
ALTER TABLE t1
PARTITION BY LIST COLUMNS(WEEK) (
PARTITION `3` VALUES IN(3),
PARTITION `4` VALUES IN(4),
PARTITION `5` VALUES IN(5));
SHOW WARNINGS;
SELECT * FROM t1;
SELECT * FROM t1 WHERE WEEK = 4;
DROP TABLE t1, t2, t3, t4;
#
# Clean up
#
--remove_file $MYSQLD_DATADIR/test/biblio.jsn
--remove_file $MYSQLD_DATADIR/test/expense.jsn
--remove_file $MYSQLD_DATADIR/test/mulexp3.jsn
--remove_file $MYSQLD_DATADIR/test/mulexp4.jsn
--remove_file $MYSQLD_DATADIR/test/mulexp5.jsn
......@@ -74,11 +74,10 @@ enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */
TAB_PLG = 20, /* PLG NIY */
TAB_PIVOT = 21, /* PIVOT table */
TAB_VIR = 22, /* Virtual tables */
// TAB_JSON = 23, /* JSON tables */
// TAB_JSN = 24, /* Semi-json tables */
TAB_JCT = 25, /* Junction tables NIY */
TAB_DMY = 26, /* DMY Dummy tables NIY */
TAB_NIY = 27}; /* Table not implemented yet */
TAB_JSON = 23, /* JSON tables */
TAB_JCT = 24, /* Junction tables NIY */
TAB_DMY = 25, /* DMY Dummy tables NIY */
TAB_NIY = 26}; /* Table not implemented yet */
enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
TYPE_AM_ROWID = 1, /* ROWID type (special column) */
......@@ -123,6 +122,8 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */
TYPE_AM_BLK = 131, /* BLK access method type no */
TYPE_AM_ZIP = 132, /* ZIP access method type no */
TYPE_AM_ZLIB = 133, /* ZLIB access method type no */
TYPE_AM_JSON = 134, /* JSON access method type no */
TYPE_AM_JSN = 135, /* JSN access method type no */
TYPE_AM_MAC = 137, /* MAC table access method type */
TYPE_AM_WMI = 139, /* WMI table access method type */
TYPE_AM_XCL = 140, /* SYS column access method type */
......
......@@ -2211,7 +2211,8 @@ int TDBDOS::WriteDB(PGLOBAL g)
htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode);
// Make the line to write
(void)PrepareWriting(g);
if (PrepareWriting(g))
return true;
if (trace > 1)
htrc("Write: line is='%s'\n", To_Line);
......
/************* tabjson C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: tabxjson 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 = N = 0;
Limit = tdp->Limit;
Pretty = tdp->Pretty;
Strict = tdp->Strict;
NextSame = false;
Comma = false;
SameRow = 0;
Xval = -1;
} // 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;
Pretty = tdbp->Pretty;
Strict = tdbp->Strict;
NextSame = tdbp->NextSame;
Comma = tdbp->Comma;
SameRow = tdbp->SameRow;
Xval = tdbp->Xval;
} // 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. */
/*******************************************************************/
for (PJCOL cp = (PJCOL)Columns; cp; cp = (PJCOL)cp->GetNext()) {
cp->Nx = 0;
cp->Arp = NULL;
} // endfor cp
Fpos= -1;
Spos = 0;
NextSame = false;
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++;
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
/* ----------------------------- JSNCOL ------------------------------- */
/***********************************************************************/
/* JSNCOL 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);
Arp = NULL;
Jpath = cdp->GetFmt();
MulVal = NULL;
Nodes = NULL;
Nod = Nx =0;
Ival = -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;
Arp = col1->Arp;
Jpath = col1->Jpath;
MulVal = col1->MulVal;
Nodes = col1->Nodes;
Nod = col1->Nod;
Ival = col1->Ival;
Nx = col1->Nx;
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
/***********************************************************************/
/* Analyse array processing options. */
/***********************************************************************/
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 = true; // Expandable object
Nodes[i].Op = OP_XX;
Tjp->Xval = i;
} else if (b) {
strcpy(g->Message, "Cannot expand more than one array");
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_XX)
if (!Value->IsTypeNum()) {
jnp->CncVal = AllocateValue(g, ", ", 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_NUM; break;
case '!': jnp->Op = OP_SEP; break; // Average
case 'x':
case 'X': // Expand this array
if (!Tjp->Xcol && nm) {
Xpd = true;
jnp->Op = OP_XX;
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
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 {
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
/***********************************************************************/
/* SetValue: Set a value from a JVALUE contains. */
/***********************************************************************/
void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
{
if (val) {
if (Nodes[n].Op == OP_NUM)
vp->SetValue(1);
else {
again:
switch (val->GetValType()) {
case TYPE_STRING:
case TYPE_INT:
case TYPE_DOUBLE:
vp->SetValue_pval(val->GetValue());
break;
case TYPE_TINY:
if (vp->IsTypeNum())
vp->SetValue(val->GetInteger() ? 1 : 0);
else
vp->SetValue_psz(val->GetInteger() ? "true" : "false");
break;
case TYPE_JAR:
val = val->GetArray()->GetValue(0);
goto again;
case TYPE_JOB:
if (!vp->IsTypeNum()) {
vp->SetValue_psz(val->GetObject()->GetText(g));
break;
} // endif Type
default:
vp->Reset();
} // endswitch Type
} // endelse
} else
vp->Reset();
} // end of SetJsonValue
/***********************************************************************/
/* GetRow: Get the object containing this column. */
/***********************************************************************/
PJSON JSONCOL::GetRow(PGLOBAL g, int mode)
{
PJVAL val;
PJAR arp;
PJSON nwr, row = Tjp->Row;
for (int i = 0; i < Nod-1 && row; i++) {
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) {
Ival = i;
arp = (PJAR)row;
if (mode < 2) // First pass
Arp = arp;
if (Nodes[i].Op != OP_XX) {
if (Nodes[i].Rank)
val = arp->GetValue(Nodes[i].Rank - 1);
else
val = arp->GetValue(arp == Arp ? Nx : 0);
} else
val = arp->GetValue(Tjp->SameRow);
} 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 if (mode == 1) { // mode write
// Construct missing objects
for (i++; row && i < Nod; i++) {
if (!Nodes[i].Key) {
// Construct intermediate array
nwr = new(g) JARRAY;
} else {
nwr = new(g) JOBJECT;
} // endif Nodes
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;
} else
row = NULL;
} // endfor i
return row;
} // end of GetRow
/***********************************************************************/
/* ReadColumn: */
/***********************************************************************/
void JSONCOL::ReadColumn(PGLOBAL g)
{
int mode = 0, n = Nod - 1;
PJSON row;
PJVAL val = NULL;
evenmore:
row = GetRow(g, mode);
more:
if (row) switch (row->GetType()) {
case TYPE_JOB:
if (Nodes[n].Key)
val = row->GetValue(Nodes[n].Key);
else
val = new(g) JVALUE(row);
break;
case TYPE_JAR:
// Multiple column ?
if (Nodes[n].Op != OP_NULL) {
Arp = (PJAR)row;
val = Arp->GetValue(Nodes[n].Rank > 0 ?
Nodes[n].Rank - 1 :
Nodes[n].Op == OP_XX ? Tjp->SameRow : Nx);
Ival = n;
} else
val = NULL;
break;
case TYPE_JVAL:
val = (PJVAL)row;
break;
default:
sprintf(g->Message, "Wrong return value type %d", row->GetType());
Value->Reset();
return;
} // endswitch Type
if (!Nx /*|| (Xpd)*/)
SetJsonValue(g, Value, val, n);
if (Arp) {
// Multiple column
int ars = (Nodes[Ival].Rank > 0) ? 1 : MY_MIN(Tjp->Limit, Arp->size());
if (Nodes[Ival].Op == OP_XX) {
if (ars > Tjp->SameRow + 1)
Tjp->NextSame = true; // More to come
else {
Tjp->NextSame = false;
Arp = NULL;
} // endelse
} else {
if (Nx && val) {
SetJsonValue(g, MulVal, val, Ival);
if (!MulVal->IsZero()) {
PVAL val[2];
bool err;
switch (Nodes[Ival].Op) {
case OP_CNC:
if (Nodes[Ival].CncVal) {
val[0] = Nodes[Ival].CncVal;
err = Value->Compute(g, val, 1, Nodes[Ival].Op);
} // endif CncVal
val[0] = MulVal;
err = Value->Compute(g, val, 1, Nodes[Ival].Op);
break;
case OP_NUM:
case OP_SEP:
val[0] = Value;
val[1] = MulVal;
err = Value->Compute(g, val, 2, OP_ADD);
break;
default:
val[0] = Value;
val[1] = MulVal;
err = Value->Compute(g, val, 2, Nodes[Ival].Op);
} // endswitch Op
if (err)
Value->Reset();
} // endif Zero
} // endif Nx
if (ars > ++Nx) {
if (Ival != n) {
mode = 2;
goto evenmore;
} else
goto more;
} else {
if (Nodes[Ival].Op == OP_SEP) {
// Calculate average
PVAL val[2];
MulVal->SetValue(ars);
val[0] = Value;
val[1] = MulVal;
if (Value->Compute(g, val, 2, OP_DIV))
Value->Reset();
} // endif Op
Arp = NULL;
Nx = 0;
} // endif ars
} // endif Op
} // endif Arp
} // end of ReadColumn
/***********************************************************************/
/* 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;
PJOB objp = NULL;
PJAR arp = NULL;
PJVAL jvp = NULL;
PJSON row = GetRow(g, 1);
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:
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 i, len;
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 != ERROR_FILE_NOT_FOUND || Mode != MODE_INSERT) {
sprintf(g->Message, MSG(OPEN_ERROR), drc, 10, filename);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, drc, 0,
(LPTSTR)filename, sizeof(filename), NULL);
strcat(g->Message, 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);
UnmapViewOfFile(memory);
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);
UnmapViewOfFile(memory);
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. */
/*******************************************************************/
for (PJCOL cp = (PJCOL)Columns; cp; cp = (PJCOL)cp->GetNext()) {
cp->Nx = 0;
cp->Arp = NULL;
} // endfor cp
Fpos= -1;
Spos = 0;
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++;
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);
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());
if (!(fop = fopen(filename, "wb"))) {
sprintf(g->Message, MSG(OPEN_MODE_ERROR),
"w", (int)errno, filename);
strcat(strcat(g->Message, ": "), strerror(errno));
} else {
// Serialize the modified table
if ((msg = Serialize(g, Top, fop, Pretty)))
printf(msg);
fclose(fop);
} // endif fop
} // end of CloseDB
/* -------------------------- End of json --------------------------- */
/*************** tabjson H Declares Source Code File (.H) **************/
/* Name: tabjson.h Version 1.0 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
/* This file contains the JSON classes declares. */
/***********************************************************************/
#include "osutil.h"
#include "block.h"
#include "colblk.h"
#include "json.h"
enum JMODE {MODE_OBJECT, MODE_ARRAY, MODE_VALUE};
typedef class JSONDEF *PJDEF;
typedef class TDBJSON *PJTDB;
typedef class JSONCOL *PJCOL;
class TDBJSN;
/***********************************************************************/
/* The JSON tree node. Can be an Object or an Array. */
/***********************************************************************/
typedef struct _jnode {
PSZ Key; // The key used for object
OPVAL Op; // Operator used for this node
PVAL CncVal; // To cont value used for OP_CNC
int Rank; // The rank in array
} JNODE, *PJNODE;
/***********************************************************************/
/* JSON table. */
/***********************************************************************/
class JSONDEF : public DOSDEF { /* Table description */
friend class TDBJSON;
friend class TDBJSN;
public:
// Constructor
JSONDEF(void);
// Implementation
virtual const char *GetType(void) {return "JSON";}
// Methods
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE m);
protected:
// Members
JMODE Jmode; /* MODE_OBJECT by default */
char *Objname; /* Name of first level object */
char *Xcol; /* Name of expandable column */
int Limit; /* Limit of multiple values */
int Pretty; /* Depends on file structure */
bool Strict; /* Strict syntax checking */
}; // end of JSONDEF
/* -------------------------- TDBJSN class --------------------------- */
/***********************************************************************/
/* This is the JSN Access Method class declaration. */
/* The table is a DOS file, each record being a JSON object. */
/***********************************************************************/
class TDBJSN : public TDBDOS {
friend class JSONCOL;
public:
// Constructor
TDBJSN(PJDEF tdp, PTXF txfp);
TDBJSN(TDBJSN *tdbp);
// Implementation
virtual AMT GetAmType(void) {return TYPE_AM_JSN;}
virtual bool SkipHeader(PGLOBAL g);
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBJSN(this);}
// Methods
virtual PTDB CopyOne(PTABS t);
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
virtual PCOL InsertSpecialColumn(PGLOBAL g, PCOL colp);
virtual int RowNumber(PGLOBAL g, BOOL b = FALSE)
{return (b) ? N : Fpos + 1;}
// Database routines
virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual bool PrepareWriting(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
protected:
// Members
PJSON Row; // The current row
PJCOL Colp; // The multiple column
JMODE Jmode; // MODE_OBJECT by default
char *Xcol; // Name of expandable column
int Fpos; // The current row index
int Spos; // DELETE start index
int N; // The current Rownum
int Limit; // Limit of multiple values
int Pretty; // Depends on file structure
bool Strict; // Strict syntax checking
bool NextSame; // Same next row
bool Comma; // Row has final comma
int SameRow; // Same row nb
int Xval; // Index of expandable array
}; // end of class TDBJSN
/* -------------------------- JSONCOL class -------------------------- */
/***********************************************************************/
/* Class JSONCOL: JSON access method column descriptor. */
/***********************************************************************/
class JSONCOL : public DOSCOL {
friend class TDBJSN;
friend class TDBJSON;
public:
// Constructors
JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
JSONCOL(JSONCOL *colp, PTDB tdbp); // Constructor used in copy process
// Implementation
virtual int GetAmType(void) {return Tjp->GetAmType();}
// Methods
virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
bool ParseJpath(PGLOBAL g);
virtual void ReadColumn(PGLOBAL g);
virtual void WriteColumn(PGLOBAL g);
protected:
bool CheckExpand(PGLOBAL g, int i, PSZ nm, bool b);
bool SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm);
PJSON GetRow(PGLOBAL g, int mode);
void SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n);
// Default constructor not to be used
JSONCOL(void) {}
// Members
TDBJSN *Tjp; // To the JSN table block
PVAL MulVal; // To value used by multiple column
PJAR Arp; // The intermediate array
char *Jpath; // The json path
JNODE *Nodes ; // The intermediate objects
int Nod; // The number of intermediate objects
int Ival; // Index of multiple values
int Nx; // The last read sub-row
bool Xpd; // True for expandable column
bool Parsed; // True when parsed
}; // end of class JSONCOL
/* -------------------------- TDBJSON class -------------------------- */
/***********************************************************************/
/* This is the JSON Access Method class declaration. */
/***********************************************************************/
class TDBJSON : public TDBJSN {
friend class JSONCOL;
public:
// Constructor
TDBJSON(PJDEF tdp, PTXF txfp);
TDBJSON(PJTDB tdbp);
// Implementation
virtual AMT GetAmType(void) {return TYPE_AM_JSON;}
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBJSON(this);}
// Methods
virtual PTDB CopyOne(PTABS t);
// Database routines
virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual void ResetSize(void);
virtual int GetRecpos(void) {return Fpos;}
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual bool PrepareWriting(PGLOBAL g) {return false;}
virtual int WriteDB(PGLOBAL g);
virtual int DeleteDB(PGLOBAL g, int irc);
virtual void CloseDB(PGLOBAL g);
// Optimization routines
virtual int MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add);
protected:
int MakeNewDoc(PGLOBAL g);
int MakeDocument(PGLOBAL g);
// Members
PJSON Top; // The file JSON tree
PJAR Doc; // The document array
char *Objname; // The table object name
int Multiple; // 0: No 1: DIR 2: Section 3: filelist
bool Done; // True when document parsing is done
bool Changed; // After Update, Insert or Delete
}; // end of class TDBJSON
......@@ -67,6 +67,22 @@
#define FOURYEARS 126230400 // Four years in seconds (1 leap)
#define MAXUINT8 ((UINT8)~((UINT8)0))
#define MAXINT8 ((INT8)(MAXUINT8 >> 1))
#define MININT8 ((INT8)~MAXINT8)
#define MAXUINT16 ((UINT16)~((UINT16)0))
#define MAXINT16 ((INT16)(MAXUINT16 >> 1))
#define MININT16 ((INT16)~MAXINT16)
#define MAXUINT32 ((UINT32)~((UINT32)0))
#define MAXINT32 ((INT32)(MAXUINT32 >> 1))
#define MININT32 ((INT32)~MAXINT32)
#define MAXUINT64 ((UINT64)~((UINT64)0))
#define MAXINT64 ((INT64)(MAXUINT64 >> 1))
#define MININT64 ((INT64)~MAXINT64)
/***********************************************************************/
/* Initialize the DTVAL static member. */
/***********************************************************************/
......@@ -434,6 +450,7 @@ PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype, int uns)
{
PSZ p, sp;
bool un = (uns < 0) ? false : (uns > 0) ? true : valp->IsUnsigned();
PVAL vp;
if (newtype == TYPE_VOID) // Means allocate a value of the same type
newtype = valp->GetType();
......@@ -445,53 +462,55 @@ PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype, int uns)
if ((sp = valp->GetCharString(p)) != p)
strcpy (p, sp);
valp = new(g) TYPVAL<PSZ>(g, p, valp->GetValLen(), valp->GetValPrec());
vp = new(g) TYPVAL<PSZ>(g, p, valp->GetValLen(), valp->GetValPrec());
break;
case TYPE_SHORT:
if (un)
valp = new(g) TYPVAL<ushort>(valp->GetUShortValue(),
TYPE_SHORT, 0, true);
vp = new(g) TYPVAL<ushort>(valp->GetUShortValue(),
TYPE_SHORT, 0, true);
else
valp = new(g) TYPVAL<short>(valp->GetShortValue(), TYPE_SHORT);
vp = new(g) TYPVAL<short>(valp->GetShortValue(), TYPE_SHORT);
break;
case TYPE_INT:
if (un)
valp = new(g) TYPVAL<uint>(valp->GetUIntValue(), TYPE_INT, 0, true);
vp = new(g) TYPVAL<uint>(valp->GetUIntValue(), TYPE_INT, 0, true);
else
valp = new(g) TYPVAL<int>(valp->GetIntValue(), TYPE_INT);
vp = new(g) TYPVAL<int>(valp->GetIntValue(), TYPE_INT);
break;
case TYPE_BIGINT:
if (un)
valp = new(g) TYPVAL<ulonglong>(valp->GetUBigintValue(),
TYPE_BIGINT, 0, true);
vp = new(g) TYPVAL<ulonglong>(valp->GetUBigintValue(),
TYPE_BIGINT, 0, true);
else
valp = new(g) TYPVAL<longlong>(valp->GetBigintValue(), TYPE_BIGINT);
vp = new(g) TYPVAL<longlong>(valp->GetBigintValue(), TYPE_BIGINT);
break;
case TYPE_DATE:
valp = new(g) DTVAL(g, valp->GetIntValue());
vp = new(g) DTVAL(g, valp->GetIntValue());
break;
case TYPE_DOUBLE:
valp = new(g) TYPVAL<double>(valp->GetFloatValue(), TYPE_DOUBLE,
(uns) ? uns : valp->GetValPrec());
vp = new(g) TYPVAL<double>(valp->GetFloatValue(), TYPE_DOUBLE,
(uns) ? uns : valp->GetValPrec());
break;
case TYPE_TINY:
if (un)
valp = new(g) TYPVAL<uchar>(valp->GetUTinyValue(),
vp = new(g) TYPVAL<uchar>(valp->GetUTinyValue(),
TYPE_TINY, 0, true);
else
valp = new(g) TYPVAL<char>(valp->GetTinyValue(), TYPE_TINY);
vp = new(g) TYPVAL<char>(valp->GetTinyValue(), TYPE_TINY);
break;
default:
sprintf(g->Message, MSG(BAD_VALUE_TYPE), newtype);
return NULL;
} // endswitch type
valp->SetGlobal(g);
return valp;
vp->SetNullable(valp->GetNullable());
vp->SetNull(valp->IsNull());
vp->SetGlobal(g);
return vp;
} // end of AllocateValue
/* -------------------------- Class VALUE ---------------------------- */
......@@ -939,7 +958,6 @@ int TYPVAL<TYPE>::CompareValue(PVAL vp)
return (Tval > n) ? 1 : (Tval < n) ? (-1) : 0;
} // end of CompareValue
#if 0
/***********************************************************************/
/* Return max type value if b is true, else min type value. */
/***********************************************************************/
......@@ -1004,7 +1022,7 @@ TYPE TYPVAL<TYPE>::SafeAdd(TYPE n1, TYPE n2)
template <>
inline double TYPVAL<double>::SafeAdd(double n1, double n2)
{
assert(false); return 0;
return n1 + n2;
} // end of SafeAdd
/***********************************************************************/
......@@ -1032,9 +1050,8 @@ TYPE TYPVAL<TYPE>::SafeMult(TYPE n1, TYPE n2)
template <>
inline double TYPVAL<double>::SafeMult(double n1, double n2)
{
assert(false); return 0;
return n1 * n2;
} // end of SafeMult
#endif // 0
/***********************************************************************/
/* Compute defined functions for the type. */
......@@ -1052,12 +1069,18 @@ bool TYPVAL<TYPE>::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
switch (op) {
case OP_ADD:
// Tval = SafeAdd(val[0], val[1]);
Tval = val[0] + val[1];
Tval = SafeAdd(val[0], val[1]);
break;
case OP_MULT:
// Tval = SafeMult(val[0], val[1]);
Tval = val[0] * val[1];
Tval = SafeMult(val[0], val[1]);
break;
case OP_DIV:
if (!val[1]) {
strcpy(g->Message, MSG(ZERO_DIVIDE));
return true;
} // endif
Tval = val[0] / val[1];
break;
default:
rc = Compall(g, vp, np, op);
......@@ -1067,7 +1090,6 @@ bool TYPVAL<TYPE>::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
return rc;
} // end of Compute
#if 0
template <>
bool TYPVAL<double>::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
{
......@@ -1092,7 +1114,6 @@ bool TYPVAL<double>::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
return rc;
} // end of Compute
#endif // 0
/***********************************************************************/
/* Compute a function for all types. */
......@@ -1106,6 +1127,18 @@ bool TYPVAL<TYPE>::Compall(PGLOBAL g, PVAL *vp, int np, OPVAL op)
val[i] = GetTypedValue(vp[i]);
switch (op) {
case OP_DIV:
if (val[0]) {
if (!val[1]) {
strcpy(g->Message, MSG(ZERO_DIVIDE));
return true;
} // endif
Tval = val[0] / val[1];
} else
Tval = 0;
break;
case OP_MIN:
Tval = MY_MIN(val[0], val[1]);
break;
......
......@@ -190,9 +190,9 @@ class DllExport TYPVAL : public VALUE {
virtual void Print(PGLOBAL g, char *, uint);
protected:
//static TYPE MinMaxVal(bool b);
// TYPE SafeAdd(TYPE n1, TYPE n2);
// TYPE SafeMult(TYPE n1, TYPE n2);
static TYPE MinMaxVal(bool b);
TYPE SafeAdd(TYPE n1, TYPE n2);
TYPE SafeMult(TYPE n1, TYPE n2);
bool Compall(PGLOBAL g, PVAL *vp, int np, OPVAL op);
// Default constructor not to be used
......
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