summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2015-03-19 12:21:08 +0100
committerOlivier Bertrand <bertrandop@gmail.com>2015-03-19 12:21:08 +0100
commit7733b247e1fae603b8a439cf03bc47c345a88370 (patch)
tree055ad753211126eb244ece16cd328fa893168267
parent73d042755345bbc113f6711ce03fd932e72aacd7 (diff)
downloadmariadb-git-7733b247e1fae603b8a439cf03bc47c345a88370.tar.gz
Same changes than in version 10.0.17
-rw-r--r--storage/connect/CMakeLists.txt8
-rw-r--r--storage/connect/array.h1
-rw-r--r--storage/connect/checklvl.h7
-rw-r--r--storage/connect/connect.cc5
-rw-r--r--storage/connect/filamap.cpp5
-rw-r--r--storage/connect/filamtxt.cpp8
-rwxr-xr-xstorage/connect/filamvct.cpp8
-rw-r--r--storage/connect/global.h2
-rw-r--r--storage/connect/ha_connect.cc338
-rw-r--r--storage/connect/ha_connect.h8
-rw-r--r--storage/connect/json.cpp187
-rw-r--r--storage/connect/json.h36
-rw-r--r--storage/connect/jsonudf.cpp626
-rw-r--r--storage/connect/libdoc.cpp5
-rw-r--r--storage/connect/myconn.cpp5
-rw-r--r--storage/connect/myutil.cpp22
-rw-r--r--storage/connect/odbccat.h14
-rw-r--r--storage/connect/odbconn.cpp159
-rw-r--r--storage/connect/odbconn.h20
-rw-r--r--storage/connect/plgdbsem.h10
-rw-r--r--storage/connect/plgdbutl.cpp27
-rw-r--r--storage/connect/plugutil.c28
-rw-r--r--storage/connect/reldef.cpp33
-rw-r--r--storage/connect/tabdos.h1
-rw-r--r--storage/connect/tabjson.cpp1126
-rw-r--r--storage/connect/tabjson.h56
-rw-r--r--storage/connect/tabmul.cpp11
-rw-r--r--storage/connect/tabmysql.cpp15
-rw-r--r--storage/connect/taboccur.cpp10
-rw-r--r--storage/connect/tabodbc.cpp237
-rw-r--r--storage/connect/tabodbc.h15
-rw-r--r--storage/connect/tabpivot.cpp28
-rw-r--r--storage/connect/tabpivot.h3
-rw-r--r--storage/connect/tabtbl.cpp14
-rw-r--r--storage/connect/tabutil.cpp33
-rw-r--r--storage/connect/tabutil.h9
-rw-r--r--storage/connect/tabxcl.cpp2
-rw-r--r--storage/connect/tabxcl.h1
-rw-r--r--storage/connect/valblk.cpp9
-rw-r--r--storage/connect/valblk.h6
-rw-r--r--storage/connect/value.cpp11
-rw-r--r--storage/connect/xindex.h1
-rw-r--r--storage/connect/xobject.cpp25
-rw-r--r--storage/connect/xobject.h1
-rw-r--r--storage/connect/xtable.h6
45 files changed, 2509 insertions, 673 deletions
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt
index 931a811d897..2b1e458e242 100644
--- a/storage/connect/CMakeLists.txt
+++ b/storage/connect/CMakeLists.txt
@@ -21,10 +21,10 @@ 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 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
+filter.cpp json.cpp jsonudf.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
diff --git a/storage/connect/array.h b/storage/connect/array.h
index 4a818414e9c..6fb38ae6b47 100644
--- a/storage/connect/array.h
+++ b/storage/connect/array.h
@@ -50,6 +50,7 @@ class DllExport ARRAY : public XOBJECT, public CSORT { // Array descblock
// void SetCorrel(bool b) {Correlated = b;}
// Methods
+ using XOBJECT::GetIntValue;
virtual void Reset(void) {Bot = -1;}
virtual int Qcompare(int *, int *);
virtual bool Compare(PXOB) {assert(FALSE); return FALSE;}
diff --git a/storage/connect/checklvl.h b/storage/connect/checklvl.h
index d1e37afbc93..0c234dfb8b8 100644
--- a/storage/connect/checklvl.h
+++ b/storage/connect/checklvl.h
@@ -40,4 +40,11 @@ enum USETEMP {TMP_NO = 0, /* Never */
TMP_FORCE = 3, /* Forced for MAP tables */
TMP_TEST = 4}; /* Testing value */
+/***********************************************************************/
+/* Following definitions indicate conversion of TEXT columns. */
+/***********************************************************************/
+enum TYPCONV {TPC_NO = 0, /* Never */
+ TPC_YES = 1, /* Always */
+ TPC_SKIP = 2}; /* Skip TEXT columns */
+
#endif // _CHKLVL_DEFINED_
diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
index a54d8ebcc44..1b0db6dca6b 100644
--- a/storage/connect/connect.cc
+++ b/storage/connect/connect.cc
@@ -469,9 +469,12 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
} while (rc == RC_NF);
+ if (rc == RC_OK)
+ rc= EvalColumns(g, tdbp, false);
+
err:
g->jump_level--;
- return (rc != RC_OK) ? rc : EvalColumns(g, tdbp, false);
+ return rc;
} // end of CntReadNext
/***********************************************************************/
diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp
index 08f87e2b836..1e65fa2a413 100644
--- a/storage/connect/filamap.cpp
+++ b/storage/connect/filamap.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -207,8 +207,7 @@ bool MAPFAM::OpenTableFile(PGLOBAL g)
/*******************************************************************/
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
fp->Type = TYPE_FB_MAP;
- fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy((char*)fp->Fname, filename);
+ fp->Fname = PlugDup(g, filename);
fp->Next = dbuserp->Openlist;
dbuserp->Openlist = fp;
fp->Count = 1;
diff --git a/storage/connect/filamtxt.cpp b/storage/connect/filamtxt.cpp
index 675c021fe51..eb4e026ee8a 100644
--- a/storage/connect/filamtxt.cpp
+++ b/storage/connect/filamtxt.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -289,8 +289,7 @@ bool TXTFAM::AddListValue(PGLOBAL g, int type, void *val, PPARM *top)
pp->Intval = *(int*)val;
break;
// case TYPE_STRING:
-// pp->Value = PlugSubAlloc(g, NULL, strlen((char*)val) + 1);
-// strcpy((char*)pp->Value, (char*)val);
+// pp->Value = PlugDup(g, (char*)val);
// break;
case TYPE_PCHAR:
pp->Value = val;
@@ -325,8 +324,7 @@ int TXTFAM::StoreValues(PGLOBAL g, bool upd)
if (Tdbp->PrepareWriting(g))
return RC_FX;
- buf = (char*)PlugSubAlloc(g, NULL, strlen(Tdbp->GetLine()) + 1);
- strcpy(buf, Tdbp->GetLine());
+ buf = PlugDup(g, Tdbp->GetLine());
rc = AddListValue(g, TYPE_PCHAR, buf, &To_Upd);
} // endif upd
diff --git a/storage/connect/filamvct.cpp b/storage/connect/filamvct.cpp
index c449347bbcb..e1f11bbf4cf 100755
--- a/storage/connect/filamvct.cpp
+++ b/storage/connect/filamvct.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -1451,8 +1451,7 @@ bool VCMFAM::OpenTableFile(PGLOBAL g)
/*******************************************************************/
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
fp->Type = TYPE_FB_MAP;
- fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy((char*)fp->Fname, filename);
+ fp->Fname = PlugDup(g, filename);
fp->Next = dbuserp->Openlist;
dbuserp->Openlist = fp;
fp->Count = 1;
@@ -2844,8 +2843,7 @@ bool VMPFAM::MapColumnFile(PGLOBAL g, MODE mode, int i)
/*******************************************************************/
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
fp->Type = TYPE_FB_MAP;
- fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy((char*)fp->Fname, filename);
+ fp->Fname = PlugDup(g, filename);
fp->Next = dup->Openlist;
dup->Openlist = fp;
fp->Count = 1;
diff --git a/storage/connect/global.h b/storage/connect/global.h
index 88e5094d6d2..a67bb605755 100644
--- a/storage/connect/global.h
+++ b/storage/connect/global.h
@@ -235,7 +235,7 @@ typedef struct _global { /* Global structure */
void *Xchk; /* indexes in create/alter */
short Alchecked; /* Checked for ALTER */
short Mrr; /* True when doing mrr */
- short Trace;
+ int N; /* Utility */
int jump_level;
jmp_buf jumper[MAX_JUMP + 2];
} GLOBAL;
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index ba594bdafb0..8d7ab3a7652 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -165,23 +165,24 @@
/***********************************************************************/
/* Initialize the ha_connect static members. */
/***********************************************************************/
-#define SZCONV 8192
-#define SZWORK 67108864 // Default work area size 64M
-#define SZWMIN 4194304 // Minimum work area size 4M
+#define SZCONV 8192
+#define SZWORK 67108864 // Default work area size 64M
+#define SZWMIN 4194304 // Minimum work area size 4M
+#define JSONMAX 10 // JSON Default max grp size
extern "C" {
- char version[]= "Version 1.03.0006 January 13, 2015";
- char compver[]= "Version 1.03.0006 " __DATE__ " " __TIME__;
+ char version[]= "Version 1.03.0006 March 16, 2015";
#if defined(WIN32)
+ char compver[]= "Version 1.03.0006 " __DATE__ " " __TIME__;
char slash= '\\';
#else // !WIN32
char slash= '/';
#endif // !WIN32
// int trace= 0; // The general trace value
- ulong xconv= 0; // The type conversion option
- int zconv= 0; // The text conversion size
+// ulong xconv= 0; // The type conversion option
+// int zconv= 0; // The text conversion size
} // extern "C"
#if defined(XMAP)
@@ -210,11 +211,16 @@ extern "C" {
/***********************************************************************/
PQRYRES OEMColumns(PGLOBAL g, PTOS topt, char *tab, char *db, bool info);
PQRYRES VirColumns(PGLOBAL g, char *tab, char *db, bool info);
+PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
+ int pretty, int lvl, int mxr, bool info);
void PushWarning(PGLOBAL g, THD *thd, int level);
bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
const char *db, char *tab, const char *src, int port);
bool ExactInfo(void);
USETEMP UseTemp(void);
+int GetConvSize(void);
+TYPCONV GetTypeConv(void);
+uint GetJsonGrpSize(void);
uint GetWorkSize(void);
void SetWorkSize(uint);
extern "C" const char *msglang(void);
@@ -289,6 +295,44 @@ static MYSQL_THDVAR_UINT(work_size,
"Size of the CONNECT work area.",
NULL, NULL, SZWORK, SZWMIN, UINT_MAX, 1);
+// Size used when converting TEXT columns to VARCHAR
+static MYSQL_THDVAR_INT(conv_size,
+ PLUGIN_VAR_RQCMDARG, // opt
+ "Size used when converting TEXT columns.",
+ NULL, NULL, SZCONV, 0, 65500, 1);
+
+/**
+ Type conversion:
+ no: Unsupported types -> TYPE_ERROR
+ yes: TEXT -> VARCHAR
+ skip: skip unsupported type columns in Discovery
+*/
+const char *xconv_names[]=
+{
+ "NO", "YES", "SKIP", NullS
+};
+
+TYPELIB xconv_typelib=
+{
+ array_elements(xconv_names) - 1, "xconv_typelib",
+ xconv_names, NULL
+};
+
+static MYSQL_THDVAR_ENUM(
+ type_conv, // name
+ PLUGIN_VAR_RQCMDARG, // opt
+ "Unsupported types conversion.", // comment
+ NULL, // check
+ NULL, // update function
+ 0, // def (no)
+ &xconv_typelib); // typelib
+
+// Estimate max number of rows for JSON aggregate functions
+static MYSQL_THDVAR_UINT(json_grp_size,
+ PLUGIN_VAR_RQCMDARG, // opt
+ "max number of rows for JSON aggregate functions.",
+ NULL, NULL, JSONMAX, 1, INT_MAX, 1);
+
#if defined(XMSG) || defined(NEWMSG)
const char *language_names[]=
{
@@ -317,6 +361,9 @@ static MYSQL_THDVAR_ENUM(
extern "C" int GetTraceValue(void) {return THDVAR(current_thd, xtrace);}
bool ExactInfo(void) {return THDVAR(current_thd, exact_info);}
USETEMP UseTemp(void) {return (USETEMP)THDVAR(current_thd, use_tempfile);}
+int GetConvSize(void) {return THDVAR(current_thd, conv_size);}
+TYPCONV GetTypeConv(void) {return (TYPCONV)THDVAR(current_thd, type_conv);}
+uint GetJsonGrpSize(void) {return THDVAR(current_thd, json_grp_size);}
uint GetWorkSize(void) {return THDVAR(current_thd, work_size);}
void SetWorkSize(uint n)
{
@@ -598,7 +645,11 @@ static int connect_init_func(void *p)
}
#endif // 0 (LINUX)
+#if defined(WIN32)
sql_print_information("CONNECT: %s", compver);
+#else // !WIN32
+ sql_print_information("CONNECT: %s", version);
+#endif // !WIN32
#ifdef LIBXML2_SUPPORT
XmlInitParserLib();
@@ -906,7 +957,7 @@ ulonglong ha_connect::table_flags() const
// HA_NULL_IN_KEY | not implemented yet
// HA_FAST_KEY_READ | causes error when sorting (???)
HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER |
- HA_NO_BLOBS | HA_CAN_TABLE_CONDITION_PUSHDOWN;
+ HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN;
ha_connect *hp= (ha_connect*)this;
PTOS pos= hp->GetTableOptionStruct();
@@ -934,6 +985,9 @@ ulonglong ha_connect::table_flags() const
char *GetListOption(PGLOBAL g, const char *opname,
const char *oplist, const char *def)
{
+ if (!oplist)
+ return (char*)def;
+
char key[16], val[256];
char *pk, *pv, *pn;
char *opval= (char*) def;
@@ -968,8 +1022,7 @@ char *GetListOption(PGLOBAL g, const char *opname,
} // endif pv
if (!stricmp(opname, key)) {
- opval= (char*)PlugSubAlloc(g, NULL, strlen(val) + 1);
- strcpy(opval, val);
+ opval= PlugDup(g, val);
break;
} else if (!pn)
break;
@@ -997,8 +1050,12 @@ char *ha_connect::GetRealString(const char *s)
char *sv;
if (IsPartitioned() && s) {
- sv= (char*)PlugSubAlloc(xp->g, NULL, strlen(s) + strlen(partname));
+// sv= (char*)PlugSubAlloc(xp->g, NULL, strlen(s) + strlen(partname));
+ // With wrong string pattern, the size of the constructed string
+ // can be more than strlen(s) + strlen(partname)
+ sv= (char*)PlugSubAlloc(xp->g, NULL, 0);
sprintf(sv, s, partname);
+ PlugSubAlloc(xp->g, NULL, strlen(sv) + 1);
} else
sv= (char*)s;
@@ -1064,9 +1121,16 @@ char *ha_connect::GetStringOption(char *opname, char *sdef)
} // endif Table_charset
- if (!opval && options && options->oplist)
+ if (!opval && options && options->oplist) {
opval= GetListOption(xp->g, opname, options->oplist);
+ if (opval && (!stricmp(opname, "connect")
+ || !stricmp(opname, "tabname")
+ || !stricmp(opname, "filename")))
+ opval = GetRealString(opval);
+
+ } // endif opval
+
if (!opval) {
if (sdef && !strcmp(sdef, "*")) {
// Return the handler default value
@@ -1443,8 +1507,7 @@ PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
// Now get index information
pn= (char*)s->keynames.type_names[n];
- name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
- strcpy(name, pn); // This is probably unuseful
+ name= PlugDup(g, pn);
unique= (kp.flags & 1) != 0;
pkp= NULL;
@@ -1454,8 +1517,7 @@ PIXDEF ha_connect::GetIndexInfo(TABLE_SHARE *s)
// Get the the key parts info
for (int k= 0; (unsigned)k < kp.user_defined_key_parts; k++) {
pn= (char*)kp.key_part[k].field->field_name;
- name= (char*)PlugSubAlloc(g, NULL, strlen(pn) + 1);
- strcpy(name, pn); // This is probably unuseful
+ name= PlugDup(g, pn);
// Allocate the key part description block
kpp= new(g) KPARTDEF(name, k + 1);
@@ -2467,6 +2529,8 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
char *body= filp->Body;
unsigned int i;
bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
+ bool nonul= (tty == TYPE_AM_ODBC && (tdbp->GetMode() == MODE_INSERT ||
+ tdbp->GetMode() == MODE_DELETE));
OPVAL vop= OP_XX;
if (!cond)
@@ -2484,7 +2548,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
if (trace)
htrc("Cond: Ftype=%d name=%s\n", cond_item->functype(),
- cond_item->func_name());
+ cond_item->func_name());
switch (cond_item->functype()) {
case Item_func::COND_AND_FUNC: vop= OP_AND; break;
@@ -2503,7 +2567,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
for (i= 0; i < arglist->elements; i++)
if ((subitem= li++)) {
if (!CheckCond(g, filp, tty, subitem)) {
- if (vop == OP_OR)
+ if (vop == OP_OR || nonul)
return NULL;
else
*p2= 0;
@@ -2599,6 +2663,8 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
if (trace) {
htrc("Field index=%d\n", pField->field->field_index);
htrc("Field name=%s\n", pField->field->field_name);
+ htrc("Field type=%d\n", pField->field->type());
+ htrc("Field_type=%d\n", args[i]->field_type());
} // endif trace
// IN and BETWEEN clauses should be col VOP list
@@ -2618,8 +2684,9 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
char buff[256];
String *res, tmp(buff, sizeof(buff), &my_charset_bin);
Item_basic_constant *pval= (Item_basic_constant *)args[i];
+ Item::Type type= args[i]->real_type();
- switch (args[i]->real_type()) {
+ switch (type) {
case COND::STRING_ITEM:
case COND::INT_ITEM:
case COND::REAL_ITEM:
@@ -2644,10 +2711,64 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
if (!x) {
// Append the value to the filter
- if (args[i]->field_type() == MYSQL_TYPE_VARCHAR)
- strcat(strncat(strcat(body, "'"), res->ptr(), res->length()), "'");
- else
- strncat(body, res->ptr(), res->length());
+ switch (args[i]->field_type()) {
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATETIME:
+ if (tty == TYPE_AM_ODBC) {
+ strcat(body, "{ts '");
+ strcat(strncat(body, res->ptr(), res->length()), "'}");
+ break;
+ } // endif ODBC
+
+ case MYSQL_TYPE_DATE:
+ if (tty == TYPE_AM_ODBC) {
+ strcat(body, "{d '");
+ strcat(strncat(body, res->ptr(), res->length()), "'}");
+ break;
+ } // endif ODBC
+
+ case MYSQL_TYPE_TIME:
+ if (tty == TYPE_AM_ODBC) {
+ strcat(body, "{t '");
+ strcat(strncat(body, res->ptr(), res->length()), "'}");
+ break;
+ } // endif ODBC
+
+ case MYSQL_TYPE_VARCHAR:
+ if (tty == TYPE_AM_ODBC && i) {
+ switch (args[0]->field_type()) {
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATETIME:
+ strcat(body, "{ts '");
+ strncat(body, res->ptr(), res->length());
+ strcat(body, "'}");
+ break;
+ case MYSQL_TYPE_DATE:
+ strcat(body, "{d '");
+ strncat(body, res->ptr(), res->length());
+ strcat(body, "'}");
+ break;
+ case MYSQL_TYPE_TIME:
+ strcat(body, "{t '");
+ strncat(body, res->ptr(), res->length());
+ strcat(body, "'}");
+ break;
+ default:
+ strcat(body, "'");
+ strncat(body, res->ptr(), res->length());
+ strcat(body, "'");
+ } // endswitch field type
+
+ } else {
+ strcat(body, "'");
+ strncat(body, res->ptr(), res->length());
+ strcat(body, "'");
+ } // endif tty
+
+ break;
+ default:
+ strncat(body, res->ptr(), res->length());
+ } // endswitch field type
} else {
if (args[i]->field_type() == MYSQL_TYPE_VARCHAR) {
@@ -2753,7 +2874,7 @@ const COND *ha_connect::cond_push(const COND *cond)
} else if (x && cond)
tdbp->SetCondFil(filp); // Wrong filter
- } else
+ } else if (tty != TYPE_AM_JSN && tty != TYPE_AM_JSON)
tdbp->SetFilter(CondFilter(g, (Item *)cond));
fin:
@@ -3232,6 +3353,7 @@ int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len
if (trace > 1)
htrc("ReadIndexed: op=%d rc=%d\n", op, rc);
+ table->status= (rc == RC_OK) ? 0 : STATUS_NOT_FOUND;
return rc;
} // end of ReadIndexed
@@ -3348,6 +3470,7 @@ int ha_connect::index_first(uchar *buf)
else if (indexing < 0)
rc= HA_ERR_INTERNAL_ERROR;
else if (CntRewindTable(xp->g, tdbp)) {
+ table->status= STATUS_NOT_FOUND;
rc= HA_ERR_INTERNAL_ERROR;
} else
rc= rnd_next(buf);
@@ -3547,6 +3670,7 @@ int ha_connect::rnd_next(uchar *buf)
xp->fnd= xp->nfd= 0;
} // endif nrd
+ table->status= (!rc) ? 0 : STATUS_NOT_FOUND;
DBUG_RETURN(rc);
} // end of rnd_next
@@ -3987,6 +4111,7 @@ MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
case SQLCOM_UPDATE_MULTI:
case SQLCOM_SELECT:
case SQLCOM_OPTIMIZE:
+ case SQLCOM_SET_OPTION:
break;
case SQLCOM_LOCK_TABLES:
locked= 1;
@@ -4612,12 +4737,12 @@ static char *encode(PGLOBAL g, const char *cnm)
@return
Return 0 if ok
*/
-static bool add_field(String *sql, const char *field_name, int typ,
- int len, int dec, char *key, uint tm, const char *rem,
- char *dft, char *xtra, int flag, bool dbf, char v)
+static bool add_field(String *sql, const char *field_name, int typ, int len,
+ int dec, char *key, uint tm, const char *rem, char *dft,
+ char *xtra, char *fmt, int flag, bool dbf, char v)
{
char var = (len > 255) ? 'V' : v;
- bool error= false;
+ bool q, error= false;
const char *type= PLGtoMYSQLtype(typ, dbf, var);
error|= sql->append('`');
@@ -4658,7 +4783,12 @@ static bool add_field(String *sql, const char *field_name, int typ,
if (dft && *dft) {
error|= sql->append(" DEFAULT ");
- if (!IsTypeNum(typ)) {
+ if (typ == TYPE_DATE)
+ q = (strspn(dft, "0123456789 -:/") == strlen(dft));
+ else
+ q = !IsTypeNum(typ);
+
+ if (q) {
error|= sql->append("'");
error|= sql->append_for_single_quote(dft, strlen(dft));
error|= sql->append("'");
@@ -4678,6 +4808,12 @@ static bool add_field(String *sql, const char *field_name, int typ,
error|= sql->append("'");
} // endif rem
+ if (fmt && *fmt) {
+ error|= sql->append(" FIELD_FORMAT='");
+ error|= sql->append_for_single_quote(fmt, strlen(fmt));
+ error|= sql->append("'");
+ } // endif flag
+
if (flag) {
error|= sql->append(" FLAG=");
error|= sql->append_ulonglong(flag);
@@ -4821,13 +4957,16 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
const char *fncn= "?";
const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src;
const char *col, *ocl, *rnk, *pic, *fcl, *skc;
- char *tab, *dsn, *shm, *dpath;
+ char *tab, *dsn, *shm, *dpath, *objn;
#if defined(WIN32)
char *nsp= NULL, *cls= NULL;
#endif // WIN32
int port= 0, hdr= 0, mxr __attribute__((unused))= 0, mxe= 0, rc= 0;
- int cop __attribute__((unused)) = 0;
+ int cop __attribute__((unused))= 0, pty= 2, lrecl= 0, lvl= 0;
#if defined(ODBC_SUPPORT)
+ POPARM sop = NULL;
+ char *ucnc = NULL;
+ bool cnc= false;
int cto= -1, qto= -1;
#endif // ODBC_SUPPORT
uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL);
@@ -4853,7 +4992,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
if (!g)
return HA_ERR_INTERNAL_ERROR;
- user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= dsn= NULL;
+ user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= dsn= objn= NULL;
// Get the useful create options
ttp= GetTypeID(topt->type);
@@ -4869,10 +5008,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
hdr= (int)topt->header;
tbl= topt->tablist;
col= topt->colist;
+ lrecl= (int)topt->lrecl;
if (topt->oplist) {
host= GetListOption(g, "host", topt->oplist, "localhost");
- user= GetListOption(g, "user", topt->oplist, "root");
+ user= GetListOption(g, "user", topt->oplist,
+ (ttp == TAB_ODBC ? NULL : "root"));
// Default value db can come from the DBNAME=xxx option.
db= GetListOption(g, "database", topt->oplist, db);
col= GetListOption(g, "colist", topt->oplist, col);
@@ -4882,6 +5023,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
skc= GetListOption(g, "skipcol", topt->oplist, NULL);
rnk= GetListOption(g, "rankcol", topt->oplist, NULL);
pwd= GetListOption(g, "password", topt->oplist);
+ objn= GetListOption(g, "Object", topt->oplist, NULL);
#if defined(WIN32)
nsp= GetListOption(g, "namespace", topt->oplist);
cls= GetListOption(g, "class", topt->oplist);
@@ -4891,14 +5033,19 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
mxr= atoi(GetListOption(g,"maxres", topt->oplist, "0"));
cto= atoi(GetListOption(g,"ConnectTimeout", topt->oplist, "-1"));
qto= atoi(GetListOption(g,"QueryTimeout", topt->oplist, "-1"));
+
+ if ((ucnc= GetListOption(g, "UseDSN", topt->oplist)))
+ cnc= (!*ucnc || *ucnc == 'y' || *ucnc == 'Y' || atoi(ucnc) != 0);
#endif
mxe= atoi(GetListOption(g,"maxerr", topt->oplist, "0"));
#if defined(PROMPT_OK)
cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0"));
#endif // PROMPT_OK
+ pty= atoi(GetListOption(g,"Pretty", topt->oplist, "2"));
+ lvl= atoi(GetListOption(g,"Level", topt->oplist, "0"));
} else {
host= "localhost";
- user= "root";
+ user= (ttp == TAB_ODBC ? NULL : "root");
} // endif option_list
if (!(shm= (char*)db))
@@ -4939,8 +5086,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
goto err;
} // endif tbl
- tab= (char*)PlugSubAlloc(g, NULL, strlen(tbl) + 1);
- strcpy(tab, tbl);
+ tab= PlugDup(g, tbl);
if ((p= strchr(tab, ',')))
*p= 0;
@@ -4975,10 +5121,18 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
} // endif dsn
#endif // PROMPT_OK
- } else if (!dsn)
+ } else if (!dsn) {
sprintf(g->Message, "Missing %s connection string", topt->type);
- else
+ } else {
+ // Store ODBC additional parameters
+ sop= (POPARM)PlugSubAlloc(g, NULL, sizeof(ODBCPARM));
+ sop->User= (char*)user;
+ sop->Pwd= (char*)pwd;
+ sop->Cto= cto;
+ sop->Qto= qto;
+ sop->UseCnc= cnc;
ok= true;
+ } // endif's
supfnc |= (FNC_TABLE | FNC_DSN | FNC_DRIVER);
break;
@@ -5059,6 +5213,13 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
strcpy(g->Message, "Missing OEM module or subtype");
break;
+ case TAB_JSON:
+ if (!fn)
+ sprintf(g->Message, "Missing %s file name", topt->type);
+ else
+ ok= true;
+
+ break;
case TAB_VIR:
ok= true;
break;
@@ -5080,7 +5241,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
} // endif src
if (ok) {
- char *cnm, *rem, *dft, *xtra, *key;
+ char *cnm, *rem, *dft, *xtra, *key, *fmt;
int i, len, prec, dec, typ, flg;
// if (cat)
@@ -5109,15 +5270,15 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
case FNC_NO:
case FNC_COL:
if (src) {
- qrp= ODBCSrcCols(g, dsn, (char*)src, cto, qto);
+ qrp= ODBCSrcCols(g, dsn, (char*)src, sop);
src= NULL; // for next tests
} else
- qrp= ODBCColumns(g, dsn, shm, tab, NULL,
- mxr, cto, qto, fnc == FNC_COL);
+ qrp= ODBCColumns(g, dsn, shm, tab, NULL,
+ mxr, fnc == FNC_COL, sop);
break;
case FNC_TABLE:
- qrp= ODBCTables(g, dsn, shm, tab, mxr, cto, qto, true);
+ qrp= ODBCTables(g, dsn, shm, tab, mxr, true, sop);
break;
case FNC_DSN:
qrp= ODBCDataSources(g, mxr, true);
@@ -5169,6 +5330,9 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
case TAB_VIR:
qrp= VirColumns(g, tab, (char*)db, fnc == FNC_COL);
break;
+ case TAB_JSON:
+ qrp= JSONColumns(g, (char*)db, fn, objn, pty, lrecl, lvl, fnc == FNC_COL);
+ break;
case TAB_OEM:
qrp= OEMColumns(g, topt, tab, (char*)db, fnc == FNC_COL);
break;
@@ -5185,7 +5349,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
if (fnc != FNC_NO || src || ttp == TAB_PIVOT) {
// Catalog like table
for (crp= qrp->Colresp; !rc && crp; crp= crp->Next) {
- cnm= encode(g, crp->Name);
+ cnm= (ttp == TAB_PIVOT) ? crp->Name : encode(g, crp->Name);
typ= crp->Type;
len= crp->Length;
dec= crp->Prec;
@@ -5201,7 +5365,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
NOT_NULL_FLAG, "", flg, dbf, v);
#else // !NEW_WAY
if (add_field(&sql, cnm, typ, len, dec, NULL, NOT_NULL_FLAG,
- NULL, NULL, NULL, flg, dbf, v))
+ NULL, NULL, NULL, NULL, flg, dbf, v))
rc= HA_ERR_OUT_OF_MEM;
#endif // !NEW_WAY
} // endfor crp
@@ -5222,7 +5386,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
typ= len= prec= dec= 0;
tm= NOT_NULL_FLAG;
cnm= (char*)"noname";
- dft= xtra= key= NULL;
+ dft= xtra= key= fmt= NULL;
v= ' ';
#if defined(NEW_WAY)
rem= "";
@@ -5234,9 +5398,10 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
for (crp= qrp->Colresp; crp; crp= crp->Next)
switch (crp->Fld) {
case FLD_NAME:
- if (ttp == TAB_CSV && topt->data_charset &&
+ if (ttp == TAB_PRX ||
+ (ttp == TAB_CSV && topt->data_charset &&
(!stricmp(topt->data_charset, "UTF8") ||
- !stricmp(topt->data_charset, "UTF-8")))
+ !stricmp(topt->data_charset, "UTF-8"))))
cnm= crp->Kdata->GetCharValue(i);
else
cnm= encode(g, crp->Kdata->GetCharValue(i));
@@ -5261,6 +5426,9 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
tm= 0; // Nullable
break;
+ case FLD_FORMAT:
+ fmt= (crp->Kdata) ? crp->Kdata->GetCharValue(i) : NULL;
+ break;
case FLD_REM:
rem= crp->Kdata->GetCharValue(i);
break;
@@ -5296,9 +5464,18 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
// typ must be PLG type, not SQL type
if (!(plgtyp= TranslateSQLType(typ, dec, prec, v))) {
- sprintf(g->Message, "Unsupported SQL type %d", typ);
- my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
- goto err;
+ if (GetTypeConv() == TPC_SKIP) {
+ // Skip this column
+ sprintf(g->Message, "Column %s skipped (unsupported type %d)",
+ cnm, typ);
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
+ continue;
+ } else {
+ sprintf(g->Message, "Unsupported SQL type %d", typ);
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ goto err;
+ } // endif type_conv
+
} else
typ= plgtyp;
@@ -5306,7 +5483,9 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
case TYPE_DOUBLE:
// Some data sources do not count dec in length (prec)
prec += (dec + 2); // To be safe
+ break;
case TYPE_DECIM:
+ prec= len;
break;
default:
dec= 0;
@@ -5327,7 +5506,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd,
tm, rem, 0, dbf, v);
#else // !NEW_WAY
if (add_field(&sql, cnm, typ, prec, dec, key, tm, rem, dft, xtra,
- 0, dbf, v))
+ fmt, 0, dbf, v))
rc= HA_ERR_OUT_OF_MEM;
#endif // !NEW_WAY
} // endfor i
@@ -6338,58 +6517,6 @@ struct st_mysql_storage_engine connect_storage_engine=
/***********************************************************************/
/* CONNECT global variables definitions. */
/***********************************************************************/
-// Size used when converting TEXT columns to VARCHAR
-#if defined(_DEBUG)
-static MYSQL_SYSVAR_INT(conv_size, zconv,
- PLUGIN_VAR_RQCMDARG, // opt
- "Size used when converting TEXT columns.",
- NULL, NULL, SZCONV, 0, 65500, 1);
-#else
-static MYSQL_SYSVAR_INT(conv_size, zconv,
- PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, // opt
- "Size used when converting TEXT columns.",
- NULL, NULL, SZCONV, 0, 65500, 1);
-#endif
-
-/**
- Type conversion:
- no: Unsupported types -> TYPE_ERROR
- yes: TEXT -> VARCHAR
- skip: skip unsupported type columns in Discovery
-*/
-const char *xconv_names[]=
-{
- "NO", "YES", "SKIP", NullS
-};
-
-TYPELIB xconv_typelib=
-{
- array_elements(xconv_names) - 1, "xconv_typelib",
- xconv_names, NULL
-};
-
-#if defined(_DEBUG)
-static MYSQL_SYSVAR_ENUM(
- type_conv, // name
- xconv, // varname
- PLUGIN_VAR_RQCMDARG, // opt
- "Unsupported types conversion.", // comment
- NULL, // check
- NULL, // update function
- 0, // def (no)
- &xconv_typelib); // typelib
-#else
-static MYSQL_SYSVAR_ENUM(
- type_conv, // name
- xconv, // varname
- PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
- "Unsupported types conversion.", // comment
- NULL, // check
- NULL, // update function
- 0, // def (no)
- &xconv_typelib); // typelib
-#endif
-
#if defined(XMAP)
// Using file mapping for indexes if true
static MYSQL_SYSVAR_BOOL(indx_map, xmap, PLUGIN_VAR_RQCMDARG,
@@ -6422,6 +6549,7 @@ static struct st_mysql_sys_var* connect_system_variables[]= {
#if defined(XMSG)
MYSQL_SYSVAR(errmsg_dir_path),
#endif // XMSG
+ MYSQL_SYSVAR(json_grp_size),
NULL
};
@@ -6438,7 +6566,7 @@ maria_declare_plugin(connect)
0x0103, /* version number (1.03) */
NULL, /* status variables */
connect_system_variables, /* system variables */
- "1.03.0005", /* string version */
+ "1.03.0006", /* string version */
MariaDB_PLUGIN_MATURITY_BETA /* maturity */
}
maria_declare_plugin_end;
diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h
index 922a69a3991..abe8fa079fe 100644
--- a/storage/connect/ha_connect.h
+++ b/storage/connect/ha_connect.h
@@ -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
@@ -56,11 +56,7 @@ public:
oldopn= newopn= NULL;
oldpix= newpix= NULL;}
- inline char *SetName(PGLOBAL g, char *name) {
- char *nm= NULL;
- if (name) {nm= (char*)PlugSubAlloc(g, NULL, strlen(name) + 1);
- strcpy(nm, name);}
- return nm;}
+ inline char *SetName(PGLOBAL g, char *name) {return PlugDup(g, name);}
bool oldsep; // Sepindex before create/alter
bool newsep; // Sepindex after create/alter
diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp
index 983f45d9cee..c3eb58a2260 100644
--- a/storage/connect/json.cpp
+++ b/storage/connect/json.cpp
@@ -1,5 +1,5 @@
/*************** json CPP Declares Source Code File (.H) ***************/
-/* Name: json.cpp Version 1.0 */
+/* Name: json.cpp Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
@@ -34,7 +34,7 @@
/***********************************************************************/
PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
{
- int i;
+ int i, rc;
bool b = false;
PJSON jsp = NULL;
STRG src;
@@ -48,22 +48,33 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
src.str = s;
src.len = len;
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ strcpy(g->Message, MSG(TOO_MANY_JUMPS));
+ return NULL;
+ } // endif jump_level
+
+ if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
+ goto err;
+ } // endif rc
+
for (i = 0; i < len; i++)
switch (s[i]) {
case '[':
if (jsp) {
strcpy(g->Message, "More than one item in file");
- return NULL;
+ goto err;
} else if (!(jsp = ParseArray(g, ++i, src)))
- return NULL;
+ goto err;
break;
case '{':
if (jsp) {
strcpy(g->Message, "More than one item in file");
- return NULL;
+ goto err;
} else if (!(jsp = ParseObject(g, ++i, src)))
- return NULL;
+ goto err;
+
break;
case ' ':
case '\t':
@@ -79,7 +90,12 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
} // endif pretty
sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty);
- return NULL;
+ goto err;
+ case '"':
+ if (!(jsp = ParseValue(g, i, src)))
+ goto err;
+
+ break;
case '(':
b = true;
break;
@@ -92,13 +108,18 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
default:
sprintf(g->Message, "Bad '%c' character near %.*s",
s[i], ARGS);
- return NULL;
+ goto err;
}; // endswitch s[i]
if (!jsp)
sprintf(g->Message, "Invalid Json string '%.*s'", 50, s);
+ g->jump_level--;
return jsp;
+
+ err:
+ g->jump_level--;
+ return NULL;
} // end of ParseJson
/***********************************************************************/
@@ -312,18 +333,25 @@ err:
/***********************************************************************/
char *ParseString(PGLOBAL g, int& i, STRG& src)
{
- char *p, *s = src.str;
- int n = 0, len = src.len;
+ char *s = src.str;
+ uchar *p;
+ int n = 0, len = src.len;
+
+ // Be sure of memory availability
+ if (len + 1 - i > (signed)((PPOOLHEADER)g->Sarea)->FreeBlk) {
+ strcpy(g->Message, "ParseString: Out of memory");
+ return NULL;
+ } // endif len
// The size to allocate is not known yet
- p = (char*)PlugSubAlloc(g, NULL, 0);
+ p = (uchar*)PlugSubAlloc(g, NULL, 0);
for (; i < len; i++)
switch (s[i]) {
case '"':
p[n++] = 0;
PlugSubAlloc(g, NULL, n);
- return p;
+ return (char*)p;
case '\\':
if (++i < len) {
if (s[i] == 'u') {
@@ -504,8 +532,11 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty)
err = (b && jp->WriteChr('\t'));
err |= SerializeObject(jp, (PJOB)jsp);
break;
+ case TYPE_JVAL:
+ err = SerializeValue(jp, (PJVAL)jsp);
+ break;
default:
- strcpy(g->Message, "json tree is not an Array or an Object");
+ strcpy(g->Message, "Invalid json tree");
} // endswitch Type
if (fs) {
@@ -575,9 +606,9 @@ bool SerializeObject(JOUT *js, PJOB jobp)
else if (js->WriteChr(','))
return true;
- if (js->WriteChr('\"') ||
+ if (js->WriteChr('"') ||
js->WriteStr(pair->Key) ||
- js->WriteChr('\"') ||
+ js->WriteChr('"') ||
js->WriteChr(':') ||
SerializeValue(js, pair->Val))
return true;
@@ -631,7 +662,7 @@ JOUTSTR::JOUTSTR(PGLOBAL g) : JOUT(g)
N = 0;
Max = pph->FreeBlk;
- Max = (Max > 512) ? Max - 512 : Max;
+ Max = (Max > 32) ? Max - 32 : Max;
Strp = (char*)PlugSubAlloc(g, NULL, 0); // Size not know yet
} // end of JOUTSTR constructor
@@ -675,12 +706,13 @@ bool JOUTSTR::Escape(const char *s)
for (unsigned int i = 0; i < strlen(s); i++)
switch (s[i]) {
+ case '"':
+ case '\\':
case '\t':
case '\n':
case '\r':
case '\b':
- case '\f':
- case '"': WriteChr('\\');
+ case '\f': WriteChr('\\');
// passthru
default:
WriteChr(s[i]);
@@ -723,12 +755,13 @@ bool JOUTFILE::Escape(const char *s)
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;
+ case '"': fputs("\\\"", Stream); break;
+ case '\\': fputs("\\\\", Stream); break;
+ 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;
default:
fputc(s[i], Stream);
break;
@@ -848,27 +881,26 @@ PJVAL JOBJECT::GetValue(const char* key)
/***********************************************************************/
/* Return the text corresponding to all keys (XML like). */
/***********************************************************************/
-PSZ JOBJECT::GetText(PGLOBAL g)
+PSZ JOBJECT::GetText(PGLOBAL g, PSZ text)
{
- char *p, *text = (char*)PlugSubAlloc(g, NULL, 0);
- bool b = true;
+ int n;
- if (!First)
- return NULL;
- else for (PJPR jp = First; jp; jp = jp->Next) {
- if (!(p = jp->Val->GetString()))
- p = "???";
+ if (!text) {
+ text = (char*)PlugSubAlloc(g, NULL, 0);
+ text[0] = 0;
+ n = 1;
+ } else
+ n = 0;
- if (b) {
- strcpy(text, p);
- b = false;
- } else
- strcat(strcat(text, " "), p);
+ if (!First && n)
+ return NULL;
+ else for (PJPR jp = First; jp; jp = jp->Next)
+ jp->Val->GetText(g, text);
- } // endfor jp
+ if (n)
+ PlugSubAlloc(g, NULL, strlen(text) + 1);
- PlugSubAlloc(g, NULL, strlen(text) + 1);
- return text;
+ return text + n;
} // end of GetValue;
/***********************************************************************/
@@ -891,6 +923,18 @@ void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key)
} // end of SetValue
+/***********************************************************************/
+/* True if void or if all members are nulls. */
+/***********************************************************************/
+bool JOBJECT::IsNull(void)
+{
+ for (PJPR jp = First; jp; jp = jp->Next)
+ if (!jp->Val->IsNull())
+ return false;
+
+ return true;
+} // end of IsNull
+
/* -------------------------- Class JARRAY --------------------------- */
/***********************************************************************/
@@ -979,6 +1023,18 @@ bool JARRAY::DeleteValue(int n)
} // end of DeleteValue
+/***********************************************************************/
+/* True if void or if all members are nulls. */
+/***********************************************************************/
+bool JARRAY::IsNull(void)
+{
+ for (int i = 0; i < Size; i++)
+ if (!Mvals[i]->IsNull())
+ return false;
+
+ return true;
+} // end of IsNull
+
/* -------------------------- Class JVALUE- -------------------------- */
/***********************************************************************/
@@ -1053,3 +1109,54 @@ PSZ JVALUE::GetString(void)
return (Value) ? Value->GetCharString(buf) : NULL;
} // end of GetString
+/***********************************************************************/
+/* Return the Value's String value. */
+/***********************************************************************/
+PSZ JVALUE::GetText(PGLOBAL g, PSZ text)
+{
+ if (Jsp && Jsp->GetType() == TYPE_JOB)
+ return Jsp->GetText(g, text);
+
+ char buf[32];
+ PSZ s = (Value) ? Value->GetCharString(buf) : NULL;
+
+ if (s)
+ strcat(strcat(text, " "), s);
+ else
+ strcat(text, " ???");
+
+ return text;
+} // end of GetText
+
+/***********************************************************************/
+/* Set the Value's value as the given integer. */
+/***********************************************************************/
+void JVALUE::SetInteger(PGLOBAL g, int n)
+{
+ Value = AllocateValue(g, &n, TYPE_INT);
+} // end of AddInteger
+
+/***********************************************************************/
+/* Set the Value's value as the given DOUBLE. */
+/***********************************************************************/
+void JVALUE::SetFloat(PGLOBAL g, double f)
+{
+ Value = AllocateValue(g, &f, TYPE_DOUBLE, 6);
+} // end of AddFloat
+
+/***********************************************************************/
+/* Set the Value's value as the given string. */
+/***********************************************************************/
+void JVALUE::SetString(PGLOBAL g, PSZ s)
+{
+ Value = AllocateValue(g, s, TYPE_STRING);
+} // end of AddFloat
+
+/***********************************************************************/
+/* True when its JSON or normal value is null. */
+/***********************************************************************/
+bool JVALUE::IsNull(void)
+{
+ return (Jsp) ? Jsp->IsNull() : (Value) ? Value->IsZero() : true;
+} // end of IsNull
+
diff --git a/storage/connect/json.h b/storage/connect/json.h
index 11e15c3acd4..3684bbddd19 100644
--- a/storage/connect/json.h
+++ b/storage/connect/json.h
@@ -1,5 +1,5 @@
/**************** json H Declares Source Code File (.H) ****************/
-/* Name: json.h Version 1.0 */
+/* Name: json.h Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
@@ -18,7 +18,8 @@ enum JTYP {TYPE_STRG = 1,
TYPE_BOOL = 4,
TYPE_INTG = 7,
TYPE_JSON = 12,
- TYPE_JAR, TYPE_JOB,
+ TYPE_JAR,
+ TYPE_JOB,
TYPE_JVAL};
class JOUT;
@@ -122,6 +123,10 @@ class JPAIR : public BLOCK {
public:
JPAIR(PSZ key) : BLOCK() {Key = key; Val = NULL; Next = NULL;}
+ inline PSZ GetKey(void) {return Key;}
+ inline PJVAL GetVal(void) {return Val;}
+ inline PJPR GetNext(void) {return Next;}
+
protected:
PSZ Key; // This pair key name
PJVAL Val; // To the value of the pair
@@ -143,20 +148,25 @@ class JSON : public BLOCK {
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 PJOB GetObject(void) {return NULL;}
+ virtual PJAR GetArray(void) {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 PJPR GetFirst(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 PSZ GetText(PGLOBAL g, PSZ text) {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 void SetString(PGLOBAL g, PSZ s) {X}
+ virtual void SetInteger(PGLOBAL g, int n) {X}
+ virtual void SetFloat(PGLOBAL g, double f) {X}
virtual bool DeleteValue(int i) {X return true;}
+ virtual bool IsNull(void) {X return true;}
protected:
int Size;
@@ -171,13 +181,17 @@ class JOBJECT : public JSON {
public:
JOBJECT(void) : JSON() {First = Last = NULL;}
+ using JSON::GetValue;
+ using JSON::SetValue;
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JOB;}
+ virtual PJPR GetFirst(void) {return First;}
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 PSZ GetText(PGLOBAL g, PSZ text);
virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key);
+ virtual bool IsNull(void);
protected:
PJPR First;
@@ -192,6 +206,8 @@ class JARRAY : public JSON {
public:
JARRAY(void) : JSON() {Alloc = 0; First = Last = NULL; Mvals = NULL;}
+ using JSON::GetValue;
+ using JSON::SetValue;
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JAR;}
virtual PJAR GetArray(void) {return this;}
@@ -200,6 +216,7 @@ class JARRAY : public JSON {
virtual PJVAL GetValue(int i);
virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i);
virtual bool DeleteValue(int n);
+ virtual bool IsNull(void);
protected:
// Members
@@ -223,6 +240,8 @@ class JVALUE : public JSON {
{Jsp = jsp; Value = NULL; Next = NULL; Del = false;}
JVALUE(PGLOBAL g, PVAL valp);
+ using JSON::GetValue;
+ using JSON::SetValue;
virtual void Clear(void)
{Jsp = NULL; Value = NULL; Next = NULL; Del = false; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JVAL;}
@@ -234,8 +253,13 @@ class JVALUE : public JSON {
virtual int GetInteger(void);
virtual double GetFloat(void);
virtual PSZ GetString(void);
+ virtual PSZ GetText(PGLOBAL g, PSZ text);
virtual void SetValue(PVAL valp) {Value = valp;}
virtual void SetValue(PJSON jsp) {Jsp = jsp;}
+ virtual void SetString(PGLOBAL g, PSZ s);
+ virtual void SetInteger(PGLOBAL g, int n);
+ virtual void SetFloat(PGLOBAL g, double f);
+ virtual bool IsNull(void);
protected:
PJSON Jsp; // To the json value
diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp
new file mode 100644
index 00000000000..194cb6defd6
--- /dev/null
+++ b/storage/connect/jsonudf.cpp
@@ -0,0 +1,626 @@
+/************* jsonudf C++ Program Source Code File (.CPP) *************/
+/* PROGRAM NAME: jsonudf Version 1.0 */
+/* (C) Copyright to the author Olivier BERTRAND 2015 */
+/* This program are the JSON User Defined Functions . */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the MariaDB header file. */
+/***********************************************************************/
+#include <my_global.h>
+#include <mysqld.h>
+#include <mysql.h>
+#include <sql_error.h>
+
+#include "global.h"
+#include "plgdbsem.h"
+#include "json.h"
+
+#define MEMFIX 512
+
+uint GetJsonGrpSize(void);
+
+extern "C" {
+DllExport my_bool Json_Value_init(UDF_INIT*, UDF_ARGS*, char*);
+DllExport char *Json_Value(UDF_INIT*, UDF_ARGS*, char*,
+ unsigned long*, char *, char *);
+DllExport void Json_Value_deinit(UDF_INIT*);
+DllExport my_bool Json_Array_init(UDF_INIT*, UDF_ARGS*, char*);
+DllExport char *Json_Array(UDF_INIT*, UDF_ARGS*, char*,
+ unsigned long*, char *, char *);
+DllExport void Json_Array_deinit(UDF_INIT*);
+DllExport my_bool Json_Array_Add_init(UDF_INIT*, UDF_ARGS*, char*);
+DllExport char *Json_Array_Add(UDF_INIT*, UDF_ARGS*, char*,
+ unsigned long*, char *, char *);
+DllExport void Json_Array_Add_deinit(UDF_INIT*);
+DllExport my_bool Json_Object_init(UDF_INIT*, UDF_ARGS*, char*);
+DllExport char *Json_Object(UDF_INIT*, UDF_ARGS*, char*,
+ unsigned long*, char *, char *);
+DllExport void Json_Object_deinit(UDF_INIT*);
+DllExport my_bool Json_Object_Nonull_init(UDF_INIT*, UDF_ARGS*, char*);
+DllExport char *Json_Object_Nonull(UDF_INIT*, UDF_ARGS*, char*,
+ unsigned long*, char *, char *);
+DllExport void Json_Object_Nonull_deinit(UDF_INIT*);
+DllExport my_bool Json_Array_Grp_init(UDF_INIT*, UDF_ARGS*, char*);
+DllExport void Json_Array_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *);
+DllExport char *Json_Array_Grp(UDF_INIT*, UDF_ARGS*, char*,
+ unsigned long*, char *, char *);
+DllExport void Json_Array_Grp_clear(UDF_INIT *, char *, char *);
+DllExport void Json_Array_Grp_deinit(UDF_INIT*);
+DllExport my_bool Json_Object_Grp_init(UDF_INIT*, UDF_ARGS*, char*);
+DllExport void Json_Object_Grp_add(UDF_INIT *, UDF_ARGS *, char *, char *);
+DllExport char *Json_Object_Grp(UDF_INIT*, UDF_ARGS*, char*,
+ unsigned long*, char *, char *);
+DllExport void Json_Object_Grp_clear(UDF_INIT *, char *, char *);
+DllExport void Json_Object_Grp_deinit(UDF_INIT*);
+} // extern "C"
+
+/***********************************************************************/
+/* Allocate and initialise the memory area. */
+/***********************************************************************/
+static my_bool JsonInit(UDF_INIT *initid, char *message,
+ unsigned long reslen, unsigned long memlen)
+{
+ PGLOBAL g = PlugInit(NULL, memlen);
+
+ if (!g) {
+ strcpy(message, "Allocation error");
+ return true;
+ } else if (g->Sarea_Size == 0) {
+ strcpy(message, g->Message);
+ PlugExit(g);
+ return true;
+ } else
+ initid->ptr = (char*)g;
+
+ initid->maybe_null = false;
+ initid->max_length = reslen;
+ return false;
+} // end of Json_Object_init
+
+/***********************************************************************/
+/* Returns true if the argument is a JSON string. */
+/***********************************************************************/
+static my_bool IsJson(UDF_ARGS *args, int i)
+{
+ return (args->arg_type[i] == STRING_RESULT &&
+ !strnicmp(args->attributes[i], "Json_", 5));
+} // end of IsJson
+
+/***********************************************************************/
+/* Calculate the reslen and memlen needed by a function. */
+/***********************************************************************/
+static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
+ unsigned long& reslen, unsigned long& memlen)
+{
+ unsigned long i, k;
+ reslen = args->arg_count + 2;
+
+ // Calculate the result max length
+ for (i = 0; i < args->arg_count; i++) {
+ if (obj) {
+ if (!(k = args->attribute_lengths[i]))
+ k = strlen(args->attributes[i]);
+
+ reslen += (k + 3); // For quotes and :
+ } // endif obj
+
+ switch (args->arg_type[i]) {
+ case STRING_RESULT:
+ if (IsJson(args, i))
+ reslen += args->lengths[i];
+ else
+ reslen += (args->lengths[i] + 1) * 2; // Pessimistic !
+
+ break;
+ case INT_RESULT:
+ reslen += 20;
+ break;
+ case REAL_RESULT:
+ reslen += 31;
+ break;
+ case DECIMAL_RESULT:
+ reslen += (args->lengths[i] + 7); // 6 decimals
+ break;
+ case TIME_RESULT:
+ case ROW_RESULT:
+ case IMPOSSIBLE_RESULT:
+ default:
+ // What should we do here ?
+ break;
+ } // endswitch arg_type
+
+ } // endfor i
+
+ // Calculate the amount of memory needed
+ memlen = MEMFIX + sizeof(JOUTSTR) + reslen;
+
+ for (i = 0; i < args->arg_count; i++) {
+ memlen += (args->lengths[i] + sizeof(JVALUE));
+
+ if (obj) {
+ if (!(k = args->attribute_lengths[i]))
+ k = strlen(args->attributes[i]);
+
+ memlen += (k + sizeof(JOBJECT) + sizeof(JPAIR));
+ } else
+ memlen += sizeof(JARRAY);
+
+ switch (args->arg_type[i]) {
+ case STRING_RESULT:
+ if (IsJson(args, i))
+ memlen += args->lengths[i] * 5; // Estimate parse memory
+
+ memlen += sizeof(TYPVAL<PSZ>);
+ break;
+ case INT_RESULT:
+ memlen += sizeof(TYPVAL<int>);
+ break;
+ case REAL_RESULT:
+ case DECIMAL_RESULT:
+ memlen += sizeof(TYPVAL<double>);
+ break;
+ case TIME_RESULT:
+ case ROW_RESULT:
+ case IMPOSSIBLE_RESULT:
+ default:
+ // What should we do here ?
+ break;
+ } // endswitch arg_type
+
+ } // endfor i
+
+ return false;
+} // end of CalcLen
+
+/***********************************************************************/
+/* Make a zero terminated string from the passed argument. */
+/***********************************************************************/
+static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i)
+{
+ if (args->args[i]) {
+ int n = args->lengths[i];
+ PSZ s = (PSZ)PlugSubAlloc(g, NULL, n + 1);
+
+ memcpy(s, args->args[i], n);
+ s[n] = 0;
+ return s;
+ } else
+ return NULL;
+
+} // end of MakePSZ
+
+/***********************************************************************/
+/* Make a valid key from the passed argument. */
+/***********************************************************************/
+static PSZ MakeKey(PGLOBAL g, UDF_ARGS *args, int i)
+{
+ int n = args->attribute_lengths[i];
+ bool b; // true if attribute is zero terminated
+ PSZ p, s = args->attributes[i];
+
+ if (s && *s && (n || *s == '\'')) {
+ if ((b = (!n || !s[n])))
+ n = strlen(s);
+
+ if (n > 5 && IsJson(args, i)) {
+ s += 5;
+ n -= 5;
+ } else if (*s == '\'' && s[n-1] == '\'') {
+ s++;
+ n -= 2;
+ b = false;
+ } // endif *s
+
+ if (n < 1)
+ return "Key";
+
+ if (!b) {
+ p = (PSZ)PlugSubAlloc(g, NULL, n + 1);
+ memcpy(p, s, n);
+ p[n] = 0;
+ s = p;
+ } // endif b
+
+ } // endif s
+
+ return s;
+} // end of MakeKey
+
+/***********************************************************************/
+/* Make a JSON value from the passed argument. */
+/***********************************************************************/
+static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i)
+{
+ char *sap = (args->arg_count > (unsigned)i) ? args->args[i] : NULL;
+ PJSON jsp;
+ PJVAL jvp = new(g) JVALUE;
+
+ if (sap) switch (args->arg_type[i]) {
+ case STRING_RESULT:
+ if (args->lengths[i]) {
+ if (IsJson(args, i)) {
+ if (!(jsp = ParseJson(g, sap, args->lengths[i], 0)))
+ push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
+ g->Message);
+
+ if (jsp && jsp->GetType() == TYPE_JVAL)
+ jvp = (PJVAL)jsp;
+ else
+ jvp->SetValue(jsp);
+
+ } else
+ jvp->SetString(g, MakePSZ(g, args, i));
+
+ } // endif str
+
+ break;
+ case INT_RESULT:
+ jvp->SetInteger(g, *(int*)sap);
+ break;
+ case REAL_RESULT:
+ jvp->SetFloat(g, *(double*)sap);
+ break;
+ case DECIMAL_RESULT:
+ jvp->SetFloat(g, atof(MakePSZ(g, args, i)));
+ break;
+ case TIME_RESULT:
+ case ROW_RESULT:
+ case IMPOSSIBLE_RESULT:
+ default:
+ break;
+ } // endswitch arg_type
+
+ return jvp;
+} // end of MakeValue
+
+/***********************************************************************/
+/* Make a Json value containing the parameter. */
+/***********************************************************************/
+my_bool Json_Value_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count > 1) {
+ strcpy(message, "Json_Value cannot accept more than 1 argument");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ return JsonInit(initid, message, reslen, memlen);
+} // end of Json_Value_init
+
+char *Json_Value(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ jvp = MakeValue(g, args, 0);
+
+ if (!(str = Serialize(g, jvp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ *res_length = strlen(str);
+ return str;
+} // end of Json_Value
+
+void Json_Value_deinit(UDF_INIT* initid)
+{
+ PlugExit((PGLOBAL)initid->ptr);
+} // end of Json_Value_deinit
+
+/***********************************************************************/
+/* Make a Json array containing all the parameters. */
+/***********************************************************************/
+my_bool Json_Array_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ CalcLen(args, false, reslen, memlen);
+ return JsonInit(initid, message, reslen, memlen);
+} // end of Json_Array_init
+
+char *Json_Array(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str;
+ uint i;
+ PJAR arp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ arp = new(g) JARRAY;
+
+ for (i = 0; i < args->arg_count; i++)
+ arp->AddValue(g, MakeValue(g, args, i));
+
+ arp->InitArray(g);
+
+ if (!(str = Serialize(g, arp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ *res_length = strlen(str);
+ return str;
+} // end of Json_Array
+
+void Json_Array_deinit(UDF_INIT* initid)
+{
+ PlugExit((PGLOBAL)initid->ptr);
+} // end of Json_Array_deinit
+
+/***********************************************************************/
+/* Add values to a Json array. */
+/***********************************************************************/
+my_bool Json_Array_Add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "Json_Value_Add must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "Json_Value_Add first argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ return JsonInit(initid, message, reslen, memlen);
+} // end of Json_Array_Add_init
+
+char *Json_Array_Add(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str;
+ PJVAL jvp;
+ PJAR arp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ jvp = MakeValue(g, args, 0);
+
+ if (jvp->GetValType() != TYPE_JAR) {
+ arp = new(g) JARRAY;
+ arp->AddValue(g, jvp);
+ } else
+ arp = jvp->GetArray();
+
+ for (uint i = 1; i < args->arg_count; i++)
+ arp->AddValue(g, MakeValue(g, args, i));
+
+ arp->InitArray(g);
+
+ if (!(str = Serialize(g, arp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ *res_length = strlen(str);
+ return str;
+} // end of Json_Array_Add
+
+void Json_Array_Add_deinit(UDF_INIT* initid)
+{
+ PlugExit((PGLOBAL)initid->ptr);
+} // end of Json_Array_Add_deinit
+
+/***********************************************************************/
+/* Make a Json Oject containing all the parameters. */
+/***********************************************************************/
+my_bool Json_Object_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ CalcLen(args, true, reslen, memlen);
+ return JsonInit(initid, message, reslen, memlen);
+} // end of Json_Object_init
+
+char *Json_Object(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str;
+ uint i;
+ PJOB objp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ objp = new(g) JOBJECT;
+
+ for (i = 0; i < args->arg_count; i++)
+ objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i));
+
+ if (!(str = Serialize(g, objp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ *res_length = strlen(str);
+ return str;
+} // end of Json_Object
+
+void Json_Object_deinit(UDF_INIT* initid)
+{
+ PlugExit((PGLOBAL)initid->ptr);
+} // end of Json_Object_deinit
+
+/***********************************************************************/
+/* Make a Json Oject containing all not null parameters. */
+/***********************************************************************/
+my_bool Json_Object_Nonull_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message)
+{
+ unsigned long reslen, memlen;
+
+ CalcLen(args, true, reslen, memlen);
+ return JsonInit(initid, message, reslen, memlen);
+} // end of Json_Object_Nonull_init
+
+char *Json_Object_Nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str;
+ uint i;
+ PJOB objp;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ objp = new(g) JOBJECT;
+
+ for (i = 0; i < args->arg_count; i++)
+ if (!(jvp = MakeValue(g, args, i))->IsNull())
+ objp->SetValue(g, jvp, MakeKey(g, args, i));
+
+ if (!(str = Serialize(g, objp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ *res_length = strlen(str);
+ return str;
+} // end of Json_Object_Nonull
+
+void Json_Object_Nonull_deinit(UDF_INIT* initid)
+{
+ PlugExit((PGLOBAL)initid->ptr);
+} // end of Json_Object_nonull_deinit
+
+/***********************************************************************/
+/* Make a Json array from values comming from rows. */
+/***********************************************************************/
+my_bool Json_Array_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, n = GetJsonGrpSize();
+
+ if (args->arg_count != 1) {
+ strcpy(message, "Json_Array_Grp can only accept 1 argument");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ reslen *= n;
+ memlen += ((memlen - MEMFIX) * (n - 1));
+
+ if (JsonInit(initid, message, reslen, memlen))
+ return true;
+
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ g->Activityp = (PACTIVITY)new(g) JARRAY;
+ g->N = (int)n;
+ return false;
+} // end of Json_Array_Grp_init
+
+void Json_Array_Grp_add(UDF_INIT *initid, UDF_ARGS *args,
+ char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PJAR arp = (PJAR)g->Activityp;
+
+ if (g->N-- > 0)
+ arp->AddValue(g, MakeValue(g, args, 0));
+
+} // end of Json_Array_Grp_add
+
+char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PJAR arp = (PJAR)g->Activityp;
+
+ if (g->N < 0)
+ push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
+ "Result truncated to json_grp_size values");
+
+ arp->InitArray(g);
+
+ if (!(str = Serialize(g, arp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ *res_length = strlen(str);
+ return str;
+} // end of Json_Array_Grp
+
+void Json_Array_Grp_clear(UDF_INIT *initid, char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ g->Activityp = (PACTIVITY)new(g) JARRAY;
+ g->N = GetJsonGrpSize();
+} // end of Json_Array_Grp_clear
+
+void Json_Array_Grp_deinit(UDF_INIT* initid)
+{
+ PlugExit((PGLOBAL)initid->ptr);
+} // end of Json_Array_Grp_deinit
+
+/***********************************************************************/
+/* Make a Json object from values comming from rows. */
+/***********************************************************************/
+my_bool Json_Object_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, n = GetJsonGrpSize();
+
+ if (args->arg_count != 2) {
+ strcpy(message, "Json_Array_Grp can only accept 2 arguments");
+ return true;
+ } else
+ CalcLen(args, true, reslen, memlen);
+
+ reslen *= n;
+ memlen += ((memlen - MEMFIX) * (n - 1));
+
+ if (JsonInit(initid, message, reslen, memlen))
+ return true;
+
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ g->Activityp = (PACTIVITY)new(g) JOBJECT;
+ g->N = (int)n;
+ return false;
+} // end of Json_Object_Grp_init
+
+void Json_Object_Grp_add(UDF_INIT *initid, UDF_ARGS *args,
+ char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PJOB objp = (PJOB)g->Activityp;
+
+ if (g->N-- > 0)
+ objp->SetValue(g, MakeValue(g, args, 0), MakePSZ(g, args, 1));
+
+} // end of Json_Object_Grp_add
+
+char *Json_Object_Grp(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PJOB objp = (PJOB)g->Activityp;
+
+ if (g->N < 0)
+ push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
+ "Result truncated to json_grp_size values");
+
+ if (!(str = Serialize(g, objp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ *res_length = strlen(str);
+ return str;
+} // end of Json_Object_Grp
+
+void Json_Object_Grp_clear(UDF_INIT *initid, char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ g->Activityp = (PACTIVITY)new(g) JOBJECT;
+ g->N = GetJsonGrpSize();
+} // end of Json_Object_Grp_clear
+
+void Json_Object_Grp_deinit(UDF_INIT* initid)
+{
+ PlugExit((PGLOBAL)initid->ptr);
+} // end of Json_Object_Grp_deinit
+
+
diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp
index e576c1cf5fa..e5c0ee6cfff 100644
--- a/storage/connect/libdoc.cpp
+++ b/storage/connect/libdoc.cpp
@@ -1,6 +1,6 @@
/******************************************************************/
/* Implementation of XML document processing using libxml2 */
-/* Author: Olivier Bertrand 2007-2013 */
+/* Author: Olivier Bertrand 2007-2015 */
/******************************************************************/
#include "my_global.h"
#include <string.h>
@@ -408,8 +408,7 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
xp->Next = (PX2BLOCK)dup->Openlist;
dup->Openlist = (PFBLOCK)xp;
xp->Type = TYPE_FB_XML2;
- xp->Fname = (LPCSTR)PlugSubAlloc(g, NULL, strlen(fn) + 1);
- strcpy((char*)xp->Fname, fn);
+ xp->Fname = (LPCSTR)PlugDup(g, fn);
xp->Count = 1;
xp->Length = (m == MODE_READ) ? 1 : 0;
xp->Retcode = rc;
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index 2f3d75b52fa..47d781d9ff6 100644
--- a/storage/connect/myconn.cpp
+++ b/storage/connect/myconn.cpp
@@ -51,7 +51,8 @@
#define DLL_EXPORT // Items are exported from this DLL
#include "myconn.h"
-extern "C" int zconv;
+//extern "C" int zconv;
+int GetConvSize(void);
extern MYSQL_PLUGIN_IMPORT uint mysqld_port;
extern MYSQL_PLUGIN_IMPORT char *mysqld_unix_port;
@@ -265,7 +266,7 @@ PQRYRES MyColumns(PGLOBAL g, THD *thd, const char *host, const char *db,
return NULL;
} else if (type == TYPE_STRING) {
if (v == 'X') {
- len = zconv;
+ len = GetConvSize();
sprintf(g->Message, "Column %s converted to varchar(%d)",
colname, len);
PushWarning(g, thd);
diff --git a/storage/connect/myutil.cpp b/storage/connect/myutil.cpp
index 0d9a1e2fe16..fe504bbe422 100644
--- a/storage/connect/myutil.cpp
+++ b/storage/connect/myutil.cpp
@@ -26,14 +26,16 @@
#include "myutil.h"
#define DLL_EXPORT // Items are exported from this DLL
-extern "C" int xconv;
+//extern "C" int xconv;
+TYPCONV GetTypeConv(void);
/************************************************************************/
/* Convert from MySQL type name to PlugDB type number */
/************************************************************************/
int MYSQLtoPLG(char *typname, char *var)
{
- int type;
+ int type;
+ TYPCONV xconv = GetTypeConv();
if (!stricmp(typname, "int") || !stricmp(typname, "mediumint") ||
!stricmp(typname, "integer"))
@@ -57,13 +59,13 @@ int MYSQLtoPLG(char *typname, char *var)
type = TYPE_TINY;
else if (!stricmp(typname, "text") && var) {
switch (xconv) {
- case 1:
+ case TPC_YES:
type = TYPE_STRING;
*var = 'X';
break;
- case 2:
+ case TPC_SKIP:
*var = 'K';
- default:
+ default: // TPC_NO
type = TYPE_ERROR;
} // endswitch xconv
@@ -88,7 +90,7 @@ int MYSQLtoPLG(char *typname, char *var)
} else if (type == TYPE_STRING && !stricmp(typname, "varchar"))
// This is to make the difference between CHAR and VARCHAR
*var = 'V';
- else if (type == TYPE_ERROR && xconv == 2)
+ else if (type == TYPE_ERROR && xconv == TPC_SKIP)
*var = 'K';
else
*var = 0;
@@ -174,7 +176,7 @@ const char *PLGtoMYSQLtype(int type, bool dbf, char v)
/************************************************************************/
int MYSQLtoPLG(int mytype, char *var)
{
- int type;
+ int type, xconv = GetTypeConv();
switch (mytype) {
case MYSQL_TYPE_SHORT:
@@ -221,7 +223,7 @@ int MYSQLtoPLG(int mytype, char *var)
case MYSQL_TYPE_LONG_BLOB:
if (var) {
switch (xconv) {
- case 1:
+ case TPC_YES:
if (*var != 'B') {
// This is a TEXT column
type = TYPE_STRING;
@@ -230,9 +232,9 @@ int MYSQLtoPLG(int mytype, char *var)
type = TYPE_ERROR;
break;
- case 2:
+ case TPC_SKIP:
*var = 'K'; // Skip
- default:
+ default: // TPC_NO
type = TYPE_ERROR;
} // endswitch xconv
diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h
index 8642d915211..1b5febadd3a 100644
--- a/storage/connect/odbccat.h
+++ b/storage/connect/odbccat.h
@@ -2,6 +2,14 @@
#define DEFAULT_LOGIN_TIMEOUT -1 // means do not set
#define DEFAULT_QUERY_TIMEOUT -1 // means do not set
+typedef struct odbc_parms {
+ char *User; // User connect info
+ char *Pwd; // Password connect info
+ int Cto; // Connect timeout
+ int Qto; // Query timeout
+ bool UseCnc; // Use SQLConnect (!SQLDriverConnect)
+ } ODBCPARM, *POPARM;
+
/***********************************************************************/
/* ODBC catalog function prototypes. */
/***********************************************************************/
@@ -10,8 +18,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, int cto, int qto, bool info);
-PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, int cto, int qto);
+ char *colpat, int maxres, bool info, POPARM sop);
+PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, POPARM sop);
PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *db, char *tabpat,
- int maxres, int cto, int qto, bool info);
+ int maxres, bool info, POPARM sop);
PQRYRES ODBCDrivers(PGLOBAL g, int maxres, bool info);
diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp
index 3e616ec8f04..2f2f5f38c29 100644
--- a/storage/connect/odbconn.cpp
+++ b/storage/connect/odbconn.cpp
@@ -1,5 +1,5 @@
/************ Odbconn C++ Functions Source Code File (.CPP) ************/
-/* Name: ODBCONN.CPP Version 2.1 */
+/* Name: ODBCONN.CPP Version 2.2 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
/* */
@@ -37,8 +37,8 @@
#include "xobject.h"
//#include "kindex.h"
#include "xtable.h"
-#include "tabodbc.h"
#include "odbccat.h"
+#include "tabodbc.h"
#include "plgcnx.h" // For DB types
#include "resource.h"
#include "valblk.h"
@@ -53,6 +53,8 @@
extern "C" HINSTANCE s_hModule; // Saved module handle
#endif // WIN32
+int GetConvSize();
+
/***********************************************************************/
/* Some macro's (should be defined elsewhere to be more accessible) */
/***********************************************************************/
@@ -100,7 +102,12 @@ static int GetSQLCType(int type)
case TYPE_BIGINT: tp = SQL_C_SBIGINT; break;
case TYPE_DOUBLE: tp = SQL_C_DOUBLE; break;
case TYPE_TINY : tp = SQL_C_TINYINT; break;
+//#if (ODBCVER >= 0x0300)
+// case TYPE_DECIM: tp = SQL_C_NUMERIC; break; (CRASH!!!)
+//#else
case TYPE_DECIM: tp = SQL_C_CHAR; break;
+//#endif
+
} // endswitch type
return tp;
@@ -122,7 +129,7 @@ int TranslateSQLType(int stp, int prec, int& len, char& v)
case SQL_LONGVARCHAR: // (-1)
v = 'V';
type = TYPE_STRING;
- len = MY_MIN(abs(len), 256);
+ len = MY_MIN(abs(len), GetConvSize());
break;
case SQL_NUMERIC: // 2
case SQL_DECIMAL: // 3
@@ -291,7 +298,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, int cto, int qto, bool info)
+ char *colpat, int maxres, bool info, POPARM sop)
{
int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING, TYPE_STRING,
TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT,
@@ -310,10 +317,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
+ if (ocp->Open(dsn, sop, 10) < 1) // openReadOnly + noODBCdialog
return NULL;
if (table && !strchr(table, '%')) {
@@ -342,7 +347,7 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *db, char *table,
} // endif ocp
if (trace)
- htrc("ODBCColumns: max=%d len=%d,%d,%d\n",
+ htrc("ODBCColumns: max=%d len=%d,%d,%d,%d\n",
maxres, length[0], length[1], length[2], length[3]);
/************************************************************************/
@@ -388,12 +393,13 @@ 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, int cto, int qto)
+PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, POPARM sop)
{
ODBConn *ocp = new(g) ODBConn(g, NULL);
- ocp->SetLoginTimeout((DWORD)cto);
- ocp->SetQueryTimeout((DWORD)qto);
+ if (ocp->Open(dsn, sop, 10) < 1) // openReadOnly + noOdbcDialog
+ return NULL;
+
return ocp->GetMetaData(g, dsn, src);
} // end of ODBCSrcCols
@@ -574,7 +580,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, int cto, int qto, bool info)
+ int maxres, bool info, POPARM sop)
{
int buftyp[] = {TYPE_STRING, TYPE_STRING, TYPE_STRING,
TYPE_STRING, TYPE_STRING};
@@ -594,10 +600,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
+ if (ocp->Open(dsn, sop, 2) < 1) // 2 is openReadOnly
return NULL;
if (!maxres)
@@ -863,8 +867,7 @@ bool DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
for (int i = 0; i < MAX_NUM_OF_MSG
&& (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
&& strcmp((char*)state, "00000"); i++) {
- m_ErrMsg[i] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1);
- strcpy(m_ErrMsg[i], (char*)msg);
+ m_ErrMsg[i] = (PSZ)PlugDup(g, (char*)msg);
if (trace)
htrc("%s: %s, Native=%d\n", state, msg, native);
@@ -878,8 +881,7 @@ bool DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt)
} else {
snprintf((char*)msg, SQL_MAX_MESSAGE_LENGTH + 1, "%s: %s", m_Msg,
MSG(BAD_HANDLE_VAL));
- m_ErrMsg[0] = (PSZ)PlugSubAlloc(g, NULL, strlen((char*)msg) + 1);
- strcpy(m_ErrMsg[0], (char*)msg);
+ m_ErrMsg[0] = (PSZ)PlugDup(g, (char*)msg);
if (trace)
htrc("%s: rc=%hd\n", SVP(m_ErrMsg[0]), m_RC);
@@ -924,12 +926,15 @@ ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp)
m_RowsetSize = (DWORD)((tdbp) ? tdbp->Rows : 10);
m_Catver = (tdbp) ? tdbp->Catver : 0;
m_Rows = 0;
+ m_Fetch = 0;
m_Connect = NULL;
+ m_User = NULL;
+ m_Pwd = NULL;
m_Updatable = true;
m_Transact = false;
m_Scrollable = (tdbp) ? tdbp->Scrollable : false;
- m_First = true;
m_Full = false;
+ m_UseCnc = false;
m_IDQuoteChar[0] = '"';
m_IDQuoteChar[1] = 0;
//*m_ErrMsg = '\0';
@@ -982,7 +987,7 @@ void ODBConn::ThrowDBX(RETCODE rc, PSZ msg, HSTMT hstmt)
void ODBConn::ThrowDBX(PSZ msg)
{
- DBX* xp = new(m_G) DBX(0, msg);
+ DBX* xp = new(m_G) DBX(0, "Error");
xp->m_ErrMsg[0] = msg;
throw xp;
@@ -1005,8 +1010,7 @@ PSZ ODBConn::GetStringInfo(ushort infotype)
// *buffer = '\0';
} // endif rc
- p = (char *)PlugSubAlloc(m_G, NULL, strlen(buffer) + 1);
- strcpy(p, buffer);
+ p = PlugDup(m_G, buffer);
return p;
} // end of GetStringInfo
@@ -1061,7 +1065,7 @@ void ODBConn::OnSetOptions(HSTMT hstmt)
/***********************************************************************/
/* Open: connect to a data source. */
/***********************************************************************/
-int ODBConn::Open(PSZ ConnectString, DWORD options)
+int ODBConn::Open(PSZ ConnectString, POPARM sop, DWORD options)
{
PGLOBAL& g = m_G;
//ASSERT_VALID(this);
@@ -1070,6 +1074,11 @@ int ODBConn::Open(PSZ ConnectString, DWORD options)
m_Updatable = !(options & openReadOnly);
m_Connect = ConnectString;
+ m_User = sop->User;
+ m_Pwd = sop->Pwd;
+ m_LoginTimeout = sop->Cto;
+ m_QueryTimeout = sop->Qto;
+ m_UseCnc = sop->UseCnc;
// Allocate the HDBC and make connection
try {
@@ -1078,18 +1087,21 @@ int ODBConn::Open(PSZ ConnectString, DWORD options)
AllocConnect(options);
/*ver = GetStringInfo(SQL_ODBC_VER);*/
- if (Connect(options)) {
- strcpy(g->Message, MSG(CONNECT_CANCEL));
- return 0;
- } // endif
+ if (!m_UseCnc) {
+ if (DriverConnect(options)) {
+ strcpy(g->Message, MSG(CONNECT_CANCEL));
+ return 0;
+ } // endif
+
+ } else // Connect using SQLConnect
+ Connect();
/*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));
+ sprintf(g->Message, "%s: %s", xp->m_Msg, xp->GetErrorMessage(0));
Close();
// Free();
return -1;
@@ -1164,9 +1176,26 @@ void ODBConn::AllocConnect(DWORD Options)
} // end of AllocConnect
/***********************************************************************/
+/* Connect to data source using SQLConnect. */
+/***********************************************************************/
+void ODBConn::Connect(void)
+ {
+ SQLRETURN rc;
+ SQLSMALLINT ul = (m_User ? SQL_NTS : 0);
+ SQLSMALLINT pl = (m_Pwd ? SQL_NTS : 0);
+
+ rc = SQLConnect(m_hdbc, (SQLCHAR*)m_Connect, SQL_NTS,
+ (SQLCHAR*)m_User, ul, (SQLCHAR*)m_Pwd, pl);
+
+ if (!Check(rc))
+ ThrowDBX(rc, "SQLConnect");
+
+ } // end of Connect
+
+/***********************************************************************/
/* Connect to data source using SQLDriverConnect. */
/***********************************************************************/
-bool ODBConn::Connect(DWORD Options)
+bool ODBConn::DriverConnect(DWORD Options)
{
RETCODE rc;
SWORD nResult;
@@ -1213,7 +1242,7 @@ bool ODBConn::Connect(DWORD Options)
// All done
return false;
- } // end of Connect
+ } // end of DriverConnect
void ODBConn::VerifyConnect()
{
@@ -1328,7 +1357,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
rc = SQLFreeStmt(m_hstmt, SQL_CLOSE);
if (!Check(rc))
- ThrowDBX(rc, "SQLFreeStmt");
+ ThrowDBX(rc, "SQLFreeStmt", m_hstmt);
m_hstmt = NULL;
} // endif m_hstmt
@@ -1343,7 +1372,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
(void*)SQL_SCROLLABLE, 0);
if (!Check(rc))
- ThrowDBX(rc, "SQLSetStmtAttr");
+ ThrowDBX(rc, "Scrollable", hstmt);
} // endif m_Scrollable
@@ -1394,7 +1423,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext())
if (!colp->IsSpecial())
- n++;
+ n++;
// n can be 0 for query such as Select count(*) from table
if (n && n != (UWORD)ncol)
@@ -1430,7 +1459,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
- strcpy(m_G->Message, x->GetErrorMessage(0));
+ sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
if (b)
SQLCancel(hstmt);
@@ -1493,7 +1522,7 @@ int ODBConn::GetResultSize(char *sql, ODBCCOL *colp)
/***********************************************************************/
/* Fetch next row. */
/***********************************************************************/
-int ODBConn::Fetch()
+int ODBConn::Fetch(int pos)
{
ASSERT(m_hstmt);
int irc;
@@ -1503,7 +1532,9 @@ int ODBConn::Fetch()
try {
// do {
- if (m_RowsetSize) {
+ if (pos) {
+ rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_ABSOLUTE, pos, &crow, NULL);
+ } else if (m_RowsetSize) {
rc = SQLExtendedFetch(m_hstmt, SQL_FETCH_NEXT, 1, &crow, NULL);
} else {
rc = SQLFetch(m_hstmt);
@@ -1516,29 +1547,22 @@ int ODBConn::Fetch()
m_hstmt, m_RowsetSize, rc);
if (!Check(rc))
- ThrowDBX(rc, "Fetch", m_hstmt);
+ ThrowDBX(rc, "Fetching", 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;
+ if (rc == SQL_NO_DATA_FOUND) {
+ m_Full = (m_Fetch == 1);
+ irc = 0;
+ } else
+ irc = (int)crow;
+ m_Fetch++;
+ m_Rows += irc;
} catch(DBX *x) {
if (trace)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
- strcpy(g->Message, x->GetErrorMessage(0));
+ sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
irc = -1;
} // end try/catch
@@ -1574,7 +1598,7 @@ int ODBConn::PrepareSQL(char *sql)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
- strcpy(g->Message, x->GetErrorMessage(0));
+ sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
} // end try/catch
} // endif Mode
@@ -1620,7 +1644,7 @@ int ODBConn::PrepareSQL(char *sql)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
- strcpy(g->Message, x->GetErrorMessage(0));
+ sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
if (b)
SQLCancel(hstmt);
@@ -1672,7 +1696,7 @@ int ODBConn::ExecuteSQL(void)
} // endif ncol
} catch(DBX *x) {
- strcpy(m_G->Message, x->GetErrorMessage(0));
+ sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
SQLCancel(m_hstmt);
rc = SQLFreeStmt(m_hstmt, SQL_DROP);
m_hstmt = NULL;
@@ -1709,9 +1733,11 @@ bool ODBConn::BindParam(ODBCCOL *colp)
ThrowDBX(rc, "SQLDescribeParam", m_hstmt);
} catch(DBX *x) {
- strcpy(m_G->Message, x->GetErrorMessage(0));
+ sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
colsize = colp->GetPrecision();
sqlt = GetSQLType(buftype);
+ dec = IsTypeChar(buftype) ? 0 : colp->GetScale();
+ nul = SQL_NULLABLE_UNKNOWN;
} // end try/catch
buf = colp->GetBuffer(0);
@@ -1815,7 +1841,7 @@ bool ODBConn::ExecSQLcommand(char *sql)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
- sprintf(g->Message, "Remote: %s", x->GetErrorMessage(0));
+ sprintf(g->Message, "Remote %s: %s", x->m_Msg, x->GetErrorMessage(0));
if (b)
SQLCancel(hstmt);
@@ -1865,9 +1891,6 @@ PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src)
RETCODE rc;
HSTMT hstmt;
- if (Open(dsn, 10) < 1) // openReadOnly + noOdbcDialog
- return NULL;
-
try {
rc = SQLAllocStmt(m_hdbc, &hstmt);
@@ -1903,7 +1926,7 @@ PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src)
} // endfor i
} catch(DBX *x) {
- strcpy(g->Message, x->GetErrorMessage(0));
+ sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
goto err;
} // end try/catch
@@ -1954,7 +1977,7 @@ PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src)
} // endfor i
} catch(DBX *x) {
- strcpy(g->Message, x->GetErrorMessage(0));
+ sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
qrp = NULL;
} // end try/catch
@@ -2006,7 +2029,7 @@ bool ODBConn::GetDataSources(PQRYRES qrp)
} // endfor i
} catch(DBX *x) {
- strcpy(m_G->Message, x->GetErrorMessage(0));
+ sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
rv = true;
} // end try/catch
@@ -2057,7 +2080,7 @@ bool ODBConn::GetDrivers(PQRYRES qrp)
} // endfor n
} catch(DBX *x) {
- strcpy(m_G->Message, x->GetErrorMessage(0));
+ sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
rv = true;
} // end try/catch
@@ -2375,7 +2398,7 @@ int ODBConn::GetCatInfo(CATPARM *cap)
for (int i = 0; i < MAX_NUM_OF_MSG && x->m_ErrMsg[i]; i++)
htrc(x->m_ErrMsg[i]);
- strcpy(g->Message, x->GetErrorMessage(0));
+ sprintf(g->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
irc = -1;
} // end try/catch
@@ -2479,7 +2502,7 @@ int ODBConn::Rewind(char *sql, ODBCCOL *tocols)
rbuf = (int)crow;
} catch(DBX *x) {
- strcpy(m_G->Message, x->GetErrorMessage(0));
+ sprintf(m_G->Message, "%s: %s", x->m_Msg, x->GetErrorMessage(0));
rbuf = -1;
} // end try/catch
diff --git a/storage/connect/odbconn.h b/storage/connect/odbconn.h
index dfdb9fe7f56..41cc2439354 100644
--- a/storage/connect/odbconn.h
+++ b/storage/connect/odbconn.h
@@ -119,7 +119,7 @@ class ODBConn : public BLOCK {
noOdbcDialog = 0x0008, // Don't display ODBC Connect dialog
forceOdbcDialog = 0x0010}; // Always display ODBC connect dialog
- int Open(PSZ ConnectString, DWORD Options = 0);
+ int Open(PSZ ConnectString, POPARM sop, DWORD Options = 0);
int Rewind(char *sql, ODBCCOL *tocols);
void Close(void);
PQRYRES AllocateResult(PGLOBAL g);
@@ -135,11 +135,13 @@ class ODBConn : public BLOCK {
public:
// Operations
- void SetLoginTimeout(DWORD sec) {m_LoginTimeout = sec;}
- void SetQueryTimeout(DWORD sec) {m_QueryTimeout = sec;}
+//void SetLoginTimeout(DWORD sec) {m_LoginTimeout = sec;}
+//void SetQueryTimeout(DWORD sec) {m_QueryTimeout = sec;}
+//void SetUserName(PSZ user) {m_User = user;}
+//void SetUserPwd(PSZ pwd) {m_Pwd = pwd;}
int GetResultSize(char *sql, ODBCCOL *colp);
int ExecDirectSQL(char *sql, ODBCCOL *tocols);
- int Fetch(void);
+ int Fetch(int pos = 0);
int PrepareSQL(char *sql);
int ExecuteSQL(void);
bool BindParam(ODBCCOL *colp);
@@ -155,7 +157,7 @@ class ODBConn : public BLOCK {
// Implementation
public:
-// virtual ~ODBConn();
+//virtual ~ODBConn();
// ODBC operations
protected:
@@ -163,7 +165,8 @@ class ODBConn : public BLOCK {
void ThrowDBX(RETCODE rc, PSZ msg, HSTMT hstmt = SQL_NULL_HSTMT);
void ThrowDBX(PSZ msg);
void AllocConnect(DWORD dwOptions);
- bool Connect(DWORD Options);
+ void Connect(void);
+ bool DriverConnect(DWORD Options);
void VerifyConnect(void);
void GetConnectInfo(void);
void Free(void);
@@ -185,11 +188,14 @@ class ODBConn : public BLOCK {
DWORD m_RowsetSize;
char m_IDQuoteChar[2];
PSZ m_Connect;
+ PSZ m_User;
+ PSZ m_Pwd;
int m_Catver;
int m_Rows;
+ int m_Fetch;
bool m_Updatable;
bool m_Transact;
bool m_Scrollable;
- bool m_First;
+ bool m_UseCnc;
bool m_Full;
}; // end of ODBConn class definition
diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h
index e61a49ba9f9..4dc8f293070 100644
--- a/storage/connect/plgdbsem.h
+++ b/storage/connect/plgdbsem.h
@@ -1,7 +1,7 @@
/************** PlgDBSem H Declares Source Code File (.H) **************/
/* Name: PLGDBSEM.H Version 3.6 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
/* */
/* This file contains the PlugDB++ application type definitions. */
/***********************************************************************/
@@ -504,9 +504,10 @@ enum XFLD {FLD_NO = 0, /* Not a field definition item */
FLD_EXTRA = 13, /* Field extra info */
FLD_PRIV = 14, /* Field priviledges */
FLD_DATEFMT = 15, /* Field date format */
- FLD_CAT = 16, /* Table catalog */
- FLD_SCHEM = 17, /* Table schema */
- FLD_TABNAME = 18}; /* Column Table name */
+ FLD_FORMAT = 16, /* Field format */
+ FLD_CAT = 17, /* Table catalog */
+ FLD_SCHEM = 18, /* Table schema */
+ FLD_TABNAME = 19}; /* Column Table name */
/***********************************************************************/
/* Result of last SQL noconv query. */
@@ -584,6 +585,7 @@ DllExport PCATLG PlgGetCatalog(PGLOBAL g, bool jump = true);
DllExport bool PlgSetXdbPath(PGLOBAL g, PSZ, PSZ, char *, int, char *, int);
DllExport void PlgDBfree(MBLOCK&);
DllExport void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size);
+DllExport char *PlgDBDup(PGLOBAL g, const char *str);
DllExport void *PlgDBalloc(PGLOBAL, void *, MBLOCK&);
DllExport void *PlgDBrealloc(PGLOBAL, void *, MBLOCK&, size_t);
DllExport void NewPointer(PTABS, void *, void *);
diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp
index d8009bcc71f..32f6d6f8366 100644
--- a/storage/connect/plgdbutl.cpp
+++ b/storage/connect/plgdbutl.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -294,8 +294,7 @@ PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
#else // !XMSG
GetRcString(ids + crp->Ncol, cname, sizeof(cname));
#endif // !XMSG
- crp->Name = (PSZ)PlugSubAlloc(g, NULL, strlen(cname) + 1);
- strcpy(crp->Name, cname);
+ crp->Name = (PSZ)PlugDup(g, cname);
} else
crp->Name = NULL; // Will be set by caller
@@ -311,7 +310,7 @@ PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
else
crp->Kdata = NULL;
- if (g->Trace)
+ if (trace)
htrc("Column(%d) %s type=%d len=%d value=%p\n",
crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata);
@@ -853,8 +852,7 @@ FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
htrc(" fp=%p\n", fp);
// fname may be in volatile memory such as stack
- fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(fname) + 1);
- strcpy((char*)fp->Fname, fname);
+ fp->Fname = PlugDup(g, fname);
fp->Count = 1;
fp->Type = TYPE_FB_FILE;
fp->File = fop;
@@ -1401,6 +1399,23 @@ void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
} // end of PlgDBSubAlloc
/***********************************************************************/
+/* Program for sub-allocating and copying a string in a storage area. */
+/***********************************************************************/
+char *PlgDBDup(PGLOBAL g, const char *str)
+ {
+ if (str) {
+ char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
+
+ if (sm)
+ strcpy(sm, str);
+
+ return sm;
+ } else
+ return NULL;
+
+ } // end of PlgDBDup
+
+/***********************************************************************/
/* PUTOUT: Plug DB object typing routine. */
/***********************************************************************/
void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c
index c77975e5e30..36d115e0096 100644
--- a/storage/connect/plugutil.c
+++ b/storage/connect/plugutil.c
@@ -6,7 +6,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 1993-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 1993-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -144,12 +144,12 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize)
return NULL;
} else {
g->Sarea_Size = worksize;
- g->Trace = 0;
g->Createas = 0;
g->Alchecked = 0;
g->Mrr = 0;
g->Activityp = g->ActivityStart = NULL;
g->Xchk = NULL;
+ g->N = 0;
strcpy(g->Message, "");
/*******************************************************************/
@@ -228,7 +228,6 @@ BOOL PlugIsAbsolutePath(LPCSTR path)
#endif
}
-
/***********************************************************************/
/* Set the full path of a file relatively to a given path. */
/* Note: this routine is not really implemented for Unix. */
@@ -385,8 +384,7 @@ char *PlugReadMessage(PGLOBAL g, int mid, char *m)
err:
if (g) {
// Called by STEP
- msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
- strcpy(msg, stmsg);
+ msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
@@ -421,8 +419,7 @@ char *PlugGetMessage(PGLOBAL g, int mid)
if (g) {
// Called by STEP
- msg = (char *)PlugSubAlloc(g, NULL, strlen(stmsg) + 1);
- strcpy(msg, stmsg);
+ msg = PlugDup(g, stmsg);
} else // Called by MSG or PlgGetErrorMsg
msg = stmsg;
@@ -537,6 +534,22 @@ void *PlugSubAlloc(PGLOBAL g, void *memp, size_t size)
} /* end of PlugSubAlloc */
/***********************************************************************/
+/* Program for sub-allocating and copying a string in a storage area. */
+/***********************************************************************/
+char *PlugDup(PGLOBAL g, const char *str)
+ {
+ if (str) {
+ char *sm = (char*)PlugSubAlloc(g, NULL, strlen(str) + 1);
+
+ strcpy(sm, str);
+ return sm;
+ } else
+ return NULL;
+
+ } // end of PlugDup
+
+#if 0
+/***********************************************************************/
/* This routine suballocate a copy of the passed string. */
/***********************************************************************/
char *PlugDup(PGLOBAL g, const char *str)
@@ -552,6 +565,7 @@ char *PlugDup(PGLOBAL g, const char *str)
return(buf);
} /* end of PlugDup */
+#endif // 0
/***********************************************************************/
/* This routine makes a pointer from an offset to a memory pointer. */
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index 51d777a7d17..c6cbedd9636 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2004-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 2004-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -153,10 +153,9 @@ char *RELDEF::GetStringCatInfo(PGLOBAL g, PSZ what, PSZ sdef)
if (s) {
if (!Hc->IsPartitioned() ||
(stricmp(what, "filename") && stricmp(what, "tabname")
- && stricmp(what, "connect"))) {
- sval= (char*)PlugSubAlloc(g, NULL, strlen(s) + 1);
- strcpy(sval, s);
- } else
+ && stricmp(what, "connect")))
+ sval= PlugDup(g, s);
+ else
sval= s;
} else if (!stricmp(what, "filename")) {
@@ -213,8 +212,7 @@ bool TABDEF::Define(PGLOBAL g, PCATLG cat, LPCSTR name, LPCSTR am)
{
int poff = 0;
- Name = (PSZ)PlugSubAlloc(g, NULL, strlen(name) + 1);
- strcpy(Name, name);
+ Name = (PSZ)PlugDup(g, name);
Cat = cat;
Hc = ((MYCAT*)cat)->GetHandler();
Catfunc = GetFuncID(GetStringCatInfo(g, "Catfunc", NULL));
@@ -712,8 +710,7 @@ COLDEF::COLDEF(void) : COLCRT()
/***********************************************************************/
int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff)
{
- Name = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Name) + 1);
- strcpy(Name, cfp->Name);
+ Name = (PSZ)PlugDup(g, cfp->Name);
if (!(cfp->Flags & U_SPECIAL)) {
Poff = poff;
@@ -735,22 +732,16 @@ int COLDEF::Define(PGLOBAL g, void *memp, PCOLINFO cfp, int poff)
Key = cfp->Key;
Freq = cfp->Freq;
- if (cfp->Remark && *cfp->Remark) {
- Desc = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Remark) + 1);
- strcpy(Desc, cfp->Remark);
- } // endif Remark
+ if (cfp->Remark && *cfp->Remark)
+ Desc = (PSZ)PlugDup(g, cfp->Remark);
- if (cfp->Datefmt) {
- Decode = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Datefmt) + 1);
- strcpy(Decode, cfp->Datefmt);
- } // endif Datefmt
+ if (cfp->Datefmt)
+ Decode = (PSZ)PlugDup(g, cfp->Datefmt);
} // endif special
- if (cfp->Fieldfmt) {
- Fmt = (PSZ)PlugSubAlloc(g, memp, strlen(cfp->Fieldfmt) + 1);
- strcpy(Fmt, cfp->Fieldfmt);
- } // endif Fieldfmt
+ if (cfp->Fieldfmt)
+ Fmt = (PSZ)PlugDup(g, cfp->Fieldfmt);
Flags = cfp->Flags;
return (Flags & (U_VIRTUAL|U_SPECIAL)) ? 0 : Long;
diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h
index 156d46b9791..b7150294e9b 100644
--- a/storage/connect/tabdos.h
+++ b/storage/connect/tabdos.h
@@ -222,6 +222,7 @@ class DllExport DOSCOL : public COLBLK {
virtual PVBLK GetDval(void) {return Dval;}
// Methods
+ using COLBLK::Print;
virtual bool VarSize(void);
virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
virtual void ReadColumn(PGLOBAL g);
diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp
index a3c56965794..9ffa1d6cc78 100644
--- a/storage/connect/tabjson.cpp
+++ b/storage/connect/tabjson.cpp
@@ -1,5 +1,5 @@
/************* tabjson C++ Program Source Code File (.CPP) *************/
-/* PROGRAM NAME: tabxjson Version 1.0 */
+/* PROGRAM NAME: tabjson Version 1.1 */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* This program are the JSON class DB execution routines. */
/***********************************************************************/
@@ -31,12 +31,451 @@
#endif // ZIP_SUPPORT
#include "tabmul.h"
#include "checklvl.h"
+#include "resource.h"
+#include "mycat.h"
+
+/***********************************************************************/
+/* This should be an option. */
+/***********************************************************************/
+#define MAXCOL 200 /* Default max column nb in result */
+#define TYPE_UNKNOWN 12 /* Must be greater than other types */
/***********************************************************************/
/* External function. */
/***********************************************************************/
USETEMP UseTemp(void);
+/***********************************************************************/
+/* Make the document tree from a file. */
+/***********************************************************************/
+PJSON MakeJsonTree(PGLOBAL g, char *fn, char *objn, int pty,
+ PJAR& doc, DWORD& drc)
+{
+ char *p, *memory, *objpath, *key;
+ int len, i = 0;
+ HANDLE hFile;
+ MEMMAP mm;
+ PJSON jsp, top;
+ PJOB objp = NULL;
+ PJAR arp = NULL;
+ PJVAL val = NULL;
+
+ /*********************************************************************/
+ /* Create the mapping file object. */
+ /*********************************************************************/
+ hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ drc = GetLastError();
+
+ if (!*g->Message)
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR), "map", (int)drc, fn);
+
+ return NULL;
+ } // 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);
+ drc = ENOENT;
+ return NULL;
+ } else if (!memory) {
+ sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, drc);
+ CloseFileHandle(hFile);
+ return NULL;
+ } // endif Memory
+
+ CloseFileHandle(hFile); // Not used anymore
+
+ /*********************************************************************/
+ /* Parse the json file and allocate its tree structure. */
+ /*********************************************************************/
+ g->Message[0] = 0;
+ jsp = top = ParseJson(g, memory, len, pty);
+ CloseMemMap(memory, len);
+ drc = EBADF;
+
+ if (!jsp && g->Message[0])
+ return NULL;
+
+ objpath = PlugDup(g, objn); // NULL if !objn
+
+ /*********************************************************************/
+ /* Find the table in the tree structure. */
+ /*********************************************************************/
+ for (doc = NULL; 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 NULL;
+ } // 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 NULL;
+ } // endif val
+
+ } else if (objpath[strlen(objpath)-1] == ']') {
+ if (jsp->GetType() != TYPE_JAR) {
+ strcpy(g->Message, "Table path does no match json file");
+ return NULL;
+ } // 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 NULL;
+ } // endif val
+
+ } else {
+ sprintf(g->Message, "Invalid Table path %s", objn);
+ return NULL;
+ } // 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 top;
+} // end of MakeJsonTree
+
+typedef struct _jncol {
+ struct _jncol *Next;
+ char *Name;
+ char *Fmt;
+ int Type;
+ int Len;
+ int Scale;
+ bool Cbn;
+ bool Found;
+} JCOL, *PJCL;
+
+/***********************************************************************/
+/* JSONColumns: construct the result blocks containing the description */
+/* of all the columns of a table contained inside a JSON file. */
+/***********************************************************************/
+PQRYRES JSONColumns(PGLOBAL g, char *dp, const char *fn, char *objn,
+ int pretty, int lrecl, int lvl, bool info)
+{
+ static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT,
+ TYPE_INT, TYPE_SHORT, TYPE_SHORT, TYPE_STRING};
+ static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC,
+ FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT};
+ static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0};
+ char filename[_MAX_PATH], colname[65], fmt[129], *buf;
+ int i, j, n = 0;
+ int ncol = sizeof(buftyp) / sizeof(int);
+ FILE *infile;
+ DWORD drc;
+ PVAL valp;
+ JCOL jcol;
+ PJCL jcp, fjcp = NULL, pjcp = NULL;
+ PJPR *jrp, jpp;
+ PJSON jsp;
+ PJVAL jvp;
+ PJOB row;
+ PJAR doc;
+ PQRYRES qrp;
+ PCOLRES crp;
+
+ if (info) {
+ length[0] = 128;
+ length[7] = 256;
+ goto skipit;
+ } // endif info
+
+ if (trace)
+ htrc("File %s pretty=%d lvl=%d lrecl=%d\n",
+ SVP(fn), pretty, lvl, lrecl);
+
+ /*********************************************************************/
+ /* Open the input file. */
+ /*********************************************************************/
+ if (!fn) {
+ strcpy(g->Message, MSG(MISSING_FNAME));
+ return NULL;
+ } else
+ PlugSetPath(filename, fn, dp);
+
+ if (pretty == 2) {
+ if (!MakeJsonTree(g, filename, objn, pretty, doc, drc))
+ return NULL;
+
+ jsp = (doc) ? doc->GetValue(0) : NULL;
+ } else {
+ if (!lrecl) {
+ sprintf(g->Message, "LRECL must be specified for pretty=%d", pretty);
+ return NULL;
+ } else if (!(buf = (char*)PlugSubAlloc(g, NULL, lrecl))) {
+ sprintf(g->Message, "Alloc error, lrecl=%d", lrecl);
+ return NULL;
+ } // endif buf
+
+ if (!(infile = global_fopen(g, MSGID_CANNOT_OPEN, filename, "r")))
+ return NULL;
+
+ // Read first record
+ for (i = 0; i <= pretty; i++)
+ if (!fgets(buf, lrecl, infile)) {
+ if (feof(infile)) {
+ strcpy(g->Message, "Void json table");
+ return NULL;
+ } // endif fgets
+
+ sprintf(g->Message, MSG(READ_ERROR), filename, strerror(0));
+ return NULL;
+ } // endif fgets
+
+ jsp = ParseJson(g, buf, strlen(buf), pretty, NULL);
+ } // endif pretty
+
+ if (!(row = (jsp) ? jsp->GetObject() : NULL)) {
+ strcpy(g->Message, "Can only retrieve columns from object rows");
+ return NULL;
+ } // endif row
+
+ jcol.Next = NULL;
+ jcol.Found = true;
+ colname[64] = 0;
+ fmt[128] = 0;
+ jrp = (PJPR*)PlugSubAlloc(g, NULL, sizeof(PJPR) * lvl);
+
+ /*********************************************************************/
+ /* Analyse the JSON tree and define columns. */
+ /*********************************************************************/
+ for (i = 1; ; i++) {
+ for (jpp = row->GetFirst(); jpp; jpp = jpp->GetNext()) {
+ for (j = 0; j < lvl; j++)
+ jrp[j] = NULL;
+
+ more:
+ strncpy(colname, jpp->GetKey(), 64);
+ *fmt = 0;
+ j = 0;
+ jvp = jpp->GetVal();
+
+ retry:
+ if ((valp = jvp ? jvp->GetValue() : NULL)) {
+ jcol.Type = valp->GetType();
+ jcol.Len = valp->GetValLen();
+ jcol.Scale = valp->GetValPrec();
+ jcol.Cbn = valp->IsNull();
+ } else if (!jvp || jvp->IsNull()) {
+ jcol.Type = TYPE_UNKNOWN;
+ jcol.Len = jcol.Scale = 0;
+ jcol.Cbn = true;
+ } else if (j < lvl) {
+ if (!*fmt)
+ strcpy(fmt, colname);
+
+ jsp = jvp->GetJson();
+
+ switch (jsp->GetType()) {
+ case TYPE_JOB:
+ if (!jrp[j])
+ jrp[j] = jsp->GetFirst();
+
+ strncat(strncat(fmt, ":", 128), jrp[j]->GetKey(), 128);
+ strncat(strncat(colname, "_", 64), jrp[j]->GetKey(), 64);
+ jvp = jrp[j]->GetVal();
+ j++;
+ break;
+ case TYPE_JAR:
+ strncat(fmt, ":", 128);
+ jvp = jsp->GetValue(0);
+ break;
+ default:
+ sprintf(g->Message, "Logical error after %s", fmt);
+ goto err;
+ } // endswitch jsp
+
+ goto retry;
+ } else {
+ jcol.Type = TYPE_STRING;
+ jcol.Len = 256;
+ jcol.Scale = 0;
+ jcol.Cbn = true;
+ } // endif's
+
+ // Check whether this column was already found
+ for (jcp = fjcp; jcp; jcp = jcp->Next)
+ if (!strcmp(colname, jcp->Name))
+ break;
+
+ if (jcp) {
+ if (jcp->Type != jcol.Type)
+ jcp->Type = TYPE_STRING;
+
+ if (*fmt && (!jcp->Fmt || strlen(jcp->Fmt) < strlen(fmt))) {
+ jcp->Fmt = PlugDup(g, fmt);
+ length[7] = MY_MAX(length[7], strlen(fmt));
+ } // endif *fmt
+
+ jcp->Len = MY_MAX(jcp->Len, jcol.Len);
+ jcp->Scale = MY_MAX(jcp->Scale, jcol.Scale);
+ jcp->Cbn |= jcol.Cbn;
+ jcp->Found = true;
+ } else {
+ // New column
+ jcp = (PJCL)PlugSubAlloc(g, NULL, sizeof(JCOL));
+ *jcp = jcol;
+ jcp->Cbn |= (i > 1);
+ jcp->Name = PlugDup(g, colname);
+ length[0] = MY_MAX(length[0], strlen(colname));
+
+ if (*fmt) {
+ jcp->Fmt = PlugDup(g, fmt);
+ length[7] = MY_MAX(length[7], strlen(fmt));
+ } else
+ jcp->Fmt = NULL;
+
+ if (pjcp) {
+ jcp->Next = pjcp->Next;
+ pjcp->Next = jcp;
+ } else
+ fjcp = jcp;
+
+ n++;
+ } // endif jcp
+
+ pjcp = jcp;
+
+ for (j = lvl - 1; j >= 0; j--)
+ if (jrp[j] && (jrp[j] = jrp[j]->GetNext()))
+ goto more;
+
+ } // endfor jpp
+
+ // Missing column can be null
+ for (jcp = fjcp; jcp; jcp = jcp->Next) {
+ jcp->Cbn |= !jcp->Found;
+ jcp->Found = false;
+ } // endfor jcp
+
+ if (pretty != 2) {
+ // Read next record
+ if (!fgets(buf, lrecl, infile)) {
+ if (!feof(infile)) {
+ sprintf(g->Message, MSG(READ_ERROR), filename, strerror(0));
+ return NULL;
+ } else
+ jsp = NULL;
+
+ } else if (pretty == 1 && strlen(buf) == 1 && *buf == ']') {
+ jsp = NULL;
+ } else
+ jsp = ParseJson(g, buf, strlen(buf), pretty, NULL);
+
+ } else
+ jsp = doc->GetValue(i);
+
+ if (!(row = (jsp) ? jsp->GetObject() : NULL))
+ break;
+
+ } // endor i
+
+ if (pretty != 2)
+ fclose(infile);
+
+ skipit:
+ if (trace)
+ htrc("CSVColumns: n=%d len=%d\n", n, length[0]);
+
+ /*********************************************************************/
+ /* Allocate the structures used to refer to the result set. */
+ /*********************************************************************/
+ qrp = PlgAllocResult(g, ncol, n, IDS_COLUMNS + 3,
+ buftyp, fldtyp, length, false, false);
+
+ crp = qrp->Colresp->Next->Next->Next->Next->Next->Next;
+ crp->Name = "Nullable";
+ crp->Next->Name = "Jpath";
+
+ if (info || !qrp)
+ return qrp;
+
+ qrp->Nblin = n;
+
+ /*********************************************************************/
+ /* Now get the results into blocks. */
+ /*********************************************************************/
+ for (i = 0, jcp = fjcp; jcp; i++, jcp = jcp->Next) {
+ if (jcp->Type == TYPE_UNKNOWN) // Void column
+ jcp->Type = TYPE_STRING;
+
+ crp = qrp->Colresp; // Column Name
+ crp->Kdata->SetValue(jcp->Name, i);
+ crp = crp->Next; // Data Type
+ crp->Kdata->SetValue(jcp->Type, i);
+ crp = crp->Next; // Type Name
+ crp->Kdata->SetValue(GetTypeName(jcp->Type), i);
+ crp = crp->Next; // Precision
+ crp->Kdata->SetValue(jcp->Len, i);
+ crp = crp->Next; // Length
+ crp->Kdata->SetValue(jcp->Len, i);
+ crp = crp->Next; // Scale (precision)
+ crp->Kdata->SetValue(jcp->Scale, i);
+ crp = crp->Next; // Nullable
+ crp->Kdata->SetValue(jcp->Cbn ? 1 : 0, i);
+ crp = crp->Next; // Field format
+
+ if (crp->Kdata)
+ crp->Kdata->SetValue(jcp->Fmt, i);
+
+ } // endfor i
+
+ /*********************************************************************/
+ /* Return the result pointer. */
+ /*********************************************************************/
+ return qrp;
+
+err:
+ if (pretty != 2)
+ fclose(infile);
+
+ return NULL;
+ } // end of JSONColumns
+
/* -------------------------- Class JSONDEF -------------------------- */
JSONDEF::JSONDEF(void)
@@ -44,7 +483,9 @@ JSONDEF::JSONDEF(void)
Jmode = MODE_OBJECT;
Objname = NULL;
Xcol = NULL;
+ Pretty = 2;
Limit = 1;
+ Level = 0;
ReadMode = 0;
} // end of JSONDEF constructor
@@ -57,6 +498,7 @@ bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Objname = GetStringCatInfo(g, "Object", NULL);
Xcol = GetStringCatInfo(g, "Expand", NULL);
Pretty = GetIntCatInfo("Pretty", 2);
+ Level = GetIntCatInfo("Level", 0);
Limit = GetIntCatInfo("Limit", 10);
return DOSDEF::DefineAM(g, "DOS", poff);
} // end of DefineAM
@@ -66,6 +508,9 @@ bool JSONDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
/***********************************************************************/
PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
{
+ if (Catfunc == FNC_COL)
+ return new(g)TDBJCL(this);
+
PTDBASE tdbp;
PTXF txfp = NULL;
@@ -117,14 +562,15 @@ TDBJSN::TDBJSN(PJDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
Jmode = tdp->Jmode;
Xcol = tdp->Xcol;
Fpos = -1;
- Spos = N = 0;
+//Spos = 0;
+ N = 0;
Limit = tdp->Limit;
+ NextSame = 0;
+ SameRow = 0;
+ Xval = -1;
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)
@@ -134,15 +580,15 @@ TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
Jmode = tdbp->Jmode;
Xcol = tdbp->Xcol;
Fpos = tdbp->Fpos;
- Spos = tdbp->Spos;
+//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;
+ Pretty = tdbp->Pretty;
+ Strict = tdbp->Strict;
+ Comma = tdbp->Comma;
} // end of TDBJSN copy constructor
// Used for update
@@ -221,14 +667,9 @@ bool TDBJSN::OpenDB(PGLOBAL g)
/*******************************************************************/
/* 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;
+// Spos = 0;
+ NextSame = 0;
SameRow = 0;
} else {
/*******************************************************************/
@@ -292,7 +733,8 @@ int TDBJSN::ReadDB(PGLOBAL g)
N++;
if (NextSame) {
- SameRow++;
+ SameRow = NextSame;
+ NextSame = 0;
return RC_OK;
} else if ((rc = TDBDOS::ReadDB(g)) == RC_OK)
if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK)) {
@@ -333,21 +775,20 @@ int TDBJSN::ReadDB(PGLOBAL g)
} // end of PrepareWriting
-/* ----------------------------- JSNCOL ------------------------------- */
+/* ---------------------------- JSONCOL ------------------------------ */
/***********************************************************************/
-/* JSNCOL public constructor. */
+/* JSONCOL public constructor. */
/***********************************************************************/
JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
: DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
{
Tjp = (TDBJSN *)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
- Arp = NULL;
Jpath = cdp->GetFmt();
MulVal = NULL;
Nodes = NULL;
- Nod = Nx =0;
- Ival = -1;
+ Nod = 0;
+ Xnod = -1;
Xpd = false;
Parsed = false;
} // end of JSONCOL constructor
@@ -359,13 +800,11 @@ JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
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;
+ Xnod = col1->Xnod;
Xpd = col1->Xpd;
Parsed = col1->Parsed;
} // end of JSONCOL copy constructor
@@ -387,17 +826,16 @@ bool JSONCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
} // end of SetBuffer
/***********************************************************************/
-/* Analyse array processing options. */
+/* Check whether this object is expanded. */
/***********************************************************************/
bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
{
- if (Tjp->Xcol && nm && !strcmp(nm, Tjp->Xcol) &&
- (Tjp->Xval < 0 || Tjp->Xval == i)) {
+ if ((Tjp->Xcol && nm && !strcmp(nm, Tjp->Xcol) &&
+ (Tjp->Xval < 0 || Tjp->Xval == i)) || Xpd) {
Xpd = true; // Expandable object
- Nodes[i].Op = OP_XX;
- Tjp->Xval = i;
+ Nodes[i].Op = OP_EXP;
} else if (b) {
- strcpy(g->Message, "Cannot expand more than one array");
+ strcpy(g->Message, "Cannot expand more than one branch");
return true;
} // endif Xcol
@@ -410,7 +848,7 @@ bool JSONCOL::CheckExpand(PGLOBAL g, int i, PSZ nm, bool b)
bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
{
int n = (int)strlen(p);
- bool dg = true;
+ bool dg = true, b = false;
PJNODE jnp = &Nodes[i];
if (*p) {
@@ -424,7 +862,8 @@ bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
return true;
} // endif p
- } // endif *p
+ } else
+ b = true;
// To check whether a numeric Rank was specified
for (int k = 0; dg && p[k]; k++)
@@ -434,13 +873,19 @@ bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
// Default specifications
if (CheckExpand(g, i, nm, false))
return true;
- else if (jnp->Op != OP_XX)
- if (!Value->IsTypeNum()) {
+ else if (jnp->Op != OP_EXP) {
+ if (b) {
+ // Return 1st value
+ jnp->Rank = 1;
+ jnp->Op = OP_EQ;
+ } else if (!Value->IsTypeNum()) {
jnp->CncVal = AllocateValue(g, (void*)", ", TYPE_STRING);
jnp->Op = OP_CNC;
} else
jnp->Op = OP_ADD;
+ } // endif OP
+
} else if (dg) {
if (atoi(p) > 0) {
// Return nth value
@@ -456,13 +901,13 @@ bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
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 '#': jnp->Op = OP_NUM; break;
case 'x':
case 'X': // Expand this array
if (!Tjp->Xcol && nm) {
Xpd = true;
- jnp->Op = OP_XX;
+ jnp->Op = OP_EXP;
Tjp->Xval = i;
Tjp->Xcol = nm;
} else if (CheckExpand(g, i, nm, true))
@@ -490,6 +935,38 @@ bool JSONCOL::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
return true;
} // endif's
+ // For calculated arrays, a local Value must be used
+ switch (jnp->Op) {
+ case OP_NUM:
+ jnp->Valp = AllocateValue(g, TYPE_INT);
+ break;
+ case OP_ADD:
+ case OP_MULT:
+ case OP_SEP:
+ if (!IsTypeChar(Buf_Type))
+ jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
+ else
+ jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);
+
+ break;
+ case OP_MIN:
+ case OP_MAX:
+ jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
+ break;
+ case OP_CNC:
+ if (IsTypeChar(Buf_Type))
+ jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
+ else
+ jnp->Valp = AllocateValue(g, TYPE_STRING, 512);
+
+ break;
+ default:
+ break;
+ } // endswitch Op
+
+ if (jnp->Valp)
+ MulVal = AllocateValue(g, jnp->Valp);
+
return false;
} // end of SetArrayOptions
@@ -512,8 +989,7 @@ bool JSONCOL::ParseJpath(PGLOBAL g)
else if (!Jpath)
Jpath = Name;
- pbuf = (char*)PlugSubAlloc(g, NULL, strlen(Jpath) + 1);
- strcpy(pbuf, Jpath);
+ pbuf = PlugDup(g, Jpath);
// The Jpath must be analyzed
for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++)
@@ -533,6 +1009,9 @@ bool JSONCOL::ParseJpath(PGLOBAL g)
if (SetArrayOptions(g, p, i, Nodes[i-1].Key))
return true;
+ } else if (*p == '*') {
+ // Return JSON
+ Nodes[i].Op = OP_XX;
} else {
Nodes[i].Key = p;
Nodes[i].Op = OP_EXIST;
@@ -546,42 +1025,50 @@ bool JSONCOL::ParseJpath(PGLOBAL g)
} // end of ParseJpath
/***********************************************************************/
+/* MakeJson: Serialize the json item and set value to it. */
+/***********************************************************************/
+PVAL JSONCOL::MakeJson(PGLOBAL g, PJSON jsp)
+ {
+ if (Value->IsTypeNum()) {
+ strcpy(g->Message, "Cannot make Json for a numeric column");
+ Value->Reset();
+ } else
+ Value->SetValue_psz(Serialize(g, jsp, NULL, 0));
+
+ return Value;
+ } // end of MakeJson
+
+/***********************************************************************/
/* SetValue: Set a value from a JVALUE contains. */
/***********************************************************************/
void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
{
if (val) {
- 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"));
-
+ switch (val->GetValType()) {
+ case TYPE_STRG:
+ case TYPE_INTG:
+ case TYPE_DBL:
+ vp->SetValue_pval(val->GetValue());
+ break;
+ case TYPE_BOOL:
+ if (vp->IsTypeNum())
+ vp->SetValue(val->GetInteger() ? 1 : 0);
+ else
+ vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));
+
+ break;
+ case TYPE_JAR:
+ SetJsonValue(g, vp, val->GetArray()->GetValue(0), n);
+ break;
+ case TYPE_JOB:
+// if (!vp->IsTypeNum() || !Strict) {
+ vp->SetValue_psz(val->GetObject()->GetText(g, NULL));
break;
- 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
+// } // endif Type
- default:
- vp->Reset();
- } // endswitch Type
-
- } // endelse
+ default:
+ vp->Reset();
+ } // endswitch Type
} else
vp->Reset();
@@ -589,16 +1076,210 @@ void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
} // end of SetJsonValue
/***********************************************************************/
+/* ReadColumn: */
+/***********************************************************************/
+void JSONCOL::ReadColumn(PGLOBAL g)
+ {
+ if (!Tjp->SameRow || Xnod >= Tjp->SameRow)
+ Value->SetValue_pval(GetColumnValue(g, Tjp->Row, 0));
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* GetColumnValue: */
+/***********************************************************************/
+PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i)
+ {
+ int n = Nod - 1;
+ bool expd = false;
+ PJAR arp;
+ PJVAL val = NULL;
+
+ for (; i < Nod && row; i++) {
+ if (Nodes[i].Op == OP_NUM) {
+ Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1);
+ return(Value);
+ } else if (Nodes[i].Op == OP_XX) {
+ return MakeJson(g, row);
+ } else switch (row->GetType()) {
+ case TYPE_JOB:
+ if (!Nodes[i].Key) {
+ // Expected Array was not there
+ if (i < Nod-1)
+ continue;
+ else
+ val = new(g) JVALUE(row);
+
+ } else
+ val = ((PJOB)row)->GetValue(Nodes[i].Key);
+
+ break;
+ case TYPE_JAR:
+ arp = (PJAR)row;
+
+ if (!Nodes[i].Key) {
+ if (Nodes[i].Op != OP_NULL) {
+ if (Nodes[i].Rank) {
+ val = arp->GetValue(Nodes[i].Rank - 1);
+ } else if (Nodes[i].Op == OP_EXP) {
+ return ExpandArray(g, arp, i);
+ } else
+ return CalculateArray(g, arp, i);
+
+ } else
+ val = NULL;
+
+ } else if (i < Nod-1) {
+ strcpy(g->Message, "Unexpected array");
+ val = NULL; // Not an expected array
+ } else
+ val = arp->GetValue(0);
+
+ break;
+ case TYPE_JVAL:
+ val = (PJVAL)row;
+ break;
+ default:
+ sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
+ val = NULL;
+ } // endswitch Type
+
+ if (i < Nod-1)
+ row = (val) ? val->GetJson() : NULL;
+
+ } // endfor i
+
+ SetJsonValue(g, Value, val, n);
+ return Value;
+ } // end of GetColumnValue
+
+/***********************************************************************/
+/* ExpandArray: */
+/***********************************************************************/
+PVAL JSONCOL::ExpandArray(PGLOBAL g, PJAR arp, int n)
+ {
+ int ars;
+ PJVAL jvp;
+ JVALUE jval;
+
+ ars = MY_MIN(Tjp->Limit, arp->size());
+
+ if (!(jvp = arp->GetValue(Nodes[n].Nx))) {
+ strcpy(g->Message, "Logical error expanding array");
+ longjmp(g->jumper[g->jump_level], 666);
+ } // endif jvp
+
+ if (n < Nod - 1 && jvp->GetJson()) {
+ jval.SetValue(GetColumnValue(g, jvp->GetJson(), n + 1));
+ jvp = &jval;
+ } // endif n
+
+ if (n >= Tjp->NextSame) {
+ if (++Nodes[n].Nx == ars) {
+ Nodes[n].Nx = 0;
+ Xnod = 0;
+ } else
+ Xnod = n;
+
+ Tjp->NextSame = Xnod;
+ } // endif NextSame
+
+ SetJsonValue(g, Value, jvp, n);
+ return Value;
+ } // end of ExpandArray
+
+/***********************************************************************/
+/* CalculateArray: */
+/***********************************************************************/
+PVAL JSONCOL::CalculateArray(PGLOBAL g, PJAR arp, int n)
+ {
+ int i, ars, nv = 0, nextsame = Tjp->NextSame;
+ bool err;
+ OPVAL op = Nodes[n].Op;
+ PVAL val[2], vp = Nodes[n].Valp;
+ PJVAL jvrp, jvp;
+ JVALUE jval;
+
+ vp->Reset();
+ ars = MY_MIN(Tjp->Limit, arp->size());
+
+ for (i = 0; i < ars; i++) {
+ jvrp = arp->GetValue(i);
+
+ do {
+ if (n < Nod - 1 && jvrp->GetJson()) {
+ Tjp->NextSame = nextsame;
+ jval.SetValue(GetColumnValue(g, jvrp->GetJson(), n + 1));
+ jvp = &jval;
+ } else
+ jvp = jvrp;
+
+ if (!nv++) {
+ SetJsonValue(g, vp, jvp, n);
+ continue;
+ } else
+ SetJsonValue(g, MulVal, jvp, n);
+
+ if (!MulVal->IsZero()) {
+ switch (op) {
+ case OP_CNC:
+ if (Nodes[n].CncVal) {
+ val[0] = Nodes[n].CncVal;
+ err = vp->Compute(g, val, 1, op);
+ } // endif CncVal
+
+ val[0] = MulVal;
+ err = vp->Compute(g, val, 1, op);
+ break;
+// case OP_NUM:
+ case OP_SEP:
+ val[0] = Nodes[n].Valp;
+ val[1] = MulVal;
+ err = vp->Compute(g, val, 2, OP_ADD);
+ break;
+ default:
+ val[0] = Nodes[n].Valp;
+ val[1] = MulVal;
+ err = vp->Compute(g, val, 2, op);
+ } // endswitch Op
+
+ if (err)
+ vp->Reset();
+
+ } // endif Zero
+
+ } while (Tjp->NextSame > nextsame);
+
+ } // endfor i
+
+ if (op == OP_SEP) {
+ // Calculate average
+ MulVal->SetValue(nv);
+ val[0] = vp;
+ val[1] = MulVal;
+
+ if (vp->Compute(g, val, 2, OP_DIV))
+ vp->Reset();
+
+ } // endif Op
+
+ Tjp->NextSame = nextsame;
+ return vp;
+ } // end of CalculateArray
+
+/***********************************************************************/
/* GetRow: Get the object containing this column. */
/***********************************************************************/
-PJSON JSONCOL::GetRow(PGLOBAL g, int mode)
+PJSON JSONCOL::GetRow(PGLOBAL g)
{
PJVAL val;
PJAR arp;
PJSON nwr, row = Tjp->Row;
for (int i = 0; i < Nod-1 && row; i++) {
- switch (row->GetType()) {
+ if (Nodes[i+1].Op == OP_XX)
+ break;
+ else switch (row->GetType()) {
case TYPE_JOB:
if (!Nodes[i].Key)
// Expected Array was not there
@@ -609,21 +1290,13 @@ PJSON JSONCOL::GetRow(PGLOBAL g, int mode)
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);
+ if (Nodes[i].Rank)
+ val = arp->GetValue(Nodes[i].Rank - 1);
+ else
+ val = arp->GetValue(Nodes[i].Nx);
- } else
- val = arp->GetValue(Tjp->SameRow);
-
} else
val = NULL;
@@ -643,15 +1316,16 @@ PJSON JSONCOL::GetRow(PGLOBAL g, int mode)
if (val) {
row = val->GetJson();
- } else if (mode == 1) { // mode write
+ } else {
// Construct missing objects
for (i++; row && i < Nod; i++) {
- if (!Nodes[i].Key) {
+ if (Nodes[i].Op == OP_XX)
+ break;
+ else if (!Nodes[i].Key)
// Construct intermediate array
nwr = new(g) JARRAY;
- } else {
+ else
nwr = new(g) JOBJECT;
- } // endif Nodes
if (row->GetType() == TYPE_JOB) {
((PJOB)row)->SetValue(g, new(g) JVALUE(nwr), Nodes[i-1].Key);
@@ -667,8 +1341,7 @@ PJSON JSONCOL::GetRow(PGLOBAL g, int mode)
} // endfor i
break;
- } else
- row = NULL;
+ } // endelse
} // endfor i
@@ -676,131 +1349,6 @@ PJSON JSONCOL::GetRow(PGLOBAL g, int mode)
} // 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)
@@ -817,10 +1365,11 @@ void JSONCOL::WriteColumn(PGLOBAL g)
if (Value->IsNull() && Tjp->Mode == MODE_INSERT)
return;
+ char *s;
PJOB objp = NULL;
PJAR arp = NULL;
PJVAL jvp = NULL;
- PJSON row = GetRow(g, 1);
+ PJSON jsp, row = GetRow(g);
JTYP type = row->GetType();
switch (row->GetType()) {
@@ -832,6 +1381,32 @@ void JSONCOL::WriteColumn(PGLOBAL g)
if (row) switch (Buf_Type) {
case TYPE_STRING:
+ if (Nodes[Nod-1].Op == OP_XX) {
+ s = Value->GetCharValue();
+
+ if (!(jsp = ParseJson(g, s, (int)strlen(s), 0))) {
+ strcpy(g->Message, s);
+ longjmp(g->jumper[g->jump_level], 666);
+ } // endif jsp
+
+ if (arp) {
+ if (Nod > 1 && Nodes[Nod-2].Rank)
+ arp->SetValue(g, new(g) JVALUE(jsp), Nodes[Nod-2].Rank-1);
+ else
+ arp->AddValue(g, new(g) JVALUE(jsp));
+
+ arp->InitArray(g);
+ } else if (objp) {
+ if (Nod > 1 && Nodes[Nod-2].Key)
+ objp->SetValue(g, new(g) JVALUE(jsp), Nodes[Nod-2].Key);
+
+ } else if (jvp)
+ jvp->SetValue(jsp);
+
+ break;
+ } // endif Op
+
+ // Passthru
case TYPE_DATE:
case TYPE_INT:
case TYPE_DOUBLE:
@@ -964,6 +1539,38 @@ int TDBJSON::MakeNewDoc(PGLOBAL g)
/***********************************************************************/
int TDBJSON::MakeDocument(PGLOBAL g)
{
+ char filename[_MAX_PATH];
+ DWORD drc;
+
+ if (Done)
+ return RC_OK;
+ else
+ Done = true;
+
+ // Now open the JSON file
+ PlugSetPath(filename, Txfp->To_File, GetPath());
+
+ /*********************************************************************/
+ /* Get top of the parsed tree and the inside table document. */
+ /*********************************************************************/
+ Top = MakeJsonTree(g, filename, Objname, Pretty, Doc, drc);
+
+ if (!Top) {
+ if (drc != ENOENT || Mode != MODE_INSERT)
+ return RC_FX;
+
+ return MakeNewDoc(g);
+ } // endif !Top
+
+ return RC_OK;
+ } // end of MakeDocument
+
+#if 0
+/***********************************************************************/
+/* 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;
@@ -1034,11 +1641,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
if (!jsp && g->Message[0])
return RC_FX;
- if (Objname) {
- objpath = (char*)PlugSubAlloc(g, NULL, strlen(Objname) + 1);
- strcpy(objpath, Objname);
- } else
- objpath = NULL;
+ objpath = PlugDup(g, Objname);
/*********************************************************************/
/* Find the table in the tree structure. */
@@ -1112,6 +1715,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
return RC_OK;
} // end of MakeDocument
+#endif // 0
/***********************************************************************/
/* JSON Cardinality: returns table size in number of rows. */
@@ -1167,6 +1771,51 @@ int TDBJSON::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
} // end of MakeIndex
/***********************************************************************/
+/* Return the position in the table. */
+/***********************************************************************/
+int TDBJSON::GetRecpos(void)
+ {
+#if 0
+ union {
+ uint Rpos;
+ BYTE Spos[4];
+ };
+
+ Rpos = htonl(Fpos);
+ Spos[0] = (BYTE)NextSame;
+ return Rpos;
+#endif // 0
+ return Fpos;
+ } // end of GetRecpos
+
+/***********************************************************************/
+/* Set the position in the table. */
+/***********************************************************************/
+bool TDBJSON::SetRecpos(PGLOBAL g, int recpos)
+ {
+#if 0
+ union {
+ uint Rpos;
+ BYTE Spos[4];
+ };
+
+ Rpos = recpos;
+ NextSame = Spos[0];
+ Spos[0] = 0;
+ Fpos = (signed)ntohl(Rpos);
+
+//if (Fpos != (signed)ntohl(Rpos)) {
+// Fpos = ntohl(Rpos);
+// same = false;
+//} else
+// same = true;
+#endif // 0
+
+ Fpos = recpos - 1;
+ return false;
+ } // end of SetRecpos
+
+/***********************************************************************/
/* JSON Access Method opening routine. */
/***********************************************************************/
bool TDBJSON::OpenDB(PGLOBAL g)
@@ -1175,13 +1824,7 @@ bool TDBJSON::OpenDB(PGLOBAL g)
/*******************************************************************/
/* 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;
@@ -1217,7 +1860,8 @@ int TDBJSON::ReadDB(PGLOBAL g)
N++;
if (NextSame) {
- SameRow++;
+ SameRow = NextSame;
+ NextSame = false;
rc = RC_OK;
} else if (++Fpos < (signed)Doc->size()) {
Row = Doc->GetValue(Fpos);
@@ -1257,9 +1901,10 @@ int TDBJSON::WriteDB(PGLOBAL g)
return RC_FX;
} else { // if (Jmode == MODE_VALUE)
- if (Mode == MODE_INSERT)
+ if (Mode == MODE_INSERT) {
Doc->AddValue(g, (PJVAL)Row);
- else if (Doc->SetValue(g, (PJVAL)Row, Fpos))
+ Row = new(g) JVALUE;
+ } else if (Doc->SetValue(g, (PJVAL)Row, Fpos))
return RC_FX;
} // endif Jmode
@@ -1319,4 +1964,27 @@ void TDBJSON::CloseDB(PGLOBAL g)
} // end of CloseDB
-/* -------------------------- End of json --------------------------- */
+/* ---------------------------TDBJCL class --------------------------- */
+
+/***********************************************************************/
+/* TDBJCL class constructor. */
+/***********************************************************************/
+TDBJCL::TDBJCL(PJDEF tdp) : TDBCAT(tdp)
+ {
+ Fn = tdp->GetFn();
+ Objn = tdp->Objname;
+ Pretty = tdp->Pretty;
+ Lrecl = tdp->Lrecl;
+ lvl = tdp->Level;
+ } // end of TDBJCL constructor
+
+/***********************************************************************/
+/* GetResult: Get the list the JSON file columns. */
+/***********************************************************************/
+PQRYRES TDBJCL::GetResult(PGLOBAL g)
+ {
+ return JSONColumns(g, ((PTABDEF)To_Def)->GetPath(), Fn, Objn,
+ Pretty, Lrecl, lvl, false);
+ } // end of GetResult
+
+/* --------------------------- End of json --------------------------- */
diff --git a/storage/connect/tabjson.h b/storage/connect/tabjson.h
index 68f79a1526a..20683a67971 100644
--- a/storage/connect/tabjson.h
+++ b/storage/connect/tabjson.h
@@ -1,5 +1,5 @@
/*************** tabjson H Declares Source Code File (.H) **************/
-/* Name: tabjson.h Version 1.0 */
+/* Name: tabjson.h Version 1.1 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
@@ -16,8 +16,6 @@ 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. */
/***********************************************************************/
@@ -25,7 +23,9 @@ 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
+ PVAL Valp; // The internal array VALUE
int Rank; // The rank in array
+ int Nx; // Same row number
} JNODE, *PJNODE;
/***********************************************************************/
@@ -34,6 +34,7 @@ typedef struct _jnode {
class JSONDEF : public DOSDEF { /* Table description */
friend class TDBJSON;
friend class TDBJSN;
+ friend class TDBJCL;
public:
// Constructor
JSONDEF(void);
@@ -52,6 +53,7 @@ class JSONDEF : public DOSDEF { /* Table description */
char *Xcol; /* Name of expandable column */
int Limit; /* Limit of multiple values */
int Pretty; /* Depends on file structure */
+ int Level; /* Used for catalog table */
bool Strict; /* Strict syntax checking */
}; // end of JSONDEF
@@ -77,7 +79,7 @@ class TDBJSN : public TDBDOS {
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)
+ virtual int RowNumber(PGLOBAL g, bool b = FALSE)
{return (b) ? N : Fpos + 1;}
// Database routines
@@ -94,15 +96,15 @@ class TDBJSN : public TDBDOS {
JMODE Jmode; // MODE_OBJECT by default
char *Xcol; // Name of expandable column
int Fpos; // The current row index
- int Spos; // DELETE start 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 NextSame; // Same next row
int SameRow; // Same row nb
int Xval; // Index of expandable array
+ bool Strict; // Strict syntax checking
+ bool Comma; // Row has final comma
}; // end of class TDBJSN
/* -------------------------- JSONCOL class -------------------------- */
@@ -130,8 +132,12 @@ class JSONCOL : public DOSCOL {
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);
+ PVAL GetColumnValue(PGLOBAL g, PJSON row, int i);
+ PVAL ExpandArray(PGLOBAL g, PJAR arp, int n);
+ PVAL CalculateArray(PGLOBAL g, PJAR arp, int n);
+ PVAL MakeJson(PGLOBAL g, PJSON jsp);
void SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n);
+ PJSON GetRow(PGLOBAL g);
// Default constructor not to be used
JSONCOL(void) {}
@@ -139,12 +145,10 @@ class JSONCOL : public DOSCOL {
// 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
+ 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
+ int Xnod; // Index of multiple values
bool Xpd; // True for expandable column
bool Parsed; // True when parsed
}; // end of class JSONCOL
@@ -172,7 +176,9 @@ class TDBJSON : public TDBJSN {
virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual void ResetSize(void);
- virtual int GetRecpos(void) {return Fpos;}
+ virtual int GetProgCur(void) {return N;}
+ virtual int GetRecpos(void);
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual bool PrepareWriting(PGLOBAL g) {return false;}
@@ -195,3 +201,25 @@ class TDBJSON : public TDBJSN {
bool Done; // True when document parsing is done
bool Changed; // After Update, Insert or Delete
}; // end of class TDBJSON
+
+/***********************************************************************/
+/* This is the class declaration for the JSON catalog table. */
+/***********************************************************************/
+class TDBJCL : public TDBCAT {
+ public:
+ // Constructor
+ TDBJCL(PJDEF tdp);
+
+ protected:
+ // Specific routines
+ virtual PQRYRES GetResult(PGLOBAL g);
+
+ // Members
+//char *Dp;
+ const char *Fn;
+ char *Objn;
+ int Pretty;
+ int Lrecl;
+ int lvl;
+ }; // end of class TDBJCL
+
diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp
index 415a1523d30..e903bad91c0 100644
--- a/storage/connect/tabmul.cpp
+++ b/storage/connect/tabmul.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to PlugDB Software Development 2003 - 2012 */
+/* (C) Copyright to PlugDB Software Development 2003 - 2015 */
/* Author: Olivier BERTRAND */
/* */
/* WHAT THIS PROGRAM DOES: */
@@ -172,8 +172,7 @@ bool TDBMUL::InitFileNames(PGLOBAL g)
while (n < PFNZ) {
strcat(strcat(strcpy(filename, drive), direc), FileData.cFileName);
- pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy(pfn[n++], filename);
+ pfn[n++] = PlugDup(g, filename);
if (!FindNextFile(hSearch, &FileData)) {
rc = GetLastError();
@@ -239,8 +238,7 @@ bool TDBMUL::InitFileNames(PGLOBAL g)
continue; // Not a match
strcat(strcpy(filename, direc), entry->d_name);
- pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy(pfn[n++], filename);
+ pfn[n] = PlugDup(g, filename);
if (trace)
htrc("Adding pfn[%d] %s\n", n, filename);
@@ -292,8 +290,7 @@ bool TDBMUL::InitFileNames(PGLOBAL g)
*(++p) = '\0';
// Suballocate the file name
- pfn[n] = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
- strcpy(pfn[n++], filename);
+ pfn[n++] = PlugDup(g, filename);
} // endfor n
} // endif Mul
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp
index 54627ba43fd..33a4dd67d38 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -1141,19 +1141,16 @@ int TDBMYSQL::WriteDB(PGLOBAL g)
int rc;
uint len = Query->GetLength();
char buf[64];
- bool b, oom = false;
+ bool oom = false;
// Make the Insert command value list
for (PCOL colp = Columns; colp; colp = colp->GetNext()) {
if (!colp->GetValue()->IsNull()) {
- if ((b = colp->GetResultType() == TYPE_STRING ||
- colp->GetResultType() == TYPE_DATE))
- oom |= Query->Append('\'');
-
- oom |= Query->Append(colp->GetValue()->GetCharString(buf));
-
- if (b)
- oom |= Query->Append('\'');
+ if (colp->GetResultType() == TYPE_STRING ||
+ colp->GetResultType() == TYPE_DATE)
+ oom |= Query->Append_quoted(colp->GetValue()->GetCharString(buf));
+ else
+ oom |= Query->Append(colp->GetValue()->GetCharString(buf));
} else
oom |= Query->Append("NULL");
diff --git a/storage/connect/taboccur.cpp b/storage/connect/taboccur.cpp
index 86f0bd20d47..82c4f888833 100644
--- a/storage/connect/taboccur.cpp
+++ b/storage/connect/taboccur.cpp
@@ -1,7 +1,7 @@
/************ TabOccur CPP Declares Source Code File (.CPP) ************/
/* Name: TABOCCUR.CPP Version 1.1 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2013 - 2015 */
/* */
/* OCCUR: Table that provides a view of a source table where the */
/* contain of several columns of the source table is placed in only */
@@ -93,8 +93,7 @@ bool OcrColumns(PGLOBAL g, PQRYRES qrp, const char *col,
} // endif col
// Prepare the column list
- colist = (char*)PlugSubAlloc(g, NULL, strlen(col) + 1);
- strcpy(colist, col);
+ colist = PlugDup(g, col);
m = PrepareColist(colist);
if ((rk = (rank && *rank))) {
@@ -191,8 +190,7 @@ bool OcrSrcCols(PGLOBAL g, PQRYRES qrp, const char *col,
} // endif col
// Prepare the column list
- colist = (char*)PlugSubAlloc(g, NULL, strlen(col) + 1);
- strcpy(colist, col);
+ colist = PlugDup(g, col);
m = PrepareColist(colist);
if ((rk = (rank && *rank)))
@@ -355,7 +353,7 @@ bool TDBOCCUR::MakeColumnList(PGLOBAL g)
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->GetAmType() == TYPE_AM_PRX)
- if (((PPRXCOL)colp)->Init(g))
+ if (((PPRXCOL)colp)->Init(g, NULL))
return true;
Col = (PCOL*)PlugSubAlloc(g, NULL, Mult * sizeof(PCOL));
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index 2b771bcbead..3bf1238cebc 100644
--- a/storage/connect/tabodbc.cpp
+++ b/storage/connect/tabodbc.cpp
@@ -66,8 +66,8 @@
#include "plgdbsem.h"
#include "mycat.h"
#include "xtable.h"
-#include "tabodbc.h"
#include "odbccat.h"
+#include "tabodbc.h"
#include "tabmul.h"
#include "reldef.h"
#include "tabcol.h"
@@ -93,9 +93,10 @@ bool ExactInfo(void);
/***********************************************************************/
ODBCDEF::ODBCDEF(void)
{
- Connect= Tabname= Tabschema= Tabcat= Srcdef= Qchar= Qrystr= Sep= NULL;
+ Connect = Tabname = Tabschema = Username = Password = NULL;
+ Tabcat = Srcdef = Qchar = Qrystr = Sep = NULL;
Catver = Options = Cto = Qto = Quoted = Maxerr = Maxres = 0;
- Scrollable = Memory = Xsrc = false;
+ Scrollable = Memory = Xsrc = UseCnc = false;
} // end of ODBCDEF constructor
/***********************************************************************/
@@ -117,6 +118,8 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
+ Username = GetStringCatInfo(g, "User", NULL);
+ Password = GetStringCatInfo(g, "Password", NULL);
if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
Read_Only = true;
@@ -132,8 +135,16 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
//Options = ODBConn::noOdbcDialog | ODBConn::useCursorLib;
Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
- Scrollable = GetBoolCatInfo("Scrollable", false);
- Memory = GetBoolCatInfo("Memory", false);
+
+ if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt)
+ Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch
+
+ UseCnc = GetBoolCatInfo("UseDSN", false);
+
+ // Memory was Boolean, it is now integer
+ if (!(Memory = GetIntCatInfo("Memory", 0)))
+ Memory = GetBoolCatInfo("Memory", false) ? 1 : 0;
+
Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
@@ -190,34 +201,40 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
Connect = tdp->Connect;
TableName = tdp->Tabname;
Schema = tdp->Tabschema;
+ Ops.User = tdp->Username;
+ Ops.Pwd = tdp->Password;
Catalog = tdp->Tabcat;
Srcdef = tdp->Srcdef;
Qrystr = tdp->Qrystr;
Sep = tdp->GetSep();
Options = tdp->Options;
- Cto = tdp->Cto;
- Qto = tdp->Qto;
+ Ops.Cto = tdp->Cto;
+ Ops.Qto = tdp->Qto;
Quoted = MY_MAX(0, tdp->GetQuoted());
Rows = tdp->GetElemt();
Catver = tdp->Catver;
- Memory = (tdp->Memory) ? 1 : 0;
+ Memory = tdp->Memory;
Scrollable = tdp->Scrollable;
+ Ops.UseCnc = tdp->UseCnc;
} else {
Connect = NULL;
TableName = NULL;
Schema = NULL;
+ Ops.User = NULL;
+ Ops.Pwd = NULL;
Catalog = NULL;
Srcdef = NULL;
Qrystr = NULL;
Sep = 0;
Options = 0;
- Cto = DEFAULT_LOGIN_TIMEOUT;
- Qto = DEFAULT_QUERY_TIMEOUT;
+ Ops.Cto = DEFAULT_LOGIN_TIMEOUT;
+ Ops.Qto = DEFAULT_QUERY_TIMEOUT;
Quoted = 0;
Rows = 0;
Catver = 0;
Memory = 0;
Scrollable = false;
+ Ops.UseCnc = false;
} // endif tdp
Quote = NULL;
@@ -228,11 +245,13 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
DBQ = NULL;
Qrp = NULL;
Fpos = 0;
+ Curpos = 0;
AftRows = 0;
CurNum = 0;
Rbuf = 0;
BufSize = 0;
Nparm = 0;
+ Placed = false;
} // end of TDBODBC standard constructor
TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
@@ -242,6 +261,7 @@ TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
Connect = tdbp->Connect;
TableName = tdbp->TableName;
Schema = tdbp->Schema;
+ Ops = tdbp->Ops;
Catalog = tdbp->Catalog;
Srcdef = tdbp->Srcdef;
Qrystr = tdbp->Qrystr;
@@ -254,19 +274,17 @@ 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;
- AftRows = tdbp->AftRows;
-//Tpos = tdbp->Tpos;
-//Spos = tdbp->Spos;
- CurNum = tdbp->CurNum;
- Rbuf = tdbp->Rbuf;
+ Fpos = 0;
+ Curpos = 0;
+ AftRows = 0;
+ CurNum = 0;
+ Rbuf = 0;
BufSize = tdbp->BufSize;
Nparm = tdbp->Nparm;
Qrp = tdbp->Qrp;
+ Placed = false;
} // end of TDBODBC copy constructor
// Method
@@ -370,7 +388,7 @@ int TDBODBC::Decode(char *txt, char *buf, size_t n)
/***********************************************************************/
char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
{
- char *colist, *tabname, *sql, buf[64];
+ char *colist, *tabname, *sql, buf[NAM_LEN * 3];
LPCSTR schmp = NULL, catp = NULL;
int len, ncol = 0;
bool first = true;
@@ -474,6 +492,9 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
if (To_CondFil)
strcat(strcat(sql, " WHERE "), To_CondFil->Body);
+
+ if (trace)
+ htrc("sql: '%s'\n", sql);
return sql;
} // end of MakeSQL
@@ -483,7 +504,7 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
/***********************************************************************/
char *TDBODBC::MakeInsert(PGLOBAL g)
{
- char *stmt, *colist, *valist;
+ char *stmt, *colist, *valist, buf[NAM_LEN * 3];
// char *tk = "`";
int len = 0;
bool b = FALSE;
@@ -510,10 +531,13 @@ char *TDBODBC::MakeInsert(PGLOBAL g)
} else
b = true;
+ // Column name can be in UTF-8 encoding
+ Decode(colp->GetName(), buf, sizeof(buf));
+
if (Quote)
- strcat(strcat(strcat(colist, Quote), colp->GetName()), Quote);
+ strcat(strcat(strcat(colist, Quote), buf), Quote);
else
- strcat(colist, colp->GetName());
+ strcat(colist, buf);
strcat(valist, "?"); // Parameter marker
} // endfor colp
@@ -558,8 +582,7 @@ bool TDBODBC::BindParameters(PGLOBAL g)
/***********************************************************************/
char *TDBODBC::MakeCommand(PGLOBAL g)
{
- char *p, name[68], *qc = Ocp->GetQuoteChar();
- char *stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+ char *p, *stmt, name[68], *body = NULL, *qc = Ocp->GetQuoteChar();
char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
bool qtd = Quoted > 0;
int i = 0, k = 0;
@@ -570,6 +593,15 @@ char *TDBODBC::MakeCommand(PGLOBAL g)
qrystr[i] = (Qrystr[i] == '`') ? *qc : tolower(Qrystr[i]);
} while (Qrystr[i++]);
+ if (To_CondFil && (p = strstr(qrystr, " where "))) {
+ p[7] = 0; // Remove where clause
+ Qrystr[(p - qrystr) + 7] = 0;
+ body = To_CondFil->Body;
+ stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr)
+ + strlen(body) + 64);
+ } else
+ stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+
// Check whether the table name is equal to a keyword
// If so, it must be quoted in the original query
strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
@@ -597,6 +629,9 @@ char *TDBODBC::MakeCommand(PGLOBAL g)
stmt[i++] = (Qrystr[k] == '`') ? *qc : Qrystr[k];
} while (Qrystr[k++]);
+ if (body)
+ strcat(stmt, body);
+
} else {
sprintf(g->Message, "Cannot use this %s command",
(Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
@@ -698,10 +733,7 @@ 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)
+ if (ocp->Open(Connect, &Ops, Options) < 1)
return -1;
// Table name can be encoded in UTF-8
@@ -762,9 +794,9 @@ int TDBODBC::GetProgMax(PGLOBAL g)
/***********************************************************************/
bool TDBODBC::OpenDB(PGLOBAL g)
{
- bool rc = false;
+ bool rc = true;
- if (g->Trace)
+ if (trace)
htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n",
this, Tdb_No, Use, Mode);
@@ -788,10 +820,12 @@ bool TDBODBC::OpenDB(PGLOBAL g)
return true;
} // endif Rewind
- } // endif Memory
+ } else
+ Rbuf = Qrp->Nblin;
CurNum = 0;
Fpos = 0;
+ Curpos = 1;
return false;
} // endif use
@@ -802,14 +836,12 @@ 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);
- Ocp->SetLoginTimeout((DWORD)Cto);
- Ocp->SetQueryTimeout((DWORD)Qto);
- } else if (Ocp->IsOpen())
+ else if (Ocp->IsOpen())
Ocp->Close();
- if (Ocp->Open(Connect, Options) < 1)
+ if (Ocp->Open(Connect, &Ops, Options) < 1)
return true;
else if (Quoted)
Quote = Ocp->GetQuoteChar();
@@ -820,6 +852,37 @@ bool TDBODBC::OpenDB(PGLOBAL g)
/* Make the command and allocate whatever is used for getting results. */
/*********************************************************************/
if (Mode == MODE_READ || Mode == MODE_READX) {
+ if (Memory > 1 && !Srcdef) {
+ char *Sql;
+ int n;
+
+ if ((Sql = MakeSQL(g, true))) {
+ // Allocate a Count(*) column
+ Cnp = new(g) ODBCCOL;
+ Cnp->InitValue(g);
+
+ if ((n = Ocp->GetResultSize(Sql, Cnp)) < 0) {
+ strcpy(g->Message, "Cannot get result size");
+ return true;
+ } // endif n
+
+ Ocp->m_Rows = n;
+
+ if ((Qrp = Ocp->AllocateResult(g)))
+ Memory = 2; // Must be filled
+ else {
+ strcpy(g->Message, "Memory allocation failed");
+ return true;
+ } // endif n
+
+ Ocp->m_Rows = 0;
+ } else {
+ strcpy(g->Message, "MakeSQL failed");
+ return true;
+ } // endif Sql
+
+ } // endif Memory
+
if ((Query = MakeSQL(g, false))) {
for (PODBCCOL colp = (PODBCCOL)Columns; colp;
colp = (PODBCCOL)colp->GetNext())
@@ -839,12 +902,12 @@ bool TDBODBC::OpenDB(PGLOBAL g)
} // endif Query
- } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
- Query = MakeCommand(g);
- else
+ } else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
+ rc = false; // wait for CheckCond before calling MakeCommand(g);
+ } else
sprintf(g->Message, "Invalid mode %d", Mode);
- if (!Query || rc) {
+ if (rc) {
Ocp->Close();
return true;
} // endif rc
@@ -861,10 +924,41 @@ bool TDBODBC::OpenDB(PGLOBAL g)
/***********************************************************************/
int TDBODBC::GetRecpos(void)
{
- return Fpos; // To be really implemented
+ return Fpos;
} // end of GetRecpos
/***********************************************************************/
+/* SetRecpos: set the position of next read record. */
+/***********************************************************************/
+bool TDBODBC::SetRecpos(PGLOBAL g, int recpos)
+ {
+ if (Ocp->m_Full) {
+ Fpos = 0;
+ CurNum = recpos - 1;
+ } else if (Memory == 3) {
+ Fpos = recpos;
+ CurNum = -1;
+ } else if (Scrollable) {
+ // Is new position in the current row set?
+ if (recpos >= Curpos && recpos < Curpos + Rbuf) {
+ CurNum = recpos - Curpos;
+ Fpos = 0;
+ } else {
+ Fpos = recpos;
+ CurNum = 0;
+ } // endif recpos
+
+ } else {
+ strcpy(g->Message, "This action requires a scrollable cursor");
+ return true;
+ } // endif's
+
+ // Indicate the table position was externally set
+ Placed = true;
+ return false;
+ } // end of SetRecpos
+
+/***********************************************************************/
/* VRDNDOS: Data Base read routine for odbc access method. */
/***********************************************************************/
int TDBODBC::ReadDB(PGLOBAL g)
@@ -876,6 +970,9 @@ int TDBODBC::ReadDB(PGLOBAL g)
GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
+ if (!Query && !(Query = MakeCommand(g)))
+ return RC_FX;
+
// Send the UPDATE/DELETE command to the remote table
if (!Ocp->ExecSQLcommand(Query)) {
sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
@@ -900,22 +997,32 @@ int TDBODBC::ReadDB(PGLOBAL g)
/* Now start the reading process. */
/* Here is the place to fetch the line(s). */
/*********************************************************************/
- if (Memory != 3) {
- if (++CurNum >= Rbuf) {
- Rbuf = Ocp->Fetch();
- CurNum = 0;
- } // endif CurNum
+ if (Placed) {
+ if (Fpos && CurNum >= 0)
+ Rbuf = Ocp->Fetch((Curpos = Fpos));
rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
- } else // Getting result from memory
- rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
+ Placed = false;
+ } else {
+ if (Memory != 3) {
+ if (++CurNum >= Rbuf) {
+ Rbuf = Ocp->Fetch();
+ Curpos = Fpos + 1;
+ CurNum = 0;
+ } // endif CurNum
- if (rc == RC_OK) {
- if (Memory == 2)
- Qrp->Nblin++;
+ rc = (Rbuf > 0) ? RC_OK : (Rbuf == 0) ? RC_EF : RC_FX;
+ } else // Getting result from memory
+ rc = (Fpos < Qrp->Nblin) ? RC_OK : RC_EF;
- Fpos++; // Used for memory
- } // endif rc
+ if (rc == RC_OK) {
+ if (Memory == 2)
+ Qrp->Nblin++;
+
+ Fpos++; // Used for memory and pos
+ } // endif rc
+
+ } // endif Placed
if (trace > 1)
htrc(" Read: Rbuf=%d rc=%d\n", Rbuf, rc);
@@ -945,6 +1052,9 @@ int TDBODBC::WriteDB(PGLOBAL g)
int TDBODBC::DeleteDB(PGLOBAL g, int irc)
{
if (irc == RC_FX) {
+ if (!Query && !(Query = MakeCommand(g)))
+ return RC_FX;
+
// Send the DELETE (all) command to the remote table
if (!Ocp->ExecSQLcommand(Query)) {
sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
@@ -1158,12 +1268,12 @@ void ODBCCOL::ReadColumn(PGLOBAL g)
} // endif Buf_Type
- if (g->Trace) {
+ if (trace) {
char buf[64];
htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n",
Name, tdbp->Rows, Bufp, Buf_Type, Value->GetCharString(buf));
- } // endif Trace
+ } // endif trace
put:
if (tdbp->Memory != 2)
@@ -1397,7 +1507,7 @@ bool TDBXDBC::OpenDB(PGLOBAL g)
{
bool rc = false;
- if (g->Trace)
+ if (trace)
htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n",
this, Tdb_No, Use, Mode);
@@ -1415,12 +1525,10 @@ bool TDBXDBC::OpenDB(PGLOBAL g)
/*********************************************************************/
if (!Ocp) {
Ocp = new(g) ODBConn(g, this);
- Ocp->SetLoginTimeout((DWORD)Cto);
- Ocp->SetQueryTimeout((DWORD)Qto);
} else if (Ocp->IsOpen())
Ocp->Close();
- if (Ocp->Open(Connect, Options) < 1)
+ if (Ocp->Open(Connect, &Ops, Options) < 1)
return true;
Use = USE_OPEN; // Do it now in case we are recursively called
@@ -1554,8 +1662,11 @@ TDBOTB::TDBOTB(PODEF tdp) : TDBDRV(tdp)
Dsn = tdp->GetConnect();
Schema = tdp->GetTabschema();
Tab = tdp->GetTabname();
- Cto = tdp->Cto;
- Qto = tdp->Qto;
+ Ops.User = tdp->Username;
+ Ops.Pwd = tdp->Password;
+ Ops.Cto = tdp->Cto;
+ Ops.Qto = tdp->Qto;
+ Ops.UseCnc = tdp->UseCnc;
} // end of TDBOTB constructor
/***********************************************************************/
@@ -1563,7 +1674,7 @@ TDBOTB::TDBOTB(PODEF tdp) : TDBDRV(tdp)
/***********************************************************************/
PQRYRES TDBOTB::GetResult(PGLOBAL g)
{
- return ODBCTables(g, Dsn, Schema, Tab, Maxres, Cto, Qto, false);
+ return ODBCTables(g, Dsn, Schema, Tab, Maxres, false, &Ops);
} // end of GetResult
/* ---------------------------TDBOCL class --------------------------- */
@@ -1573,7 +1684,7 @@ PQRYRES TDBOTB::GetResult(PGLOBAL g)
/***********************************************************************/
PQRYRES TDBOCL::GetResult(PGLOBAL g)
{
- return ODBCColumns(g, Dsn, Schema, Tab, NULL, Maxres, Cto, Qto, false);
+ return ODBCColumns(g, Dsn, Schema, Tab, NULL, Maxres, false, &Ops);
} // end of GetResult
/* ------------------------ End of Tabodbc --------------------------- */
diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h
index d8644c8b6de..b8c9d85aae5 100644
--- a/storage/connect/tabodbc.h
+++ b/storage/connect/tabodbc.h
@@ -50,6 +50,8 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
PSZ Connect; /* ODBC connection string */
PSZ Tabname; /* External table name */
PSZ Tabschema; /* External table schema */
+ PSZ Username; /* User connect name */
+ PSZ Password; /* Password connect info */
PSZ Tabcat; /* External table catalog */
PSZ Srcdef; /* The source table SQL definition */
PSZ Qchar; /* Identifier quoting character */
@@ -62,9 +64,10 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
int Quoted; /* Identifier quoting level */
int Maxerr; /* Maxerr for an Exec table */
int Maxres; /* Maxres for a catalog table */
+ int Memory; /* Put result set in memory */
bool Scrollable; /* Use scrollable cursor */
- bool Memory; /* Put result set in memory */
bool Xsrc; /* Execution type */
+ bool UseCnc; /* Use SQLConnect (!SQLDriverConnect) */
}; // end of ODBCDEF
#if !defined(NODBC)
@@ -90,6 +93,7 @@ class TDBODBC : public TDBASE {
// Methods
virtual PTDB CopyOne(PTABS t);
virtual int GetRecpos(void);
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
virtual PSZ GetFile(PGLOBAL g);
virtual void SetFile(PGLOBAL g, PSZ fn);
virtual void ResetSize(void);
@@ -124,9 +128,12 @@ class TDBODBC : public TDBASE {
// Members
ODBConn *Ocp; // Points to an ODBC connection class
ODBCCOL *Cnp; // Points to count(*) column
+ ODBCPARM Ops; // Additional parameters
char *Connect; // Points to connection string
char *TableName; // Points to ODBC table name
char *Schema; // Points to ODBC table Schema
+ char *User; // User connect info
+ char *Pwd; // Password connect info
char *Catalog; // Points to ODBC table Catalog
char *Srcdef; // The source table SQL definition
char *Query; // Points to SQL statement
@@ -142,6 +149,7 @@ class TDBODBC : public TDBASE {
int Qto; // Query timeout
int Quoted; // The identifier quoting level
int Fpos; // Position of last read record
+ int Curpos; // Cursor position of last fetch
int AftRows; // The number of affected rows
int Rows; // Rowset size
int Catver; // Catalog ODBC version
@@ -151,6 +159,8 @@ class TDBODBC : public TDBASE {
int Nparm; // The number of statement parameters
int Memory; // 0: No 1: Alloc 2: Put 3: Get
bool Scrollable; // Use scrollable cursor
+ bool Placed; // True for position reading
+ bool UseCnc; // Use SQLConnect (!SQLDriverConnect)
PQRYRES Qrp; // Points to storage result
}; // end of class TDBODBC
@@ -316,8 +326,7 @@ 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
+ ODBCPARM Ops; // Additional parameters
}; // end of class TDBOTB
/***********************************************************************/
diff --git a/storage/connect/tabpivot.cpp b/storage/connect/tabpivot.cpp
index 94e9d7187f0..4b333b2eb56 100644
--- a/storage/connect/tabpivot.cpp
+++ b/storage/connect/tabpivot.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -150,11 +150,18 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
query = Tabsrc;
// Open a MySQL connection for this table
- if (Myc.Open(g, Host, Database, User, Pwd, Port))
- return NULL;
- else
+ if (!Myc.Open(g, Host, Database, User, Pwd, Port)) {
b = true;
+ // Returned values must be in their original character set
+ if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
+ goto err;
+ else
+ Myc.FreeResult();
+
+ } else
+ return NULL;
+
// Send the source command to MySQL
if (Myc.ExecSQL(g, query, &w) == RC_FX)
goto err;
@@ -241,6 +248,10 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
} else {
// The query was limited, we must get pivot column values
+ // Returned values must be in their original character set
+// if (Myc.ExecSQL(g, "SET character_set_results=NULL", &w) == RC_FX)
+// goto err;
+
query = (char*)PlugSubAlloc(g, NULL, 0);
sprintf(query, "SELECT DISTINCT `%s` FROM `%s`", Picol, Tabname);
PlugSubAlloc(g, NULL, strlen(query) + 1);
@@ -284,8 +295,7 @@ PQRYRES PIVAID::MakePivotColumns(PGLOBAL g)
valp->SetValue_pvblk(Rblkp, i);
colname = valp->GetCharString(buf);
- crp->Name = (char*)PlugSubAlloc(g, NULL, strlen(colname) + 1);
- strcpy(crp->Name, colname);
+ crp->Name = PlugDup(g, colname);
crp->Flag = 1;
// Add this column
@@ -558,7 +568,7 @@ bool TDBPIVOT::MakePivotColumns(PGLOBAL g)
// Check and initialize the subtable columns
for (PCOL cp = Columns; cp; cp = cp->GetNext())
if (cp->GetAmType() == TYPE_AM_SRC) {
- if (((PSRCCOL)cp)->Init(g))
+ if (((PSRCCOL)cp)->Init(g, NULL))
return TRUE;
} else if (cp->GetAmType() == TYPE_AM_FNC)
@@ -874,9 +884,9 @@ SRCCOL::SRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int n)
/***********************************************************************/
/* Initialize the column as pointing to the source column. */
/***********************************************************************/
-bool SRCCOL::Init(PGLOBAL g)
+bool SRCCOL::Init(PGLOBAL g, PTDBASE tp)
{
- if (PRXCOL::Init(g))
+ if (PRXCOL::Init(g, tp))
return true;
AddStatus(BUF_READ); // All is done here
diff --git a/storage/connect/tabpivot.h b/storage/connect/tabpivot.h
index 25d139e895f..c397af05234 100644
--- a/storage/connect/tabpivot.h
+++ b/storage/connect/tabpivot.h
@@ -180,9 +180,10 @@ class SRCCOL : public PRXCOL {
virtual int GetAmType(void) {return TYPE_AM_SRC;}
// Methods
+ using PRXCOL::Init;
virtual void Reset(void) {}
void SetColumn(void);
- bool Init(PGLOBAL g);
+ virtual bool Init(PGLOBAL g, PTDBASE tp);
bool CompareLast(void);
protected:
diff --git a/storage/connect/tabtbl.cpp b/storage/connect/tabtbl.cpp
index 22ec3849b6f..a33d6caa83a 100644
--- a/storage/connect/tabtbl.cpp
+++ b/storage/connect/tabtbl.cpp
@@ -266,7 +266,7 @@ bool TDBTBL::InitTableList(PGLOBAL g)
// Real initialization will be done later.
for (colp = Columns; colp; colp = colp->GetNext())
if (!colp->IsSpecial())
- if (((PPRXCOL)colp)->Init(g) && !Accept)
+ if (((PPRXCOL)colp)->Init(g, NULL) && !Accept)
return TRUE;
if (Tablist)
@@ -352,7 +352,9 @@ bool TDBTBL::TestFil(PGLOBAL g, PCFIL filp, PTABLE tabp)
/***********************************************************************/
int TDBTBL::Cardinality(PGLOBAL g)
{
- if (Cardinal < 0) {
+ if (!g)
+ return 0; // Cannot make the table list
+ else if (Cardinal < 0) {
int tsz;
if (!Tablist && InitTableList(g))
@@ -468,7 +470,7 @@ bool TDBTBL::OpenDB(PGLOBAL g)
for (PCOL cp = Columns; cp; cp = cp->GetNext())
if (cp->GetAmType() == TYPE_AM_TABID)
cp->COLBLK::Reset();
- else if (((PPRXCOL)cp)->Init(g) && !Accept)
+ else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
return TRUE;
if (trace)
@@ -523,7 +525,7 @@ int TDBTBL::ReadDB(PGLOBAL g)
if (cp->GetAmType() == TYPE_AM_TABID ||
cp->GetAmType() == TYPE_AM_SRVID)
cp->COLBLK::Reset();
- else if (((PPRXCOL)cp)->Init(g) && !Accept)
+ else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
return RC_FX;
if (trace)
@@ -716,7 +718,7 @@ bool TDBTBM::OpenDB(PGLOBAL g)
for (PCOL cp = Columns; cp; cp = cp->GetNext())
if (cp->GetAmType() == TYPE_AM_TABID)
cp->COLBLK::Reset();
- else if (((PPRXCOL)cp)->Init(g) && !Accept)
+ else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
return TRUE;
if (trace)
@@ -807,7 +809,7 @@ int TDBTBM::ReadNextRemote(PGLOBAL g)
for (PCOL cp = Columns; cp; cp = cp->GetNext())
if (cp->GetAmType() == TYPE_AM_TABID)
cp->COLBLK::Reset();
- else if (((PPRXCOL)cp)->Init(g) && !Accept)
+ else if (((PPRXCOL)cp)->Init(g, NULL) && !Accept)
return RC_FX;
if (trace)
diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp
index d469594916f..c114497aa69 100644
--- a/storage/connect/tabutil.cpp
+++ b/storage/connect/tabutil.cpp
@@ -1,7 +1,7 @@
/************* Tabutil cpp Declares Source Code File (.CPP) ************/
-/* Name: TABUTIL.CPP Version 1.0 */
+/* Name: TABUTIL.CPP Version 1.1 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2013 - 2015 */
/* */
/* Utility function used by the PROXY, XCOL, OCCUR, and TBL tables. */
/***********************************************************************/
@@ -9,7 +9,8 @@
/***********************************************************************/
/* Include relevant section of system dependant header files. */
/***********************************************************************/
-#include "my_global.h"
+#define MYSQL_SERVER 1
+#include <my_global.h>
#include "sql_class.h"
#include "table.h"
#include "field.h"
@@ -54,7 +55,8 @@
#include "tabutil.h"
#include "ha_connect.h"
-extern "C" int zconv;
+//extern "C" int zconv;
+int GetConvSize(void);
/************************************************************************/
/* Used by MYSQL tables to get MySQL parameters from the calling proxy */
@@ -107,6 +109,9 @@ TABLE_SHARE *GetTableShare(PGLOBAL g, THD *thd, const char *db,
} // endif is_view
} else {
+ if (thd->is_error())
+ thd->clear_error(); // Avoid stopping info commands
+
sprintf(g->Message, "Error %d opening share\n", s->error);
free_table_share(s);
return NULL;
@@ -132,6 +137,7 @@ PQRYRES TabColumns(PGLOBAL g, THD *thd, const char *db,
char *fld, *colname, *chset, *fmt, v;
int i, n, ncol = sizeof(buftyp) / sizeof(int);
int prec, len, type, scale;
+ int zconv = GetConvSize();
bool mysql;
TABLE_SHARE *s = NULL;
Field* *field;
@@ -669,6 +675,22 @@ PRXCOL::PRXCOL(PRXCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
} // end of PRXCOL copy constructor
/***********************************************************************/
+/* Convert an UTF-8 name to latin characters. */
+/***********************************************************************/
+char *PRXCOL::Decode(PGLOBAL g, const char *cnm)
+ {
+ char *buf= (char*)PlugSubAlloc(g, NULL, strlen(cnm) + 1);
+ uint dummy_errors;
+ uint32 len= copy_and_convert(buf, strlen(cnm) + 1,
+ &my_charset_latin1,
+ cnm, strlen(cnm),
+ &my_charset_utf8_general_ci,
+ &dummy_errors);
+ buf[len]= '\0';
+ return buf;
+ } // end of Decode
+
+/***********************************************************************/
/* PRXCOL initialization routine. */
/* Look for the matching column in the object table. */
/***********************************************************************/
@@ -683,6 +705,9 @@ bool PRXCOL::Init(PGLOBAL g, PTDBASE tp)
if (Colp) {
MODE mode = To_Tdb->GetMode();
+ // Needed for MYSQL subtables
+ ((XCOLBLK*)Colp)->Name = Decode(g, Colp->GetName());
+
// May not have been done elsewhere
Colp->InitValue(g);
To_Val = Colp->GetValue();
diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h
index 606e532d526..c5935d72184 100644
--- a/storage/connect/tabutil.h
+++ b/storage/connect/tabutil.h
@@ -108,15 +108,18 @@ class DllExport PRXCOL : public COLBLK {
virtual int GetAmType(void) {return TYPE_AM_PRX;}
// Methods
+ using COLBLK::Init;
virtual void Reset(void);
virtual bool IsSpecial(void) {return Pseudo;}
virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
{return false;}
virtual void ReadColumn(PGLOBAL g);
virtual void WriteColumn(PGLOBAL g);
- virtual bool Init(PGLOBAL g, PTDBASE tp = NULL);
+ virtual bool Init(PGLOBAL g, PTDBASE tp);
protected:
+ char *Decode(PGLOBAL g, const char *cnm);
+
// Default constructor not to be used
PRXCOL(void) {}
@@ -144,4 +147,8 @@ class TDBTBC : public TDBCAT {
PSZ Tab; // Table name
}; // end of class TDBMCL
+class XCOLBLK : public COLBLK {
+ friend class PRXCOL;
+}; // end of class XCOLBLK
+
#endif // TABUTIL
diff --git a/storage/connect/tabxcl.cpp b/storage/connect/tabxcl.cpp
index 57f0e1e03b9..0de01927c5a 100644
--- a/storage/connect/tabxcl.cpp
+++ b/storage/connect/tabxcl.cpp
@@ -183,7 +183,7 @@ bool TDBXCL::OpenDB(PGLOBAL g)
/*********************************************************************/
for (PCOL cp = Columns; cp; cp = cp->GetNext())
if (!cp->IsSpecial())
- if (((PPRXCOL)cp)->Init(g))
+ if (((PPRXCOL)cp)->Init(g, NULL))
return TRUE;
/*********************************************************************/
diff --git a/storage/connect/tabxcl.h b/storage/connect/tabxcl.h
index 7e11600c090..ed15a67b629 100644
--- a/storage/connect/tabxcl.h
+++ b/storage/connect/tabxcl.h
@@ -88,6 +88,7 @@ class XCLCOL : public PRXCOL {
XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i);
// Methods
+ using PRXCOL::Init;
virtual void Reset(void) {} // Evaluated only by TDBXCL
virtual void ReadColumn(PGLOBAL g);
virtual bool Init(PGLOBAL g, PTDBASE tp = NULL);
diff --git a/storage/connect/valblk.cpp b/storage/connect/valblk.cpp
index df7011583cd..e731ad156d9 100644
--- a/storage/connect/valblk.cpp
+++ b/storage/connect/valblk.cpp
@@ -1,7 +1,7 @@
/************ Valblk C++ Functions Source Code File (.CPP) *************/
/* Name: VALBLK.CPP Version 2.1 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
/* */
/* This file contains the VALBLK and derived classes functions. */
/* Second family is VALBLK, representing simple suballocated arrays */
@@ -1155,10 +1155,9 @@ void STRBLK::SetValue(PVAL valp, int n)
void STRBLK::SetValue(PSZ p, int n)
{
if (p) {
- if (!Sorted || !n || !Strp[n-1] || strcmp(p, Strp[n-1])) {
- Strp[n] = (PSZ)PlugSubAlloc(Global, NULL, strlen(p) + 1);
- strcpy(Strp[n], p);
- } else
+ if (!Sorted || !n || !Strp[n-1] || strcmp(p, Strp[n-1]))
+ Strp[n] = (PSZ)PlugDup(Global, p);
+ else
Strp[n] = Strp[n-1];
} else
diff --git a/storage/connect/valblk.h b/storage/connect/valblk.h
index 654db0b57b7..5a98257f98f 100644
--- a/storage/connect/valblk.h
+++ b/storage/connect/valblk.h
@@ -163,6 +163,7 @@ class TYPBLK : public VALBLK {
virtual void Reset(int n) {Typp[n] = 0;}
// Methods
+ using VALBLK::SetValue;
virtual void SetValue(PSZ sp, int n);
virtual void SetValue(char *sp, uint len, int n);
virtual void SetValue(short sval, int n)
@@ -233,6 +234,7 @@ class CHRBLK : public VALBLK {
virtual bool IsCi(void) {return Ci;}
// Methods
+ using VALBLK::SetValue;
virtual void SetValue(PSZ sp, int n);
virtual void SetValue(char *sp, uint len, int n);
virtual void SetValue(PVAL valp, int n);
@@ -286,6 +288,7 @@ class STRBLK : public VALBLK {
virtual void Reset(int n) {Strp[n] = NULL;}
// Methods
+ using VALBLK::SetValue;
virtual void SetValue(PSZ sp, int n);
virtual void SetValue(char *sp, uint len, int n);
virtual void SetValue(PVAL valp, int n);
@@ -322,6 +325,7 @@ class DATBLK : public TYPBLK<int> {
virtual char *GetCharString(char *p, int n);
// Methods
+ using TYPBLK<int>::SetValue;
virtual void SetValue(PSZ sp, int n);
protected:
@@ -345,6 +349,8 @@ class PTRBLK : public STRBLK {
// Implementation
// Methods
+ using STRBLK::SetValue;
+ using STRBLK::CompVal;
virtual void SetValue(PSZ p, int n) {Strp[n] = p;}
virtual int CompVal(int i1, int i2);
diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp
index 1cc40473433..e012e12d00b 100644
--- a/storage/connect/value.cpp
+++ b/storage/connect/value.cpp
@@ -436,6 +436,9 @@ PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype, int uns)
bool un = (uns < 0) ? false : (uns > 0) ? true : valp->IsUnsigned();
PVAL vp;
+ if (!valp)
+ return NULL;
+
if (newtype == TYPE_VOID) // Means allocate a value of the same type
newtype = valp->GetType();
@@ -443,8 +446,8 @@ PVAL AllocateValue(PGLOBAL g, PVAL valp, int newtype, int uns)
case TYPE_STRING:
p = (PSZ)PlugSubAlloc(g, NULL, 1 + valp->GetValLen());
- if ((sp = valp->GetCharString(p)) != p)
- strcpy (p, sp);
+ if ((sp = valp->GetCharString(p)) != p && sp)
+ strcpy(p, sp);
vp = new(g) TYPVAL<PSZ>(g, p, valp->GetValLen(), valp->GetValPrec());
break;
@@ -1216,12 +1219,12 @@ TYPVAL<PSZ>::TYPVAL(PSZ s) : VALUE(TYPE_STRING)
TYPVAL<PSZ>::TYPVAL(PGLOBAL g, PSZ s, int n, int c)
: VALUE(TYPE_STRING)
{
- Len = (g) ? n : strlen(s);
+ Len = (g) ? n : (s) ? strlen(s) : 0;
if (!s) {
if (g) {
if ((Strp = (char *)PlgDBSubAlloc(g, NULL, Len + 1)))
- Strp[Len] = '\0';
+ memset(Strp, 0, Len + 1);
else
Len = 0;
diff --git a/storage/connect/xindex.h b/storage/connect/xindex.h
index c702baeec83..6e40e9b160e 100644
--- a/storage/connect/xindex.h
+++ b/storage/connect/xindex.h
@@ -391,6 +391,7 @@ class DllExport XHUGE : public XLOAD {
XHUGE(void) : XLOAD() {}
// Methods
+ using XLOAD::Close;
virtual bool Open(PGLOBAL g, char *filename, int id, MODE mode);
virtual bool Seek(PGLOBAL g, int low, int high, int origin);
virtual bool Read(PGLOBAL g, void *buf, int n, int size);
diff --git a/storage/connect/xobject.cpp b/storage/connect/xobject.cpp
index 4ddd4f5b30f..817acb561fe 100644
--- a/storage/connect/xobject.cpp
+++ b/storage/connect/xobject.cpp
@@ -347,6 +347,31 @@ bool STRING::Append(char c)
} // end of Append
/***********************************************************************/
+/* Append a quoted PSZ to a STRING. */
+/***********************************************************************/
+bool STRING::Append_quoted(PSZ s)
+{
+ bool b = Append('\'');
+
+ if (s) for (char *p = s; !b && *p; p++)
+ switch (*p) {
+ case '\'':
+ case '\\':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\b':
+ case '\f': b |= Append('\\');
+ // passthru
+ default:
+ b |= Append(*p);
+ break;
+ } // endswitch *p
+
+ return (b |= Append('\''));
+} // end of Append_quoted
+
+/***********************************************************************/
/* Resize to given length but only when last suballocated. */
/* New size should be greater than string length. */
/***********************************************************************/
diff --git a/storage/connect/xobject.h b/storage/connect/xobject.h
index bb7b0150ed8..3660ee86918 100644
--- a/storage/connect/xobject.h
+++ b/storage/connect/xobject.h
@@ -138,6 +138,7 @@ class DllExport STRING : public BLOCK {
bool Append(STRING &str);
bool Append(char c);
bool Resize(uint n);
+ bool Append_quoted(PSZ s);
inline void Trim(void) {(void)Resize(Length + 1);}
inline void Chop(void) {if (Length) Strp[--Length] = 0;}
inline void RepLast(char c) {if (Length) Strp[Length-1] = c;}
diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h
index 501a5e87cfa..d1ea2b0d85f 100644
--- a/storage/connect/xtable.h
+++ b/storage/connect/xtable.h
@@ -1,7 +1,7 @@
/**************** Table H Declares Source Code File (.H) ***************/
/* Name: TABLE.H Version 2.3 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 1999-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 1999-2015 */
/* */
/* This file contains the TBX, OPJOIN and TDB class definitions. */
/***********************************************************************/
@@ -24,9 +24,7 @@ typedef class CMD *PCMD;
class CMD : public BLOCK {
public:
// Constructor
- CMD(PGLOBAL g, char *cmd) {
- Cmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1);
- strcpy(Cmd, cmd); Next = NULL; }
+ CMD(PGLOBAL g, char *cmd) {Cmd = PlugDup(g, cmd); Next = NULL;}
// Members
PCMD Next;