summaryrefslogtreecommitdiff
path: root/storage/connect
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2015-01-31 21:48:47 +0100
committerSergei Golubchik <serg@mariadb.org>2015-01-31 21:48:47 +0100
commit4b21cd21fef2763d757aa15681c9c9a7ed5db3c9 (patch)
treee9e233392b47f93de12cecce1f7f403ce26057b0 /storage/connect
parent0b049b40124d72d77c008d4441e4db2e77f0f127 (diff)
parenta06624d61f36c70edd63adcfe2803bb7a8564de5 (diff)
downloadmariadb-git-4b21cd21fef2763d757aa15681c9c9a7ed5db3c9.tar.gz
Merge branch '10.0' into merge-wip
Diffstat (limited to 'storage/connect')
-rw-r--r--storage/connect/CMakeLists.txt14
-rw-r--r--storage/connect/connect.cc15
-rw-r--r--storage/connect/engmsg.h5
-rw-r--r--storage/connect/filamtxt.h1
-rw-r--r--storage/connect/ha_connect.cc81
-rw-r--r--storage/connect/ha_connect.h5
-rw-r--r--storage/connect/json.cpp1055
-rw-r--r--storage/connect/json.h246
-rw-r--r--storage/connect/maputil.h4
-rw-r--r--storage/connect/msgid.h5
-rw-r--r--storage/connect/mycat.cc13
-rw-r--r--storage/connect/myconn.cpp17
-rw-r--r--storage/connect/myconn.h3
-rw-r--r--storage/connect/mysql-test/connect/r/json.result439
-rw-r--r--storage/connect/mysql-test/connect/std_data/biblio.jsn45
-rw-r--r--storage/connect/mysql-test/connect/std_data/expense.jsn158
-rw-r--r--storage/connect/mysql-test/connect/std_data/mulexp3.jsn52
-rw-r--r--storage/connect/mysql-test/connect/std_data/mulexp4.jsn52
-rw-r--r--storage/connect/mysql-test/connect/std_data/mulexp5.jsn52
-rw-r--r--storage/connect/mysql-test/connect/t/json.test247
-rw-r--r--storage/connect/odbccat.h10
-rw-r--r--storage/connect/odbconn.cpp173
-rw-r--r--storage/connect/odbconn.h12
-rw-r--r--storage/connect/plgdbsem.h9
-rw-r--r--storage/connect/rcmsg.c3
-rw-r--r--storage/connect/reldef.cpp3
-rw-r--r--storage/connect/reldef.h2
-rw-r--r--storage/connect/tabdos.cpp3
-rw-r--r--storage/connect/tabjson.cpp1322
-rw-r--r--storage/connect/tabjson.h197
-rw-r--r--storage/connect/table.cpp2
-rw-r--r--storage/connect/tabmysql.cpp4
-rw-r--r--storage/connect/tabodbc.cpp135
-rw-r--r--storage/connect/tabodbc.h16
-rw-r--r--storage/connect/value.cpp294
-rw-r--r--storage/connect/value.h16
-rw-r--r--storage/connect/xtable.h1
37 files changed, 4566 insertions, 145 deletions
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt
index b0a2e3607ed..931a811d897 100644
--- a/storage/connect/CMakeLists.txt
+++ b/storage/connect/CMakeLists.txt
@@ -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
diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
index 87c782ba953..a54d8ebcc44 100644
--- a/storage/connect/connect.cc
+++ b/storage/connect/connect.cc
@@ -52,7 +52,7 @@
/* Routines called internally by semantic routines. */
/***********************************************************************/
void CntEndDB(PGLOBAL);
-RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr= false);
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr= false);
/***********************************************************************/
/* MySQL routines called externally by semantic routines. */
@@ -388,7 +388,7 @@ bool CntRewindTable(PGLOBAL g, PTDB tdbp)
/***********************************************************************/
/* Evaluate all columns after a record is read. */
/***********************************************************************/
-RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr)
+RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool reset, bool mrr)
{
RCODE rc= RC_OK;
PCOL colp;
@@ -413,7 +413,8 @@ RCODE EvalColumns(PGLOBAL g, PTDB tdbp, bool mrr)
for (colp= tdbp->GetColumns(); rc == RC_OK && colp;
colp= colp->GetNext()) {
- colp->Reset();
+ if (reset)
+ colp->Reset();
// Virtual columns are computed by MariaDB
if (!colp->GetColUse(U_VIRTUAL) && (!mrr || colp->GetKcol()))
@@ -457,6 +458,10 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
goto err;
} // endif rc
+ // Do it now to avoid double eval when filtering
+ for (PCOL colp= tdbp->GetColumns(); colp; colp= colp->GetNext())
+ colp->Reset();
+
do {
if ((rc= (RCODE)tdbp->ReadDB(g)) == RC_OK)
if (!ApplyFilter(g, tdbp->GetFilter()))
@@ -466,7 +471,7 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
err:
g->jump_level--;
- return (rc != RC_OK) ? rc : EvalColumns(g, tdbp);
+ return (rc != RC_OK) ? rc : EvalColumns(g, tdbp, false);
} // end of CntReadNext
/***********************************************************************/
@@ -812,7 +817,7 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
rnd:
if ((rc= (RCODE)ptdb->ReadDB(g)) == RC_OK)
- rc= EvalColumns(g, ptdb, mrr);
+ rc= EvalColumns(g, ptdb, true, mrr);
return rc;
} // end of CntIndexRead
diff --git a/storage/connect/engmsg.h b/storage/connect/engmsg.h
index ad6dc6b5689..14808758efd 100644
--- a/storage/connect/engmsg.h
+++ b/storage/connect/engmsg.h
@@ -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"
diff --git a/storage/connect/filamtxt.h b/storage/connect/filamtxt.h
index b89d58965f9..864ca66dd34 100644
--- a/storage/connect/filamtxt.h
+++ b/storage/connect/filamtxt.h
@@ -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;
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index e505907442c..ba594bdafb0 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2014
+/* Copyright (C) Olivier Bertrand 2004 - 2015
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -170,8 +170,8 @@
#define SZWMIN 4194304 // Minimum work area size 4M
extern "C" {
- char version[]= "Version 1.03.0005 November 08, 2014";
- 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= '\\';
@@ -714,7 +714,7 @@ ha_connect::ha_connect(handlerton *hton, TABLE_SHARE *table_arg)
datapath= "./";
#endif // !WIN32
tdbp= NULL;
- sdvalin= NULL;
+ sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
sdvalout= NULL;
xmod= MODE_ANY;
istable= false;
@@ -1055,6 +1055,14 @@ char *ha_connect::GetStringOption(char *opname, char *sdef)
opval= (char*)options->colist;
else if (!stricmp(opname, "Data_charset"))
opval= (char*)options->data_charset;
+ else if (!stricmp(opname, "Table_charset")) {
+ const CHARSET_INFO *chif= (tshp) ? tshp->table_charset
+ : table->s->table_charset;
+
+ if (chif)
+ opval= (char*)chif->csname;
+
+ } // endif Table_charset
if (!opval && options && options->oplist)
opval= GetListOption(xp->g, opname, options->oplist);
@@ -1326,6 +1334,7 @@ void *ha_connect::GetColumnOption(PGLOBAL g, void *field, PCOLINFO pcf)
datm.tm_mday= 12;
datm.tm_mon= 11;
datm.tm_year= 112;
+ mktime(&datm); // set other fields get proper day name
len= strftime(buf, 256, pdtp->OutFmt, &datm);
} else
len= 0;
@@ -1806,7 +1815,7 @@ int ha_connect::CloseTable(PGLOBAL g)
{
int rc= CntCloseTable(g, tdbp, nox, abort);
tdbp= NULL;
- sdvalin=NULL;
+ sdvalin1= sdvalin2= sdvalin3= sdvalin4= NULL;
sdvalout=NULL;
valid_info= false;
indexing= -1;
@@ -1960,7 +1969,7 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *buf)
char *fmt;
int rc= 0;
PCOL colp;
- PVAL value;
+ PVAL value, sdvalin;
Field *fp;
PTDBASE tp= (PTDBASE)tdbp;
String attribute(attr_buffer, sizeof(attr_buffer),
@@ -2003,25 +2012,45 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *buf)
value->SetValue(fp->val_real());
break;
case TYPE_DATE:
- if (!sdvalin)
- sdvalin= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
-
// Get date in the format produced by MySQL fields
switch (fp->type()) {
case MYSQL_TYPE_DATE:
- fmt= "YYYY-MM-DD";
+ if (!sdvalin2) {
+ sdvalin2= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
+ fmt= "YYYY-MM-DD";
+ ((DTVAL*)sdvalin2)->SetFormat(g, fmt, strlen(fmt));
+ } // endif sdvalin1
+
+ sdvalin= sdvalin2;
break;
case MYSQL_TYPE_TIME:
- fmt= "hh:mm:ss";
+ if (!sdvalin3) {
+ sdvalin3= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
+ fmt= "hh:mm:ss";
+ ((DTVAL*)sdvalin3)->SetFormat(g, fmt, strlen(fmt));
+ } // endif sdvalin1
+
+ sdvalin= sdvalin3;
break;
case MYSQL_TYPE_YEAR:
- fmt= "YYYY";
+ if (!sdvalin4) {
+ sdvalin4= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
+ fmt= "YYYY";
+ ((DTVAL*)sdvalin4)->SetFormat(g, fmt, strlen(fmt));
+ } // endif sdvalin1
+
+ sdvalin= sdvalin4;
break;
default:
- fmt= "YYYY-MM-DD hh:mm:ss";
+ if (!sdvalin1) {
+ sdvalin1= (DTVAL*)AllocateValue(xp->g, TYPE_DATE, 19);
+ fmt= "YYYY-MM-DD hh:mm:ss";
+ ((DTVAL*)sdvalin1)->SetFormat(g, fmt, strlen(fmt));
+ } // endif sdvalin1
+
+ sdvalin= sdvalin1;
} // endswitch type
- ((DTVAL*)sdvalin)->SetFormat(g, fmt, strlen(fmt));
sdvalin->SetNullable(colp->IsNullable());
fp->val_str(&attribute);
sdvalin->SetValue_psz(attribute.c_ptr_safe());
@@ -3802,6 +3831,7 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
case TAB_XML:
case TAB_INI:
case TAB_VEC:
+ case TAB_JSON:
if (options->filename && *options->filename) {
char *s, path[FN_REFLEN], dbpath[FN_REFLEN];
#if defined(WIN32)
@@ -4797,6 +4827,9 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
#endif // WIN32
int port= 0, hdr= 0, mxr __attribute__((unused))= 0, mxe= 0, rc= 0;
int cop __attribute__((unused)) = 0;
+#if defined(ODBC_SUPPORT)
+ int cto= -1, qto= -1;
+#endif // ODBC_SUPPORT
uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
bool bif, ok= false, dbf= false;
TABTYPE ttp= TAB_UNDEF;
@@ -4831,7 +4864,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
fncn= topt->catfunc;
fnc= GetFuncID(fncn);
sep= topt->separator;
- spc= (!sep || !strcmp(sep, "\\t")) ? '\t' : *sep;
+ spc= (!sep) ? ',' : (!strcmp(sep, "\\t")) ? '\t' : *sep;
qch= topt->qchar ? *topt->qchar : (signed)topt->quoted >= 0 ? '"' : 0;
hdr= (int)topt->header;
tbl= topt->tablist;
@@ -4856,6 +4889,8 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
port= atoi(GetListOption(g, "port", topt->oplist, "0"));
#if defined(ODBC_SUPPORT)
mxr= atoi(GetListOption(g,"maxres", topt->oplist, "0"));
+ cto= atoi(GetListOption(g,"ConnectTimeout", topt->oplist, "-1"));
+ qto= atoi(GetListOption(g,"QueryTimeout", topt->oplist, "-1"));
#endif
mxe= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
#if defined(PROMPT_OK)
@@ -5074,14 +5109,15 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
case FNC_NO:
case FNC_COL:
if (src) {
- qrp= ODBCSrcCols(g, dsn, (char*)src);
+ qrp= ODBCSrcCols(g, dsn, (char*)src, cto, qto);
src= NULL; // for next tests
} else
- qrp= ODBCColumns(g, dsn, shm, tab, NULL, mxr, fnc == FNC_COL);
+ qrp= ODBCColumns(g, dsn, shm, tab, NULL,
+ mxr, cto, qto, fnc == FNC_COL);
break;
case FNC_TABLE:
- qrp= ODBCTables(g, dsn, shm, tab, mxr, true);
+ qrp= ODBCTables(g, dsn, shm, tab, mxr, cto, qto, true);
break;
case FNC_DSN:
qrp= ODBCDataSources(g, mxr, true);
@@ -5187,6 +5223,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
tm= NOT_NULL_FLAG;
cnm= (char*)"noname";
dft= xtra= key= NULL;
+ v= ' ';
#if defined(NEW_WAY)
rem= "";
// cs= NULL;
@@ -5197,7 +5234,13 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
for (crp= qrp->Colresp; crp; crp= crp->Next)
switch (crp->Fld) {
case FLD_NAME:
- cnm= encode(g, crp->Kdata->GetCharValue(i));
+ if (ttp == TAB_CSV && topt->data_charset &&
+ (!stricmp(topt->data_charset, "UTF8") ||
+ !stricmp(topt->data_charset, "UTF-8")))
+ cnm= crp->Kdata->GetCharValue(i);
+ else
+ cnm= encode(g, crp->Kdata->GetCharValue(i));
+
break;
case FLD_TYPE:
typ= crp->Kdata->GetIntValue(i);
diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h
index 6c3ed87d5f6..922a69a3991 100644
--- a/storage/connect/ha_connect.h
+++ b/storage/connect/ha_connect.h
@@ -543,7 +543,10 @@ protected:
query_id_t creat_query_id; // The one when handler was allocated
char *datapath; // Is the Path of DB data directory
PTDB tdbp; // To table class object
- PVAL sdvalin; // Used to convert date values
+ PVAL sdvalin1; // Used to convert date values
+ PVAL sdvalin2; // Used to convert date values
+ PVAL sdvalin3; // Used to convert date values
+ PVAL sdvalin4; // Used to convert date values
PVAL sdvalout; // Used to convert date values
bool istable; // True for table handler
char partname[64]; // The partition name
diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp
new file mode 100644
index 00000000000..983f45d9cee
--- /dev/null
+++ b/storage/connect/json.cpp
@@ -0,0 +1,1055 @@
+/*************** 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->WriteChr('\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->WriteChr('\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->WriteChr('['))
+ return true;
+ else if (b && (js->WriteStr(EL) || js->WriteChr('\t')))
+ return true;
+
+ for (int i = 0; i < jarp->size(); i++) {
+ if (first)
+ first = false;
+ else if (js->WriteChr(','))
+ return true;
+ else if (b && (js->WriteStr(EL) || js->WriteChr('\t')))
+ return true;
+
+ if (SerializeValue(js, jarp->GetValue(i)))
+ return true;
+
+ } // endfor i
+
+ if (b && js->WriteStr(EL))
+ return true;
+
+ return js->WriteChr(']');
+} // end of SerializeArray
+
+/***********************************************************************/
+/* Serialize a JSON Object. */
+/***********************************************************************/
+bool SerializeObject(JOUT *js, PJOB jobp)
+{
+ bool first = true;
+
+ if (js->WriteChr('{'))
+ return true;
+
+ for (PJPR pair = jobp->First; pair; pair = pair->Next) {
+ if (first)
+ first = false;
+ else if (js->WriteChr(','))
+ return true;
+
+ if (js->WriteChr('\"') ||
+ js->WriteStr(pair->Key) ||
+ js->WriteChr('\"') ||
+ js->WriteChr(':') ||
+ SerializeValue(js, pair->Val))
+ return true;
+
+ } // endfor i
+
+ return js->WriteChr('}');
+} // 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->WriteStr("null");
+ else switch (valp->GetType()) {
+ case TYPE_TINY:
+ return js->WriteStr(valp->GetTinyValue() ? "true" : "false");
+ case TYPE_STRING:
+ return js->Escape(valp->GetCharValue());
+ default:
+ if (valp->IsTypeNum()) {
+ char buf[32];
+
+ return js->WriteStr(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::WriteStr(const 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 WriteStr
+
+/***********************************************************************/
+/* Concatenate a character to the Serialize string. */
+/***********************************************************************/
+bool JOUTSTR::WriteChr(const char c)
+{
+ if (N + 1 > Max)
+ return true;
+
+ Strp[N++] = c;
+ return false;
+} // end of WriteChr
+
+/***********************************************************************/
+/* Escape and Concatenate a string to the Serialize string. */
+/***********************************************************************/
+bool JOUTSTR::Escape(const char *s)
+{
+ WriteChr('"');
+
+ for (unsigned int i = 0; i < strlen(s); i++)
+ switch (s[i]) {
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\b':
+ case '\f':
+ case '"': WriteChr('\\');
+ // passthru
+ default:
+ WriteChr(s[i]);
+ break;
+ } // endswitch s[i]
+
+ WriteChr('"');
+ return false;
+} // end of Escape
+
+/* ------------------------- Class JOUTFILE -------------------------- */
+
+/***********************************************************************/
+/* Write a string to the Serialize file. */
+/***********************************************************************/
+bool JOUTFILE::WriteStr(const char *s)
+{
+ // This is temporary
+ fputs(s, Stream);
+ return false;
+} // end of WriteStr
+
+/***********************************************************************/
+/* Write a character to the Serialize file. */
+/***********************************************************************/
+bool JOUTFILE::WriteChr(const char c)
+{
+ // This is temporary
+ fputc(c, Stream);
+ return false;
+} // end of WriteChr
+
+/***********************************************************************/
+/* Escape and Concatenate a string to the Serialize string. */
+/***********************************************************************/
+bool JOUTFILE::Escape(const 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::WriteStr(const 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 WriteStr
+
+/***********************************************************************/
+/* Write a character to the Serialize pretty file. */
+/***********************************************************************/
+bool JOUTPRT::WriteChr(const 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 WriteChr
+
+/* -------------------------- 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
+
diff --git a/storage/connect/json.h b/storage/connect/json.h
new file mode 100644
index 00000000000..11e15c3acd4
--- /dev/null
+++ b/storage/connect/json.h
@@ -0,0 +1,246 @@
+/**************** 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_STRG = 1,
+ TYPE_DBL = 2,
+ TYPE_BOOL = 4,
+ TYPE_INTG = 7,
+ 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 WriteStr(const char *s) = 0;
+ virtual bool WriteChr(const char c) = 0;
+ virtual bool Escape(const 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 WriteStr(const char *s);
+ virtual bool WriteChr(const char c);
+ virtual bool Escape(const 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 WriteStr(const char *s);
+ virtual bool WriteChr(const char c);
+ virtual bool Escape(const 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 WriteStr(const char *s);
+ virtual bool WriteChr(const 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
+
diff --git a/storage/connect/maputil.h b/storage/connect/maputil.h
index b5e54affcea..e310488eb5d 100644
--- a/storage/connect/maputil.h
+++ b/storage/connect/maputil.h
@@ -11,8 +11,8 @@ typedef struct {
DWORD lenH;
} MEMMAP;
-HANDLE CreateFileMap(PGLOBAL, LPCSTR, MEMMAP *, MODE, bool);
-bool CloseMemMap(void *memory, size_t dwSize);
+DllExport HANDLE CreateFileMap(PGLOBAL, LPCSTR, MEMMAP *, MODE, bool);
+DllExport bool CloseMemMap(void *memory, size_t dwSize);
#ifdef __cplusplus
}
diff --git a/storage/connect/msgid.h b/storage/connect/msgid.h
index 4496994afa3..0e9c036dc49 100644
--- a/storage/connect/msgid.h
+++ b/storage/connect/msgid.h
@@ -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
diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc
index fc6c29092a1..0ae0537ba6f 100644
--- a/storage/connect/mycat.cc
+++ b/storage/connect/mycat.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2013
+/* Copyright (C) Olivier Bertrand 2004 - 2014
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
/* ------------- */
/* Version 1.4 */
/* */
-/* Author: Olivier Bertrand 2012 - 2013 */
+/* Author: Olivier Bertrand 2012 - 2014 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -89,6 +89,7 @@
#include "tabpivot.h"
#endif // PIVOT_SUPPORT
#include "tabvir.h"
+#include "tabjson.h"
#include "ha_connect.h"
#include "mycat.h"
@@ -139,6 +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, "OEM")) ? TAB_OEM : TAB_NIY;
} // end of GetTypeID
@@ -159,6 +161,7 @@ bool IsFileType(TABTYPE type)
case TAB_XML:
case TAB_INI:
case TAB_VEC:
+ case TAB_JSON:
isfile= true;
break;
default:
@@ -181,6 +184,7 @@ bool IsExactType(TABTYPE type)
case TAB_BIN:
case TAB_DBF:
// case TAB_XML: depends on Multiple || Xpand || Coltype
+// case TAB_JSON: depends on Multiple || Xpand || Coltype
case TAB_VEC:
case TAB_VIR:
exact= true;
@@ -214,7 +218,7 @@ bool IsTypeNullable(TABTYPE type)
} // end of IsTypeNullable
/***********************************************************************/
-/* Return true for indexable table by XINDEX. */
+/* Return true for fixed record length tables. */
/***********************************************************************/
bool IsTypeFixed(TABTYPE type)
{
@@ -250,6 +254,7 @@ bool IsTypeIndexable(TABTYPE type)
case TAB_BIN:
case TAB_VEC:
case TAB_DBF:
+ case TAB_JSON:
idx= true;
break;
default:
@@ -275,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:
@@ -538,6 +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;
default:
sprintf(g->Message, MSG(BAD_TABLE_TYPE), am, name);
} // endswitch
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index 92c2faea676..2f3d75b52fa 100644
--- a/storage/connect/myconn.cpp
+++ b/storage/connect/myconn.cpp
@@ -430,10 +430,11 @@ int MYSQLC::GetResultSize(PGLOBAL g, PSZ sql)
/***********************************************************************/
int MYSQLC::Open(PGLOBAL g, const char *host, const char *db,
const char *user, const char *pwd,
- int pt)
+ int pt, const char *csname)
{
const char *pipe = NULL;
- uint cto = 6000, nrt = 12000;
+ uint cto = 6000, nrt = 12000;
+ my_bool my_true= 1;
m_DB = mysql_init(NULL);
@@ -470,6 +471,18 @@ int MYSQLC::Open(PGLOBAL g, const char *host, const char *db,
} // endif pwd
#endif // 0
+/***********************************************************************/
+/* BUG# 17044 Federated Storage Engine is not UTF8 clean */
+/* Add set names to whatever charset the table is at open of table */
+/* this sets the csname like 'set names utf8'. */
+/***********************************************************************/
+ if (csname)
+ mysql_options(m_DB, MYSQL_SET_CHARSET_NAME, csname);
+
+ // Don't know what this one do but FEDERATED does it
+ mysql_options(m_DB, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY,
+ (char*)&my_true);
+
if (!mysql_real_connect(m_DB, host, user, pwd, db, pt, pipe, CLIENT_MULTI_RESULTS)) {
#if defined(_DEBUG)
sprintf(g->Message, "mysql_real_connect failed: (%d) %s",
diff --git a/storage/connect/myconn.h b/storage/connect/myconn.h
index 65e6531aee4..79b8a43fe5a 100644
--- a/storage/connect/myconn.h
+++ b/storage/connect/myconn.h
@@ -67,7 +67,7 @@ class DllItem MYSQLC {
int GetTableSize(PGLOBAL g, PSZ query);
int Open(PGLOBAL g, const char *host, const char *db,
const char *user= "root", const char *pwd= "*",
- int pt= 0);
+ int pt= 0, const char *csname = NULL);
int KillQuery(ulong id);
int ExecSQL(PGLOBAL g, const char *query, int *w = NULL);
int ExecSQLcmd(PGLOBAL g, const char *query, int *w);
@@ -98,5 +98,6 @@ class DllItem MYSQLC {
int m_Fields; // The number of result fields
int m_Afrw; // The number of affected rows
bool m_Use; // Use or store result set
+ const char *csname; // Table charset name
}; // end of class MYSQLC
diff --git a/storage/connect/mysql-test/connect/r/json.result b/storage/connect/mysql-test/connect/r/json.result
new file mode 100644
index 00000000000..094bb669d18
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/json.result
@@ -0,0 +1,439 @@
+#
+# 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 François 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 François 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 ORDER BY WHO, WEEK, WHAT, AMOUNT;
+WHO WEEK WHAT AMOUNT
+Beth 3 Beer 16.00
+Beth 4 Beer 15.00
+Beth 4 Food 17.00
+Beth 5 Beer 20.00
+Beth 5 Food 12.00
+Janet 3 Beer 18.00
+Janet 3 Car 19.00
+Janet 3 Food 18.00
+Janet 4 Car 17.00
+Janet 5 Beer 14.00
+Janet 5 Beer 19.00
+Janet 5 Car 12.00
+Janet 5 Food 12.00
+Joe 3 Beer 18.00
+Joe 3 Car 20.00
+Joe 3 Food 12.00
+Joe 3 Food 19.00
+Joe 4 Beer 14.00
+Joe 4 Beer 16.00
+Joe 4 Beer 19.00
+Joe 4 Food 17.00
+Joe 4 Food 17.00
+Joe 5 Beer 14.00
+Joe 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;
diff --git a/storage/connect/mysql-test/connect/std_data/biblio.jsn b/storage/connect/mysql-test/connect/std_data/biblio.jsn
new file mode 100644
index 00000000000..bab8fd24305
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/biblio.jsn
@@ -0,0 +1,45 @@
+[
+ {
+ "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
+ }
+]
diff --git a/storage/connect/mysql-test/connect/std_data/expense.jsn b/storage/connect/mysql-test/connect/std_data/expense.jsn
new file mode 100644
index 00000000000..e65ad5261f1
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/expense.jsn
@@ -0,0 +1,158 @@
+[
+ {
+ "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
+ }
+ ]
+ }
+ ]
+ }
+]
diff --git a/storage/connect/mysql-test/connect/std_data/mulexp3.jsn b/storage/connect/mysql-test/connect/std_data/mulexp3.jsn
new file mode 100644
index 00000000000..c228448b073
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/mulexp3.jsn
@@ -0,0 +1,52 @@
+[
+ {
+ "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
+ }
+ ]
+ }
+]
diff --git a/storage/connect/mysql-test/connect/std_data/mulexp4.jsn b/storage/connect/mysql-test/connect/std_data/mulexp4.jsn
new file mode 100644
index 00000000000..0e43ffec07b
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/mulexp4.jsn
@@ -0,0 +1,52 @@
+[
+ {
+ "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
+ }
+ ]
+ }
+]
diff --git a/storage/connect/mysql-test/connect/std_data/mulexp5.jsn b/storage/connect/mysql-test/connect/std_data/mulexp5.jsn
new file mode 100644
index 00000000000..7a707506c2f
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/mulexp5.jsn
@@ -0,0 +1,52 @@
+[
+ {
+ "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
+ }
+ ]
+ }
+]
diff --git a/storage/connect/mysql-test/connect/t/json.test b/storage/connect/mysql-test/connect/t/json.test
new file mode 100644
index 00000000000..a7e630ed0cf
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/json.test
@@ -0,0 +1,247 @@
+--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 ORDER BY WHO, WEEK, WHAT, AMOUNT;
+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
diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h
index 9cc14695977..8642d915211 100644
--- a/storage/connect/odbccat.h
+++ b/storage/connect/odbccat.h
@@ -1,3 +1,7 @@
+// Timeout and net wait defaults
+#define DEFAULT_LOGIN_TIMEOUT -1 // means do not set
+#define DEFAULT_QUERY_TIMEOUT -1 // means do not set
+
/***********************************************************************/
/* ODBC catalog function prototypes. */
/***********************************************************************/
@@ -6,8 +10,8 @@ char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop);
#endif // PROMPT_OK
PQRYRES ODBCDataSources(PGLOBAL g, int maxres, bool info);
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *db, char *table,
- char *colpat, int maxres, bool info);
-PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src);
+ char *colpat, int maxres, int cto, int qto, bool info);
+PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, int cto, int qto);
PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *db, char *tabpat,
- int maxres, bool info);
+ int maxres, int cto, int qto, bool info);
PQRYRES ODBCDrivers(PGLOBAL g, int maxres, bool info);
diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp
index bef735b4a6d..3e616ec8f04 100644
--- a/storage/connect/odbconn.cpp
+++ b/storage/connect/odbconn.cpp
@@ -1,7 +1,7 @@
/************ Odbconn C++ Functions Source Code File (.CPP) ************/
-/* Name: ODBCONN.CPP Version 2.0 */
+/* Name: ODBCONN.CPP Version 2.1 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
/* */
/* This file contains the ODBC connection classes functions. */
/***********************************************************************/
@@ -146,18 +146,25 @@ int TranslateSQLType(int stp, int prec, int& len, char& v)
type = TYPE_DOUBLE;
break;
case SQL_DATETIME: // 9
-// case SQL_DATE: // 9
+ type = TYPE_DATE;
+ len = 19;
+ break;
+ case SQL_TYPE_DATE: // 91
type = TYPE_DATE;
len = 10;
+ v = 'D';
break;
case SQL_INTERVAL: // 10
-// case SQL_TIME: // 10
+ case SQL_TYPE_TIME: // 92
type = TYPE_STRING;
len = 8 + ((prec) ? (prec+1) : 0);
+ v = 'T';
break;
case SQL_TIMESTAMP: // 11
+ case SQL_TYPE_TIMESTAMP: // 93
type = TYPE_DATE;
len = 19 + ((prec) ? (prec+1) : 0);
+ v = 'S';
break;
case SQL_BIGINT: // (-5)
type = TYPE_BIGINT;
@@ -284,7 +291,7 @@ static void ResetNullValues(CATPARM *cap)
/* of an ODBC table that will be retrieved by GetData commands. */
/***********************************************************************/
PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *db, char *table,
- char *colpat, int maxres, bool info)
+ char *colpat, int maxres, int cto, int qto, bool info)
{
int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING,
TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT,
@@ -303,6 +310,8 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *db, char *table,
/************************************************************************/
if (!info) {
ocp = new(g) ODBConn(g, NULL);
+ ocp->SetLoginTimeout((DWORD)cto);
+ ocp->SetQueryTimeout((DWORD)qto);
if (ocp->Open(dsn, 10) < 1) // openReadOnly + noODBCdialog
return NULL;
@@ -379,10 +388,12 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *db, char *table,
/* ODBCSrcCols: constructs the result blocks containing the */
/* description of all the columns of a Srcdef option. */
/**************************************************************************/
-PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src)
+PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, int cto, int qto)
{
ODBConn *ocp = new(g) ODBConn(g, NULL);
+ ocp->SetLoginTimeout((DWORD)cto);
+ ocp->SetQueryTimeout((DWORD)qto);
return ocp->GetMetaData(g, dsn, src);
} // end of ODBCSrcCols
@@ -563,7 +574,7 @@ PQRYRES ODBCDataSources(PGLOBAL g, int maxres, bool info)
/* an ODBC database that will be retrieved by GetData commands. */
/**************************************************************************/
PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *db, char *tabpat,
- int maxres, bool info)
+ int maxres, int cto, int qto, bool info)
{
int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
TYPE_STRING, TYPE_STRING};
@@ -583,6 +594,8 @@ PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *db, char *tabpat,
/* Open the connection with the ODBC data source. */
/**********************************************************************/
ocp = new(g) ODBConn(g, NULL);
+ ocp->SetLoginTimeout((DWORD)cto);
+ ocp->SetQueryTimeout((DWORD)qto);
if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly
return NULL;
@@ -910,10 +923,13 @@ ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp)
m_UpdateOptions = 0;
m_RowsetSize = (DWORD)((tdbp) ? tdbp->Rows : 10);
m_Catver = (tdbp) ? tdbp->Catver : 0;
+ m_Rows = 0;
m_Connect = NULL;
m_Updatable = true;
m_Transact = false;
m_Scrollable = (tdbp) ? tdbp->Scrollable : false;
+ m_First = true;
+ m_Full = false;
m_IDQuoteChar[0] = '"';
m_IDQuoteChar[1] = 0;
//*m_ErrMsg = '\0';
@@ -1068,6 +1084,9 @@ int ODBConn::Open(PSZ ConnectString, DWORD options)
} // endif
/*ver = GetStringInfo(SQL_DRIVER_ODBC_VER);*/
+ // Verify support for required functionality and cache info
+// VerifyConnect(); Deprecated
+ GetConnectInfo();
} catch(DBX *xp) {
// strcpy(g->Message, xp->m_ErrMsg[0]);
strcpy(g->Message, xp->GetErrorMessage(0));
@@ -1076,9 +1095,6 @@ int ODBConn::Open(PSZ ConnectString, DWORD options)
return -1;
} // end try-catch
- // Verify support for required functionality and cache info
- VerifyConnect();
- GetConnectInfo();
return 1;
} // end of Open
@@ -1124,10 +1140,13 @@ void ODBConn::AllocConnect(DWORD Options)
} // endif
#endif // _DEBUG
- rc = SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT, m_LoginTimeout);
+ if ((signed)m_LoginTimeout >= 0) {
+ rc = SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT, m_LoginTimeout);
+
+ if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
+ htrc("Warning: Failure setting login timeout\n");
- if (trace && rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
- htrc("Warning: Failure setting login timeout\n");
+ } // endif Timeout
if (!m_Updatable) {
rc = SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE, SQL_MODE_READ_ONLY);
@@ -1500,6 +1519,20 @@ int ODBConn::Fetch()
ThrowDBX(rc, "Fetch", m_hstmt);
irc = (rc == SQL_NO_DATA_FOUND) ? 0 : (int)crow;
+
+ if (m_First) {
+ // First fetch. Check whether the full table was read
+ if ((m_Full = irc < (signed)m_RowsetSize)) {
+ m_Tdb->Memory = 0; // Not needed anymore
+ m_Rows = irc; // Table size
+ } // endif m_Full
+
+ m_First = false;
+ } // endif m_First
+
+ if (m_Tdb->Memory == 1)
+ m_Rows += irc;
+
} catch(DBX *x) {
if (trace)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
@@ -2150,6 +2183,7 @@ int ODBConn::GetCatInfo(CATPARM *cap)
HSTMT hstmt = NULL;
SQLLEN *vl, *vlen = NULL;
PVAL *pval = NULL;
+ char* *pbuf = NULL;
try {
b = false;
@@ -2226,6 +2260,7 @@ int ODBConn::GetCatInfo(CATPARM *cap)
// Unconditional to handle STRBLK's
pval = (PVAL *)PlugSubAlloc(g, NULL, n * sizeof(PVAL));
vlen = (SQLLEN *)PlugSubAlloc(g, NULL, n * sizeof(SQLLEN));
+ pbuf = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));
// Now bind the column buffers
for (n = 0, crp = qrp->Colresp; crp; crp = crp->Next) {
@@ -2240,7 +2275,13 @@ int ODBConn::GetCatInfo(CATPARM *cap)
} // endif len
pval[n] = AllocateValue(g, crp->Type, len);
- buffer = pval[n]->GetTo_Val();
+
+ if (crp->Type == TYPE_STRING) {
+ pbuf[n] = (char*)PlugSubAlloc(g, NULL, len);
+ buffer = pbuf[n];
+ } else
+ buffer = pval[n]->GetTo_Val();
+
vl = vlen + n;
// n + 1 because column numbers begin with 1
@@ -2288,7 +2329,13 @@ int ODBConn::GetCatInfo(CATPARM *cap)
} // endif rc
for (n = 0, crp = qrp->Colresp; crp; n++, crp = crp->Next) {
- pval[n]->SetNull(vlen[n] == SQL_NULL_DATA);
+ if (vlen[n] == SQL_NULL_DATA)
+ pval[n]->SetNull(true);
+ else if (crp->Type == TYPE_STRING && vlen[n] != SQL_NULL_DATA)
+ pval[n]->SetValue_char(pbuf[n], vlen[n]);
+ else
+ pval[n]->SetNull(false);
+
crp->Kdata->SetValue(pval[n], i);
cap->Vlen[n][i] = vlen[n];
} // endfor crp
@@ -2343,31 +2390,103 @@ int ODBConn::GetCatInfo(CATPARM *cap)
} // end of GetCatInfo
/***********************************************************************/
+/* Allocate a CONNECT result structure from the ODBC result. */
+/***********************************************************************/
+PQRYRES ODBConn::AllocateResult(PGLOBAL g)
+ {
+ bool uns;
+ PODBCCOL colp;
+ PCOLRES *pcrp, crp;
+ PQRYRES qrp;
+
+ if (!m_Rows) {
+ strcpy(g->Message, "Void result");
+ return NULL;
+ } // endif m_Res
+
+ /*********************************************************************/
+ /* Allocate the result storage for future retrieval. */
+ /*********************************************************************/
+ qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
+ pcrp = &qrp->Colresp;
+ qrp->Continued = FALSE;
+ qrp->Truncated = FALSE;
+ qrp->Info = FALSE;
+ qrp->Suball = TRUE;
+ qrp->BadLines = 0;
+ qrp->Maxsize = m_Rows;
+ qrp->Maxres = m_Rows;
+ qrp->Nbcol = 0;
+ qrp->Nblin = 0;
+ qrp->Cursor = 0;
+
+ for (colp = (PODBCCOL)m_Tdb->Columns; colp;
+ colp = (PODBCCOL)colp->GetNext())
+ if (!colp->IsSpecial()) {
+ *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
+ crp = *pcrp;
+ pcrp = &crp->Next;
+ memset(crp, 0, sizeof(COLRES));
+ crp->Ncol = ++qrp->Nbcol;
+ crp->Name = colp->GetName();
+ crp->Type = colp->GetResultType();
+ crp->Prec = colp->GetScale();
+ crp->Length = colp->GetLength();
+ crp->Clen = colp->GetBuflen();
+ uns = colp->IsUnsigned();
+
+ if (!(crp->Kdata = AllocValBlock(g, NULL, crp->Type, m_Rows,
+ crp->Clen, 0, FALSE, TRUE, uns))) {
+ sprintf(g->Message, MSG(INV_RESULT_TYPE),
+ GetFormatType(crp->Type));
+ return NULL;
+ } // endif Kdata
+
+ if (!colp->IsNullable())
+ crp->Nulls = NULL;
+ else {
+ crp->Nulls = (char*)PlugSubAlloc(g, NULL, m_Rows);
+ memset(crp->Nulls, ' ', m_Rows);
+ } // endelse Nullable
+
+ colp->SetCrp(crp);
+ } // endif colp
+
+ *pcrp = NULL;
+//qrp->Nblin = n;
+ return qrp;
+ } // end of AllocateResult
+
+/***********************************************************************/
/* Restart from beginning of result set */
/***********************************************************************/
-bool ODBConn::Rewind(char *sql, ODBCCOL *tocols)
+int ODBConn::Rewind(char *sql, ODBCCOL *tocols)
{
- RETCODE rc;
+ int rc, rbuf = -1;
if (!m_hstmt)
- return false;
+ rbuf = -1;
+ else if (m_Full)
+ rbuf = m_Rows; // No need to "rewind"
+ else if (m_Scrollable) {
+ SQLULEN crow;
- if (m_Scrollable) {
try {
- rc = SQLFetchScroll(m_hstmt, SQL_FETCH_ABSOLUTE, 0);
+ rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_FIRST, 1, &crow, NULL);
- if (rc != SQL_NO_DATA_FOUND)
- ThrowDBX(rc, "SQLFetchScroll", m_hstmt);
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLExtendedFetch", m_hstmt);
+ rbuf = (int)crow;
} catch(DBX *x) {
strcpy(m_G->Message, x->GetErrorMessage(0));
- return true;
+ rbuf = -1;
} // end try/catch
- } else if (ExecDirectSQL(sql, tocols) < 0)
- return true;
+ } else if (ExecDirectSQL(sql, tocols) >= 0)
+ rbuf = 0;
- return false;
+ return rbuf;
} // end of Rewind
/***********************************************************************/
@@ -2382,7 +2501,7 @@ void ODBConn::Close()
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
m_hstmt = NULL;
} // endif m_hstmt
-
+
if (m_hdbc != SQL_NULL_HDBC) {
if (m_Transact) {
rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_COMMIT);
diff --git a/storage/connect/odbconn.h b/storage/connect/odbconn.h
index 1dd2aa2c16e..dfdb9fe7f56 100644
--- a/storage/connect/odbconn.h
+++ b/storage/connect/odbconn.h
@@ -33,10 +33,6 @@
typedef unsigned char *PUCHAR;
#endif // !WIN32
-// Timeout and net wait defaults
-#define DEFAULT_LOGIN_TIMEOUT 15 // seconds to before fail on connect
-#define DEFAULT_QUERY_TIMEOUT 15 // seconds to before fail waiting for results
-
// Field Flags, used to indicate status of fields
//efine SQL_FIELD_FLAG_DIRTY 0x1
//efine SQL_FIELD_FLAG_NULL 0x2
@@ -124,8 +120,9 @@ class ODBConn : public BLOCK {
forceOdbcDialog = 0x0010}; // Always display ODBC connect dialog
int Open(PSZ ConnectString, DWORD Options = 0);
- bool Rewind(char *sql, ODBCCOL *tocols);
+ int Rewind(char *sql, ODBCCOL *tocols);
void Close(void);
+ PQRYRES AllocateResult(PGLOBAL g);
// Attributes
public:
@@ -187,9 +184,12 @@ class ODBConn : public BLOCK {
DWORD m_UpdateOptions;
DWORD m_RowsetSize;
char m_IDQuoteChar[2];
- int m_Catver;
PSZ m_Connect;
+ int m_Catver;
+ int m_Rows;
bool m_Updatable;
bool m_Transact;
bool m_Scrollable;
+ bool m_First;
+ bool m_Full;
}; // end of ODBConn class definition
diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h
index bbbbc1486b6..e61a49ba9f9 100644
--- a/storage/connect/plgdbsem.h
+++ b/storage/connect/plgdbsem.h
@@ -74,9 +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_JCT = 23, /* Junction tables NIY */
- TAB_DMY = 24, /* DMY Dummy tables NIY */
- TAB_NIY = 25}; /* 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) */
@@ -121,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 */
diff --git a/storage/connect/rcmsg.c b/storage/connect/rcmsg.c
index abd74d169cc..9eea944c697 100644
--- a/storage/connect/rcmsg.c
+++ b/storage/connect/rcmsg.c
@@ -31,6 +31,8 @@ char *GetMsgid(int id)
{
char *p = NULL;
+ // This conditional until a real fix is found for MDEV-7304
+#if defined(FRENCH)
if (!stricmp(msglang(), "french"))
switch (id) {
#include "frids.h"
@@ -40,6 +42,7 @@ char *GetMsgid(int id)
} // endswitch(id)
else // English
+#endif // FRENCH
switch (id) {
#include "enids.h"
#if defined(NEWMSG)
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index e469ae40f1f..51d777a7d17 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -202,6 +202,8 @@ TABDEF::TABDEF(void)
Degree = 0;
Pseudo = 0;
Read_Only = false;
+ m_data_charset = NULL;
+ csname = NULL;
} // end of TABDEF constructor
/***********************************************************************/
@@ -224,6 +226,7 @@ bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am)
m_data_charset= data_charset_name ?
get_charset_by_csname(data_charset_name, MY_CS_PRIMARY, 0):
NULL;
+ csname = GetStringCatInfo(g, "Table_charset", NULL);
// Get The column definitions
if ((poff = GetColCatInfo(g)) < 0)
diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h
index a1dfe87dca8..6160ea71680 100644
--- a/storage/connect/reldef.h
+++ b/storage/connect/reldef.h
@@ -68,6 +68,7 @@ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */
friend class CATALOG;
friend class PLUGCAT;
friend class MYCAT;
+ friend class TDBASE;
public:
// Constructor
TABDEF(void); // Constructor
@@ -110,6 +111,7 @@ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */
int Pseudo; /* Bit: 1 ROWID Ok, 2 FILEID Ok */
bool Read_Only; /* true for read only tables */
const CHARSET_INFO *m_data_charset;
+ const char *csname; /* Table charset name */
}; // end of TABDEF
/***********************************************************************/
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index 0ef9625ac9b..ba22da52998 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -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);
diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp
new file mode 100644
index 00000000000..a3c56965794
--- /dev/null
+++ b/storage/connect/tabjson.cpp
@@ -0,0 +1,1322 @@
+/************* 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, (void*)", ", TYPE_STRING);
+ jnp->Op = OP_CNC;
+ } else
+ jnp->Op = OP_ADD;
+
+ } else if (dg) {
+ if (atoi(p) > 0) {
+ // Return nth value
+ jnp->Rank = atoi(p);
+ jnp->Op = OP_EQ;
+ } else // Ignore array
+ jnp->Op = OP_NULL;
+
+ } else if (n == 1) {
+ // Set the Op value;
+ switch (*p) {
+ case '+': jnp->Op = OP_ADD; break;
+ case '*': jnp->Op = OP_MULT; break;
+ case '>': jnp->Op = OP_MAX; break;
+ case '<': jnp->Op = OP_MIN; break;
+ case '#': jnp->Op = OP_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_STRG:
+ case TYPE_INTG:
+ case TYPE_DBL:
+ vp->SetValue_pval(val->GetValue());
+ break;
+ case TYPE_BOOL:
+ if (vp->IsTypeNum())
+ vp->SetValue(val->GetInteger() ? 1 : 0);
+ else
+ vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));
+
+ break;
+ case TYPE_JAR:
+ 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 len, i = 0;
+ HANDLE hFile;
+ MEMMAP mm;
+ PJSON jsp;
+ PJOB objp = NULL;
+ PJAR arp = NULL;
+ PJVAL val = NULL;
+
+ if (Done)
+ return RC_OK;
+ else
+ Done = true;
+
+ // Now open the JSON file
+ PlugSetPath(filename, Txfp->To_File, GetPath());
+
+ /*********************************************************************/
+ /* Create the mapping file object. */
+ /*********************************************************************/
+ hFile = CreateFileMap(g, filename, &mm, MODE_READ, false);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ DWORD drc = GetLastError();
+
+ if (drc != ENOENT || Mode != MODE_INSERT) {
+ if (!(*g->Message))
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "map", (int)drc, filename);
+
+ return RC_FX;
+ } else
+ return MakeNewDoc(g);
+
+ } // endif hFile
+
+ /*********************************************************************/
+ /* Get the file size (assuming file is smaller than 4 GB) */
+ /*********************************************************************/
+ len = mm.lenL;
+ memory = (char *)mm.memory;
+
+ if (!len) { // Empty file
+ CloseFileHandle(hFile);
+ CloseMemMap(memory, len);
+
+ if (Mode == MODE_INSERT)
+ return MakeNewDoc(g);
+
+ } // endif len
+
+ if (!memory) {
+ CloseFileHandle(hFile);
+ sprintf(g->Message, MSG(MAP_VIEW_ERROR), filename, GetLastError());
+ return RC_FX;
+ } // endif Memory
+
+ CloseFileHandle(hFile); // Not used anymore
+ hFile = INVALID_HANDLE_VALUE; // For Fblock
+
+ /*********************************************************************/
+ /* Parse the json file and allocate its tree structure. */
+ /*********************************************************************/
+ g->Message[0] = 0;
+ jsp = Top = ParseJson(g, memory, len, Pretty);
+ CloseMemMap(memory, len);
+
+ if (!jsp && g->Message[0])
+ return RC_FX;
+
+ if (Objname) {
+ objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname) + 1);
+ strcpy(objpath, Objname);
+ } else
+ objpath = NULL;
+
+ /*********************************************************************/
+ /* Find the table in the tree structure. */
+ /*********************************************************************/
+ for (; jsp && objpath; objpath = p) {
+ if ((p = strchr(objpath, ':')))
+ *p++ = 0;
+
+ if (*objpath != '[') { // objpass is a key
+ if (jsp->GetType() != TYPE_JOB) {
+ strcpy(g->Message, "Table path does no match json file");
+ return RC_FX;
+ } // endif Type
+
+ key = objpath;
+ objp = jsp->GetObject();
+ arp = NULL;
+ val = objp->GetValue(key);
+
+ if (!val || !(jsp = val->GetJson())) {
+ sprintf(g->Message, "Cannot find object key %s", key);
+ return RC_FX;
+ } // endif val
+
+ } else if (objpath[strlen(objpath)-1] == ']') {
+ if (jsp->GetType() != TYPE_JAR) {
+ strcpy(g->Message, "Table path does no match json file");
+ return RC_FX;
+ } // endif Type
+
+ arp = jsp->GetArray();
+ objp = NULL;
+ i = atoi(objpath+1) - 1;
+ val = arp->GetValue(i);
+
+ if (!val) {
+ sprintf(g->Message, "Cannot find array value %d", i);
+ return RC_FX;
+ } // endif val
+
+ } else {
+ sprintf(g->Message, "Invalid Table path %s", Objname);
+ return RC_FX;
+ } // endif objpath
+
+ jsp = val->GetJson();
+ } // endfor objpath
+
+ if (jsp && jsp->GetType() == TYPE_JAR)
+ Doc = jsp->GetArray();
+ else {
+ // The table is void or is just one object or one value
+ Doc = new(g) JARRAY;
+
+ if (val) {
+ Doc->AddValue(g, val);
+ Doc->InitArray(g);
+ } else if (jsp) {
+ Doc->AddValue(g, new(g) JVALUE(jsp));
+ Doc->InitArray(g);
+ } // endif val
+
+ if (objp)
+ objp->SetValue(g, new(g) JVALUE(Doc), key);
+ else if (arp)
+ arp->SetValue(g, new(g) JVALUE(Doc), i);
+ else
+ Top = Doc;
+
+ } // endif jsp
+
+ return RC_OK;
+ } // end of MakeDocument
+
+/***********************************************************************/
+/* JSON Cardinality: returns table size in number of rows. */
+/***********************************************************************/
+int TDBJSON::Cardinality(PGLOBAL g)
+ {
+ if (!g)
+ return (Xcol || Multiple) ? 0 : 1;
+ else if (Cardinal < 0)
+ if (!Multiple) {
+ if (MakeDocument(g) == RC_OK)
+ Cardinal = Doc->size();
+
+ } else
+ return 10;
+
+ return Cardinal;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* JSON GetMaxSize: returns table size estimate in number of rows. */
+/***********************************************************************/
+int TDBJSON::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize < 0)
+ MaxSize = Cardinality(g) * ((Xcol) ? Limit : 1);
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* ResetSize: call by TDBMUL when calculating size estimate. */
+/***********************************************************************/
+void TDBJSON::ResetSize(void)
+ {
+ MaxSize = Cardinal = -1;
+ Fpos = -1;
+ N = 0;
+ Done = false;
+ } // end of ResetSize
+
+/***********************************************************************/
+/* TDBJSON is not indexable. */
+/***********************************************************************/
+int TDBJSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
+ {
+ if (pxdf) {
+ strcpy(g->Message, "JSON not indexable when pretty = 2");
+ return RC_FX;
+ } else
+ return RC_OK;
+
+ } // end of MakeIndex
+
+/***********************************************************************/
+/* JSON Access Method opening routine. */
+/***********************************************************************/
+bool TDBJSON::OpenDB(PGLOBAL g)
+ {
+ if (Use == USE_OPEN) {
+ /*******************************************************************/
+ /* Table already open replace it at its beginning. */
+ /*******************************************************************/
+ 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());
+
+ // Serialize the modified table
+ if (!(fop = fopen(filename, "wb"))) {
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "w", (int)errno, filename);
+ strcat(strcat(g->Message, ": "), strerror(errno));
+ } else if ((msg = Serialize(g, Top, fop, Pretty)))
+ puts(msg);
+
+ } // end of CloseDB
+
+/* -------------------------- End of json --------------------------- */
diff --git a/storage/connect/tabjson.h b/storage/connect/tabjson.h
new file mode 100644
index 00000000000..68f79a1526a
--- /dev/null
+++ b/storage/connect/tabjson.h
@@ -0,0 +1,197 @@
+/*************** 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
diff --git a/storage/connect/table.cpp b/storage/connect/table.cpp
index b093e2102c2..cbd5910d8c8 100644
--- a/storage/connect/table.cpp
+++ b/storage/connect/table.cpp
@@ -146,6 +146,7 @@ TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp)
Knum = 0;
Read_Only = (tdp) ? tdp->IsReadOnly() : false;
m_data_charset= (tdp) ? tdp->data_charset() : NULL;
+ csname = (tdp) ? tdp->csname : NULL;
} // end of TDBASE constructor
TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp)
@@ -161,6 +162,7 @@ TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp)
Knum = tdbp->Knum;
Read_Only = tdbp->Read_Only;
m_data_charset= tdbp->m_data_charset;
+ csname = tdbp->csname;
} // end of TDBASE copy constructor
/***********************************************************************/
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp
index 3ec9a1feaee..54627ba43fd 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -777,7 +777,7 @@ int TDBMYSQL::Cardinality(PGLOBAL g)
char query[96];
MYSQLC myc;
- if (myc.Open(g, Host, Database, User, Pwd, Port))
+ if (myc.Open(g, Host, Database, User, Pwd, Port, csname))
return -1;
strcpy(query, "SELECT COUNT(*) FROM ");
@@ -871,7 +871,7 @@ bool TDBMYSQL::OpenDB(PGLOBAL g)
/* servers allowing concurency in getting results ??? */
/*********************************************************************/
if (!Myc.Connected()) {
- if (Myc.Open(g, Host, Database, User, Pwd, Port))
+ if (Myc.Open(g, Host, Database, User, Pwd, Port, csname))
return true;
} // endif Connected
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index bbc17129aaf..2b771bcbead 100644
--- a/storage/connect/tabodbc.cpp
+++ b/storage/connect/tabodbc.cpp
@@ -1,11 +1,11 @@
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABODBC */
/* ------------- */
-/* Version 2.8 */
+/* Version 2.9 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2000-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 2000-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -94,8 +94,8 @@ bool ExactInfo(void);
ODBCDEF::ODBCDEF(void)
{
Connect= Tabname= Tabschema= Tabcat= Srcdef= Qchar= Qrystr= Sep= NULL;
- Catver = Options = Quoted = Maxerr = Maxres = 0;
- Scrollable = Xsrc = false;
+ Catver = Options = Cto = Qto = Quoted = Maxerr = Maxres = 0;
+ Scrollable = Memory = Xsrc = false;
} // end of ODBCDEF constructor
/***********************************************************************/
@@ -130,10 +130,10 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Quoted = GetIntCatInfo("Quoted", 0);
Options = ODBConn::noOdbcDialog;
//Options = ODBConn::noOdbcDialog | ODBConn::useCursorLib;
-
- if ((Scrollable = GetBoolCatInfo("Scrollable", false)))
- Elemt = 0; // Not compatible with extended fetch
-
+ Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
+ Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
+ Scrollable = GetBoolCatInfo("Scrollable", false);
+ Memory = GetBoolCatInfo("Memory", false);
Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
@@ -195,9 +195,12 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
Qrystr = tdp->Qrystr;
Sep = tdp->GetSep();
Options = tdp->Options;
+ Cto = tdp->Cto;
+ Qto = tdp->Qto;
Quoted = MY_MAX(0, tdp->GetQuoted());
Rows = tdp->GetElemt();
Catver = tdp->Catver;
+ Memory = (tdp->Memory) ? 1 : 0;
Scrollable = tdp->Scrollable;
} else {
Connect = NULL;
@@ -208,9 +211,12 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
Qrystr = NULL;
Sep = 0;
Options = 0;
+ Cto = DEFAULT_LOGIN_TIMEOUT;
+ Qto = DEFAULT_QUERY_TIMEOUT;
Quoted = 0;
Rows = 0;
Catver = 0;
+ Memory = 0;
Scrollable = false;
} // endif tdp
@@ -220,6 +226,7 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
//Where = NULL;
MulConn = NULL;
DBQ = NULL;
+ Qrp = NULL;
Fpos = 0;
AftRows = 0;
CurNum = 0;
@@ -238,6 +245,7 @@ TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
Catalog = tdbp->Catalog;
Srcdef = tdbp->Srcdef;
Qrystr = tdbp->Qrystr;
+ Memory = tdbp->Memory;
Scrollable = tdbp->Scrollable;
Quote = tdbp->Quote;
Query = tdbp->Query;
@@ -246,6 +254,8 @@ TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
MulConn = tdbp->MulConn;
DBQ = tdbp->DBQ;
Options = tdbp->Options;
+ Cto = tdbp->Cto;
+ Qto = tdbp->Qto;
Quoted = tdbp->Quoted;
Rows = tdbp->Rows;
Fpos = tdbp->Fpos;
@@ -256,6 +266,7 @@ TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
Rbuf = tdbp->Rbuf;
BufSize = tdbp->BufSize;
Nparm = tdbp->Nparm;
+ Qrp = tdbp->Qrp;
} // end of TDBODBC copy constructor
// Method
@@ -687,6 +698,9 @@ int TDBODBC::Cardinality(PGLOBAL g)
char qry[96], tbn[64];
ODBConn *ocp = new(g) ODBConn(g, this);
+ ocp->SetLoginTimeout((DWORD)Cto);
+ ocp->SetQueryTimeout((DWORD)Qto);
+
if (ocp->Open(Connect, Options) < 1)
return -1;
@@ -758,18 +772,25 @@ bool TDBODBC::OpenDB(PGLOBAL g)
/*******************************************************************/
/* Table already open, just replace it at its beginning. */
/*******************************************************************/
-// if (To_Kindex)
- /*****************************************************************/
- /* Table is to be accessed through a sorted index table. */
- /*****************************************************************/
-// To_Kindex->Reset();
-
-// rewind(Stream); >>>>>>> Something to be done with Cursor <<<<<<<
- if (Ocp->Rewind(Query, (PODBCCOL)Columns)) {
- Ocp->Close();
- return true;
- } // endif Rewind
+ if (Memory == 1) {
+ if ((Qrp = Ocp->AllocateResult(g)))
+ Memory = 2; // Must be filled
+ else
+ Memory = 0; // Allocation failed, don't use it
+
+ } else if (Memory == 2)
+ Memory = 3; // Ok to use memory result
+
+ if (Memory < 3) {
+ // Method will depend on cursor type
+ if ((Rbuf = Ocp->Rewind(Query, (PODBCCOL)Columns)) < 0) {
+ Ocp->Close();
+ return true;
+ } // endif Rewind
+
+ } // endif Memory
+ CurNum = 0;
Fpos = 0;
return false;
} // endif use
@@ -781,9 +802,11 @@ bool TDBODBC::OpenDB(PGLOBAL g)
/* and if so to allocate just a new result set. But this only for */
/* drivers allowing concurency in getting results ??? */
/*********************************************************************/
- if (!Ocp)
+ if (!Ocp) {
Ocp = new(g) ODBConn(g, this);
- else if (Ocp->IsOpen())
+ Ocp->SetLoginTimeout((DWORD)Cto);
+ Ocp->SetQueryTimeout((DWORD)Qto);
+ } else if (Ocp->IsOpen())
Ocp->Close();
if (Ocp->Open(Connect, Options) < 1)
@@ -870,20 +893,29 @@ int TDBODBC::ReadDB(PGLOBAL g)
if (To_Kindex) {
// Direct access of ODBC tables is not implemented yet
strcpy(g->Message, MSG(NO_ODBC_DIRECT));
- longjmp(g->jumper[g->jump_level], GetAmType());
+ return RC_FX;
} // endif To_Kindex
/*********************************************************************/
/* Now start the reading process. */
/* Here is the place to fetch the line(s). */
/*********************************************************************/
- if (++CurNum >= Rbuf) {
- Rbuf = Ocp->Fetch();
- CurNum = 0;
- } // endif CurNum
+ if (Memory != 3) {
+ if (++CurNum >= Rbuf) {
+ Rbuf = Ocp->Fetch();
+ CurNum = 0;
+ } // endif CurNum
- rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
- Fpos++; // Used for progress info
+ rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
+ } else // Getting result from memory
+ rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
+
+ if (rc == RC_OK) {
+ if (Memory == 2)
+ Qrp->Nblin++;
+
+ Fpos++; // Used for memory
+ } // endif rc
if (trace > 1)
htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
@@ -966,6 +998,7 @@ ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
} // endif cprec
// Set additional ODBC access method information for column.
+ Crp = NULL;
//Long = cdp->GetLong();
Long = Precision;
//strcpy(F_Date, cdp->F_Date);
@@ -987,6 +1020,7 @@ ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
/***********************************************************************/
ODBCCOL::ODBCCOL(void) : COLBLK()
{
+ Crp = NULL;
Buf_Type = TYPE_INT; // This is a count(*) column
// Set additional Dos access method information for column.
Long = sizeof(int);
@@ -1005,6 +1039,7 @@ ODBCCOL::ODBCCOL(void) : COLBLK()
/***********************************************************************/
ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
{
+ Crp = col1->Crp;
Long = col1->Long;
//strcpy(F_Date, col1->F_Date);
To_Val = col1->To_Val;
@@ -1070,7 +1105,20 @@ bool ODBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
void ODBCCOL::ReadColumn(PGLOBAL g)
{
PTDBODBC tdbp = (PTDBODBC)To_Tdb;
- int n = tdbp->CurNum;
+ int i = tdbp->Fpos - 1, n = tdbp->CurNum;
+
+ if (tdbp->Memory == 3) {
+ // Get the value from the stored memory
+ if (Crp->Nulls && Crp->Nulls[i] == '*') {
+ Value->Reset();
+ Value->SetNull(true);
+ } else {
+ Value->SetValue_pvblk(Crp->Kdata, i);
+ Value->SetNull(false);
+ } // endif Nulls
+
+ return;
+ } // endif Memory
if (StrLen[n] == SQL_NULL_DATA) {
// Null value
@@ -1078,7 +1126,7 @@ void ODBCCOL::ReadColumn(PGLOBAL g)
Value->SetNull(true);
Value->Reset();
- return;
+ goto put;
} else
Value->SetNull(false);
@@ -1117,6 +1165,21 @@ void ODBCCOL::ReadColumn(PGLOBAL g)
Name, tdbp->Rows, Bufp, Buf_Type, Value->GetCharString(buf));
} // endif Trace
+ put:
+ if (tdbp->Memory != 2)
+ return;
+
+ /*********************************************************************/
+ /* Fill the allocated result structure. */
+ /*********************************************************************/
+ if (Value->IsNull()) {
+ if (Crp->Nulls)
+ Crp->Nulls[i] = '*'; // Null value
+
+ Crp->Kdata->Reset(i);
+ } else
+ Crp->Kdata->SetValue(Value, i);
+
} // end of ReadColumn
/***********************************************************************/
@@ -1350,9 +1413,11 @@ bool TDBXDBC::OpenDB(PGLOBAL g)
/* and if so to allocate just a new result set. But this only for */
/* drivers allowing concurency in getting results ??? */
/*********************************************************************/
- if (!Ocp)
+ if (!Ocp) {
Ocp = new(g) ODBConn(g, this);
- else if (Ocp->IsOpen())
+ Ocp->SetLoginTimeout((DWORD)Cto);
+ Ocp->SetQueryTimeout((DWORD)Qto);
+ } else if (Ocp->IsOpen())
Ocp->Close();
if (Ocp->Open(Connect, Options) < 1)
@@ -1489,6 +1554,8 @@ TDBOTB::TDBOTB(PODEF tdp) : TDBDRV(tdp)
Dsn = tdp->GetConnect();
Schema = tdp->GetTabschema();
Tab = tdp->GetTabname();
+ Cto = tdp->Cto;
+ Qto = tdp->Qto;
} // end of TDBOTB constructor
/***********************************************************************/
@@ -1496,7 +1563,7 @@ TDBOTB::TDBOTB(PODEF tdp) : TDBDRV(tdp)
/***********************************************************************/
PQRYRES TDBOTB::GetResult(PGLOBAL g)
{
- return ODBCTables(g, Dsn, Schema, Tab, Maxres, false);
+ return ODBCTables(g, Dsn, Schema, Tab, Maxres, Cto, Qto, false);
} // end of GetResult
/* ---------------------------TDBOCL class --------------------------- */
@@ -1506,7 +1573,7 @@ PQRYRES TDBOTB::GetResult(PGLOBAL g)
/***********************************************************************/
PQRYRES TDBOCL::GetResult(PGLOBAL g)
{
- return ODBCColumns(g, Dsn, Schema, Tab, NULL, Maxres, false);
+ return ODBCColumns(g, Dsn, Schema, Tab, NULL, Maxres, Cto, Qto, false);
} // end of GetResult
/* ------------------------ End of Tabodbc --------------------------- */
diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h
index f042b0c73ca..d8644c8b6de 100644
--- a/storage/connect/tabodbc.h
+++ b/storage/connect/tabodbc.h
@@ -1,7 +1,7 @@
/*************** Tabodbc H Declares Source Code File (.H) **************/
-/* Name: TABODBC.H Version 1.6 */
+/* Name: TABODBC.H Version 1.8 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2000-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2000-2015 */
/* */
/* This file contains the TDBODBC classes declares. */
/***********************************************************************/
@@ -24,6 +24,7 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
friend class TDBODBC;
friend class TDBXDBC;
friend class TDBDRV;
+ friend class TDBOTB;
public:
// Constructor
ODBCDEF(void);
@@ -56,10 +57,13 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
PSZ Sep; /* Decimal separator */
int Catver; /* ODBC version for catalog functions */
int Options; /* Open connection options */
+ int Cto; /* Open connection timeout */
+ int Qto; /* Query (command) timeout */
int Quoted; /* Identifier quoting level */
int Maxerr; /* Maxerr for an Exec table */
int Maxres; /* Maxres for a catalog table */
bool Scrollable; /* Use scrollable cursor */
+ bool Memory; /* Put result set in memory */
bool Xsrc; /* Execution type */
}; // end of ODBCDEF
@@ -134,6 +138,8 @@ class TDBODBC : public TDBASE {
char *Qrystr; // The original query
char Sep; // The decimal separator
int Options; // Connect options
+ int Cto; // Connect timeout
+ int Qto; // Query timeout
int Quoted; // The identifier quoting level
int Fpos; // Position of last read record
int AftRows; // The number of affected rows
@@ -143,7 +149,9 @@ class TDBODBC : public TDBASE {
int Rbuf; // Number of lines read in buffer
int BufSize; // Size of connect string buffer
int Nparm; // The number of statement parameters
+ int Memory; // 0: No 1: Alloc 2: Put 3: Get
bool Scrollable; // Use scrollable cursor
+ PQRYRES Qrp; // Points to storage result
}; // end of class TDBODBC
/***********************************************************************/
@@ -162,6 +170,7 @@ class ODBCCOL : public COLBLK {
SQLLEN *GetStrLen(void) {return StrLen;}
int GetRank(void) {return Rank;}
// PVBLK GetBlkp(void) {return Blkp;}
+ void SetCrp(PCOLRES crp) {Crp = crp;}
// Methods
virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
@@ -178,6 +187,7 @@ class ODBCCOL : public COLBLK {
// Members
TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
+ PCOLRES Crp; // To storage result
void *Bufp; // To extended buffer
PVBLK Blkp; // To Value Block
//char F_Date[12]; // Internal Date format
@@ -306,6 +316,8 @@ class TDBOTB : public TDBDRV {
char *Dsn; // Points to connection string
char *Schema; // Points to schema name or NULL
char *Tab; // Points to ODBC table name or pattern
+ int Cto; // Connect timeout
+ int Qto; // Query timeout
}; // end of class TDBOTB
/***********************************************************************/
diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp
index 81d00862703..1cc40473433 100644
--- a/storage/connect/value.cpp
+++ b/storage/connect/value.cpp
@@ -330,7 +330,7 @@ int ConvertType(int target, int type, CONV kind, bool match)
/***********************************************************************/
/* AllocateConstant: allocates a constant Value. */
/***********************************************************************/
-PVAL AllocateValue(PGLOBAL g, void *value, short type)
+PVAL AllocateValue(PGLOBAL g, void *value, short type, short prec)
{
PVAL valp;
@@ -351,7 +351,7 @@ PVAL AllocateValue(PGLOBAL g, void *value, short type)
valp = new(g) TYPVAL<longlong>(*(longlong*)value, TYPE_BIGINT);
break;
case TYPE_DOUBLE:
- valp = new(g) TYPVAL<double>(*(double *)value, TYPE_DOUBLE, 2);
+ valp = new(g) TYPVAL<double>(*(double *)value, TYPE_DOUBLE, prec);
break;
case TYPE_TINY:
valp = new(g) TYPVAL<char>(*(char *)value, TYPE_TINY);
@@ -434,6 +434,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 +446,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,
- 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 ---------------------------- */
@@ -542,6 +545,15 @@ BYTE VALUE::TestValue(PVAL vp)
return (n > 0) ? 0x04 : (n < 0) ? 0x02 : 0x01;
} // end of TestValue
+/***********************************************************************/
+/* Compute a function on a string. */
+/***********************************************************************/
+bool VALUE::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
+ {
+ strcpy(g->Message, "Compute not implemented for this value type");
+ return true;
+ } // end of Compute
+
/* -------------------------- Class TYPVAL ---------------------------- */
/***********************************************************************/
@@ -931,6 +943,202 @@ int TYPVAL<TYPE>::CompareValue(PVAL vp)
} // end of CompareValue
/***********************************************************************/
+/* Return max type value if b is true, else min type value. */
+/***********************************************************************/
+template <>
+short TYPVAL<short>::MinMaxVal(bool b)
+ {return (b) ? INT_MAX16 : INT_MIN16;}
+
+template <>
+ushort TYPVAL<ushort>::MinMaxVal(bool b)
+ {return (b) ? UINT_MAX16 : 0;}
+
+template <>
+int TYPVAL<int>::MinMaxVal(bool b)
+ {return (b) ? INT_MAX32 : INT_MIN32;}
+
+template <>
+uint TYPVAL<uint>::MinMaxVal(bool b)
+ {return (b) ? UINT_MAX32 : 0;}
+
+template <>
+longlong TYPVAL<longlong>::MinMaxVal(bool b)
+ {return (b) ? INT_MAX64 : INT_MIN64;}
+
+template <>
+ulonglong TYPVAL<ulonglong>::MinMaxVal(bool b)
+ {return (b) ? 0xFFFFFFFFFFFFFFFFLL : 0;}
+
+template <>
+double TYPVAL<double>::MinMaxVal(bool b)
+ {assert(false); return 0.0;}
+
+template <>
+char TYPVAL<char>::MinMaxVal(bool b)
+ {return (b) ? INT_MAX8 : INT_MIN8;}
+
+template <>
+uchar TYPVAL<uchar>::MinMaxVal(bool b)
+ {return (b) ? UINT_MAX8 : 0;}
+
+/***********************************************************************/
+/* SafeAdd: adds a value and test whether overflow/underflow occured. */
+/***********************************************************************/
+template <class TYPE>
+TYPE TYPVAL<TYPE>::SafeAdd(TYPE n1, TYPE n2)
+ {
+ PGLOBAL& g = Global;
+ TYPE n = n1 + n2;
+
+ if ((n2 > 0) && (n < n1)) {
+ // Overflow
+ strcpy(g->Message, MSG(FIX_OVFLW_ADD));
+ longjmp(g->jumper[g->jump_level], 138);
+ } else if ((n2 < 0) && (n > n1)) {
+ // Underflow
+ strcpy(g->Message, MSG(FIX_UNFLW_ADD));
+ longjmp(g->jumper[g->jump_level], 138);
+ } // endif's n2
+
+ return n;
+ } // end of SafeAdd
+
+template <>
+inline double TYPVAL<double>::SafeAdd(double n1, double n2)
+ {
+ return n1 + n2;
+ } // end of SafeAdd
+
+/***********************************************************************/
+/* SafeMult: multiply values and test whether overflow occured. */
+/***********************************************************************/
+template <class TYPE>
+TYPE TYPVAL<TYPE>::SafeMult(TYPE n1, TYPE n2)
+ {
+ PGLOBAL& g = Global;
+ double n = (double)n1 * (double)n2;
+
+ if (n > MinMaxVal(true)) {
+ // Overflow
+ strcpy(g->Message, MSG(FIX_OVFLW_TIMES));
+ longjmp(g->jumper[g->jump_level], 138);
+ } else if (n < MinMaxVal(false)) {
+ // Underflow
+ strcpy(g->Message, MSG(FIX_UNFLW_TIMES));
+ longjmp(g->jumper[g->jump_level], 138);
+ } // endif's n2
+
+ return (TYPE)n;
+ } // end of SafeMult
+
+template <>
+inline double TYPVAL<double>::SafeMult(double n1, double n2)
+ {
+ return n1 * n2;
+ } // end of SafeMult
+
+/***********************************************************************/
+/* Compute defined functions for the type. */
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
+ {
+ bool rc = false;
+ TYPE val[2];
+
+ assert(np == 2);
+
+ for (int i = 0; i < np; i++)
+ val[i] = GetTypedValue(vp[i]);
+
+ switch (op) {
+ case OP_ADD:
+ Tval = SafeAdd(val[0], val[1]);
+ break;
+ case OP_MULT:
+ 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);
+ break;
+ } // endswitch op
+
+ return rc;
+ } // end of Compute
+
+template <>
+bool TYPVAL<double>::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
+ {
+ bool rc = false;
+ double val[2];
+
+ assert(np == 2);
+
+ for (int i = 0; i < np; i++)
+ val[i] = vp[i]->GetFloatValue();
+
+ switch (op) {
+ case OP_ADD:
+ Tval = val[0] + val[1];
+ break;
+ case OP_MULT:
+ Tval = val[0] * val[1];
+ break;
+ default:
+ rc = Compall(g, vp, np, op);
+ } // endswitch op
+
+ return rc;
+ } // end of Compute
+
+/***********************************************************************/
+/* Compute a function for all types. */
+/***********************************************************************/
+template <class TYPE>
+bool TYPVAL<TYPE>::Compall(PGLOBAL g, PVAL *vp, int np, OPVAL op)
+ {
+ TYPE val[2];
+
+ for (int i = 0; i < np; i++)
+ 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;
+ case OP_MAX:
+ Tval = MY_MAX(val[0], val[1]);
+ break;
+ default:
+// sprintf(g->Message, MSG(BAD_EXP_OPER), op);
+ strcpy(g->Message, "Function not supported");
+ return true;
+ } // endswitch op
+
+ return false;
+ } // end of Compall
+
+/***********************************************************************/
/* FormatValue: This function set vp (a STRING value) to the string */
/* constructed from its own value formated using the fmt format. */
/* This function assumes that the format matches the value type. */
@@ -1410,6 +1618,45 @@ int TYPVAL<PSZ>::CompareValue(PVAL vp)
} // end of CompareValue
/***********************************************************************/
+/* Compute a function on a string. */
+/***********************************************************************/
+bool TYPVAL<PSZ>::Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op)
+ {
+ char *p[2], val[2][32];
+ int i;
+
+ for (i = 0; i < np; i++)
+ p[i] = vp[i]->GetCharString(val[i]);
+
+ switch (op) {
+ case OP_CNC:
+ assert(np == 1 || np == 2);
+
+ if (np == 2)
+ strncpy(Strp, p[0], Len);
+
+ if ((i = Len - (signed)strlen(Strp)) > 0)
+ strncat(Strp, p[np - 1], i);
+
+ break;
+ case OP_MIN:
+ assert(np == 2);
+ strcpy(Strp, (strcmp(p[0], p[1]) < 0) ? p[0] : p[1]);
+ break;
+ case OP_MAX:
+ assert(np == 2);
+ strcpy(Strp, (strcmp(p[0], p[1]) > 0) ? p[0] : p[1]);
+ break;
+ default:
+// sprintf(g->Message, MSG(BAD_EXP_OPER), op);
+ strcpy(g->Message, "Function not supported");
+ return true;
+ } // endswitch op
+
+ return false;
+ } // end of Compute
+
+/***********************************************************************/
/* FormatValue: This function set vp (a STRING value) to the string */
/* constructed from its own value formated using the fmt format. */
/* This function assumes that the format matches the value type. */
@@ -2187,10 +2434,11 @@ static void TIME_to_localtime(struct tm *tm, const MYSQL_TIME *ltime)
tm->tm_year= ltime->year - 1900;
tm->tm_mon= ltime->month - 1;
tm->tm_mday= ltime->day;
+ mktime(tm); // set tm->tm_wday tm->yday fields to get proper day name (OB)
tm->tm_hour= ltime->hour;
tm->tm_min= ltime->minute;
tm->tm_sec= ltime->second;
-}
+} // end of TIME_to_localtime
// Added by Alexander Barkov
static struct tm *gmtime_mysql(const time_t *timep, struct tm *tm)
@@ -2199,7 +2447,7 @@ static struct tm *gmtime_mysql(const time_t *timep, struct tm *tm)
thd_gmt_sec_to_TIME(current_thd, &ltime, (my_time_t) *timep);
TIME_to_localtime(tm, &ltime);
return tm;
-}
+} // end of gmtime_mysql
/***********************************************************************/
/* GetGmTime: returns a pointer to a static tm structure obtained */
@@ -2511,6 +2759,8 @@ char *DTVAL::ShowValue(char *buf, int len)
if (!Null) {
size_t m, n = 0;
struct tm tm, *ptm = GetGmTime(&tm);
+
+
if (Len < len) {
p = buf;
@@ -2596,7 +2846,7 @@ bool DTVAL::WeekNum(PGLOBAL g, int& nval)
/***********************************************************************/
bool DTVAL::FormatValue(PVAL vp, char *fmt)
{
- char *buf = (char*)vp->GetTo_Val(); // Should be big enough
+ char *buf = (char*)vp->GetTo_Val(); // Should be big enough
struct tm tm, *ptm = GetGmTime(&tm);
if (trace > 1)
diff --git a/storage/connect/value.h b/storage/connect/value.h
index 3ce7027aeeb..c5aeb5c2a2f 100644
--- a/storage/connect/value.h
+++ b/storage/connect/value.h
@@ -29,12 +29,9 @@ enum CONV {CNV_ANY = 0, /* Convert to any type */
class CONSTANT; // For friend setting
typedef struct _datpar *PDTP; // For DTVAL
-
/***********************************************************************/
/* Utilities used to test types and to allocated values. */
/***********************************************************************/
-PVAL AllocateValue(PGLOBAL, void *, short);
-
// Exported functions
DllExport PSZ GetTypeName(int);
DllExport int GetTypeSize(int, int);
@@ -47,6 +44,7 @@ DllExport int GetFormatType(char);
DllExport bool IsTypeChar(int type);
DllExport bool IsTypeNum(int type);
DllExport int ConvertType(int, int, CONV, bool match = false);
+DllExport PVAL AllocateValue(PGLOBAL, void *, short, short = 2);
DllExport PVAL AllocateValue(PGLOBAL, PVAL, int = TYPE_VOID, int = 0);
DllExport PVAL AllocateValue(PGLOBAL, int, int len = 0, int prec = 0,
bool uns = false, PSZ fmt = NULL);
@@ -114,6 +112,7 @@ class DllExport VALUE : public BLOCK {
virtual char *ShowValue(char *buf, int len = 0) = 0;
virtual char *GetCharString(char *p) = 0;
virtual bool IsEqual(PVAL vp, bool chktype) = 0;
+ virtual bool Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op);
virtual bool FormatValue(PVAL vp, char *fmt) = 0;
protected:
@@ -149,9 +148,9 @@ class DllExport TYPVAL : public VALUE {
virtual bool IsZero(void) {return Tval == 0;}
virtual void Reset(void) {Tval = 0;}
virtual int GetValLen(void);
- virtual int GetValPrec() {return 0;}
+ virtual int GetValPrec() {return Prec;}
virtual int GetSize(void) {return sizeof(TYPE);}
- virtual PSZ GetCharValue(void) {return VALUE::GetCharValue();}
+//virtual PSZ GetCharValue(void) {return VALUE::GetCharValue();}
virtual char GetTinyValue(void) {return (char)Tval;}
virtual uchar GetUTinyValue(void) {return (uchar)Tval;}
virtual short GetShortValue(void) {return (short)Tval;}
@@ -184,12 +183,18 @@ class DllExport TYPVAL : public VALUE {
virtual char *ShowValue(char *buf, int);
virtual char *GetCharString(char *p);
virtual bool IsEqual(PVAL vp, bool chktype);
+ virtual bool Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op);
virtual bool SetConstFormat(PGLOBAL, FORMAT&);
virtual bool FormatValue(PVAL vp, char *fmt);
virtual void Print(PGLOBAL g, FILE *, uint);
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);
+ bool Compall(PGLOBAL g, PVAL *vp, int np, OPVAL op);
+
// Default constructor not to be used
TYPVAL(void) : VALUE(TYPE_ERROR) {}
@@ -253,6 +258,7 @@ class DllExport TYPVAL<PSZ>: public VALUE {
virtual char *ShowValue(char *buf, int);
virtual char *GetCharString(char *p);
virtual bool IsEqual(PVAL vp, bool chktype);
+ virtual bool Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op);
virtual bool FormatValue(PVAL vp, char *fmt);
virtual bool SetConstFormat(PGLOBAL, FORMAT&);
diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h
index 49fbbb0de26..501a5e87cfa 100644
--- a/storage/connect/xtable.h
+++ b/storage/connect/xtable.h
@@ -209,6 +209,7 @@ class DllExport TDBASE : public TDB {
int Knum; // Size of key arrays
bool Read_Only; // True for read only tables
const CHARSET_INFO *m_data_charset;
+ const char *csname; // Table charset name
}; // end of class TDBASE
/***********************************************************************/