summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2015-12-13 23:52:43 +0100
committerSergei Golubchik <serg@mariadb.org>2015-12-13 23:52:43 +0100
commit6b4cc43f171d37f6913d847fd61e613e671e9205 (patch)
tree5ef55bcde98a1e34cd20a8e6687a95e4e75e6143 /storage
parentb418e978260777b88b7291db98682559212cb632 (diff)
parent92326bf8ace284f958f2e2b321a0a4cb535dd267 (diff)
downloadmariadb-git-6b4cc43f171d37f6913d847fd61e613e671e9205.tar.gz
Merge branch 'connect/10.0' into 10.0
Diffstat (limited to 'storage')
-rw-r--r--storage/connect/CMakeLists.txt8
-rw-r--r--storage/connect/connect.cc28
-rw-r--r--storage/connect/connect.h4
-rw-r--r--storage/connect/filamzip.cpp7
-rw-r--r--storage/connect/ha_connect.cc301
-rw-r--r--storage/connect/ha_connect.h11
-rw-r--r--storage/connect/json.cpp401
-rw-r--r--storage/connect/json.h91
-rw-r--r--storage/connect/jsonudf.cpp4954
-rw-r--r--storage/connect/jsonudf.h292
-rw-r--r--storage/connect/mycat.cc5
-rw-r--r--storage/connect/mycat.h1
-rw-r--r--storage/connect/mysql-test/connect/r/datest.result27
-rw-r--r--storage/connect/mysql-test/connect/r/grant.result7
-rw-r--r--storage/connect/mysql-test/connect/r/ini_grant.result2
-rw-r--r--storage/connect/mysql-test/connect/r/json.result34
-rw-r--r--storage/connect/mysql-test/connect/r/json_udf.result622
-rw-r--r--storage/connect/mysql-test/connect/r/json_udf_bin.result588
-rw-r--r--storage/connect/mysql-test/connect/r/mysql_grant.result2
-rw-r--r--storage/connect/mysql-test/connect/r/xml_grant.result1
-rw-r--r--storage/connect/mysql-test/connect/std_data/bib0.json2
-rw-r--r--storage/connect/mysql-test/connect/std_data/biblio.json10
-rw-r--r--storage/connect/mysql-test/connect/std_data/gloss.json22
-rw-r--r--storage/connect/mysql-test/connect/t/datest.test12
-rw-r--r--storage/connect/mysql-test/connect/t/grant.inc1
-rw-r--r--storage/connect/mysql-test/connect/t/grant.test3
-rw-r--r--storage/connect/mysql-test/connect/t/ini_grant.test2
-rw-r--r--storage/connect/mysql-test/connect/t/json.test30
-rw-r--r--storage/connect/mysql-test/connect/t/json_udf.inc66
-rw-r--r--storage/connect/mysql-test/connect/t/json_udf.test256
-rw-r--r--storage/connect/mysql-test/connect/t/json_udf2.inc49
-rw-r--r--storage/connect/mysql-test/connect/t/json_udf_bin.test212
-rw-r--r--storage/connect/mysql-test/connect/t/mysql_grant.test2
-rw-r--r--storage/connect/plugutil.c6
-rw-r--r--storage/connect/reldef.cpp1
-rw-r--r--storage/connect/tabdos.cpp6
-rw-r--r--storage/connect/tabjson.cpp185
-rw-r--r--storage/connect/tabjson.h49
-rw-r--r--storage/connect/tabmysql.cpp58
-rw-r--r--storage/connect/tabmysql.h2
-rw-r--r--storage/connect/tabodbc.cpp426
-rw-r--r--storage/connect/tabodbc.h16
-rw-r--r--storage/connect/tabxml.cpp18
-rw-r--r--storage/connect/value.cpp6
-rw-r--r--storage/connect/value.h2
-rw-r--r--storage/connect/xobject.cpp24
-rw-r--r--storage/connect/xobject.h2
-rw-r--r--storage/connect/xtable.h29
48 files changed, 7783 insertions, 1100 deletions
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt
index 02fe5ee8dad..7ff2ca7753c 100644
--- a/storage/connect/CMakeLists.txt
+++ b/storage/connect/CMakeLists.txt
@@ -28,8 +28,8 @@ tabvct.cpp tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp
array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h
engmsg.h filamap.h filamdbf.h filamfix.h filamtxt.h filamvct.h filamzip.h
-filter.h global.h ha_connect.h inihandl.h json.h maputil.h msgid.h mycat.h
-myconn.h myutil.h os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h
+filter.h global.h ha_connect.h inihandl.h json.h jsonudf.h maputil.h msgid.h
+mycat.h myconn.h myutil.h os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h
resource.h tabcol.h tabdos.h tabfix.h tabfmt.h tabjson.h tabmul.h tabmysql.h
taboccur.h tabpivot.h tabsys.h tabtbl.h tabutil.h tabvct.h tabvir.h tabxcl.h
user_connect.h valblk.h value.h xindex.h xobject.h xtable.h)
@@ -48,7 +48,7 @@ IF(UNIX)
# Bar: -Wfatal-errors removed (does not present in gcc on solaris10)
if(WITH_WARNINGS)
add_definitions(-Wall -Wextra -Wmissing-declarations)
- message(STATUS "CONNECT: GCC: All warnings enabled")
+ #message(STATUS "CONNECT: GCC: All warnings enabled")
else()
add_definitions(-Wall -Wmissing-declarations)
add_definitions(-Wno-write-strings)
@@ -69,7 +69,7 @@ IF(UNIX)
# These switches are for C++ only
# add_definitions(-Wno-reorder)
- message(STATUS "CONNECT: GCC: Some warnings disabled")
+ #message(STATUS "CONNECT: GCC: Some warnings disabled")
endif(WITH_WARNINGS)
add_definitions( -DUNIX -DLINUX -DUBUNTU )
diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
index 4e554b16638..56a0fc4fd4f 100644
--- a/storage/connect/connect.cc
+++ b/storage/connect/connect.cc
@@ -735,17 +735,12 @@ static void SetSwapValue(PVAL valp, char *kp)
/* IndexRead: fetch a record having the index value. */
/***********************************************************************/
RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
- const void *key, int len, bool mrr)
+ const key_range *kr, bool mrr)
{
- char *kp= (char*)key;
int n, x;
- short lg;
- bool rcb;
RCODE rc;
- PVAL valp;
- PCOL colp;
XXBASE *xbp;
- PTDBDOX tdbp;
+ PTDBDOX tdbp;
if (!ptdb)
return RC_FX;
@@ -757,13 +752,13 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
return RC_FX;
} else if (x == 2) {
// Remote index
- if (ptdb->ReadKey(g, op, key, len))
+ if (ptdb->ReadKey(g, op, kr))
return RC_FX;
goto rnd;
} else if (x == 3) {
- if (key)
- ((PTDBASE)ptdb)->SetRecpos(g, *(int*)key);
+ if (kr)
+ ((PTDBASE)ptdb)->SetRecpos(g, *(int*)kr->key);
if (op == OP_SAME)
return RC_NF;
@@ -790,7 +785,14 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
xbp= (XXBASE*)tdbp->To_Kindex;
- if (key) {
+ if (kr) {
+ char *kp= (char*)kr->key;
+ int len= kr->length;
+ short lg;
+ bool rcb;
+ PVAL valp;
+ PCOL colp;
+
for (n= 0; n < tdbp->Knum; n++) {
colp= (PCOL)tdbp->To_Key_Col[n];
@@ -832,10 +834,10 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
kp+= valp->GetClen();
- if (len == kp - (char*)key) {
+ if (len == kp - (char*)kr->key) {
n++;
break;
- } else if (len < kp - (char*)key) {
+ } else if (len < kp - (char*)kr->key) {
strcpy(g->Message, "Key buffer is too small");
return RC_FX;
} // endif len
diff --git a/storage/connect/connect.h b/storage/connect/connect.h
index fd8b7e9442f..bbefda52274 100644
--- a/storage/connect/connect.h
+++ b/storage/connect/connect.h
@@ -36,7 +36,7 @@ bool CntRewindTable(PGLOBAL g, PTDB tdbp);
int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort);
int CntIndexInit(PGLOBAL g, PTDB tdbp, int id, bool sorted);
RCODE CntReadNext(PGLOBAL g, PTDB tdbp);
-RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const void *k, int n, bool mrr);
+RCODE CntIndexRead(PGLOBAL g, PTDB, OPVAL op, const key_range *kr, bool mrr);
RCODE CntWriteRow(PGLOBAL g, PTDB tdbp);
RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp);
RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all);
@@ -60,7 +60,7 @@ class TDBDOX: public TDBDOS {
friend int MakeIndex(PGLOBAL, PTDB, PIXDEF);
friend int CntCloseTable(PGLOBAL, PTDB, bool, bool);
friend int CntIndexInit(PGLOBAL, PTDB, int, bool);
- friend RCODE CntIndexRead(PGLOBAL, PTDB, OPVAL, const void*, int, bool);
+ friend RCODE CntIndexRead(PGLOBAL, PTDB, OPVAL, const key_range*, bool);
friend RCODE CntDeleteRow(PGLOBAL, PTDB, bool);
friend int CntIndexRange(PGLOBAL, PTDB, const uchar**, uint*,
bool*, key_part_map*);
diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp
index 56faa555069..d9834e56dcd 100644
--- a/storage/connect/filamzip.cpp
+++ b/storage/connect/filamzip.cpp
@@ -1404,8 +1404,13 @@ void ZLBFAM::Rewind(void)
// We must be positioned after the header block
if (CurBlk >= 0) { // Nothing to do if no block read yet
if (!Optimized) { // If optimized, fseek will be done in ReadBuffer
+ size_t st;
+
rewind(Stream);
- fread(Zlenp, sizeof(int), 1, Stream);
+
+ if (!(st = fread(Zlenp, sizeof(int), 1, Stream)) && trace)
+ htrc("fread error %d in Rewind", errno);
+
fseek(Stream, *Zlenp + sizeof(int), SEEK_SET);
OldBlk = -1;
} // endif Optimized
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index 4536e65fab4..049d6db21fd 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -169,9 +169,9 @@
#define JSONMAX 10 // JSON Default max grp size
extern "C" {
- char version[]= "Version 1.03.0007 October 20, 2015";
+ char version[]= "Version 1.04.0005 December 11, 2015";
#if defined(__WIN__)
- char compver[]= "Version 1.03.0007 " __DATE__ " " __TIME__;
+ char compver[]= "Version 1.04.0005 " __DATE__ " " __TIME__;
char slash= '\\';
#else // !__WIN__
char slash= '/';
@@ -1134,10 +1134,10 @@ PTOS ha_connect::GetTableOptionStruct(TABLE_SHARE *s)
{
TABLE_SHARE *tsp= (tshp) ? tshp : (s) ? s : table_share;
- return (tsp && (!tsp->db_plugin ||
- !stricmp(plugin_name(tsp->db_plugin)->str, "connect") ||
- !stricmp(plugin_name(tsp->db_plugin)->str, "partition")))
- ? tsp->option_struct : NULL;
+ return (tsp && (!tsp->db_plugin ||
+ !stricmp(plugin_name(tsp->db_plugin)->str, "connect") ||
+ !stricmp(plugin_name(tsp->db_plugin)->str, "partition")))
+ ? tsp->option_struct : NULL;
} // end of GetTableOptionStruct
/****************************************************************************/
@@ -2191,99 +2191,159 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *)
/***********************************************************************/
int ha_connect::CheckRecord(PGLOBAL g, const uchar *, uchar *newbuf)
{
- return ScanRecord(g, newbuf);
+ return ScanRecord(g, newbuf);
} // end of dummy CheckRecord
/***********************************************************************/
-/* Return the where clause for remote indexed read. */
+/* Return true if this field is used in current indexing. */
/***********************************************************************/
-bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q,
- const void *key, int klen)
+bool ha_connect::IsIndexed(Field *fp)
{
- const uchar *ptr;
- uint rem, len, stlen; //, prtlen;
- bool nq, oom, b= false;
- Field *fp;
- KEY *kfp;
- KEY_PART_INFO *kpart;
+ if (active_index < MAX_KEY) {
+ KEY_PART_INFO *kpart;
+ KEY *kfp= &table->key_info[active_index];
+ uint rem= kfp->user_defined_key_parts;
- if (active_index == MAX_KEY)
- return false;
- else if (!key) {
- strcpy(g->Message, "MakeKeyWhere: No key");
- return true;
- } // endif key
-
- oom= qry->Append(" WHERE (");
- kfp= &table->key_info[active_index];
- rem= kfp->user_defined_key_parts,
- len= klen,
- ptr= (const uchar *)key;
-
- for (kpart= kfp->key_part; rem; rem--, kpart++) {
- fp= kpart->field;
- stlen= kpart->store_length;
-// prtlen= MY_MIN(stlen, len);
- nq= fp->str_needs_quotes();
-
- if (b)
- oom|= qry->Append(" AND ");
- else
- b= true;
+ for (kpart= kfp->key_part; rem; rem--, kpart++)
+ if (kpart->field == fp)
+ return true;
- oom|= qry->Append(q);
- oom|= qry->Append((PSZ)fp->field_name);
- oom|= qry->Append(q);
-
- switch (op) {
- case OP_EQ:
- case OP_GT:
- case OP_GE:
- case OP_LT:
- case OP_LE:
- oom |= qry->Append((PSZ)GetValStr(op, false));
- break;
- default:
- oom|= qry->Append(" ??? ");
- } // endwitch op
+ } // endif active_index
- if (nq)
- oom|= qry->Append('\'');
+ return false;
+} // end of IsIndexed
- if (kpart->key_part_flag & HA_VAR_LENGTH_PART) {
- String varchar;
- uint var_length= uint2korr(ptr);
- varchar.set_quick((char*) ptr+HA_KEY_BLOB_LENGTH,
- var_length, &my_charset_bin);
- oom|= qry->Append(varchar.ptr(), varchar.length());
- } else {
- char strbuff[MAX_FIELD_WIDTH];
- String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
-
- res= fp->val_str(&str, ptr);
- oom|= qry->Append(res->ptr(), res->length());
- } // endif flag
-
- if (nq)
- oom|= qry->Append('\'');
-
- if (stlen >= len)
- break;
+/***********************************************************************/
+/* Return the where clause for remote indexed read. */
+/***********************************************************************/
+bool ha_connect::MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL vop, char q,
+ const key_range *kr)
+{
+ const uchar *ptr;
+//uint i, rem, len, klen, stlen;
+ uint i, rem, len, stlen;
+ bool nq, both, oom= false;
+ OPVAL op;
+ Field *fp;
+ const key_range *ranges[2];
+ my_bitmap_map *old_map;
+ KEY *kfp;
+ KEY_PART_INFO *kpart;
- len-= stlen;
+ if (active_index == MAX_KEY)
+ return false;
- /* For nullable columns, null-byte is already skipped before, that is
- ptr was incremented by 1. Since store_length still counts null-byte,
- we need to subtract 1 from store_length. */
- ptr+= stlen - MY_TEST(kpart->null_bit);
- } // endfor kpart
+ ranges[0]= kr;
+ ranges[1]= (end_range && !eq_range) ? &save_end_range : NULL;
+
+ if (!ranges[0] && !ranges[1]) {
+ strcpy(g->Message, "MakeKeyWhere: No key");
+ return true;
+ } else
+ both= ranges[0] && ranges[1];
+
+ kfp= &table->key_info[active_index];
+ old_map= dbug_tmp_use_all_columns(table, table->write_set);
+
+ for (i = 0; i <= 1; i++) {
+ if (ranges[i] == NULL)
+ continue;
+
+ if (both && i > 0)
+ oom|= qry->Append(") AND (");
+ else
+ oom|= qry->Append(" WHERE (");
+
+// klen= len= ranges[i]->length;
+ len= ranges[i]->length;
+ rem= kfp->user_defined_key_parts;
+ ptr= ranges[i]->key;
+
+ for (kpart= kfp->key_part; rem; rem--, kpart++) {
+ fp= kpart->field;
+ stlen= kpart->store_length;
+ nq= fp->str_needs_quotes();
+
+ if (kpart != kfp->key_part)
+ oom|= qry->Append(" AND ");
+
+ if (q) {
+ oom|= qry->Append(q);
+ oom|= qry->Append((PSZ)fp->field_name);
+ oom|= qry->Append(q);
+ } else
+ oom|= qry->Append((PSZ)fp->field_name);
+
+ switch (ranges[i]->flag) {
+ case HA_READ_KEY_EXACT:
+// op= (stlen >= len || !nq || fp->result_type() != STRING_RESULT)
+// ? OP_EQ : OP_LIKE;
+ op= OP_EQ;
+ break;
+ case HA_READ_AFTER_KEY:
+ op= (stlen >= len) ? (!i ? OP_GT : OP_LE) : OP_GE;
+ break;
+ case HA_READ_KEY_OR_NEXT:
+ op= OP_GE;
+ break;
+ case HA_READ_BEFORE_KEY:
+ op= (stlen >= len) ? OP_LT : OP_LE;
+ break;
+ case HA_READ_KEY_OR_PREV:
+ op= OP_LE;
+ break;
+ default:
+ sprintf(g->Message, "cannot handle flag %d", ranges[i]->flag);
+ goto err;
+ } // endswitch flag
+
+ oom|= qry->Append((PSZ)GetValStr(op, false));
+
+ if (nq)
+ oom|= qry->Append('\'');
+
+ if (kpart->key_part_flag & HA_VAR_LENGTH_PART) {
+ String varchar;
+ uint var_length= uint2korr(ptr);
+
+ varchar.set_quick((char*)ptr + HA_KEY_BLOB_LENGTH,
+ var_length, &my_charset_bin);
+ oom|= qry->Append(varchar.ptr(), varchar.length(), nq);
+ } else {
+ char strbuff[MAX_FIELD_WIDTH];
+ String str(strbuff, sizeof(strbuff), kpart->field->charset()), *res;
+
+ res= fp->val_str(&str, ptr);
+ oom|= qry->Append(res->ptr(), res->length(), nq);
+ } // endif flag
+
+ if (nq)
+ oom |= qry->Append('\'');
+
+ if (stlen >= len)
+ break;
+
+ len-= stlen;
+
+ /* For nullable columns, null-byte is already skipped before, that is
+ ptr was incremented by 1. Since store_length still counts null-byte,
+ we need to subtract 1 from store_length. */
+ ptr+= stlen - MY_TEST(kpart->null_bit);
+ } // endfor kpart
+
+ } // endfor i
if ((oom|= qry->Append(")")))
strcpy(g->Message, "Out of memory");
- return oom;
+ dbug_tmp_restore_column_map(table->write_set, old_map);
+ return oom;
+
+err:
+ dbug_tmp_restore_column_map(table->write_set, old_map);
+ return true;
} // end of MakeKeyWhere
@@ -2483,11 +2543,11 @@ PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_NEWDATE:
return NULL;
- default:
- break;
+ default:
+ break;
} // endswitch type
- if (trace) {
+ if (trace) {
htrc("Field index=%d\n", pField->field->field_index);
htrc("Field name=%s\n", pField->field->field_name);
} // endif trace
@@ -2564,8 +2624,9 @@ PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond)
/***********************************************************************/
/* Check the WHERE condition and return a MYSQL/ODBC/WQL filter. */
/***********************************************************************/
-PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
+PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
{
+ AMT tty = filp->Type;
char *body= filp->Body;
unsigned int i;
bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
@@ -2598,7 +2659,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
List<Item>* arglist= cond_item->argument_list();
List_iterator<Item> li(*arglist);
- Item *subitem;
+ const Item *subitem;
p1= body + strlen(body);
strcpy(p1, "(");
@@ -2606,7 +2667,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 (!CheckCond(g, filp, subitem)) {
if (vop == OP_OR || nonul)
return NULL;
else
@@ -2628,26 +2689,27 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
} else if (cond->type() == COND::FUNC_ITEM) {
unsigned int i;
-// int n;
bool iscol, neg= FALSE;
Item_func *condf= (Item_func *)cond;
Item* *args= condf->arguments();
if (trace)
htrc("Func type=%d argnum=%d\n", condf->functype(),
- condf->argument_count());
-
-// neg= condf->
+ condf->argument_count());
switch (condf->functype()) {
case Item_func::EQUAL_FUNC:
- case Item_func::EQ_FUNC: vop= OP_EQ; break;
- case Item_func::NE_FUNC: vop= OP_NE; break;
- case Item_func::LT_FUNC: vop= OP_LT; break;
- case Item_func::LE_FUNC: vop= OP_LE; break;
- case Item_func::GE_FUNC: vop= OP_GE; break;
- case Item_func::GT_FUNC: vop= OP_GT; break;
- case Item_func::IN_FUNC: vop= OP_IN;
+ case Item_func::EQ_FUNC: vop= OP_EQ; break;
+ case Item_func::NE_FUNC: vop= OP_NE; break;
+ case Item_func::LT_FUNC: vop= OP_LT; break;
+ case Item_func::LE_FUNC: vop= OP_LE; break;
+ case Item_func::GE_FUNC: vop= OP_GE; break;
+ case Item_func::GT_FUNC: vop= OP_GT; break;
+ case Item_func::LIKE_FUNC: vop= OP_LIKE; break;
+ case Item_func::ISNOTNULL_FUNC:
+ neg = true;
+ case Item_func::ISNULL_FUNC: vop= OP_NULL; break;
+ case Item_func::IN_FUNC: vop= OP_IN;
case Item_func::BETWEEN:
ismul= true;
neg= ((Item_func_opt_neg *)condf)->negated;
@@ -2660,7 +2722,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
else if (ismul && tty == TYPE_AM_WMI)
return NULL; // Not supported by WQL
- if (x && (neg || !(vop == OP_EQ || vop == OP_IN)))
+ if (x && (neg || !(vop == OP_EQ || vop == OP_IN || vop == OP_NULL)))
return NULL;
for (i= 0; i < condf->argument_count(); i++) {
@@ -2681,9 +2743,10 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
if (x && i)
return NULL;
-
- if (pField->field->table != table)
- return NULL; // Field does not belong to this table
+ else if (pField->field->table != table)
+ return NULL; // Field does not belong to this table
+ else if (tty != TYPE_AM_WMI && IsIndexed(pField->field))
+ return NULL; // Will be handled by ReadKey
else
fop= GetFieldOptionStruct(pField->field);
@@ -2714,7 +2777,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond)
strcat(body, fnm);
} else if (args[i]->type() == COND::FUNC_ITEM) {
if (tty == TYPE_AM_MYSQL) {
- if (!CheckCond(g, filp, tty, args[i]))
+ if (!CheckCond(g, filp, args[i]))
return NULL;
} else
@@ -2903,14 +2966,17 @@ const COND *ha_connect::cond_push(const COND *cond)
goto fin;
if (b) {
- PCFIL filp= (PCFIL)PlugSubAlloc(g, NULL, sizeof(CONDFIL));
+ PCFIL filp;
+ if ((filp= tdbp->GetCondFil()) && filp->Cond == cond &&
+ filp->Idx == active_index && filp->Type == tty)
+ goto fin; // Already done
+
+ filp= new(g) CONDFIL(cond, active_index, tty);
filp->Body= (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0);
*filp->Body= 0;
- filp->Op= OP_XX;
- filp->Cmds= NULL;
- if (CheckCond(g, filp, tty, (Item *)cond)) {
+ if (CheckCond(g, filp, cond)) {
if (trace)
htrc("cond_push: %s\n", filp->Body);
@@ -3374,13 +3440,13 @@ int ha_connect::index_end()
/****************************************************************************/
/* This is internally called by all indexed reading functions. */
/****************************************************************************/
-int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const uchar *key, uint key_len)
+int ha_connect::ReadIndexed(uchar *buf, OPVAL op, const key_range *kr)
{
int rc;
//statistic_increment(ha_read_key_count, &LOCK_status);
- switch (CntIndexRead(xp->g, tdbp, op, key, (int)key_len, mrr)) {
+ switch (CntIndexRead(xp->g, tdbp, op, kr, mrr)) {
case RC_OK:
xp->fnd++;
rc= MakeRecord((char*)buf);
@@ -3446,7 +3512,12 @@ int ha_connect::index_read(uchar * buf, const uchar * key, uint key_len,
htrc("%p index_read: op=%d\n", this, op);
if (indexing > 0) {
- rc= ReadIndexed(buf, op, key, key_len);
+ start_key.key= key;
+ start_key.length= key_len;
+ start_key.flag= find_flag;
+ start_key.keypart_map= 0;
+
+ rc= ReadIndexed(buf, op, &start_key);
if (rc == HA_ERR_INTERNAL_ERROR) {
nox= true; // To block making indexes
@@ -3752,7 +3823,7 @@ void ha_connect::position(const uchar *)
//if (((PTDBASE)tdbp)->GetDef()->Indexable())
my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos());
- if (trace)
+ if (trace > 1)
htrc("position: pos=%d\n", ((PTDBASE)tdbp)->GetRecpos());
DBUG_VOID_RETURN;
@@ -6688,10 +6759,10 @@ maria_declare_plugin(connect)
PLUGIN_LICENSE_GPL,
connect_init_func, /* Plugin Init */
connect_done_func, /* Plugin Deinit */
- 0x0103, /* version number (1.03) */
+ 0x0104, /* version number (1.04) */
NULL, /* status variables */
connect_system_variables, /* system variables */
- "1.03.0007", /* string version */
- MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */
+ "1.04.0005", /* 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 611f9ba0b54..05cc872fa2a 100644
--- a/storage/connect/ha_connect.h
+++ b/storage/connect/ha_connect.h
@@ -241,11 +241,12 @@ public:
int MakeRecord(char *buf);
int ScanRecord(PGLOBAL g, uchar *buf);
int CheckRecord(PGLOBAL g, const uchar *oldbuf, uchar *newbuf);
- int ReadIndexed(uchar *buf, OPVAL op, const uchar* key= NULL,
- uint key_len= 0);
+ int ReadIndexed(uchar *buf, OPVAL op, const key_range *kr= NULL);
+ bool IsIndexed(Field *fp);
bool MakeKeyWhere(PGLOBAL g, PSTRG qry, OPVAL op, char q,
- const void *key, int klen);
- inline char *Strz(LEX_STRING &ls);
+ const key_range *kr);
+ inline char *Strz(LEX_STRING &ls);
+ key_range start_key;
/** @brief
@@ -374,7 +375,7 @@ public:
condition stack.
*/
virtual const COND *cond_push(const COND *cond);
-PCFIL CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond);
+PCFIL CheckCond(PGLOBAL g, PCFIL filp, const Item *cond);
const char *GetValStr(OPVAL vop, bool neg);
PFIL CondFilter(PGLOBAL g, Item *cond);
//PFIL CheckFilter(PGLOBAL g);
diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp
index 3d03bea5d00..fc7edb84c0f 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.1 */
+/* Name: json.cpp Version 1.2 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
@@ -31,11 +31,12 @@
/***********************************************************************/
/* Parse a json string. */
+/* Note: when pretty is not known, the caller set pretty to 3. */
/***********************************************************************/
-PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
+PJSON ParseJson(PGLOBAL g, char *s, int len, int *ptyp, bool *comma)
{
- int i, rc;
- bool b = false;
+ int i, rc, pretty = (ptyp) ? *ptyp : 3;
+ bool b = false, pty[3] = {true, true, true};
PJSON jsp = NULL;
STRG src;
@@ -48,6 +49,10 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
src.str = s;
src.len = len;
+ // Trying to guess the pretty format
+ if (s[0] == '[' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
+ pty[0] = false;
+
// Save stack and allocation environment and prepare error return
if (g->jump_level == MAX_JUMP) {
strcpy(g->Message, MSG(TOO_MANY_JUMPS));
@@ -58,21 +63,19 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
goto err;
} // endif rc
- for (i = 0; i < len; i++)
+ for (i = 0; i < len; i++)
switch (s[i]) {
case '[':
- if (jsp) {
- strcpy(g->Message, "More than one item in file");
- goto err;
- } else if (!(jsp = ParseArray(g, ++i, src)))
+ if (jsp)
+ goto tryit;
+ else if (!(jsp = ParseArray(g, ++i, src, pty)))
goto err;
break;
case '{':
- if (jsp) {
- strcpy(g->Message, "More than one item in file");
- goto err;
- } else if (!(jsp = ParseObject(g, ++i, src)))
+ if (jsp)
+ goto tryit;
+ else if (!(jsp = ParseObject(g, ++i, src, pty)))
goto err;
break;
@@ -82,20 +85,16 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
case '\r':
break;
case ',':
- if (jsp && pretty == 1) {
+ if (jsp && (pretty == 1 || pretty == 3)) {
if (comma)
*comma = true;
+ pty[0] = pty[2] = false;
break;
} // endif pretty
sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty);
goto err;
- case '"':
- if (!(jsp = ParseValue(g, i, src)))
- goto err;
-
- break;
case '(':
b = true;
break;
@@ -106,18 +105,41 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
} // endif b
default:
- sprintf(g->Message, "Bad '%c' character near %.*s",
- s[i], ARGS);
- goto err;
- }; // endswitch s[i]
+ if (jsp)
+ goto tryit;
+ else if (!(jsp = ParseValue(g, i, src, pty)))
+ goto err;
+
+ break;
+ }; // endswitch s[i]
+
+ if (!jsp)
+ sprintf(g->Message, "Invalid Json string '%.*s'", 50, s);
+ else if (ptyp && pretty == 3) {
+ *ptyp = 3; // Not recognized pretty
- if (!jsp)
- sprintf(g->Message, "Invalid Json string '%.*s'", 50, s);
+ for (i = 0; i < 3; i++)
+ if (pty[i]) {
+ *ptyp = i;
+ break;
+ } // endif pty
+
+ } // endif ptyp
g->jump_level--;
return jsp;
- err:
+tryit:
+ if (pty[0] && (!pretty || pretty > 2)) {
+ if ((jsp = ParseArray(g, (i = 0), src, pty)) && ptyp && pretty == 3)
+ *ptyp = (pty[0]) ? 0 : 3;
+
+ g->jump_level--;
+ return jsp;
+ } else
+ strcpy(g->Message, "More than one item in file");
+
+err:
g->jump_level--;
return NULL;
} // end of ParseJson
@@ -125,11 +147,12 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma)
/***********************************************************************/
/* Parse a JSON Array. */
/***********************************************************************/
-PJAR ParseArray(PGLOBAL g, int& i, STRG& src)
+PJAR ParseArray(PGLOBAL g, int& i, STRG& src, bool *pty)
{
char *s = src.str;
int len = src.len;
int level = 0;
+ bool b = (!i);
PJAR jarp = new(g) JARRAY;
PJVAL jvp = NULL;
@@ -151,25 +174,32 @@ PJAR ParseArray(PGLOBAL g, int& i, STRG& src)
jarp->InitArray(g);
return jarp;
- case ' ':
+ case '\n':
+ if (!b)
+ pty[0] = pty[1] = false;
+ case '\r':
+ case ' ':
case '\t':
- case '\n':
- case '\r':
break;
default:
if (level == 2) {
sprintf(g->Message, "Unexpected value near %.*s", ARGS);
return NULL;
- } else if ((jvp = ParseValue(g, i, src))) {
+ } else if ((jvp = ParseValue(g, i, src, pty)))
jarp->AddValue(g, jvp);
- level = 2;
- } else
+ else
return NULL;
- level = 2;
+ level = (b) ? 1 : 2;
break;
}; // endswitch s[i]
+ if (b) {
+ // Case of Pretty == 0
+ jarp->InitArray(g);
+ return jarp;
+ } // endif b
+
strcpy(g->Message, "Unexpected EOF in array");
return NULL;
} // end of ParseArray
@@ -177,7 +207,7 @@ PJAR ParseArray(PGLOBAL g, int& i, STRG& src)
/***********************************************************************/
/* Parse a JSON Object. */
/***********************************************************************/
-PJOB ParseObject(PGLOBAL g, int& i, STRG& src)
+PJOB ParseObject(PGLOBAL g, int& i, STRG& src, bool *pty)
{
PSZ key;
char *s = src.str;
@@ -204,7 +234,7 @@ PJOB ParseObject(PGLOBAL g, int& i, STRG& src)
break;
case ':':
if (level == 1) {
- if (!(jpp->Val = ParseValue(g, ++i, src)))
+ if (!(jpp->Val = ParseValue(g, ++i, src, pty)))
return NULL;
level = 2;
@@ -229,10 +259,11 @@ PJOB ParseObject(PGLOBAL g, int& i, STRG& src)
} // endif level
return jobp;
- case ' ':
+ case '\n':
+ pty[0] = pty[1] = false;
+ case '\r':
+ case ' ':
case '\t':
- case '\n':
- case '\r':
break;
default:
sprintf(g->Message, "Unexpected character '%c' near %.*s",
@@ -247,7 +278,7 @@ PJOB ParseObject(PGLOBAL g, int& i, STRG& src)
/***********************************************************************/
/* Parse a JSON Value. */
/***********************************************************************/
-PJVAL ParseValue(PGLOBAL g, int& i, STRG& src)
+PJVAL ParseValue(PGLOBAL g, int& i, STRG& src, bool *pty)
{
char *strval, *s = src.str;
int n, len = src.len;
@@ -255,10 +286,11 @@ PJVAL ParseValue(PGLOBAL g, int& i, STRG& src)
for (; i < len; i++)
switch (s[i]) {
- case ' ':
+ case '\n':
+ pty[0] = pty[1] = false;
+ case '\r':
+ case ' ':
case '\t':
- case '\n':
- case '\r':
break;
default:
goto suite;
@@ -267,12 +299,12 @@ PJVAL ParseValue(PGLOBAL g, int& i, STRG& src)
suite:
switch (s[i]) {
case '[':
- if (!(jvp->Jsp = ParseArray(g, ++i, src)))
+ if (!(jvp->Jsp = ParseArray(g, ++i, src, pty)))
return NULL;
break;
case '{':
- if (!(jvp->Jsp = ParseObject(g, ++i, src)))
+ if (!(jvp->Jsp = ParseObject(g, ++i, src, pty)))
return NULL;
break;
@@ -319,7 +351,6 @@ PJVAL ParseValue(PGLOBAL g, int& i, STRG& src)
}; // endswitch s[i]
- jvp->Size = 1;
return jvp;
err:
@@ -481,9 +512,9 @@ PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src)
valp = AllocateValue(g, &dv, TYPE_DOUBLE, nd);
} else {
- int iv = strtol(buf, NULL, 10);
+ long long iv = strtoll(buf, NULL, 10);
- valp = AllocateValue(g, &iv, TYPE_INT);
+ valp = AllocateValue(g, &iv, TYPE_BIGINT);
} // endif has
i--; // Unstack following character
@@ -501,35 +532,56 @@ PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src)
/***********************************************************************/
/* Serialize a JSON tree: */
/***********************************************************************/
-PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty)
+PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty)
{
+ PSZ str = NULL;
bool b = false, err = true;
JOUT *jp;
+ FILE *fs = NULL;
+
+ g->Message[0] = 0;
- g->Message[0] = 0;
+ // 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 (!jsp) {
+ if (setjmp(g->jumper[++g->jump_level])) {
+ str = NULL;
+ goto fin;
+ } // endif jmp
+
+ if (!jsp) {
strcpy(g->Message, "Null json tree");
- return NULL;
- } else if (!fs) {
+ goto fin;
+ } else if (!fn) {
// Serialize to a string
jp = new(g) JOUTSTR(g);
b = pretty == 1;
- } else if (pretty == 2) {
- // Serialize to a pretty file
- jp = new(g) JOUTPRT(g, fs);
- } else {
- // Serialize to a flat file
- jp = new(g) JOUTFILE(g, fs);
- b = pretty == 1;
- } // endif's
+ } else {
+ if (!(fs = fopen(fn, "wb"))) {
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR),
+ "w", (int)errno, fn);
+ strcat(strcat(g->Message, ": "), strerror(errno));
+ goto fin;;
+ } else if (pretty >= 2) {
+ // Serialize to a pretty file
+ jp = new(g)JOUTPRT(g, fs);
+ } else {
+ // Serialize to a flat file
+ b = true;
+ jp = new(g)JOUTFILE(g, fs, pretty);
+ } // endif's
+
+ } // endif's
switch (jsp->GetType()) {
case TYPE_JAR:
err = SerializeArray(jp, (PJAR)jsp, b);
break;
case TYPE_JOB:
- err = (b && jp->WriteChr('\t'));
+ err = ((b && jp->Prty()) && jp->WriteChr('\t'));
err |= SerializeObject(jp, (PJOB)jsp);
break;
case TYPE_JVAL:
@@ -540,22 +592,22 @@ PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty)
} // endswitch Type
if (fs) {
- fputc('\n', fs);
+ fputs(EL, fs);
fclose(fs);
- return (err) ? g->Message : NULL;
+ str = (err) ? NULL : "Ok";
} else if (!err) {
- PSZ str = ((JOUTSTR*)jp)->Strp;
-
+ str = ((JOUTSTR*)jp)->Strp;
jp->WriteChr('\0');
PlugSubAlloc(g, NULL, ((JOUTSTR*)jp)->N);
- return str;
} else {
if (!g->Message[0])
strcpy(g->Message, "Error in Serialize");
- return NULL;
} // endif's
+fin:
+ g->jump_level--;
+ return str;
} // end of Serialize
/***********************************************************************/
@@ -565,29 +617,40 @@ bool SerializeArray(JOUT *js, PJAR jarp, bool b)
{
bool first = true;
+ if (b) {
+ if (js->Prty()) {
+ if (js->WriteChr('['))
+ return true;
+ else if (js->Prty() == 1 && (js->WriteStr(EL) || js->WriteChr('\t')))
+ return true;
- if (js->WriteChr('['))
- return true;
- else if (b && (js->WriteStr(EL) || js->WriteChr('\t')))
- return true;
+ } // endif Prty
+
+ } else if (js->WriteChr('['))
+ return true;
for (int i = 0; i < jarp->size(); i++) {
if (first)
first = false;
- else if (js->WriteChr(','))
- return true;
- else if (b && (js->WriteStr(EL) || js->WriteChr('\t')))
+ else if ((!b || js->Prty()) && js->WriteChr(','))
return true;
+ else if (b) {
+ if (js->Prty() < 2 && js->WriteStr(EL))
+ return true;
+ else if (js->Prty() == 1 && js->WriteChr('\t'))
+ return true;
+
+ } // endif b
if (SerializeValue(js, jarp->GetValue(i)))
return true;
} // endfor i
- if (b && js->WriteStr(EL))
+ if (b && js->Prty() == 1 && js->WriteStr(EL))
return true;
- return js->WriteChr(']');
+ return ((!b || js->Prty()) && js->WriteChr(']'));
} // end of SerializeArray
/***********************************************************************/
@@ -647,8 +710,8 @@ bool SerializeValue(JOUT *js, PJVAL jvp)
} // endswitch Type
-strcpy(js->g->Message, "Unrecognized value");
-return true;
+ strcpy(js->g->Message, "Unrecognized value");
+ return true;
} // end of SerializeValue
/* -------------------------- Class JOUTSTR -------------------------- */
@@ -867,6 +930,20 @@ PJPR JOBJECT::AddPair(PGLOBAL g, PSZ key)
} // end of AddPair
/***********************************************************************/
+/* Return all keys as an array. */
+/***********************************************************************/
+PJAR JOBJECT::GetKeyList(PGLOBAL g)
+{
+ PJAR jarp = new(g) JARRAY();
+
+ for (PJPR jpp = First; jpp; jpp = jpp->Next)
+ jarp->AddValue(g, new(g) JVALUE(g, jpp->GetKey()));
+
+ jarp->InitArray(g);
+ return jarp;
+} // end of GetKeyList
+
+/***********************************************************************/
/* Get the value corresponding to the given key. */
/***********************************************************************/
PJVAL JOBJECT::GetValue(const char* key)
@@ -904,11 +981,29 @@ PSZ JOBJECT::GetText(PGLOBAL g, PSZ text)
} // end of GetValue;
/***********************************************************************/
+/* Merge two objects. */
+/***********************************************************************/
+bool JOBJECT::Merge(PGLOBAL g, PJSON jsp)
+{
+ if (jsp->GetType() != TYPE_JOB) {
+ strcpy(g->Message, "Second argument is not an object");
+ return true;
+ } // endif Type
+
+ PJOB jobp = (PJOB)jsp;
+
+ for (PJPR jpp = jobp->First; jpp; jpp = jpp->Next)
+ SetValue(g, jpp->GetVal(), jpp->GetKey());
+
+ return false;
+} // end of Marge;
+
+/***********************************************************************/
/* Set or add a value corresponding to the given key. */
/***********************************************************************/
void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key)
{
- PJPR jp;
+ PJPR jp;
for (jp = First; jp; jp = jp->Next)
if (!strcmp(jp->Key, key)) {
@@ -924,6 +1019,23 @@ void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key)
} // end of SetValue
/***********************************************************************/
+/* Delete a value corresponding to the given key. */
+/***********************************************************************/
+void JOBJECT::DeleteKey(PSZ key)
+{
+ PJPR jp, *pjp = &First;
+
+ for (jp = First; jp; jp = jp->Next)
+ if (!strcmp(jp->Key, key)) {
+ *pjp = jp->Next;
+ Size--;
+ break;
+ } else
+ pjp = &jp->Next;
+
+} // end of DeleteKey
+
+/***********************************************************************/
/* True if void or if all members are nulls. */
/***********************************************************************/
bool JOBJECT::IsNull(void)
@@ -943,23 +1055,25 @@ bool JOBJECT::IsNull(void)
void JARRAY::InitArray(PGLOBAL g)
{
int i;
- PJVAL jvp;
+ PJVAL jvp, *pjvp = &First;
for (Size = 0, jvp = First; jvp; jvp = jvp->Next)
if (!jvp->Del)
Size++;
- if (!Size) {
- return;
- } else if (Size > Alloc) {
+ if (Size > Alloc) {
// No need to realloc after deleting values
Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL));
Alloc = Size;
} // endif Size
for (i = 0, jvp = First; jvp; jvp = jvp->Next)
- if (!jvp->Del)
- Mvals[i++] = jvp;
+ if (!jvp->Del) {
+ Mvals[i++] = jvp;
+ pjvp = &jvp->Next;
+ Last = jvp;
+ } else
+ *pjvp = jvp->Next;
} // end of InitArray
@@ -975,31 +1089,66 @@ PJVAL JARRAY::GetValue(int i)
} // end of GetValue
/***********************************************************************/
-/* Add a Value to the Arrays Value list. */
+/* Add a Value to the Array Value list. */
/***********************************************************************/
-PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp)
+PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp, int *x)
{
if (!jvp)
jvp = new(g) JVALUE;
- if (Last)
- Last->Next = jvp;
- else
- First = jvp;
+ if (x) {
+ int i = 0, n = *x;
+ PJVAL jp, *jpp = &First;
+
+ for (jp = First; jp && i < n; i++, jp = *(jpp = &jp->Next));
+
+ (*jpp) = jvp;
+
+ if (!(jvp->Next = jp))
+ Last = jvp;
+
+ } else {
+ if (!First)
+ First = jvp;
+ else if (Last == First)
+ First->Next = Last = jvp;
+ else
+ Last->Next = jvp;
+
+ Last = jvp;
+ } // endif x
- Last = jvp;
return jvp;
} // end of AddValue
/***********************************************************************/
-/* Add a Value to the Arrays Value list. */
+/* Merge two arrays. */
+/***********************************************************************/
+bool JARRAY::Merge(PGLOBAL g, PJSON jsp)
+{
+ if (jsp->GetType() != TYPE_JAR) {
+ strcpy(g->Message, "Second argument is not an array");
+ return true;
+ } // endif Type
+
+ PJAR arp = (PJAR)jsp;
+
+ for (int i = 0; i < jsp->size(); i++)
+ AddValue(g, arp->GetValue(i));
+
+ InitArray(g);
+ return false;
+} // end of Merge
+
+/***********************************************************************/
+/* Set the nth Value of the Array Value list. */
/***********************************************************************/
bool JARRAY::SetValue(PGLOBAL g, PJVAL jvp, int n)
{
int i = 0;
PJVAL jp, *jpp = &First;
- for (i = 0, jp = First; i < n; i++, jp = *(jpp = &jp->Next))
+ for (jp = First; i < n; i++, jp = *(jpp = &jp->Next))
if (!jp)
*jpp = jp = new(g) JVALUE;
@@ -1049,6 +1198,17 @@ JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON()
} // end of JVALUE constructor
/***********************************************************************/
+/* Constructor for a given string. */
+/***********************************************************************/
+JVALUE::JVALUE(PGLOBAL g, PSZ strp) : JSON()
+{
+ Jsp = NULL;
+ Value = AllocateValue(g, strp, TYPE_STRING);
+ Next = NULL;
+ Del = false;
+} // end of JVALUE constructor
+
+/***********************************************************************/
/* Returns the type of the Value's value. */
/***********************************************************************/
JTYP JVALUE::GetValType(void)
@@ -1093,6 +1253,14 @@ int JVALUE::GetInteger(void)
} // end of GetInteger
/***********************************************************************/
+/* Return the Value's Big integer value. */
+/***********************************************************************/
+long long JVALUE::GetBigint(void)
+{
+ return (Value) ? Value->GetBigintValue() : 0;
+} // end of GetBigint
+
+/***********************************************************************/
/* Return the Value's Double value. */
/***********************************************************************/
double JVALUE::GetFloat(void)
@@ -1128,13 +1296,44 @@ PSZ JVALUE::GetText(PGLOBAL g, PSZ text)
return text;
} // end of GetText
+void JVALUE::SetValue(PJSON jsp)
+{
+ if (jsp && jsp->GetType() == TYPE_JVAL) {
+ Jsp = jsp->GetJsp();
+ Value = jsp->GetValue();
+ } else {
+ Jsp = jsp;
+ Value = NULL;
+ } // endif Type
+
+} // end of SetValue;
+
/***********************************************************************/
/* 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
+ Jsp = NULL;
+} // end of SetInteger
+
+/***********************************************************************/
+/* Set the Value's Boolean value as a tiny integer. */
+/***********************************************************************/
+void JVALUE::SetTiny(PGLOBAL g, char n)
+{
+ Value = AllocateValue(g, &n, TYPE_TINY);
+ Jsp = NULL;
+} // end of SetInteger
+
+/***********************************************************************/
+/* Set the Value's value as the given big integer. */
+/***********************************************************************/
+void JVALUE::SetBigint(PGLOBAL g, long long ll)
+{
+ Value = AllocateValue(g, &ll, TYPE_BIGINT);
+ Jsp = NULL;
+} // end of SetBigint
/***********************************************************************/
/* Set the Value's value as the given DOUBLE. */
@@ -1142,15 +1341,17 @@ void JVALUE::SetInteger(PGLOBAL g, int n)
void JVALUE::SetFloat(PGLOBAL g, double f)
{
Value = AllocateValue(g, &f, TYPE_DOUBLE, 6);
-} // end of AddFloat
+ Jsp = NULL;
+} // end of SetFloat
/***********************************************************************/
/* Set the Value's value as the given string. */
/***********************************************************************/
-void JVALUE::SetString(PGLOBAL g, PSZ s)
+void JVALUE::SetString(PGLOBAL g, PSZ s, short c)
{
- Value = AllocateValue(g, s, TYPE_STRING);
-} // end of AddFloat
+ Value = AllocateValue(g, s, TYPE_STRING, c);
+ Jsp = NULL;
+} // end of SetString
/***********************************************************************/
/* True when its JSON or normal value is null. */
diff --git a/storage/connect/json.h b/storage/connect/json.h
index da45157f124..4ea169e1b18 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.1 */
+/* Name: json.h Version 1.2 */
/* */
/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
/* */
@@ -16,8 +16,9 @@
enum JTYP {TYPE_STRG = 1,
TYPE_DBL = 2,
TYPE_BOOL = 4,
- TYPE_INTG = 7,
- TYPE_JSON = 12,
+ TYPE_BINT = 5,
+ TYPE_INTG = 7,
+ TYPE_JSON = 12,
TYPE_JAR,
TYPE_JOB,
TYPE_JVAL};
@@ -40,13 +41,13 @@ typedef struct {
int len;
} STRG, *PSG;
-PJSON ParseJson(PGLOBAL g, char *s, int n, int prty, bool *b = NULL);
-PJAR ParseArray(PGLOBAL g, int& i, STRG& src);
-PJOB ParseObject(PGLOBAL g, int& i, STRG& src);
-PJVAL ParseValue(PGLOBAL g, int& i, STRG& src);
+PJSON ParseJson(PGLOBAL g, char *s, int n, int *prty = NULL, bool *b = NULL);
+PJAR ParseArray(PGLOBAL g, int& i, STRG& src, bool *pty);
+PJOB ParseObject(PGLOBAL g, int& i, STRG& src, bool *pty);
+PJVAL ParseValue(PGLOBAL g, int& i, STRG& src, bool *pty);
char *ParseString(PGLOBAL g, int& i, STRG& src);
PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src);
-PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty);
+PSZ Serialize(PGLOBAL g, PJSON jsp, char *fn, int pretty);
bool SerializeArray(JOUT *js, PJAR jarp, bool b);
bool SerializeObject(JOUT *js, PJOB jobp);
bool SerializeValue(JOUT *js, PJVAL jvp);
@@ -56,14 +57,16 @@ bool SerializeValue(JOUT *js, PJVAL jvp);
/***********************************************************************/
class JOUT : public BLOCK {
public:
- JOUT(PGLOBAL gp) : BLOCK() {g = gp;}
+ JOUT(PGLOBAL gp) : BLOCK() {g = gp; Pretty = 3;}
virtual bool WriteStr(const char *s) = 0;
virtual bool WriteChr(const char c) = 0;
virtual bool Escape(const char *s) = 0;
+ int Prty(void) {return Pretty;}
// Member
PGLOBAL g;
+ int Pretty;
}; // end of class JOUT
/***********************************************************************/
@@ -88,7 +91,7 @@ class JOUTSTR : public JOUT {
/***********************************************************************/
class JOUTFILE : public JOUT {
public:
- JOUTFILE(PGLOBAL g, FILE *str) : JOUT(g) {Stream = str;}
+ JOUTFILE(PGLOBAL g, FILE *str, int pty) : JOUT(g) {Stream = str; Pretty = pty;}
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
@@ -103,7 +106,7 @@ class JOUTFILE : public JOUT {
/***********************************************************************/
class JOUTPRT : public JOUTFILE {
public:
- JOUTPRT(PGLOBAL g, FILE *str) : JOUTFILE(g, str) {M = 0; B = false;}
+ JOUTPRT(PGLOBAL g, FILE *str) : JOUTFILE(g, str, 2) {M = 0; B = false;}
virtual bool WriteStr(const char *s);
virtual bool WriteChr(const char c);
@@ -118,7 +121,8 @@ class JOUTPRT : public JOUTFILE {
/***********************************************************************/
class JPAIR : public BLOCK {
friend class JOBJECT;
- friend PJOB ParseObject(PGLOBAL, int&, STRG&);
+ friend class JSNX;
+ friend PJOB ParseObject(PGLOBAL, int&, STRG&, bool*);
friend bool SerializeObject(JOUT *, PJOB);
public:
JPAIR(PSZ key) : BLOCK() {Key = key; Val = NULL; Next = NULL;}
@@ -145,28 +149,32 @@ class JSON : public BLOCK {
virtual JTYP GetType(void) {return TYPE_JSON;}
virtual JTYP GetValType(void) {X return TYPE_JSON;}
virtual void InitArray(PGLOBAL g) {X}
- virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL) {X return NULL;}
+//virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL, int *x = NULL) {X return NULL;}
virtual PJPR AddPair(PGLOBAL g, PSZ key) {X return NULL;}
- virtual PJVAL GetValue(const char *key) {X return NULL;}
+ virtual PJAR GetKeyList(PGLOBAL g) {X return NULL;}
+ virtual PJVAL GetValue(const char *key) {X return NULL;}
virtual PJOB GetObject(void) {return NULL;}
virtual PJAR GetArray(void) {return NULL;}
- virtual PJVAL GetValue(int i) {X return NULL;}
+ virtual PJVAL GetValue(int i) {X return NULL;}
virtual PVAL GetValue(void) {X return NULL;}
- virtual PJSON GetJson(void) {X return NULL;}
+ virtual PJSON GetJsp(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, PSZ text) {X return NULL;}
- virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i) {X return true;}
+ virtual bool Merge(PGLOBAL g, PJSON jsp) { X return true; }
+ 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 SetString(PGLOBAL g, PSZ s, short c) {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;}
+ virtual void DeleteKey(char *k) {X}
+ virtual bool DeleteValue(int i) {X return true;}
+ virtual bool IsNull(void) {X return true;}
protected:
int Size;
@@ -176,8 +184,9 @@ class JSON : public BLOCK {
/* Class JOBJECT: contains a list of value pairs. */
/***********************************************************************/
class JOBJECT : public JSON {
- friend PJOB ParseObject(PGLOBAL, int&, STRG&);
+ friend PJOB ParseObject(PGLOBAL, int&, STRG&, bool*);
friend bool SerializeObject(JOUT *, PJOB);
+ friend class JSNX;
public:
JOBJECT(void) : JSON() {First = Last = NULL;}
@@ -189,9 +198,12 @@ class JOBJECT : public JSON {
virtual PJPR AddPair(PGLOBAL g, PSZ key);
virtual PJOB GetObject(void) {return this;}
virtual PJVAL GetValue(const char* key);
- virtual PSZ GetText(PGLOBAL g, PSZ text);
- virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key);
- virtual bool IsNull(void);
+ virtual PJAR GetKeyList(PGLOBAL g);
+ virtual PSZ GetText(PGLOBAL g, PSZ text);
+ virtual bool Merge(PGLOBAL g, PJSON jsp);
+ virtual void SetValue(PGLOBAL g, PJVAL jvp, PSZ key);
+ virtual void DeleteKey(char *k);
+ virtual bool IsNull(void);
protected:
PJPR First;
@@ -202,7 +214,7 @@ class JOBJECT : public JSON {
/* Class JARRAY. */
/***********************************************************************/
class JARRAY : public JSON {
- friend PJAR ParseArray(PGLOBAL, int&, STRG&);
+ friend PJAR ParseArray(PGLOBAL, int&, STRG&, bool*);
public:
JARRAY(void) : JSON() {Alloc = 0; First = Last = NULL; Mvals = NULL;}
@@ -211,10 +223,11 @@ class JARRAY : public JSON {
virtual void Clear(void) {First = Last = NULL; Size = 0;}
virtual JTYP GetType(void) {return TYPE_JAR;}
virtual PJAR GetArray(void) {return this;}
- virtual PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL);
+ PJVAL AddValue(PGLOBAL g, PJVAL jvp = NULL, int *x = NULL);
virtual void InitArray(PGLOBAL g);
virtual PJVAL GetValue(int i);
- virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i);
+ virtual bool Merge(PGLOBAL g, PJSON jsp);
+ virtual bool SetValue(PGLOBAL g, PJVAL jvp, int i);
virtual bool DeleteValue(int n);
virtual bool IsNull(void);
@@ -231,7 +244,8 @@ class JARRAY : public JSON {
/***********************************************************************/
class JVALUE : public JSON {
friend class JARRAY;
- friend PJVAL ParseValue(PGLOBAL, int&, STRG&);
+ friend class JSNX;
+ friend PJVAL ParseValue(PGLOBAL, int&, STRG&, bool*);
friend bool SerializeValue(JOUT *, PJVAL);
public:
JVALUE(void) : JSON()
@@ -239,6 +253,7 @@ class JVALUE : public JSON {
JVALUE(PJSON jsp) : JSON()
{Jsp = jsp; Value = NULL; Next = NULL; Del = false;}
JVALUE(PGLOBAL g, PVAL valp);
+ JVALUE(PGLOBAL g, PSZ strp);
using JSON::GetValue;
using JSON::SetValue;
@@ -249,17 +264,21 @@ class JVALUE : public JSON {
virtual PJOB GetObject(void);
virtual PJAR GetArray(void);
virtual PVAL GetValue(void) {return Value;}
- virtual PJSON GetJson(void) {return (Jsp ? Jsp : this);}
- virtual int GetInteger(void);
- virtual double GetFloat(void);
+ virtual PJSON GetJsp(void) {return Jsp;}
+ virtual PJSON GetJson(void) { return (Jsp ? Jsp : this); }
+ virtual int GetInteger(void);
+ virtual long long GetBigint(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 SetValue(PJSON jsp);
+ virtual void SetValue(PVAL valp) { Value = valp; Jsp = NULL; }
+ virtual void SetString(PGLOBAL g, PSZ s, short c = 0);
virtual void SetInteger(PGLOBAL g, int n);
- virtual void SetFloat(PGLOBAL g, double f);
- virtual bool IsNull(void);
+ virtual void SetBigint(PGLOBAL g, longlong ll);
+ virtual void SetFloat(PGLOBAL g, double f);
+ virtual void SetTiny(PGLOBAL g, char f);
+ virtual bool IsNull(void);
protected:
PJSON Jsp; // To the json value
diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp
index b2d983712aa..40685ae0f0e 100644
--- a/storage/connect/jsonudf.cpp
+++ b/storage/connect/jsonudf.cpp
@@ -1,69 +1,1077 @@
-/************* 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. */
-/***********************************************************************/
+/****************** jsonudf C++ Program Source Code File (.CPP) ******************/
+/* PROGRAM NAME: jsonudf Version 1.3 */
+/* (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 <stdio.h>
-#include "global.h"
-#include "plgdbsem.h"
-#include "json.h"
+#include "jsonudf.h"
-#define MEMFIX 512
-#define UDF_EXEC_ARGS \
- UDF_INIT*, UDF_ARGS*, char*, unsigned long*, char*, char*
+#if defined(UNIX) || defined(UNIV_LINUX)
+#define _O_RDONLY O_RDONLY
+#endif
+
+#define MEMFIX 4096
+#if defined(connect_EXPORTS)
+#define PUSH_WARNING(M) push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, M)
+#else
+#define PUSH_WARNING(M) htrc(M)
+#endif
+#define M 7
uint GetJsonGrpSize(void);
+static int IsJson(UDF_ARGS *args, uint i);
+static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i);
-extern "C" {
-DllExport my_bool Json_Value_init(UDF_INIT*, UDF_ARGS*, char*);
-DllExport char *Json_Value(UDF_EXEC_ARGS);
-DllExport void Json_Value_deinit(UDF_INIT*);
+/* ----------------------------------- JSNX ------------------------------------ */
-DllExport my_bool Json_Array_init(UDF_INIT*, UDF_ARGS*, char*);
-DllExport char *Json_Array(UDF_EXEC_ARGS);
-DllExport void Json_Array_deinit(UDF_INIT*);
+/*********************************************************************************/
+/* JSNX public constructor. */
+/*********************************************************************************/
+JSNX::JSNX(PGLOBAL g, PJSON row, int type, int len, int prec, my_bool wr)
+{
+ Row = row;
+ Jvalp = NULL;
+ Jpnp = NULL;
+ Jp = NULL;
+ Nodes = NULL;
+ Value = AllocateValue(g, type, len, prec);
+ MulVal = NULL;
+ Jpath = NULL;
+ Buf_Type = type;
+ Long = len;
+ Prec = prec;
+ Nod = 0;
+ Xnod = -1;
+ K = 0;
+ I = -1;
+ Imax = 9;
+ B = 0;
+ Xpd = false;
+ Parsed = false;
+ Found = false;
+ Wr = wr;
+ Jb = false;
+} // end of JSNX constructor
+
+/*********************************************************************************/
+/* SetJpath: set and parse the json path. */
+/*********************************************************************************/
+my_bool JSNX::SetJpath(PGLOBAL g, char *path, my_bool jb)
+{
+ // Check Value was allocated
+ if (!Value)
+ return true;
+
+ Value->SetNullable(true);
+
+#if 0
+ if (jb) {
+ // Path must return a Json item
+ size_t n = strlen(path);
+
+ if (!n || path[n - 1] != '*') {
+ Jpath = (char*)PlugSubAlloc(g, NULL, n + 3);
+ strcat(strcpy(Jpath, path), (n) ? ":*" : "*");
+ } else
+ Jpath = path;
+
+ } else
+#endif // 0
+ Jpath = path;
+
+ // Parse the json path
+ Parsed = false;
+ Nod = 0;
+ Jb = jb;
+ return ParseJpath(g);
+} // end of SetJpath
+
+/*********************************************************************************/
+/* Analyse array processing options. */
+/*********************************************************************************/
+my_bool JSNX::SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm)
+{
+ int n = (int)strlen(p);
+ my_bool dg = true, b = false;
+ PJNODE jnp = &Nodes[i];
+
+ if (*p) {
+ if (p[--n] == ']') {
+ p[n--] = 0;
+ p++;
+ } else {
+ // Wrong array specification
+ sprintf(g->Message, "Invalid array specification %s", p);
+ return true;
+ } // endif p
+
+ } else
+ b = true;
+
+ // To check whether a numeric Rank was specified
+ for (int k = 0; dg && p[k]; k++)
+ dg = isdigit(p[k]) > 0;
+
+ if (!n) {
+ // Default specifications
+ if (jnp->Op != OP_EXP) {
+ if (Wr) {
+ // Force append
+ jnp->Rank = INT_MAX32;
+ jnp->Op = OP_LE;
+ } else if (Jb) {
+ // Return a Json item
+ jnp->Op = OP_XX;
+ } else if (b) {
+ // Return 1st value (B is the index base)
+ jnp->Rank = B;
+ jnp->Op = OP_LE;
+ } 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) {
+ // Return nth value
+ jnp->Rank = atoi(p) - B;
+ jnp->Op = OP_EQ;
+ } else if (Wr) {
+ sprintf(g->Message, "Invalid specification %s in a write path", p);
+ return true;
+ } else if (n == 1) {
+ // Set the Op value;
+ switch (*p) {
+ case '+': jnp->Op = OP_ADD; break;
+ case '*': jnp->Op = OP_MULT; break;
+ case '>': jnp->Op = OP_MAX; break;
+ case '<': jnp->Op = OP_MIN; break;
+ case '!': jnp->Op = OP_SEP; break; // Average
+ case '#': jnp->Op = OP_NUM; break;
+ case 'x':
+ case 'X': // Expand this array
+ strcpy(g->Message, "Expand not supported by this function");
+ return true;
+ default:
+ sprintf(g->Message, "Invalid function specification %c", *p);
+ return true;
+ } // endswitch *p
+
+ } else if (*p == '"' && p[n - 1] == '"') {
+ // This is a concat specification
+ jnp->Op = OP_CNC;
+
+ if (n > 2) {
+ // Set concat intermediate string
+ p[n - 1] = 0;
+ jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
+ } // endif n
+
+ } else {
+ strcpy(g->Message, "Wrong array specification");
+ return true;
+ } // endif's
+
+ // For calculated arrays, a local Value must be used
+ switch (jnp->Op) {
+ case OP_NUM:
+ jnp->Valp = AllocateValue(g, TYPE_INT);
+ break;
+ case OP_ADD:
+ case OP_MULT:
+ case OP_SEP:
+ if (!IsTypeChar(Buf_Type))
+ jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
+ else
+ jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);
+
+ break;
+ case OP_MIN:
+ case OP_MAX:
+ jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
+ break;
+ case OP_CNC:
+ if (IsTypeChar(Buf_Type))
+ jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
+ else
+ jnp->Valp = AllocateValue(g, TYPE_STRING, 512);
+
+ break;
+ default:
+ break;
+ } // endswitch Op
+
+ if (jnp->Valp)
+ MulVal = AllocateValue(g, jnp->Valp);
+
+ return false;
+} // end of SetArrayOptions
+
+/*********************************************************************************/
+/* Parse the eventual passed Jpath information. */
+/* This information can be specified in the Fieldfmt column option when */
+/* creating the table. It permits to indicate the position of the node */
+/* corresponding to that column. */
+/*********************************************************************************/
+my_bool JSNX::ParseJpath(PGLOBAL g)
+{
+ char *p, *p2 = NULL, *pbuf = NULL;
+ int i;
+ my_bool mul = false;
+
+ if (Parsed)
+ return false; // Already done
+ else if (!Jpath)
+ // Jpath = Name;
+ return true;
+
+ pbuf = PlugDup(g, Jpath);
+
+ // The Jpath must be analyzed
+ for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++)
+ Nod++; // One path node found
+
+ Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE));
+ memset(Nodes, 0, (Nod)* sizeof(JNODE));
+
+ // Analyze the Jpath for this column
+ for (i = 0, p = pbuf; i < Nod; i++, p = (p2 ? p2 + 1 : p + strlen(p))) {
+ if ((p2 = strchr(p, ':')))
+ *p2 = 0;
+
+ // Jpath must be explicit
+ if (*p == 0 || *p == '[') {
+ // Analyse intermediate array processing
+ if (SetArrayOptions(g, p, i, Nodes[i-1].Key))
+ return true;
+
+ } else if (*p == '*') {
+ if (Wr) {
+ sprintf(g->Message, "Invalid specification %c in a write path", *p);
+ return true;
+ } else // Return JSON
+ Nodes[i].Op = OP_XX;
+
+ } else {
+ Nodes[i].Key = p;
+ Nodes[i].Op = OP_EXIST;
+ } // endif's
+
+ } // endfor i, p
+
+ MulVal = AllocateValue(g, Value);
+ Parsed = true;
+ return false;
+} // end of ParseJpath
+
+/*********************************************************************************/
+/* MakeJson: Serialize the json item and set value to it. */
+/*********************************************************************************/
+PVAL JSNX::MakeJson(PGLOBAL g, PJSON jsp)
+{
+ if (Value->IsTypeNum()) {
+ strcpy(g->Message, "Cannot make Json for a numeric value");
+ Value->Reset();
+ } else if (jsp->GetType() != TYPE_JAR && jsp->GetType() != TYPE_JOB) {
+ strcpy(g->Message, "Target is not an array or object");
+ 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 JSNX::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
+{
+ if (val) {
+ if (Jb) {
+ vp->SetValue_psz(Serialize(g, val->GetJsp(), NULL, 0));
+ } else switch (val->GetValType()) {
+ case TYPE_STRG:
+ case TYPE_INTG:
+ case TYPE_BINT:
+ case TYPE_DBL:
+ vp->SetValue_pval(val->GetValue());
+ break;
+ case TYPE_BOOL:
+ if (vp->IsTypeNum())
+ vp->SetValue(val->GetInteger() ? 1 : 0);
+ else
+ vp->SetValue_psz((PSZ)(val->GetInteger() ? "true" : "false"));
+
+ break;
+ case TYPE_JAR:
+ SetJsonValue(g, vp, val->GetArray()->GetValue(0), n);
+ break;
+ case TYPE_JOB:
+ // if (!vp->IsTypeNum() || !Strict) {
+ vp->SetValue_psz(val->GetObject()->GetText(g, NULL));
+ break;
+ // } // endif Type
+
+ default:
+ vp->Reset();
+ } // endswitch Type
+
+ } else {
+ vp->SetNull(true);
+ vp->Reset();
+ } // endif val
+
+} // end of SetJsonValue
+
+/*********************************************************************************/
+/* GetJson: */
+/*********************************************************************************/
+PJVAL JSNX::GetJson(PGLOBAL g)
+{
+ return GetRowValue(g, Row, 0);
+} // end of GetJson
-DllExport my_bool Json_Array_Add_init(UDF_INIT*, UDF_ARGS*, char*);
-DllExport char *Json_Array_Add(UDF_EXEC_ARGS);
-DllExport void Json_Array_Add_deinit(UDF_INIT*);
+/*********************************************************************************/
+/* ReadValue: */
+/*********************************************************************************/
+void JSNX::ReadValue(PGLOBAL g)
+{
+ Value->SetValue_pval(GetColumnValue(g, Row, 0));
+} // end of ReadValue
-DllExport my_bool Json_Array_Delete_init(UDF_INIT*, UDF_ARGS*, char*);
-DllExport char *Json_Array_Delete(UDF_EXEC_ARGS);
-DllExport void Json_Array_Delete_deinit(UDF_INIT*);
+/*********************************************************************************/
+/* GetColumnValue: */
+/*********************************************************************************/
+PVAL JSNX::GetColumnValue(PGLOBAL g, PJSON row, int i)
+{
+ int n = Nod - 1;
+ PJVAL val = NULL;
+
+ val = GetRowValue(g, row, i);
+ SetJsonValue(g, Value, val, n);
+ return Value;
+} // end of GetColumnValue
+
+/*********************************************************************************/
+/* GetRowValue: */
+/*********************************************************************************/
+PJVAL JSNX::GetRowValue(PGLOBAL g, PJSON row, int i, my_bool b)
+{
+ my_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);
+ val = new(g) JVALUE(g, Value);
+ return val;
+ } else if (Nodes[i].Op == OP_XX) {
+ Jb = b;
+ return new(g)JVALUE(row);
+ } else switch (row->GetType()) {
+ case TYPE_JOB:
+ if (!Nodes[i].Key) {
+ // Expected Array was not there
+ if (Nodes[i].Op == OP_LE) {
+ if (i < Nod-1)
+ continue;
+ else
+ val = new(g)JVALUE(row);
+
+ } else {
+ strcpy(g->Message, "Unexpected object");
+ val = NULL;
+ } //endif Op
+
+ } else
+ val = ((PJOB)row)->GetValue(Nodes[i].Key);
+
+ break;
+ case TYPE_JAR:
+ arp = (PJAR)row;
+
+ if (!Nodes[i].Key) {
+ if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
+ val = arp->GetValue(Nodes[i].Rank);
+ else if (Nodes[i].Op == OP_EXP)
+ return (PJVAL)ExpandArray(g, arp, i);
+ else
+ return new(g) JVALUE(g, CalculateArray(g, arp, i));
+
+ } else {
+ // Unexpected array, unwrap it as [0]
+ val = arp->GetValue(0);
+ i--;
+ } // endif's
+
+ 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)
+ if (!(row = (val) ? val->GetJsp() : NULL))
+ val = NULL;
+// row = (val) ? val->GetJson() : NULL;
+
+ } // endfor i
+
+ // SetJsonValue(g, Value, val, n);
+ return val;
+} // end of GetRowValue
+
+/*********************************************************************************/
+/* ExpandArray: */
+/*********************************************************************************/
+PVAL JSNX::ExpandArray(PGLOBAL g, PJAR arp, int n)
+{
+ strcpy(g->Message, "Expand cannot be done by this function");
+ return NULL;
+} // end of ExpandArray
+
+/*********************************************************************************/
+/* CalculateArray: */
+/*********************************************************************************/
+PVAL JSNX::CalculateArray(PGLOBAL g, PJAR arp, int n)
+{
+//int i, ars, nv = 0, nextsame = Tjp->NextSame;
+ int i, ars, nv = 0, nextsame = 0;
+ my_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());
+ ars = 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
+
+/*********************************************************************************/
+/* CheckPath: Checks whether the path exists in the document. */
+/*********************************************************************************/
+my_bool JSNX::CheckPath(PGLOBAL g)
+{
+ PJVAL val;
+ PJSON row = Row;
+
+ for (int i = 0; i < Nod && row; i++) {
+ val = NULL;
+
+ if (Nodes[i].Op == OP_NUM || Nodes[i].Op == OP_XX) {
+ } else switch (row->GetType()) {
+ case TYPE_JOB:
+ if (Nodes[i].Key)
+ val = ((PJOB)row)->GetValue(Nodes[i].Key);
-DllExport my_bool Json_Object_init(UDF_INIT*, UDF_ARGS*, char*);
-DllExport char *Json_Object(UDF_EXEC_ARGS);
-DllExport void Json_Object_deinit(UDF_INIT*);
+ break;
+ case TYPE_JAR:
+ if (!Nodes[i].Key)
+ if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
+ val = ((PJAR)row)->GetValue(Nodes[i].Rank);
-DllExport my_bool Json_Object_Nonull_init(UDF_INIT*, UDF_ARGS*, char*);
-DllExport char *Json_Object_Nonull(UDF_EXEC_ARGS);
-DllExport void Json_Object_Nonull_deinit(UDF_INIT*);
+ break;
+ case TYPE_JVAL:
+ val = (PJVAL)row;
+ break;
+ default:
+ sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
+ } // endswitch Type
-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_EXEC_ARGS);
-DllExport void Json_Array_Grp_clear(UDF_INIT *, char *, char *);
-DllExport void Json_Array_Grp_deinit(UDF_INIT*);
+ if (i < Nod-1)
+ if (!(row = (val) ? val->GetJsp() : NULL))
+ val = NULL;
-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_EXEC_ARGS);
-DllExport void Json_Object_Grp_clear(UDF_INIT *, char *, char *);
-DllExport void Json_Object_Grp_deinit(UDF_INIT*);
-} // extern "C"
+ } // endfor i
+ return (val != NULL);
+} // end of CheckPath
+
+/***********************************************************************/
+/* GetRow: Set the complete path of the object to be set. */
/***********************************************************************/
-/* Allocate and initialise the memory area. */
+PJSON JSNX::GetRow(PGLOBAL g)
+{
+ PJVAL val = NULL;
+ PJAR arp;
+ PJSON nwr, row = Row;
+
+ for (int i = 0; i < Nod - 1 && row; i++) {
+ if (Nodes[i].Op == OP_XX)
+ break;
+ else switch (row->GetType()) {
+ case TYPE_JOB:
+ if (!Nodes[i].Key)
+ // Expected Array was not there, wrap the value
+ continue;
+
+ val = ((PJOB)row)->GetValue(Nodes[i].Key);
+ break;
+ case TYPE_JAR:
+ arp = (PJAR)row;
+
+ if (!Nodes[i].Key) {
+ if (Nodes[i].Op == OP_EQ)
+ val = arp->GetValue(Nodes[i].Rank);
+ else
+ val = arp->GetValue(Nodes[i].Rx);
+
+ } else {
+ // Unexpected array, unwrap it as [0]
+ val = arp->GetValue(0);
+ i--;
+ } // endif Nodes
+
+ break;
+ case TYPE_JVAL:
+ val = (PJVAL)row;
+ break;
+ default:
+ sprintf(g->Message, "Invalid row JSON type %d", row->GetType());
+ val = NULL;
+ } // endswitch Type
+
+ if (val) {
+ row = val->GetJson();
+ } else {
+ // Construct missing objects
+ for (i++; row && i < Nod; i++) {
+ if (Nodes[i].Op == OP_XX)
+ break;
+ else if (!Nodes[i].Key)
+ // Construct intermediate array
+ nwr = new(g)JARRAY;
+ else
+ nwr = new(g)JOBJECT;
+
+ if (row->GetType() == TYPE_JOB) {
+ ((PJOB)row)->SetValue(g, new(g)JVALUE(nwr), Nodes[i-1].Key);
+ } else if (row->GetType() == TYPE_JAR) {
+ ((PJAR)row)->AddValue(g, new(g)JVALUE(nwr));
+ ((PJAR)row)->InitArray(g);
+ } else {
+ strcpy(g->Message, "Wrong type when writing new row");
+ nwr = NULL;
+ } // endif's
+
+ row = nwr;
+ } // endfor i
+
+ break;
+ } // endelse
+
+ } // endfor i
+
+ return row;
+} // end of GetRow
+
/***********************************************************************/
-static my_bool JsonInit(UDF_INIT *initid, char *message,
+/* WriteValue: */
+/***********************************************************************/
+my_bool JSNX::WriteValue(PGLOBAL g, PJVAL jvalp)
+{
+ PJOB objp = NULL;
+ PJAR arp = NULL;
+ PJVAL jvp = NULL;
+ PJSON row = GetRow(g);
+
+ if (!row)
+ return true;
+
+ switch (row->GetType()) {
+ case TYPE_JOB: objp = (PJOB)row; break;
+ case TYPE_JAR: arp = (PJAR)row; break;
+ case TYPE_JVAL: jvp = (PJVAL)row; break;
+ default:
+ strcpy(g->Message, "Invalid target type");
+ return true;
+ } // endswitch Type
+
+ if (arp) {
+ if (!Nodes[Nod-1].Key) {
+ if (Nodes[Nod-1].Op == OP_EQ)
+ arp->SetValue(g, jvalp, Nodes[Nod-1].Rank);
+ else
+ arp->AddValue(g, jvalp);
+
+ arp->InitArray(g);
+ } // endif Key
+
+ } else if (objp) {
+ if (Nodes[Nod-1].Key)
+ objp->SetValue(g, jvalp, Nodes[Nod-1].Key);
+
+ } else if (jvp)
+ jvp->SetValue(jvalp);
+
+ return false;
+} // end of WriteValue
+
+/*********************************************************************************/
+/* Locate a value in a JSON tree: */
+/*********************************************************************************/
+PSZ JSNX::Locate(PGLOBAL g, PJSON jsp, PJVAL jvp, int k)
+{
+ my_bool b = false, err = true;
+
+ g->Message[0] = 0;
+
+ if (!jsp) {
+ strcpy(g->Message, "Null json tree");
+ return NULL;
+ } // endif jsp
+
+ // Write to the path string
+ Jp = new(g) JOUTSTR(g);
+ Jvalp = jvp;
+ K = k;
+
+ switch (jsp->GetType()) {
+ case TYPE_JAR:
+ err = LocateArray((PJAR)jsp);
+ break;
+ case TYPE_JOB:
+ err = LocateObject((PJOB)jsp);
+ break;
+ case TYPE_JVAL:
+ err = LocateValue((PJVAL)jsp);
+ break;
+ default:
+ err = true;
+ } // endswitch Type
+
+ if (err) {
+ if (!g->Message[0])
+ strcpy(g->Message, "Invalid json tree");
+
+ } else if (Found) {
+ Jp->WriteChr('\0');
+ PlugSubAlloc(g, NULL, Jp->N);
+ return Jp->Strp;
+ } // endif's
+
+ return NULL;
+} // end of Locate
+
+/*********************************************************************************/
+/* Locate in a JSON Array. */
+/*********************************************************************************/
+my_bool JSNX::LocateArray(PJAR jarp)
+{
+ char s[16];
+ size_t m = Jp->N;
+
+ for (int i = 0; i < jarp->size() && !Found; i++) {
+ Jp->N = m;
+ sprintf(s, "[%d]", i + B);
+
+ if (Jp->WriteStr(s))
+ return true;
+
+ if (LocateValue(jarp->GetValue(i)))
+ return true;
+
+ } // endfor i
+
+ return false;
+} // end of LocateArray
+
+/*********************************************************************************/
+/* Locate in a JSON Object. */
+/*********************************************************************************/
+my_bool JSNX::LocateObject(PJOB jobp)
+{
+ size_t m = Jp->N;
+
+ for (PJPR pair = jobp->First; pair && !Found; pair = pair->Next) {
+ Jp->N = m;
+
+ if (Jp->WriteStr(pair->Key))
+ return true;
+
+ if (LocateValue(pair->Val))
+ return true;
+
+ } // endfor i
+
+ return false;
+} // end of LocateObject
+
+/*********************************************************************************/
+/* Locate a JSON Value. */
+/*********************************************************************************/
+my_bool JSNX::LocateValue(PJVAL jvp)
+{
+ if (CompareTree(Jvalp, jvp)) {
+ Found = (--K == 0);
+ } else if (jvp->GetArray()) {
+ if (Jp->WriteChr(':'))
+ return true;
+
+ return LocateArray(jvp->GetArray());
+ } else if (jvp->GetObject()) {
+ if (Jp->WriteChr(':'))
+ return true;
+
+ return LocateObject(jvp->GetObject());
+ } // endif's
+
+ return false;
+} // end of LocateValue
+
+/*********************************************************************************/
+/* Locate all occurrences of a value in a JSON tree: */
+/*********************************************************************************/
+PSZ JSNX::LocateAll(PGLOBAL g, PJSON jsp, PJVAL jvp, int mx)
+{
+ my_bool b = false, err = true;
+ PJPN jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx);
+
+ memset(jnp, 0, sizeof(JPN) * mx);
+ g->Message[0] = 0;
+
+ if (!jsp) {
+ strcpy(g->Message, "Null json tree");
+ return NULL;
+ } // endif jsp
+
+ // Write to the path string
+ Jp = new(g)JOUTSTR(g);
+ Jvalp = jvp;
+ Imax = mx - 1;
+ Jpnp = jnp;
+ Jp->WriteChr('[');
+
+ switch (jsp->GetType()) {
+ case TYPE_JAR:
+ err = LocateArrayAll((PJAR)jsp);
+ break;
+ case TYPE_JOB:
+ err = LocateObjectAll((PJOB)jsp);
+ break;
+ case TYPE_JVAL:
+ err = LocateValueAll((PJVAL)jsp);
+ break;
+ default:
+ err = true;
+ } // endswitch Type
+
+ if (err) {
+ if (!g->Message[0])
+ strcpy(g->Message, "Invalid json tree");
+
+ } else {
+ if (Jp->N > 1)
+ Jp->N--;
+
+ Jp->WriteChr(']');
+ Jp->WriteChr('\0');
+ PlugSubAlloc(g, NULL, Jp->N);
+ return Jp->Strp;
+ } // endif's
+
+ return NULL;
+} // end of LocateAll
+
+/*********************************************************************************/
+/* Locate in a JSON Array. */
+/*********************************************************************************/
+my_bool JSNX::LocateArrayAll(PJAR jarp)
+{
+ if (I < Imax) {
+ Jpnp[++I].Type = TYPE_JAR;
+
+ for (int i = 0; i < jarp->size(); i++) {
+ Jpnp[I].N = i;
+
+ if (LocateValueAll(jarp->GetValue(i)))
+ return true;
+
+ } // endfor i
+
+ I--;
+ } // endif I
+
+ return false;
+} // end of LocateArrayAll
+
+/*********************************************************************************/
+/* Locate in a JSON Object. */
+/*********************************************************************************/
+my_bool JSNX::LocateObjectAll(PJOB jobp)
+{
+ if (I < Imax) {
+ Jpnp[++I].Type = TYPE_JOB;
+
+ for (PJPR pair = jobp->First; pair; pair = pair->Next) {
+ Jpnp[I].Key = pair->Key;
+
+ if (LocateValueAll(pair->Val))
+ return true;
+
+ } // endfor i
+
+ I--;
+ } // endif I
+
+ return false;
+} // end of LocateObjectAll
+
+/*********************************************************************************/
+/* Locate a JSON Value. */
+/*********************************************************************************/
+my_bool JSNX::LocateValueAll(PJVAL jvp)
+{
+ if (CompareTree(Jvalp, jvp))
+ return AddPath();
+ else if (jvp->GetArray())
+ return LocateArrayAll(jvp->GetArray());
+ else if (jvp->GetObject())
+ return LocateObjectAll(jvp->GetObject());
+
+ return false;
+} // end of LocateValueAll
+
+/*********************************************************************************/
+/* Compare two JSON trees. */
+/*********************************************************************************/
+my_bool JSNX::CompareTree(PJSON jp1, PJSON jp2)
+{
+ if (!jp1 || !jp2 || jp1->GetType() != jp2->GetType()
+ || jp1->size() != jp2->size())
+ return false;
+
+ my_bool found = true;
+
+ if (jp1->GetType() == TYPE_JVAL) {
+ PVAL v1 = jp1->GetValue(), v2 = jp2->GetValue();
+
+ if (v1 && v2) {
+ if (v1->GetType() == v2->GetType())
+ found = !v1->CompareValue(v2);
+ else
+ found = false;
+
+ } else
+ found = CompareTree(jp1->GetJsp(), jp2->GetJsp());
+
+ } else if (jp1->GetType() == TYPE_JAR) {
+ for (int i = 0; found && i < jp1->size(); i++)
+ found = (CompareTree(jp1->GetValue(i), jp2->GetValue(i)));
+
+ } else if (jp1->GetType() == TYPE_JOB) {
+ PJPR p1 = jp1->GetFirst(), p2 = jp2->GetFirst();
+
+ for (; found && p1 && p2; p1 = p1->Next, p2 = p2->Next)
+ found = CompareTree(p1->Val, p2->Val);
+
+ } else
+ found = false;
+
+ return found;
+} // end of CompareTree
+
+/*********************************************************************************/
+/* Add the found path to the list. */
+/*********************************************************************************/
+my_bool JSNX::AddPath(void)
+{
+ char s[16];
+ my_bool b = false;
+
+ if (Jp->WriteChr('"'))
+ return true;
+
+ for (int i = 0; i <= I; i++) {
+ if (b) {
+ if (Jp->WriteChr(':')) return true;
+ } else
+ b = true;
+
+ if (Jpnp[i].Type == TYPE_JAR) {
+ sprintf(s, "[%d]", Jpnp[i].N + B);
+
+ if (Jp->WriteStr(s))
+ return true;
+
+ } else if (Jp->WriteStr(Jpnp[i].Key))
+ return true;
+
+ } // endfor i
+
+ if (Jp->WriteStr("\","))
+ return true;
+
+ return false;
+} // end of AddPath
+
+/* --------------------------------- JSON UDF ---------------------------------- */
+
+// BSON size should be equal on Linux and Windows
+#define BMX 255
+typedef struct BSON *PBSON;
+
+/*********************************************************************************/
+/* Structure used to return binary json. */
+/*********************************************************************************/
+struct BSON {
+ char Msg[BMX + 1];
+ char *Filename;
+ PGLOBAL G;
+ int Pretty;
+ ulong Reslen;
+ my_bool Changed;
+ PJSON Top;
+ PJSON Jsp;
+ PBSON Bsp;
+}; // end of struct BSON
+
+/*********************************************************************************/
+/* Allocate and initialize a BSON structure. */
+/*********************************************************************************/
+static PBSON JbinAlloc(PGLOBAL g, UDF_ARGS *args, ulong len, PJSON jsp)
+{
+ PBSON bsp = (PBSON)PlgDBSubAlloc(g, NULL, sizeof(BSON));
+
+ if (bsp) {
+ strcpy(bsp->Msg, "Binary Json");
+ bsp->Msg[BMX] = 0;
+ bsp->Filename = NULL;
+ bsp->G = g;
+ bsp->Pretty = 2;
+ bsp->Reslen = len;
+ bsp->Changed = false;
+ bsp->Top = bsp->Jsp = jsp;
+ bsp->Bsp = (IsJson(args, 0) == 3) ? (PBSON)args->args[0] : NULL;
+ } else
+ PUSH_WARNING(g->Message);
+
+ return bsp;
+} /* end of JbinAlloc */
+
+/*********************************************************************************/
+/* Set the BSON chain as changed. */
+/*********************************************************************************/
+static void SetChanged(PBSON bsp)
+{
+ if (bsp->Bsp)
+ SetChanged(bsp->Bsp);
+
+ bsp->Changed = true;
+} /* end of SetChanged */
+
+/*********************************************************************************/
+/* Program for SubSet re-initialization of the memory pool. */
+/*********************************************************************************/
+static my_bool JsonSubSet(PGLOBAL g)
+{
+ PPOOLHEADER pph = (PPOOLHEADER)g->Sarea;
+
+ pph->To_Free = (OFFSET)((g->Createas) ? g->Createas : sizeof(POOLHEADER));
+ pph->FreeBlk = g->Sarea_Size - pph->To_Free;
+ return FALSE;
+} /* end of JsonSubSet */
+
+/*********************************************************************************/
+/* Program for saving the status of the memory pools. */
+/*********************************************************************************/
+inline void JsonMemSave(PGLOBAL g)
+{
+ g->Createas = (int)((PPOOLHEADER)g->Sarea)->To_Free;
+} /* end of JsonMemSave */
+
+/*********************************************************************************/
+/* Program for freeing the memory pools. */
+/*********************************************************************************/
+inline void JsonFreeMem(PGLOBAL g)
+{
+ PlugExit(g);
+} /* end of JsonFreeMem */
+
+/*********************************************************************************/
+/* Allocate and initialise the memory area. */
+/*********************************************************************************/
+static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args,
+ char *message, my_bool mbn,
unsigned long reslen, unsigned long memlen)
{
PGLOBAL g = PlugInit(NULL, memlen);
@@ -72,37 +1080,226 @@ static my_bool JsonInit(UDF_INIT *initid, char *message,
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;
+ strcpy(message, g->Message);
+ PlugExit(g);
+ return true;
+ } // endif g
- initid->maybe_null = false;
+ g->Mrr = (args->arg_count && args->args[0]) ? 1 : 0;
+ initid->maybe_null = mbn;
initid->max_length = reslen;
- return false;
-} // end of Json_Object_init
+ initid->ptr = (char*)g;
+ return false;
+} // end of JsonInit
+
+/*********************************************************************************/
+/* Check if a path was specified and set jvp according to it. */
+/*********************************************************************************/
+static my_bool CheckPath(PGLOBAL g, UDF_ARGS *args, PJSON jsp, PJVAL& jvp, int n)
+{
+ for (uint i = n; i < args->arg_count; i++)
+ if (args->arg_type[i] == STRING_RESULT && args->args[i]) {
+ // A path to a subset of the json tree is given
+ char *path = MakePSZ(g, args, i);
-/***********************************************************************/
-/* Returns true if the argument is a JSON string. */
-/***********************************************************************/
-static my_bool IsJson(UDF_ARGS *args, int i)
+ if (path) {
+ PJSNX jsx = new(g)JSNX(g, jsp, TYPE_STRING);
+
+ if (jsx->SetJpath(g, path))
+ return true;
+
+ if (!(jvp = jsx->GetJson(g))) {
+ sprintf(g->Message, "No sub-item at '%s'", path);
+ return true;
+ } // endif jvp
+
+ } else {
+ strcpy(g->Message, "Path argument is null");
+ return true;
+ } // endif path
+
+ break;
+ } // endif type
+
+ return false;
+} // end of CheckPath
+
+/*********************************************************************************/
+/* Make the result according to the first argument type. */
+/*********************************************************************************/
+static char *MakeResult(PGLOBAL g, UDF_ARGS *args, PJSON top, uint n = 2)
{
- return (args->arg_type[i] == STRING_RESULT &&
- !strnicmp(args->attributes[i], "Json_", 5));
+ char *str;
+
+ if (IsJson(args, 0) == 2) {
+ // Make the change in the json file
+ int pretty = 2;
+
+ for (uint i = n; i < args->arg_count; i++)
+ if (args->arg_type[i] == INT_RESULT) {
+ pretty = (int)*(longlong*)args->args[i];
+ break;
+ } // endif type
+
+ if (!Serialize(g, top, MakePSZ(g, args, 0), pretty))
+ PUSH_WARNING(g->Message);
+
+ str = NULL;
+ } else if (IsJson(args, 0) == 3) {
+ PBSON bsp = (PBSON)args->args[0];
+
+ if (bsp->Filename) {
+ // Make the change in the json file
+ if (!Serialize(g, top, bsp->Filename, bsp->Pretty))
+ PUSH_WARNING(g->Message);
+
+ str = bsp->Filename;
+ } else if (!(str = Serialize(g, top, NULL, 0)))
+ PUSH_WARNING(g->Message);
+
+ SetChanged(bsp);
+ } else if (!(str = Serialize(g, top, NULL, 0)))
+ PUSH_WARNING(g->Message);
+
+ return str;
+} // end of MakeResult
+
+/*********************************************************************************/
+/* Make the binary result according to the first argument type. */
+/*********************************************************************************/
+static PBSON MakeBinResult(PGLOBAL g, UDF_ARGS *args, PJSON top, ulong len, int n = 2)
+{
+ PBSON bsnp = JbinAlloc(g, args, len, top);
+
+ if (!bsnp)
+ return NULL;
+
+ if (IsJson(args, 0) == 2) {
+ int pretty = 0;
+
+ for (uint i = n; i < args->arg_count; i++)
+ if (args->arg_type[i] == INT_RESULT) {
+ pretty = (int)*(longlong*)args->args[i];
+ break;
+ } // endif type
+
+ bsnp->Pretty = pretty;
+
+ if (bsnp->Filename = (char*)args->args[0]) {
+ bsnp->Filename = MakePSZ(g, args, 0);
+ strncpy(bsnp->Msg, bsnp->Filename, BMX);
+ } else
+ strncpy(bsnp->Msg, "null filename", BMX);
+
+ } else if (IsJson(args, 0) == 3) {
+ PBSON bsp = (PBSON)args->args[0];
+
+ if (bsp->Filename) {
+ bsnp->Filename = bsp->Filename;
+ strncpy(bsnp->Msg, bsp->Filename, BMX);
+ bsnp->Pretty = bsp->Pretty;
+ } else
+ strcpy(bsnp->Msg, "Json Binary item");
+
+ } else
+ strcpy(bsnp->Msg, "Json Binary item");
+
+ return bsnp;
+} // end of MakeBinResult
+
+/*********************************************************************************/
+/* Returns a pointer to the first integer argument found from the nth argument. */
+/*********************************************************************************/
+static int *GetIntArgPtr(PGLOBAL g, UDF_ARGS *args, uint& n)
+{
+ int *x = NULL;
+
+ for (uint i = n; i < args->arg_count; i++)
+ if (args->arg_type[i] == INT_RESULT) {
+ if (args->args[i]) {
+ x = (int*)PlugSubAlloc(g, NULL, sizeof(int));
+ *x = (int)*(longlong*)args->args[i];
+ } // endif args
+
+ n = i + 1;
+ break;
+ } // endif arg_type
+
+ return x;
+} // end of GetIntArgPtr
+
+/*********************************************************************************/
+/* Returns not 0 if the argument is a JSON item or file name. */
+/*********************************************************************************/
+static int IsJson(UDF_ARGS *args, uint i)
+{
+ int n = 0;
+
+ if (i >= args->arg_count || args->arg_type[i] != STRING_RESULT) {
+ } else if (!strnicmp(args->attributes[i], "Json_", 5)) {
+ if (!args->args[i] || strchr("[{ \t\r\n", *args->args[i]))
+ n = 1; // arg should be is a json item
+ else
+ n = 2; // A file name may have been returned
+
+ } else if (!strnicmp(args->attributes[i], "Jbin_", 5)) {
+ if (args->lengths[i] == sizeof(BSON))
+ n = 3; // arg is a binary json item
+ else
+ n = 2; // A file name may have been returned
+
+ } else if (!strnicmp(args->attributes[i], "Jfile_", 6))
+ n = 2; // arg is a json file name
+
+ return n;
} // end of IsJson
-/***********************************************************************/
-/* Calculate the reslen and memlen needed by a function. */
-/***********************************************************************/
+/*********************************************************************************/
+/* GetMemPtr: returns the memory pointer used by this argument. */
+/*********************************************************************************/
+static PGLOBAL GetMemPtr(PGLOBAL g, UDF_ARGS *args, uint i)
+{
+ return (IsJson(args, i) == 3) ? ((PBSON)args->args[i])->G : g;
+} // end of GetMemPtr
+
+/*********************************************************************************/
+/* GetFileLength: returns file size in number of bytes. */
+/*********************************************************************************/
+static long GetFileLength(char *fn)
+{
+ int h;
+ long len;
+
+ h= open(fn, _O_RDONLY);
+
+ if (h != -1) {
+ if ((len = _filelength(h)) < 0)
+ len = 0;
+
+ close(h);
+ } else
+ len = 0;
+
+ return len;
+} // end of GetFileLength
+
+/*********************************************************************************/
+/* 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& reslen, unsigned long& memlen,
+ my_bool mod = false)
{
- unsigned long i, k;
+ char fn[_MAX_PATH];
+ unsigned long i, k, m, n;
+ long fl, j = -1;
+
reslen = args->arg_count + 2;
// Calculate the result max length
for (i = 0; i < args->arg_count; i++) {
+ n = IsJson(args, i);
+
if (obj) {
if (!(k = args->attribute_lengths[i]))
k = strlen(args->attributes[i]);
@@ -112,9 +1309,22 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
switch (args->arg_type[i]) {
case STRING_RESULT:
- if (IsJson(args, i))
+ if (n == 2 && args->args[i]) {
+ if (!mod) {
+ m = MY_MIN(args->lengths[i], sizeof(fn) - 1);
+ memcpy(fn, args->args[i], m);
+ fn[m] = 0;
+ j = i;
+ fl = GetFileLength(fn);
+ reslen += fl;
+ } else
+ reslen += args->lengths[i];
+
+ } else if (n == 3 && args->args[i])
+ reslen += ((PBSON)args->args[i])->Reslen;
+ else if (n == 1)
reslen += args->lengths[i];
- else
+ else
reslen += (args->lengths[i] + 1) * 2; // Pessimistic !
break;
@@ -129,7 +1339,6 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
break;
case TIME_RESULT:
case ROW_RESULT:
- case IMPOSSIBLE_RESULT:
default:
// What should we do here ?
break;
@@ -137,11 +1346,12 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
} // endfor i
- // Calculate the amount of memory needed
- memlen = MEMFIX + sizeof(JOUTSTR) + reslen;
+ // 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));
+ n = IsJson(args, i);
+ memlen += (args->lengths[i] + sizeof(JVALUE));
if (obj) {
if (!(k = args->attribute_lengths[i]))
@@ -151,11 +1361,26 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
} else
memlen += sizeof(JARRAY);
- switch (args->arg_type[i]) {
- case STRING_RESULT:
- if (IsJson(args, i))
- memlen += args->lengths[i] * 5; // Estimate parse memory
-
+ switch (args->arg_type[i]) {
+ case STRING_RESULT:
+ if (n == 2 && args->args[i]) {
+ if ((signed)i != j) {
+ m = MY_MIN(args->lengths[i], sizeof(fn) - 1);
+ memcpy(fn, args->args[i], m);
+ fn[m] = 0;
+ j = -1;
+ fl = GetFileLength(fn);
+ } // endif i
+
+ memlen += fl * M;
+ } else if (n == 1) {
+ if (i == 0)
+ memlen += sizeof(BSON); // For Jbin functions
+
+ memlen += args->lengths[i] * M; // Estimate parse memory
+ } else if (n == 3)
+ memlen += sizeof(JVALUE);
+
memlen += sizeof(TYPVAL<PSZ>);
break;
case INT_RESULT:
@@ -167,7 +1392,6 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
break;
case TIME_RESULT:
case ROW_RESULT:
- case IMPOSSIBLE_RESULT:
default:
// What should we do here ?
break;
@@ -175,15 +1399,68 @@ static my_bool CalcLen(UDF_ARGS *args, my_bool obj,
} // endfor i
- return false;
+ return false;
} // end of CalcLen
-/***********************************************************************/
-/* Make a zero terminated string from the passed argument. */
-/***********************************************************************/
+/*********************************************************************************/
+/* Check if the calculated memory is enough. */
+/*********************************************************************************/
+static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n,
+ my_bool m, my_bool obj = false, my_bool mod = false)
+{
+ unsigned long rl, ml;
+ my_bool b = false;
+
+ n = MY_MIN(n, args->arg_count);
+
+ for (uint i = 0; i < n; i++)
+ if (IsJson(args, i) == 2 ||
+ (b = (m && !i && args->arg_type[0] == STRING_RESULT && !IsJson(args, 0)))) {
+ if (CalcLen(args, obj, rl, ml, mod))
+ return true;
+ else if (b) {
+ ulong len;
+ char *p = args->args[0];
+
+ // Is this a file name?
+ if (!strchr("[{ \t\r\n", *p) && (len = GetFileLength(p)))
+ ml += len * (M + 1);
+ else
+ ml += args->lengths[0] * M;
+
+ } // endif b
+
+ if (ml > g->Sarea_Size) {
+ free(g->Sarea);
+
+ if (!(g->Sarea = PlugAllocMem(g, ml))) {
+ char errmsg[256];
+
+ sprintf(errmsg, MSG(WORK_AREA), g->Message);
+ strcpy(g->Message, errmsg);
+ g->Sarea_Size = 0;
+ return true;
+ } // endif Alloc
+
+ g->Sarea_Size = ml;
+ g->Createas = 0;
+ g->Xchk = NULL;
+ initid->max_length = rl;
+ } // endif Size
+
+ break;
+ } // endif IsJson
+
+ JsonSubSet(g);
+ return false;
+} // end of CheckMemory
+
+/*********************************************************************************/
+/* Make a zero terminated string from the passed argument. */
+/*********************************************************************************/
static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i)
{
- if (args->args[i]) {
+ if (args->arg_count > (unsigned)i && args->args[i]) {
int n = args->lengths[i];
PSZ s = (PSZ)PlugSubAlloc(g, NULL, n + 1);
@@ -195,73 +1472,205 @@ static PSZ MakePSZ(PGLOBAL g, UDF_ARGS *args, int i)
} // end of MakePSZ
-/***********************************************************************/
-/* Make a valid key from the passed argument. */
-/***********************************************************************/
+/*********************************************************************************/
+/* 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;
+ if (args->arg_count > (unsigned)i) {
+ int j = 0, n = args->attribute_lengths[i];
+ my_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 (IsJson(args, i))
+ j = strchr(s, '_') - s + 1;
+
+ if (j && n > j) {
+ s += j;
+ n -= j;
+ } 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;
+ } // endif count
+
+ return "Key";
} // end of MakeKey
-/***********************************************************************/
-/* Make a JSON value from the passed argument. */
-/***********************************************************************/
-static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i)
+/*********************************************************************************/
+/* Parse a json file. */
+/*********************************************************************************/
+static PJSON ParseJsonFile(PGLOBAL g, char *fn, int *pretty, int& len)
+{
+ char *memory;
+ HANDLE hFile;
+ MEMMAP mm;
+ PJSON jsp;
+
+ /*******************************************************************************/
+ /* Create the mapping file object. */
+ /*******************************************************************************/
+ hFile = CreateFileMap(g, fn, &mm, MODE_READ, false);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ DWORD rc = GetLastError();
+
+ if (!(*g->Message))
+ sprintf(g->Message, MSG(OPEN_MODE_ERROR), "map", (int)rc, 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 or deleted file
+ CloseFileHandle(hFile);
+ return NULL;
+ } // endif len
+
+ if (!memory) {
+ CloseFileHandle(hFile);
+ sprintf(g->Message, MSG(MAP_VIEW_ERROR), fn, GetLastError());
+ return NULL;
+ } // endif Memory
+
+ CloseFileHandle(hFile); // Not used anymore
+
+ /*********************************************************************************/
+ /* Parse the json file and allocate its tree structure. */
+ /*********************************************************************************/
+ g->Message[0] = 0;
+ jsp = ParseJson(g, memory, len, pretty);
+ CloseMemMap(memory, len);
+ return jsp;
+} // end of ParseJsonFile
+
+/*********************************************************************************/
+/* Return a json file contains. */
+/*********************************************************************************/
+static char *GetJsonFile(PGLOBAL g, char *fn)
+{
+ char *str;
+ int h, n, len;
+
+#if defined(UNIX) || defined(UNIV_LINUX)
+ h= open(fn, O_RDONLY);
+#else
+ h= open(fn, _O_RDONLY, _O_TEXT);
+#endif
+
+ if (h == -1) {
+ sprintf(g->Message, "Error %d opening %s", errno, fn);
+ return NULL;
+ } // endif h
+
+ if ((len = _filelength(h)) < 0) {
+ sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn);
+ close(h);
+ return NULL;
+ } // endif len
+
+ str = (char*)PlugSubAlloc(g, NULL, len + 1);
+
+ if ((n = read(h, str, len)) < 0) {
+ sprintf(g->Message, "Error %d reading %d bytes from %s", errno, len, fn);
+ return NULL;
+ } // endif n
+
+ str[n] = 0;
+ close(h);
+ return str;
+} // end of GetJsonFile
+
+/*********************************************************************************/
+/* Make a JSON value from the passed argument. */
+/*********************************************************************************/
+static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, uint i, PJSON *top = NULL)
{
- char *sap = (args->arg_count > (unsigned)i) ? args->args[i] : NULL;
+ char *sap = (args->arg_count > i) ? args->args[i] : NULL;
+ int n, len;
+ short c;
+ long long bigint;
PJSON jsp;
PJVAL jvp = new(g) JVALUE;
+ if (top)
+ *top = NULL;
+
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 ((len = args->lengths[i])) {
+ if ((n = IsJson(args, i)) < 3)
+ sap = MakePSZ(g, args, i);
+
+ if (n) {
+ if (n == 3) {
+ if (top)
+ *top = ((PBSON)sap)->Top;
+
+ jsp = ((PBSON)sap)->Jsp;
+ } else {
+ if (n == 2) {
+ if (!(sap = GetJsonFile(g, sap))) {
+ PUSH_WARNING(g->Message);
+ return jvp;
+ } // endif sap
+
+ len = strlen(sap);
+ } // endif n
+
+ if (!(jsp = ParseJson(g, sap, strlen(sap))))
+ PUSH_WARNING(g->Message);
+ else if (top)
+ *top = jsp;
+
+ } // endif's n
if (jsp && jsp->GetType() == TYPE_JVAL)
jvp = (PJVAL)jsp;
else
jvp->SetValue(jsp);
- } else
- jvp->SetString(g, MakePSZ(g, args, i));
+ } else {
+ c = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1;
+ jvp->SetString(g, sap, c);
+ } // endif n
- } // endif str
+ } // endif len
break;
case INT_RESULT:
- jvp->SetInteger(g, (int)*(long long*)sap);
+ bigint = *(long long*)sap;
+
+ if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) ||
+ (bigint == 1LL && !strcmp(args->attributes[i], "TRUE")))
+ jvp->SetTiny(g, (char)bigint);
+ else
+ jvp->SetBigint(g, bigint);
+
break;
case REAL_RESULT:
jvp->SetFloat(g, *(double*)sap);
@@ -271,7 +1680,6 @@ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i)
break;
case TIME_RESULT:
case ROW_RESULT:
- case IMPOSSIBLE_RESULT:
default:
break;
} // endswitch arg_type
@@ -279,291 +1687,732 @@ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i)
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)
+/*********************************************************************************/
+/* Make a Json value containing the parameter. */
+/*********************************************************************************/
+my_bool jsonvalue_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");
+ strcpy(message, "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
+ return JsonInit(initid, args, message, false, reslen, memlen);
+} // end of jsonvalue_init
-char *Json_Value(UDF_INIT *initid, UDF_ARGS *args, char *result,
+char *jsonvalue(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
- PJVAL jvp;
PGLOBAL g = (PGLOBAL)initid->ptr;
- PlugSubSet(g, g->Sarea, g->Sarea_Size);
- jvp = MakeValue(g, args, 0);
+ if (!g->Xchk) {
+ if (!CheckMemory(g, initid, args, 1, false)) {
+ PJVAL jvp = MakeValue(g, args, 0);
- if (!(str = Serialize(g, jvp, NULL, 0)))
- str = strcpy(result, g->Message);
+ if (!(str = Serialize(g, jvp, NULL, 0)))
+ str = strcpy(result, g->Message);
- *res_length = strlen(str);
+ } else
+ str = strcpy(result, g->Message);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else
+ str = (char*)g->Xchk;
+
+ *res_length = strlen(str);
return str;
-} // end of Json_Value
+} // end of JsonValue
-void Json_Value_deinit(UDF_INIT* initid)
+void jsonvalue_deinit(UDF_INIT* initid)
{
- PlugExit((PGLOBAL)initid->ptr);
-} // end of Json_Value_deinit
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jsonvalue_deinit
-/***********************************************************************/
-/* Make a Json array containing all the parameters. */
-/***********************************************************************/
-my_bool Json_Array_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+/*********************************************************************************/
+/* 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
+ return JsonInit(initid, args, message, false, reslen, memlen);
+} // end of json_array_init
-char *Json_Array(UDF_INIT *initid, UDF_ARGS *args, char *result,
- unsigned long *res_length, char *, char *)
+char *json_array(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *, char *)
{
char *str;
- uint i;
- PJAR arp;
PGLOBAL g = (PGLOBAL)initid->ptr;
- PlugSubSet(g, g->Sarea, g->Sarea_Size);
- arp = new(g) JARRAY;
+ if (!g->Xchk) {
+ if (!CheckMemory(g, initid, args, args->arg_count, false)) {
+ PJAR arp = new(g)JARRAY;
- for (i = 0; i < args->arg_count; i++)
- arp->AddValue(g, MakeValue(g, args, i));
+ for (uint i = 0; i < args->arg_count; i++)
+ arp->AddValue(g, MakeValue(g, args, i));
- arp->InitArray(g);
+ arp->InitArray(g);
- if (!(str = Serialize(g, arp, NULL, 0)))
- str = strcpy(result, g->Message);
+ if (!(str = Serialize(g, arp, NULL, 0)))
+ str = strcpy(result, g->Message);
- *res_length = strlen(str);
+ } else
+ str = strcpy(result, g->Message);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else
+ str = (char*)g->Xchk;
+
+ *res_length = strlen(str);
return str;
-} // end of Json_Array
+} // end of json_array
-void Json_Array_deinit(UDF_INIT* initid)
+void json_array_deinit(UDF_INIT* initid)
{
- PlugExit((PGLOBAL)initid->ptr);
-} // end of Json_Array_deinit
+ JsonFreeMem((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)
+/*********************************************************************************/
+/* Add one or several values to a Json array. */
+/*********************************************************************************/
+my_bool json_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json string or item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_array_add_values_init
+
+char *json_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *)
+{
+ char *str = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (!g->Xchk) {
+ if (!CheckMemory(g, initid, args, args->arg_count, true)) {
+ char *p;
+ PJSON top;
+ PJAR arp;
+ PJVAL jvp = MakeValue(g, args, 0, &top);
+
+ if ((p = jvp->GetString())) {
+ if (!(top = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ return NULL;
+ } // endif jsp
+
+ jvp->SetValue(top);
+ } // endif p
+
+ 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);
+// str = Serialize(g, arp, NULL, 0);
+ str = MakeResult(g, args, top, args->arg_count);
+ } // endif CheckMemory
+
+ if (!str) {
+ PUSH_WARNING(g->Message);
+ str = args->args[0];
+ } // endif str
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else
+ str = (char*)g->Xchk;
+
+ if (!str) {
+ *res_length = 0;
+ *is_null = 1;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_array_add_values
+
+void json_array_add_values_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_array_add_values_deinit
+
+/*********************************************************************************/
+/* Add one value 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");
+ if (args->arg_count < 2) {
+ strcpy(message, "This function 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");
+ strcpy(message, "First argument must be a json item");
return true;
- } else
- CalcLen(args, false, reslen, memlen);
+ } else
+ CalcLen(args, false, reslen, memlen, true);
- return JsonInit(initid, message, reslen, memlen);
-} // end of Json_Array_Add_init
+ return JsonInit(initid, args, message, true, 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 *, char *)
+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;
+ char *str = NULL;
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)
+ if (g->Xchk) {
+ // This constant function was recalled
+ str = (char*)g->Xchk;
+ goto fin;
+ } // endif Xchk
+
+ if (!CheckMemory(g, initid, args, 2, false, false, true)) {
+ int *x;
+ uint n = 2;
+ PJSON jsp, top;
+ PJVAL jvp;
+ PJAR arp;
+
+ jvp = MakeValue(g, args, 0, &top);
+ jsp = jvp->GetJson();
+ x = GetIntArgPtr(g, args, n);
+
+ if (CheckPath(g, args, jsp, jvp, 2))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JAR) {
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ arp = jvp->GetArray();
+ arp->AddValue(gb, MakeValue(gb, args, 1), x);
+ arp->InitArray(gb);
+ str = MakeResult(g, args, top, n);
+ } else {
+ PUSH_WARNING("First argument target is not an array");
+// if (g->Mrr) *error = 1; (only if no path)
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error or file, return unchanged argument
+ if (!str)
+ str = MakePSZ(g, args, 0);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = str;
+
+ fin:
+ if (!str) {
+ *res_length = 0;
+ *is_null = 1;
+ *error = 1;
+ } else
+ *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
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_array_add_deinit
-/***********************************************************************/
-/* Delete a value from a Json array. */
-/***********************************************************************/
-my_bool Json_Array_Delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+/*********************************************************************************/
+/* Delete a value from a Json array. */
+/*********************************************************************************/
+my_bool json_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
unsigned long reslen, memlen;
- if (args->arg_count != 2) {
- strcpy(message, "Json_Value_Delete must have 2 arguments");
- return true;
- } else if (!IsJson(args, 0)) {
- strcpy(message, "Json_Value_Delete first argument must be a json item");
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
return true;
- } else
- CalcLen(args, false, reslen, memlen);
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen, true);
- return JsonInit(initid, message, reslen, memlen);
-} // end of Json_Array_Delete_init
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_array_delete_init
-char *Json_Array_Delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
- unsigned long *res_length, char *, char *)
+char *json_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
{
- char *str;
- int n;
- PJVAL jvp;
- PJAR arp;
+ char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
- PlugSubSet(g, g->Sarea, g->Sarea_Size);
- jvp = MakeValue(g, args, 0);
-
- if (jvp->GetValType() != TYPE_JAR) {
- push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
- "First argument is not an array");
- str = args->args[0];
- } else if (args->arg_type[1] != INT_RESULT) {
- push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0,
- "Second argument is not an integer");
- str = args->args[0];
- } else {
- n = *(int*)args->args[1];
- arp = jvp->GetArray();
- arp->DeleteValue(n);
- arp->InitArray(g);
-
- if (!(str = Serialize(g, arp, NULL, 0))) {
- str = strcpy(result, g->Message);
- push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, str);
- } // endif str
-
- } // endif's
-
- *res_length = strlen(str);
- return str;
-} // end of Json_Array_Delete
-
-void Json_Array_Delete_deinit(UDF_INIT* initid)
+ if (g->Xchk) {
+ // This constant function was recalled
+ str = (char*)g->Xchk;
+ goto fin;
+ } // endif Xchk
+
+ if (!CheckMemory(g, initid, args, 1, false, false, true)) {
+ int *x;
+ uint n = 1;
+ PJSON top;
+ PJAR arp;
+ PJVAL jvp = MakeValue(g, args, 0, &top);
+
+ if (!(x = GetIntArgPtr(g, args, n)))
+ PUSH_WARNING("Missing or null array index");
+ else if (CheckPath(g, args, jvp->GetJson(), jvp, 1))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JAR) {
+ arp = jvp->GetArray();
+ arp->DeleteValue(*x);
+ arp->InitArray(GetMemPtr(g, args, 0));
+ str = MakeResult(g, args, top, n);
+ } else {
+ PUSH_WARNING("First argument target is not an array");
+// if (g->Mrr) *error = 1;
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error or file, return unchanged argument
+ if (!str)
+ str = MakePSZ(g, args, 0);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = str;
+
+ fin:
+ if (!str) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_array_delete
+
+void json_array_delete_deinit(UDF_INIT* initid)
{
- PlugExit((PGLOBAL)initid->ptr);
-} // end of Json_Array_Delete_deinit
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_array_delete_deinit
-/***********************************************************************/
-/* Make a Json Oject containing all the parameters. */
-/***********************************************************************/
-my_bool Json_Object_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+/*********************************************************************************/
+/* Make a Json Object 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
+ return JsonInit(initid, args, message, false, reslen, memlen);
+} // end of json_object_init
-char *Json_Object(UDF_INIT *initid, UDF_ARGS *args, char *result,
+char *json_object(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *res_length, char *, char *)
{
- char *str;
- uint i;
- PJOB objp;
+ char *str = NULL;
PGLOBAL g = (PGLOBAL)initid->ptr;
- PlugSubSet(g, g->Sarea, g->Sarea_Size);
- objp = new(g) JOBJECT;
+ if (!g->Xchk) {
+ if (!CheckMemory(g, initid, args, args->arg_count, false, false, true)) {
+ PJOB objp = new(g)JOBJECT;
- for (i = 0; i < args->arg_count; i++)
- objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i));
+ for (uint 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);
+ str = Serialize(g, objp, NULL, 0);
+ } // endif CheckMemory
- *res_length = strlen(str);
+ if (!str)
+ str = strcpy(result, g->Message);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else
+ str = (char*)g->Xchk;
+
+ *res_length = strlen(str);
return str;
-} // end of Json_Object
+} // end of json_object
-void Json_Object_deinit(UDF_INIT* initid)
+void json_object_deinit(UDF_INIT* initid)
{
- PlugExit((PGLOBAL)initid->ptr);
-} // end of Json_Object_deinit
+ JsonFreeMem((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,
+/*********************************************************************************/
+/* Make a Json Object 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
+ return JsonInit(initid, args, message, false, 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 *, char *)
+char *json_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *, char *)
{
char *str;
- uint i;
- PJOB objp;
- PJVAL jvp;
PGLOBAL g = (PGLOBAL)initid->ptr;
- PlugSubSet(g, g->Sarea, g->Sarea_Size);
- objp = new(g) JOBJECT;
+ if (!g->Xchk) {
+ if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
+ PJVAL jvp;
+ PJOB 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));
+ for (uint 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);
+ str = Serialize(g, objp, NULL, 0);
+ } // endif CheckMemory
- *res_length = strlen(str);
+ if (!str)
+ str = strcpy(result, g->Message);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else
+ str = (char*)g->Xchk;
+
+ *res_length = strlen(str);
return str;
-} // end of Json_Object_Nonull
+} // end of json_object_nonull
-void Json_Object_Nonull_deinit(UDF_INIT* initid)
+void json_object_nonull_deinit(UDF_INIT* initid)
{
- PlugExit((PGLOBAL)initid->ptr);
-} // end of Json_Object_nonull_deinit
+ JsonFreeMem((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)
+/*********************************************************************************/
+/* Make a Json Object containing all the key/value parameters. */
+/*********************************************************************************/
+my_bool json_object_key_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count % 2) {
+ strcpy(message, "This function must have an even number of arguments");
+ return true;
+ } // endif arg_count
+
+ CalcLen(args, true, reslen, memlen);
+ return JsonInit(initid, args, message, false, reslen, memlen);
+} // end of json_object_key_init
+
+char *json_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *, char *)
+{
+ char *str = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (!g->Xchk) {
+ if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
+ PJOB objp = new(g)JOBJECT;
+
+ for (uint i = 0; i < args->arg_count; i += 2)
+ objp->SetValue(g, MakeValue(g, args, i+1), MakePSZ(g, args, i));
+
+ str = Serialize(g, objp, NULL, 0);
+ } // endif CheckMemory
+
+ if (!str)
+ str = strcpy(result, g->Message);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else
+ str = (char*)g->Xchk;
+
+ *res_length = strlen(str);
+ return str;
+} // end of json_object_key
+
+void json_object_key_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_object_key_deinit
+
+/*********************************************************************************/
+/* Add or replace a value in a Json Object. */
+/*********************************************************************************/
+my_bool json_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, true, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_object_add_init
+
+char *json_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *key, *str = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->Xchk) {
+ // This constant function was recalled
+ str = (char*)g->Xchk;
+ goto fin;
+ } // endif Xchk
+
+ if (!CheckMemory(g, initid, args, 2, false, true, true)) {
+ PJOB jobp;
+ PJVAL jvp;
+ PJSON jsp, top;
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ jvp = MakeValue(g, args, 0, &top);
+ jsp = jvp->GetJson();
+
+ if (CheckPath(g, args, jsp, jvp, 2))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JOB) {
+ jobp = jvp->GetObject();
+ jvp = MakeValue(gb, args, 1);
+ key = MakeKey(gb, args, 1);
+ jobp->SetValue(gb, jvp, key);
+ str = MakeResult(g, args, top);
+ } else {
+ PUSH_WARNING("First argument target is not an object");
+// if (g->Mrr) *error = 1; (only if no path)
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error or file, return unchanged argument
+ if (!str)
+ str = MakePSZ(g, args, 0);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = str;
+
+ fin:
+ if (!str) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_object_add
+
+void json_object_add_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_object_add_deinit
+
+/*********************************************************************************/
+/* Delete a value from a Json object. */
+/*********************************************************************************/
+my_bool json_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have 2 or 3 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument must be a key string");
+ return true;
+ } else
+ CalcLen(args, true, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_object_delete_init
+
+char *json_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->Xchk) {
+ // This constant function was recalled
+ str = (char*)g->Xchk;
+ goto fin;
+ } // endif Xchk
+
+ if (!CheckMemory(g, initid, args, 1, false, true, true)) {
+ char *key;
+ PJOB jobp;
+ PJSON jsp, top;
+ PJVAL jvp = MakeValue(g, args, 0, &top);
+
+ jsp = jvp->GetJson();
+
+ if (CheckPath(g, args, jsp, jvp, 2))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JOB) {
+ key = MakeKey(GetMemPtr(g, args, 0), args, 1);
+ jobp = jvp->GetObject();
+ jobp->DeleteKey(key);
+ str = MakeResult(g, args, top);
+ } else {
+ PUSH_WARNING("First argument target is not an object");
+// if (g->Mrr) *error = 1; (only if no path)
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error or file, return unchanged argument
+ if (!str)
+ str = MakePSZ(g, args, 0);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = str;
+
+ fin:
+ if (!str) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_object_delete
+
+void json_object_delete_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_object_delete_deinit
+
+/*********************************************************************************/
+/* Returns an array of the Json object keys. */
+/*********************************************************************************/
+my_bool json_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count != 1) {
+ strcpy(message, "This function must have 1 argument");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "Argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_object_list_init
+
+char *json_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (!g->N) {
+ if (!CheckMemory(g, initid, args, 1, true, true)) {
+ char *p;
+ PJSON jsp;
+ PJVAL jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ return NULL;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (jsp->GetType() == TYPE_JOB) {
+ PJAR jarp = ((PJOB)jsp)->GetKeyList(g);
+
+ if (!(str = Serialize(g, jarp, NULL, 0)))
+ PUSH_WARNING(g->Message);
+
+ } else {
+ PUSH_WARNING("First argument is not an object");
+ if (g->Mrr) *error = 1;
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ if (initid->const_item) {
+ // Keep result of constant function
+ g->Xchk = str;
+ g->N = 1; // str can be NULL
+ } // endif const_item
+
+ } else
+ str = (char*)g->Xchk;
+
+ if (!str) {
+ *is_null = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_object_list
+
+void json_object_list_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_object_list_deinit
+
+/*********************************************************************************/
+/* Make a Json array from values coming 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");
+ strcpy(message, "This function can only accept 1 argument");
return true;
- } else
+ } else if (IsJson(args, 0) == 3) {
+ strcpy(message, "This function does not support Jbin arguments");
+ return true;
+ } else
CalcLen(args, false, reslen, memlen);
reslen *= n;
memlen += ((memlen - MEMFIX) * (n - 1));
- if (JsonInit(initid, message, reslen, memlen))
+ if (JsonInit(initid, args, message, false, reslen, memlen))
return true;
PGLOBAL g = (PGLOBAL)initid->ptr;
@@ -572,9 +2421,9 @@ my_bool Json_Array_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
g->Activityp = (PACTIVITY)new(g) JARRAY;
g->N = (int)n;
return false;
-} // end of Json_Array_Grp_init
+} // end of json_array_grp_init
-void Json_Array_Grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*)
+void json_array_grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*)
{
PGLOBAL g = (PGLOBAL)initid->ptr;
PJAR arp = (PJAR)g->Activityp;
@@ -582,9 +2431,9 @@ void Json_Array_Grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*)
if (g->N-- > 0)
arp->AddValue(g, MakeValue(g, args, 0));
-} // end of Json_Array_Grp_add
+} // end of json_array_grp_add
-char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *, char *result,
+char *json_array_grp(UDF_INIT *initid, UDF_ARGS *, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
@@ -592,8 +2441,7 @@ char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *, char *result,
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");
+ PUSH_WARNING("Result truncated to json_grp_size values");
arp->InitArray(g);
@@ -602,39 +2450,42 @@ char *Json_Array_Grp(UDF_INIT *initid, UDF_ARGS *, char *result,
*res_length = strlen(str);
return str;
-} // end of Json_Array_Grp
+} // end of json_array_grp
-void Json_Array_Grp_clear(UDF_INIT *initid, char*, char*)
+void json_array_grp_clear(UDF_INIT *initid, char*, char*)
{
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
+} // end of json_array_grp_clear
-void Json_Array_Grp_deinit(UDF_INIT* initid)
+void json_array_grp_deinit(UDF_INIT* initid)
{
- PlugExit((PGLOBAL)initid->ptr);
-} // end of Json_Array_Grp_deinit
+ JsonFreeMem((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)
+/*********************************************************************************/
+/* Make a Json object from values coming 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");
+ if (args->arg_count != 2) {
+ strcpy(message, "This function requires 2 arguments (key, value)");
return true;
- } else
+ } else if (IsJson(args, 0) == 3) {
+ strcpy(message, "This function does not support Jbin arguments");
+ return true;
+ } else
CalcLen(args, true, reslen, memlen);
reslen *= n;
memlen += ((memlen - MEMFIX) * (n - 1));
- if (JsonInit(initid, message, reslen, memlen))
+ if (JsonInit(initid, args, message, false, reslen, memlen))
return true;
PGLOBAL g = (PGLOBAL)initid->ptr;
@@ -643,19 +2494,19 @@ my_bool Json_Object_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
g->Activityp = (PACTIVITY)new(g) JOBJECT;
g->N = (int)n;
return false;
-} // end of Json_Object_Grp_init
+} // end of json_object_grp_init
-void Json_Object_Grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*)
+void json_object_grp_add(UDF_INIT *initid, UDF_ARGS *args, char*, char*)
{
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));
+ if (g->N-- > 0)
+ objp->SetValue(g, MakeValue(g, args, 1), MakePSZ(g, args, 0));
-} // end of Json_Object_Grp_add
+} // end of json_object_grp_add
-char *Json_Object_Grp(UDF_INIT *initid, UDF_ARGS *, char *result,
+char *json_object_grp(UDF_INIT *initid, UDF_ARGS *, char *result,
unsigned long *res_length, char *, char *)
{
char *str;
@@ -663,28 +2514,2439 @@ char *Json_Object_Grp(UDF_INIT *initid, UDF_ARGS *, char *result,
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");
+ PUSH_WARNING("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
+} // end of json_object_grp
-void Json_Object_Grp_clear(UDF_INIT *initid, char*, char*)
+void json_object_grp_clear(UDF_INIT *initid, char*, char*)
{
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
+} // end of json_object_grp_clear
+
+void json_object_grp_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_object_grp_deinit
+
+/*********************************************************************************/
+/* Merge two arrays or objects. */
+/*********************************************************************************/
+my_bool json_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0)) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (!IsJson(args, 1)) {
+ strcpy(message, "Second argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen, true);
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_item_merge_init
+
+char *json_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->Xchk) {
+ // This constant function was recalled
+ str = (char*)g->Xchk;
+ goto fin;
+ } // endif Xchk
+
+ if (!CheckMemory(g, initid, args, 2, false, false, true)) {
+ PJSON top;
+ PJVAL jvp;
+ PJSON jsp[2] = {NULL, NULL};
+
+ for (int i = 0; i < 2; i++) {
+ jvp = MakeValue(g, args, i);
+ if (!i) top = jvp->GetJson();
+
+ if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) {
+ sprintf(g->Message, "Argument %d is not an array or object", i);
+ PUSH_WARNING(g->Message);
+ } else
+ jsp[i] = jvp->GetJsp();
+
+ } // endfor i
+
+ if (jsp[0]) {
+ if (jsp[0]->Merge(GetMemPtr(g, args, 0), jsp[1]))
+ PUSH_WARNING(GetMemPtr(g, args, 0)->Message);
+ else
+ str = MakeResult(g, args, top);
+
+ } // endif jsp
+
+ } // endif CheckMemory
+
+ // In case of error or file, return unchanged argument
+ if (!str)
+ str = MakePSZ(g, args, 0);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = str;
+
+ fin:
+ if (!str) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_item_merge
+
+void json_item_merge_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_item_merge_deinit
+
+/*********************************************************************************/
+/* Get a Json item from a Json document. */
+/*********************************************************************************/
+my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+ int n = IsJson(args, 0);
+
+ if (args->arg_count < 2) {
+ strcpy(message, "This function must have at least 2 arguments");
+ return true;
+ } else if (!n && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument is not a string (jpath)");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ if (n == 2 && args->args[0]) {
+ char fn[_MAX_PATH];
+ long fl;
+
+ memcpy(fn, args->args[0], args->lengths[0]);
+ fn[args->lengths[0]] = 0;
+ fl = GetFileLength(fn);
+ memlen += fl * 3;
+ } else if (n != 3)
+ memlen += args->lengths[0] * 3;
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_get_item_init
+
+char *json_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *)
+{
+ char *p, *path, *str = NULL;
+ PJSON jsp;
+ PJVAL jvp;
+ PJSNX jsx;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ str = (char*)g->Activityp;
+ goto fin;
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true, true)) {
+ PUSH_WARNING("CheckMemory error");
+ goto fin;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ return NULL;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ path = MakePSZ(g, args, 1);
+ jsx = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length);
+
+ if (jsx->SetJpath(g, path, true)) {
+ PUSH_WARNING(g->Message);
+ *is_null = 1;
+ return NULL;
+ } // endif SetJpath
+
+ jsx->ReadValue(g);
+
+ if (!jsx->GetValue()->IsNull())
+ str = jsx->GetValue()->GetCharValue();
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)str;
+
+ fin:
+ if (!str) {
+ *is_null = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_get_item
+
+void json_get_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_get_item_deinit
+
+/*********************************************************************************/
+/* Get a string value from a Json item. */
+/*********************************************************************************/
+my_bool jsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, more = 1024;
+ int n = IsJson(args, 0);
+
+ if (args->arg_count < 2) {
+ strcpy(message, "At least 2 arguments required");
+ return true;
+ } else if (!n && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument is not a string (jpath)");
+ return true;
+ } else if (args->arg_count > 2) {
+ if (args->arg_type[2] == INT_RESULT && args->args[2])
+ more += (unsigned long)*(long long*)args->args[2];
+ else
+ strcpy(message, "Third argument is not an integer (memory)");
+
+ } // endif's
+
+ CalcLen(args, false, reslen, memlen);
+ memlen += more;
+
+ if (n == 2 && args->args[0]) {
+ char fn[_MAX_PATH];
+ long fl;
+
+ memcpy(fn, args->args[0], args->lengths[0]);
+ fn[args->lengths[0]] = 0;
+ fl = GetFileLength(fn);
+ memlen += fl * 3;
+ } else if (n != 3)
+ memlen += args->lengths[0] * 3;
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jsonget_string_init
+
+char *jsonget_string(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *)
+{
+ char *p, *path, *str = NULL;
+ int rc;
+ PJSON jsp;
+ PJSNX jsx;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ str = (char*)g->Activityp;
+ goto fin;
+ } else if (initid->const_item)
+ g->N = 1;
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ PUSH_WARNING(MSG(TOO_MANY_JUMPS));
+ *is_null = 1;
+ return NULL;
+ } // endif jump_level
+
+ if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
+ PUSH_WARNING(g->Message);
+ str = NULL;
+ goto err;
+ } // endif rc
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true)) {
+ PUSH_WARNING("CheckMemory error");
+ goto err;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto err;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ path = MakePSZ(g, args, 1);
+ jsx = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length);
+
+ if (jsx->SetJpath(g, path)) {
+ PUSH_WARNING(g->Message);
+ goto err;
+ } // endif SetJpath
+
+ jsx->ReadValue(g);
+
+ if (!jsx->GetValue()->IsNull())
+ str = jsx->GetValue()->GetCharValue();
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)str;
+
+ err:
+ g->jump_level--;
+
+ fin:
+ if (!str) {
+ *is_null = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of jsonget_string
+
+void jsonget_string_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jsonget_string_deinit
+
+/*********************************************************************************/
+/* Get an integer value from a Json item. */
+/*********************************************************************************/
+my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count != 2) {
+ strcpy(message, "This function must have 2 arguments");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument is not a (jpath) string");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ if (IsJson(args, 0) != 3)
+ memlen += 1000; // TODO: calculate this
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jsonget_int_init
+
+long long jsonget_int(UDF_INIT *initid, UDF_ARGS *args,
+ char *is_null, char *error)
+{
+ char *p, *path;
+ long long n;
+ PJSON jsp;
+ PJSNX jsx;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ if (!g->Activityp) {
+ *is_null = 1;
+ return 0LL;
+ } else
+ return *(long long*)g->Activityp;
+
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true)) {
+ PUSH_WARNING("CheckMemory error");
+ if (g->Mrr) *error = 1;
+ *is_null = 1;
+ return 0LL;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ if (g->Mrr) *error = 1;
+ *is_null = 1;
+ return 0;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ path = MakePSZ(g, args, 1);
+ jsx = new(g) JSNX(g, jsp, TYPE_BIGINT);
+
+ if (jsx->SetJpath(g, path)) {
+ PUSH_WARNING(g->Message);
+ *is_null = 1;
+ return 0;
+ } // endif SetJpath
+
+ jsx->ReadValue(g);
+
+ if (jsx->GetValue()->IsNull()) {
+ *is_null = 1;
+ return 0;
+ } // endif IsNull
+
+ n = jsx->GetValue()->GetBigintValue();
+
+ if (initid->const_item) {
+ // Keep result of constant function
+ long long *np = (long long*)PlugSubAlloc(g, NULL, sizeof(long long));
+ *np = n;
+ g->Activityp = (PACTIVITY)np;
+ } // endif const_item
+
+ return n;
+} // end of jsonget_int
+
+void jsonget_int_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jsonget_int_deinit
+
+/*********************************************************************************/
+/* Get a double value from a Json item. */
+/*********************************************************************************/
+my_bool jsonget_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "At least 2 arguments required");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument is not a (jpath) string");
+ return true;
+ } else if (args->arg_count > 2) {
+ if (args->arg_type[2] != INT_RESULT) {
+ strcpy(message, "Third argument is not an integer (decimals)");
+ return true;
+ } else
+ initid->decimals = (uint)*(longlong*)args->args[2];
+
+ } else
+ initid->decimals = 15;
+
+ CalcLen(args, false, reslen, memlen);
+
+ if (IsJson(args, 0) != 3)
+ memlen += 1000; // TODO: calculate this
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jsonget_real_init
+
+double jsonget_real(UDF_INIT *initid, UDF_ARGS *args,
+ char *is_null, char *error)
+{
+ char *p, *path;
+ double d;
+ PJSON jsp;
+ PJSNX jsx;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ if (!g->Activityp) {
+ *is_null = 1;
+ return 0.0;
+ } else
+ return *(double*)g->Activityp;
+
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true)) {
+ PUSH_WARNING("CheckMemory error");
+ if (g->Mrr) *error = 1;
+ *is_null = 1;
+ return 0.0;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ *is_null = 1;
+ return 0.0;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ path = MakePSZ(g, args, 1);
+ jsx = new(g) JSNX(g, jsp, TYPE_DOUBLE);
+
+ if (jsx->SetJpath(g, path)) {
+ PUSH_WARNING(g->Message);
+ *is_null = 1;
+ return 0.0;
+ } // endif SetJpath
+
+ jsx->ReadValue(g);
+
+ if (jsx->GetValue()->IsNull()) {
+ *is_null = 1;
+ return 0.0;
+ } // endif IsNull
+
+ d = jsx->GetValue()->GetFloatValue();
+
+ if (initid->const_item) {
+ // Keep result of constant function
+ double *dp = (double*)PlugSubAlloc(g, NULL, sizeof(double));
+ *dp = d;
+ g->Activityp = (PACTIVITY)dp;
+ } // endif const_item
+
+ return d;
+} // end of jsonget_real
+
+void jsonget_real_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jsonget_real_deinit
+
+/*********************************************************************************/
+/* Locate a value in a Json tree. */
+/*********************************************************************************/
+my_bool jsonlocate_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, more = 1000;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "At least 2 arguments required");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
+ strcpy(message, "Third argument is not an integer (rank)");
+ return true;
+ } else if (args->arg_count > 3)
+ if (args->arg_type[3] != INT_RESULT) {
+ strcpy(message, "Fourth argument is not an integer (memory)");
+ return true;
+ } else
+ more += (ulong)*(longlong*)args->args[2];
+
+ CalcLen(args, false, reslen, memlen);
+
+ if (IsJson(args, 0) != 3)
+ memlen += more; // TODO: calculate this
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jsonlocate_init
+
+char *jsonlocate(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *p, *path = NULL;
+ int k, rc;
+ PJVAL jvp, jvp2;
+ PJSON jsp;
+ PJSNX jsx;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ if (g->Activityp) {
+ path = (char*)g->Activityp;
+ *res_length = strlen(path);
+ return path;
+ } else {
+ *res_length = 0;
+ *is_null = 1;
+ return NULL;
+ } // endif Activityp
+
+ } else if (initid->const_item)
+ g->N = 1;
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ PUSH_WARNING(MSG(TOO_MANY_JUMPS));
+ *error = 1;
+ *is_null = 1;
+ return NULL;
+ } // endif jump_level
+
+ if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
+ PUSH_WARNING(g->Message);
+ *error = 1;
+ path = NULL;
+ goto err;
+ } // endif rc
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, !g->Xchk)) {
+ PUSH_WARNING("CheckMemory error");
+ *error = 1;
+ goto err;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto err;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ // The item to locate
+ jvp2 = MakeValue(g, args, 1);
+
+ k = (args->arg_count > 2) ? (int)*(long long*)args->args[2] : 1;
+
+ jsx = new(g) JSNX(g, jsp, TYPE_STRING);
+ path = jsx->Locate(g, jsp, jvp2, k);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)path;
+
+ err:
+ g->jump_level--;
+
+ if (!path) {
+ *res_length = 0;
+ *is_null = 1;
+ } else
+ *res_length = strlen(path);
+
+ return path;
+} // end of jsonlocate
+
+void jsonlocate_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jsonlocate_deinit
+
+/*********************************************************************************/
+/* Locate all occurences of a value in a Json tree. */
+/*********************************************************************************/
+my_bool json_locate_all_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, more = 1000;
+
+ if (args->arg_count < 2) {
+ strcpy(message, "At least 2 arguments required");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
+ strcpy(message, "Third argument is not an integer (Depth)");
+ return true;
+ } else if (args->arg_count > 3)
+ if (args->arg_type[3] != INT_RESULT) {
+ strcpy(message, "Fourth argument is not an integer (memory)");
+ return true;
+ } else
+ more += (ulong)*(longlong*)args->args[2];
+
+ CalcLen(args, false, reslen, memlen);
+
+ if (IsJson(args, 0) != 3)
+ memlen += more; // TODO: calculate this
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_locate_all_init
+
+char *json_locate_all(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *p, *path = NULL;
+ int rc, mx = 10;
+ PJVAL jvp, jvp2;
+ PJSON jsp;
+ PJSNX jsx;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ if (g->Activityp) {
+ path = (char*)g->Activityp;
+ *res_length = strlen(path);
+ return path;
+ } else {
+ *error = 1;
+ *res_length = 0;
+ *is_null = 1;
+ return NULL;
+ } // endif Activityp
+
+ } else if (initid->const_item)
+ g->N = 1;
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ PUSH_WARNING(MSG(TOO_MANY_JUMPS));
+ *error = 1;
+ *is_null = 1;
+ return NULL;
+ } // endif jump_level
+
+ if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
+ PUSH_WARNING(g->Message);
+ *error = 1;
+ path = NULL;
+ goto err;
+ } // endif rc
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true)) {
+ PUSH_WARNING("CheckMemory error");
+ *error = 1;
+ goto err;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto err;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ // The item to locate
+ jvp2 = MakeValue(g, args, 1);
+
+ if (args->arg_count > 2)
+ mx = (int)*(long long*)args->args[2];
+
+ jsx = new(g) JSNX(g, jsp, TYPE_STRING);
+ path = jsx->LocateAll(g, jsp, jvp2, mx);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)path;
+
+ err:
+ g->jump_level--;
+
+ if (!path) {
+ *res_length = 0;
+ *is_null = 1;
+ } else
+ *res_length = strlen(path);
+
+ return path;
+} // end of json_locate_all
+
+void json_locate_all_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_locate_all_deinit
+
+/*********************************************************************************/
+/* Check whether the document contains a value or item. */
+/*********************************************************************************/
+my_bool jsoncontains_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, more = 1024;
+ int n = IsJson(args, 0);
+
+ if (args->arg_count < 2) {
+ strcpy(message, "At least 2 arguments required");
+ return true;
+ } else if (!n && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
+ strcpy(message, "Third argument is not an integer (index)");
+ return true;
+ } else if (args->arg_count > 3) {
+ if (args->arg_type[3] == INT_RESULT && args->args[3])
+ more += (unsigned long)*(long long*)args->args[3];
+ else
+ strcpy(message, "Fourth argument is not an integer (memory)");
+
+ } // endif's
+
+ CalcLen(args, false, reslen, memlen);
+ memlen += more;
+
+ if (IsJson(args, 0) != 3)
+ memlen += 1000; // TODO: calculate this
+
+ return JsonInit(initid, args, message, false, reslen, memlen);
+} // end of jsoncontains_init
+
+long long jsoncontains(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *p, res[256];
+ long long n;
+ unsigned long reslen;
+
+ *is_null = 0;
+ p = jsonlocate(initid, args, res, &reslen, is_null, error);
+ n = (*is_null) ? 0LL : 1LL;
+ return n;
+} // end of jsoncontains
+
+void jsoncontains_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jsoncontains_deinit
+
+/*********************************************************************************/
+/* Check whether the document contains a path. */
+/*********************************************************************************/
+my_bool jsoncontains_path_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, more = 1024;
+ int n = IsJson(args, 0);
+
+ if (args->arg_count < 2) {
+ strcpy(message, "At least 2 arguments required");
+ return true;
+ } else if (!n && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument is not a string (path)");
+ return true;
+ } else if (args->arg_count > 2) {
+ if (args->arg_type[2] == INT_RESULT && args->args[2])
+ more += (unsigned long)*(long long*)args->args[2];
+ else
+ strcpy(message, "Third argument is not an integer (memory)");
+
+ } // endif's
+
+ CalcLen(args, false, reslen, memlen);
+ memlen += more;
+
+ if (IsJson(args, 0) != 3)
+ memlen += 1000; // TODO: calculate this
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jsoncontains_path_init
+
+long long jsoncontains_path(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *p, *path;
+ long long n;
+ PJSON jsp;
+ PJSNX jsx;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ if (!g->Activityp) {
+ *is_null = 1;
+ return 0LL;
+ } else
+ return *(long long*)g->Activityp;
+
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true)) {
+ PUSH_WARNING("CheckMemory error");
+ goto err;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto err;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ path = MakePSZ(g, args, 1);
+ jsx = new(g)JSNX(g, jsp, TYPE_BIGINT);
+
+ if (jsx->SetJpath(g, path)) {
+ PUSH_WARNING(g->Message);
+ goto err;
+ } // endif SetJpath
+
+ n = (jsx->CheckPath(g)) ? 1LL : 0LL;
+
+ if (initid->const_item) {
+ // Keep result of constant function
+ long long *np = (long long*)PlugSubAlloc(g, NULL, sizeof(long long));
+ *np = n;
+ g->Activityp = (PACTIVITY)np;
+ } // endif const_item
+
+ return n;
+
+ err:
+ if (g->Mrr) *error = 1;
+ *is_null = 1;
+ return 0LL;
+} // end of jsoncontains_path
+
+void jsoncontains_path_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jsoncontains_path_deinit
+
+/*********************************************************************************/
+/* Set Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+ int n = IsJson(args, 0);
+
+ if (!(args->arg_count % 2)) {
+ strcpy(message, "This function must have an odd number of arguments");
+ return true;
+ } else if (!n && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ if (n == 2 && args->args[0]) {
+ char fn[_MAX_PATH];
+ long fl;
+
+ memcpy(fn, args->args[0], args->lengths[0]);
+ fn[args->lengths[0]] = 0;
+ fl = GetFileLength(fn);
+ memlen += fl * 3;
+ } else if (n != 3)
+ memlen += args->lengths[0] * 3;
+
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_set_item_init
+
+char *json_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *p, *path, *str = NULL;
+ int w, rc;
+ my_bool b = true;
+ PJSON jsp;
+ PJSNX jsx;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ if (g->N) {
+ str = (char*)g->Activityp;
+ goto fin;
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if (!strcmp(result, "$insert"))
+ w = 1;
+ else if (!strcmp(result, "$update"))
+ w = 2;
+ else
+ w = 0;
+
+ // Save stack and allocation environment and prepare error return
+ if (g->jump_level == MAX_JUMP) {
+ PUSH_WARNING(MSG(TOO_MANY_JUMPS));
+ *error = 1;
+ goto fin;
+ } // endif jump_level
+
+ if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
+ PUSH_WARNING(g->Message);
+ str = NULL;
+ goto err;
+ } // endif rc
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true, false, true)) {
+ PUSH_WARNING("CheckMemory error");
+ goto err;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto err;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ jsx = new(g)JSNX(g, jsp, TYPE_STRING, initid->max_length, 0, true);
+
+ for (uint i = 1; i+1 < args->arg_count; i += 2) {
+ jvp = MakeValue(gb, args, i);
+ path = MakePSZ(g, args, i+1);
+
+ if (jsx->SetJpath(g, path, false)) {
+ PUSH_WARNING(g->Message);
+ continue;
+ } // endif SetJpath
+
+ if (w) {
+ jsx->ReadValue(g);
+ b = jsx->GetValue()->IsNull();
+ b = (w == 1) ? b : !b;
+ } // endif w
+
+ if (b && jsx->WriteValue(gb, jvp))
+ PUSH_WARNING(g->Message);
+
+ } // endfor i
+
+ // In case of error or file, return unchanged argument
+ if (!(str = MakeResult(g, args, jsp, INT_MAX32)))
+ str = MakePSZ(g, args, 0);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)str;
+
+ err:
+ g->jump_level--;
+
+ fin:
+ if (!str) {
+ *is_null = 1;
+ *res_length = 0;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_set_item
+
+void json_set_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_set_item_deinit
+
+/*********************************************************************************/
+/* Insert Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool json_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_set_item_init(initid, args, message);
+} // end of json_insert_item_init
+
+char *json_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *p)
+{
+ strcpy(result, "$insert");
+ return json_set_item(initid, args, result, res_length, is_null, p);
+} // end of json_insert_item
+
+void json_insert_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_insert_item_deinit
+
+/*********************************************************************************/
+/* Update Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool json_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_set_item_init(initid, args, message);
+} // end of json_update_item_init
+
+char *json_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *p)
+{
+ strcpy(result, "$update");
+ return json_set_item(initid, args, result, res_length, is_null, p);
+} // end of json_update_item
+
+void json_update_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_update_item_deinit
+
+/*********************************************************************************/
+/* Returns a json file as a json string. */
+/*********************************************************************************/
+my_bool json_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, fl, more = 1024;
+
+ if (args->arg_count < 1 || args->arg_count > 4) {
+ strcpy(message, "This function only accepts 1 to 4 arguments");
+ return true;
+ } else if (!args->args[0] || args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a constant string (file name)");
+ return true;
+ } // endif's args[0]
+
+ for (unsigned int i = 1; i < args->arg_count; i++) {
+ if (!(args->arg_type[i] == INT_RESULT || args->arg_type[i] == STRING_RESULT)) {
+ sprintf(message, "Argument %d is not an integer or a string (pretty or path)", i);
+ return true;
+ } // endif arg_type
+
+ // Take care of eventual memory argument
+ if (args->arg_type[i] == INT_RESULT && args->args[i])
+ more += (ulong)*(longlong*)args->args[i];
+
+ } // endfor i
+
+ initid->maybe_null = 1;
+ CalcLen(args, false, reslen, memlen);
+ fl = GetFileLength(args->args[0]);
+ reslen += fl;
+
+ if (initid->const_item)
+ more += fl;
+
+ if (args->arg_count > 1)
+ more += fl * M;
+
+ memlen += more;
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of json_file_init
+
+char *json_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *str, *fn;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ str = (char*)g->Xchk;
+ goto fin;
+ } else if (initid->const_item)
+ g->N = 1;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ fn = MakePSZ(g, args, 0);
+
+ if (args->arg_count > 1) {
+ int len, pretty, pty = 3;
+ PJSON jsp;
+ PJVAL jvp = NULL;
+
+ pretty = (args->arg_type[1] == INT_RESULT) ? (int)*(longlong*)args->args[1]
+ : (args->arg_count > 2 && args->arg_type[2] == INT_RESULT)
+ ? (int)*(longlong*)args->args[2] : 3;
+
+ /*******************************************************************************/
+ /* Parse the json file and allocate its tree structure. */
+ /*******************************************************************************/
+ if (!(jsp = ParseJsonFile(g, fn, &pty, len))) {
+ PUSH_WARNING(g->Message);
+ str = NULL;
+ goto fin;
+ } // endif jsp
+
+ if (pty == 3)
+ PUSH_WARNING("File pretty format cannot be determined");
+ else if (pretty != 3 && pty != pretty)
+ PUSH_WARNING("File pretty format doesn't match the specified pretty value");
+ else if (pretty == 3)
+ pretty = pty;
+
+ // Check whether a path was specified
+ if (CheckPath(g, args, jsp, jvp, 1)) {
+ PUSH_WARNING(g->Message);
+ str = NULL;
+ goto fin;
+ } else if (jvp)
+ jsp = jvp->GetJson();
+
+ if (!(str = Serialize(g, jsp, NULL, 0)))
+ PUSH_WARNING(g->Message);
+
+ } else
+ if (!(str = GetJsonFile(g, fn)))
+ PUSH_WARNING(g->Message);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = str;
+
+ fin:
+ if (!str) {
+ *res_length = 0;
+ *is_null = 1;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of json_file
+
+void json_file_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_file_deinit
+
+/*********************************************************************************/
+/* Make a json file from a json item. */
+/*********************************************************************************/
+my_bool jfile_make_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, more = 1024;
+
+ if (args->arg_count < 1 || args->arg_count > 3) {
+ strcpy(message, "Wrong number of arguments");
+ return true;
+ } else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "First argument must be a json item");
+ return true;
+ } // endif
+
+ CalcLen(args, false, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jfile_make_init
+
+char *jfile_make(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *)
+{
+ char *p, *str = NULL, *fn = NULL;
+ int n, pretty = 2;
+ PJSON jsp;
+ PJVAL jvp;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ str = (char*)g->Activityp;
+ goto fin;
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if ((n = IsJson(args, 0)) == 3) {
+ // Get default file name and pretty
+ PBSON bsp = (PBSON)args->args[0];
+
+ fn = bsp->Filename;
+ pretty = bsp->Pretty;
+ } else if (n == 2)
+ fn = args->args[0];
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true)) {
+ PUSH_WARNING("CheckMemory error");
+ goto fin;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!strchr("[{ \t\r\n", *p)) {
+ // Is this a file name?
+ if (!(p = GetJsonFile(g, p))) {
+ PUSH_WARNING(g->Message);
+ goto fin;
+ } else
+ fn = jvp->GetString();
+
+ } // endif p
+
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto fin;
+ } // endif jsp
+
+ jvp->SetValue(jsp);
+ } // endif p
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jvp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jvp = (PJVAL)g->Xchk;
+
+ for (uint i = 1; i < args->arg_count; i++)
+ switch (args->arg_type[i]) {
+ case STRING_RESULT:
+ fn = MakePSZ(g, args, i);
+ break;
+ case INT_RESULT:
+ pretty = (int)*(longlong*)args->args[i];
+ break;
+ default:
+ PUSH_WARNING("Unexpected argument type in jfile_make");
+ } // endswitch arg_type
+
+ if (fn) {
+ if (!Serialize(g, jvp->GetJson(), fn, pretty))
+ PUSH_WARNING(g->Message);
+ } else
+ PUSH_WARNING("Missing file name");
+
+ str= fn;
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)str;
+
+ fin:
+ if (!str) {
+ *res_length = 0;
+ *is_null = 1;
+ } else
+ *res_length = strlen(str);
+
+ return str;
+} // end of jfile_make
+
+void jfile_make_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jfile_make_deinit
+
+/*********************************************************************************/
+/* Make and return a binary Json array containing all the parameters. */
+/*********************************************************************************/
+my_bool jbin_array_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ CalcLen(args, false, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jbin_array_init
+
+char *jbin_array(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (!bsp || bsp->Changed) {
+ if (!CheckMemory(g, initid, args, args->arg_count, false)) {
+ PJAR arp = new(g) JARRAY;
+
+ bsp = JbinAlloc(g, args, initid->max_length, arp);
+ strcat(bsp->Msg, " array");
+
+ for (uint i = 0; i < args->arg_count; i++)
+ arp->AddValue(g, MakeValue(g, args, i));
+
+ arp->InitArray(g);
+ } else
+ if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
+ strncpy(bsp->Msg, g->Message, 139);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? bsp : NULL;
+ } // endif bsp
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_array
+
+void jbin_array_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_array_deinit
-void Json_Object_Grp_deinit(UDF_INIT* initid)
+/*********************************************************************************/
+/* Add one or several values to a Json array. */
+/*********************************************************************************/
+my_bool jbin_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- PlugExit((PGLOBAL)initid->ptr);
-} // end of Json_Object_Grp_deinit
+ return json_array_add_values_init(initid, args, message);
+} // end of jbin_array_add_values_init
+char *jbin_array_add_values(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (!bsp || bsp->Changed) {
+ if (!CheckMemory(g, initid, args, args->arg_count, true)) {
+ char *p;
+ PJSON top;
+ PJAR arp;
+ PJVAL jvp = MakeValue(g, args, 0, &top);
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(top = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ return NULL;
+ } // endif jsp
+
+ jvp->SetValue(top);
+ } // endif p
+
+ if (jvp->GetValType() != TYPE_JAR) {
+ arp = new(gb)JARRAY;
+ arp->AddValue(gb, jvp);
+ } else
+ arp = jvp->GetArray();
+
+ for (uint i = 1; i < args->arg_count; i++)
+ arp->AddValue(gb, MakeValue(gb, args, i));
+
+ arp->InitArray(gb);
+
+ if ((bsp = JbinAlloc(g, args, initid->max_length, top))) {
+ strcat(bsp->Msg, " array");
+ bsp->Jsp = arp;
+ } // endif bsp
+
+ } else
+ if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
+ strncpy(bsp->Msg, g->Message, BMX);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? bsp : NULL;
+ } // endif bsp
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_array_add_values
+
+void jbin_array_add_values_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_array_add_values_deinit
+
+/*********************************************************************************/
+/* Add one value to a Json array. */
+/*********************************************************************************/
+my_bool jbin_array_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_array_add_init(initid, args, message);
+} // end of jbin_array_add_init
+
+char *jbin_array_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ int n = 2;
+ PJSON top = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (bsp && !bsp->Changed) {
+ // This constant function was recalled
+ *res_length = sizeof(BSON);
+ return (char*)bsp;
+ } // endif bsp
+
+ if (!CheckMemory(g, initid, args, 2, false, false, true)) {
+ int *x = NULL;
+ uint n = 2;
+// PJSON jsp;
+ PJVAL jvp;
+ PJAR arp;
+
+ jvp = MakeValue(g, args, 0, &top);
+// jsp = jvp->GetJson();
+ x = GetIntArgPtr(g, args, n);
+
+ if (CheckPath(g, args, top, jvp, n))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JAR) {
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ arp = jvp->GetArray();
+ arp->AddValue(gb, MakeValue(gb, args, 1), x);
+ arp->InitArray(gb);
+ } else {
+ PUSH_WARNING("First argument is not an array");
+// if (g->Mrr) *error = 1; (only if no path)
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error unchanged argument will be returned
+ bsp = MakeBinResult(g, args, top, initid->max_length, n);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = bsp;
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_array_add
+
+void jbin_array_add_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_array_add_deinit
+
+/*********************************************************************************/
+/* Delete a value from a Json array. */
+/*********************************************************************************/
+my_bool jbin_array_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_array_delete_init(initid, args, message);
+} // end of jbin_array_delete_init
+
+char *jbin_array_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PJSON top = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (bsp && !bsp->Changed) {
+ // This constant function was recalled
+ *res_length = sizeof(BSON);
+ return (char*)bsp;
+ } // endif bsp
+
+ if (!CheckMemory(g, initid, args, 1, false, false, true)) {
+ int *x;
+ uint n = 1;
+ PJAR arp;
+ PJVAL jvp = MakeValue(g, args, 0, &top);
+
+ if (CheckPath(g, args, top, jvp, 1))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JAR) {
+ if ((x = GetIntArgPtr(g, args, n))) {
+ arp = jvp->GetArray();
+ arp->DeleteValue(*x);
+ arp->InitArray(GetMemPtr(g, args, 0));
+ } else
+ PUSH_WARNING("Missing or null array index");
+
+ } else {
+ PUSH_WARNING("First argument is not an array");
+ if (g->Mrr) *error = 1;
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error unchanged argument will be returned
+ bsp = MakeBinResult(g, args, top, initid->max_length);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = bsp;
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_array_delete
+
+void jbin_array_delete_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_array_delete_deinit
+
+/*********************************************************************************/
+/* Make a Json Object containing all the parameters. */
+/*********************************************************************************/
+my_bool jbin_object_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ CalcLen(args, true, reslen, memlen);
+ return JsonInit(initid, args, message, false, reslen, memlen);
+} // end of jbin_object_init
+
+char *jbin_object(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (!bsp || bsp->Changed) {
+ if (!CheckMemory(g, initid, args, args->arg_count, true)) {
+ PJOB objp = new(g)JOBJECT;
+
+ for (uint i = 0; i < args->arg_count; i++)
+ objp->SetValue(g, MakeValue(g, args, i), MakeKey(g, args, i));
+
+ if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
+ strcat(bsp->Msg, " object");
+
+ } else
+ if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
+ strncpy(bsp->Msg, g->Message, BMX);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? bsp : NULL;
+ } // endif bsp
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_object
+
+void jbin_object_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_object_deinit
+
+/*********************************************************************************/
+/* Make a Json Object containing all not null parameters. */
+/*********************************************************************************/
+my_bool jbin_object_nonull_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ CalcLen(args, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jbin_object_nonull_init
+
+char *jbin_object_nonull(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (!bsp || bsp->Changed) {
+ if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
+ PJVAL jvp;
+ PJOB objp = new(g)JOBJECT;
+
+ for (uint i = 0; i < args->arg_count; i++)
+ if (!(jvp = MakeValue(g, args, i))->IsNull())
+ objp->SetValue(g, jvp, MakeKey(g, args, i));
+
+ if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
+ strcat(bsp->Msg, " object");
+
+ } else
+ if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
+ strncpy(bsp->Msg, g->Message, BMX);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? bsp : NULL;
+ } // endif bsp
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_object_nonull
+
+void jbin_object_nonull_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_object_nonull_deinit
+
+/*********************************************************************************/
+/* Make a Json Object containing all the key/value parameters. */
+/*********************************************************************************/
+my_bool jbin_object_key_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count % 2) {
+ strcpy(message, "This function must have an even number of arguments");
+ return true;
+ } // endif arg_count
+
+ CalcLen(args, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jbin_object_key_init
+
+char *jbin_object_key(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (!bsp || bsp->Changed) {
+ if (!CheckMemory(g, initid, args, args->arg_count, false, true)) {
+ PJOB objp = new(g)JOBJECT;
+
+ for (uint i = 0; i < args->arg_count; i += 2)
+ objp->SetValue(g, MakeValue(g, args, i+1), MakePSZ(g, args, i));
+
+ if ((bsp = JbinAlloc(g, args, initid->max_length, objp)))
+ strcat(bsp->Msg, " object");
+
+ } else
+ if ((bsp = JbinAlloc(g, args, initid->max_length, NULL)))
+ strncpy(bsp->Msg, g->Message, BMX);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? bsp : NULL;
+ } // endif bsp
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_object_key
+
+void jbin_object_key_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_object_key_deinit
+
+/*********************************************************************************/
+/* Add or replace a value in a Json Object. */
+/*********************************************************************************/
+my_bool jbin_object_add_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_object_add_init(initid, args, message);
+} // end of jbin_object_add_init
+
+char *jbin_object_add(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PJSON top = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (bsp && !bsp->Changed) {
+ // This constant function was recalled
+ bsp = (PBSON)g->Xchk;
+ *res_length = sizeof(BSON);
+ return (char*)bsp;
+ } // endif bsp
+
+ if (!CheckMemory(g, initid, args, 2, false, true, true)) {
+ char *key;
+ PJOB jobp;
+ PJVAL jvp = MakeValue(g, args, 0, &top);
+ PJSON jsp = jvp->GetJson();
+
+ if (CheckPath(g, args, jsp, jvp, 2))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JOB) {
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ jobp = jvp->GetObject();
+ jvp = MakeValue(gb, args, 1);
+ key = MakeKey(gb, args, 1);
+ jobp->SetValue(gb, jvp, key);
+ } else {
+ PUSH_WARNING("First argument target is not an object");
+// if (g->Mrr) *error = 1; (only if no path)
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error unchanged argument will be returned
+ bsp = MakeBinResult(g, args, top, initid->max_length);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = bsp;
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_object_add
+
+void jbin_object_add_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_object_add_deinit
+
+/*********************************************************************************/
+/* Delete a value from a Json object. */
+/*********************************************************************************/
+my_bool jbin_object_delete_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_object_delete_init(initid, args, message);
+} // end of jbin_object_delete_init
+
+char *jbin_object_delete(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PJSON top = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (bsp && !bsp->Changed) {
+ // This constant function was recalled
+ bsp = (PBSON)g->Xchk;
+ *res_length = sizeof(BSON);
+ return (char*)bsp;
+ } // endif bsp
+
+ if (!CheckMemory(g, initid, args, 1, false, true, true)) {
+ char *key;
+ PJOB jobp;
+ PJVAL jvp = MakeValue(g, args, 0, &top);
+ PJSON jsp = jvp->GetJson();
+
+ if (CheckPath(g, args, top, jvp, 2))
+ PUSH_WARNING(g->Message);
+ else if (jvp && jvp->GetValType() == TYPE_JOB) {
+ key = MakeKey(g, args, 1);
+ jobp = jvp->GetObject();
+ jobp->DeleteKey(key);
+ } else {
+ PUSH_WARNING("First argument target is not an object");
+// if (g->Mrr) *error = 1; (only if no path)
+ } // endif jvp
+
+ } // endif CheckMemory
+
+ // In case of error unchanged argument will be returned
+ bsp = MakeBinResult(g, args, top, initid->max_length);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = bsp;
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_object_delete
+
+void jbin_object_delete_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_object_delete_deinit
+
+/*********************************************************************************/
+/* Returns an array of the Json object keys. */
+/*********************************************************************************/
+my_bool jbin_object_list_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_object_list_init(initid, args, message);
+} // end of jbin_object_list_init
+
+char *jbin_object_list(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PJAR jarp = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (!bsp || bsp->Changed) {
+ if (!CheckMemory(g, initid, args, 1, true, true)) {
+ char *p;
+ PJSON jsp;
+ PJVAL jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ return NULL;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (jsp->GetType() == TYPE_JOB) {
+ jarp = ((PJOB)jsp)->GetKeyList(g);
+ } else {
+ PUSH_WARNING("First argument is not an object");
+ if (g->Mrr) *error = 1;
+ } // endif jsp type
+
+ } // endif CheckMemory
+
+ if ((bsp = JbinAlloc(g, args, initid->max_length, jarp)))
+ strcat(bsp->Msg, " array");
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? bsp : NULL;
+ } // endif bsp
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_object_list
+
+void jbin_object_list_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_object_list_deinit
+
+/*********************************************************************************/
+/* Get a Json item from a Json document. */
+/*********************************************************************************/
+my_bool jbin_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_get_item_init(initid, args, message);
+} // end of jbin_get_item_init
+
+char *jbin_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *p, *path;
+ PJSON jsp;
+ PJSNX jsx;
+ PJVAL jvp;
+ PBSON bsp = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (g->N) {
+ bsp = (PBSON)g->Activityp;
+ goto fin;
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true, true)) {
+ PUSH_WARNING("CheckMemory error");
+ goto fin;
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto fin;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ path = MakePSZ(g, args, 1);
+ jsx = new(g) JSNX(g, jsp, TYPE_STRING, initid->max_length);
+
+ if (jsx->SetJpath(g, path, false)) {
+ PUSH_WARNING(g->Message);
+ goto fin;
+ } // endif SetJpath
+
+ // Get the json tree
+ if ((jvp = jsx->GetRowValue(g, jsp, 0, false))) {
+ jsp = (jvp->GetJsp()) ? jvp->GetJsp() : new(g) JVALUE(g, jvp->GetValue());
+
+ if ((bsp = JbinAlloc(g, args, initid->max_length, jsp)))
+ strcat(bsp->Msg, " item");
+ else
+ *error = 1;
+
+ } // endif jvp
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)bsp;
+
+ fin:
+ if (!bsp) {
+ *is_null = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_get_item
+
+void jbin_get_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_get_item_deinit
+
+/*********************************************************************************/
+/* Merge two arrays or objects. */
+/*********************************************************************************/
+my_bool jbin_item_merge_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_item_merge_init(initid, args, message);
+} // end of jbin_item_merge_init
+
+char *jbin_item_merge(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ PJSON top = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (bsp && !bsp->Changed) {
+ // This constant function was recalled
+ *res_length = sizeof(BSON);
+ return (char*)bsp;
+ } // endif bsp
+
+ if (!CheckMemory(g, initid, args, 2, false, false, true)) {
+ PJVAL jvp;
+ PJSON jsp[2] = {NULL, NULL};
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ for (int i = 0; i < 2; i++) {
+ jvp = MakeValue(g, args, i);
+ if (!i) top = jvp->GetJson();
+
+ if (jvp->GetValType() != TYPE_JAR && jvp->GetValType() != TYPE_JOB) {
+ sprintf(g->Message, "Argument %d is not an array or object", i);
+ PUSH_WARNING(g->Message);
+ } else
+ jsp[i] = jvp->GetJsp();
+
+ } // endfor i
+
+ if (jsp[0] && jsp[0]->Merge(gb, jsp[1]))
+ PUSH_WARNING(gb->Message);
+
+ } // endif CheckMemory
+
+ // In case of error unchanged first argument will be returned
+ bsp = MakeBinResult(g, args, top, initid->max_length);
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = bsp;
+
+ if (!bsp) {
+ *is_null = 1;
+ *error = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_item_merge
+
+void jbin_item_merge_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_item_merge_deinit
+/*********************************************************************************/
+/* Set Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_set_item_init(initid, args, message);
+} // end of jbin_set_item_init
+
+char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *p, *path;
+ int w;
+ my_bool b = true;
+ PJSON jsp;
+ PJSNX jsx;
+ PJVAL jvp;
+ PBSON bsp = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PGLOBAL gb = GetMemPtr(g, args, 0);
+
+ if (g->N) {
+ bsp = (PBSON)g->Activityp;
+ goto fin;
+ } else if (initid->const_item)
+ g->N = 1;
+
+ if (!strcmp(result, "$insert"))
+ w = 1;
+ else if (!strcmp(result, "$update"))
+ w = 2;
+ else
+ w = 0;
+
+ if (!g->Xchk) {
+ if (CheckMemory(g, initid, args, 1, true, false, true)) {
+ PUSH_WARNING("CheckMemory error");
+ } else
+ jvp = MakeValue(g, args, 0);
+
+ if ((p = jvp->GetString())) {
+ if (!(jsp = ParseJson(g, p, strlen(p)))) {
+ PUSH_WARNING(g->Message);
+ goto fin;
+ } // endif jsp
+
+ } else
+ jsp = jvp->GetJson();
+
+ if (g->Mrr) { // First argument is a constant
+ g->Xchk = jsp;
+ JsonMemSave(g);
+ } // endif Mrr
+
+ } else
+ jsp = (PJSON)g->Xchk;
+
+ jsx = new(g)JSNX(g, jsp, TYPE_STRING, initid->max_length, 0, true);
+
+ for (uint i = 1; i+1 < args->arg_count; i += 2) {
+ jvp = MakeValue(gb, args, i);
+ path = MakePSZ(g, args, i+1);
+
+ if (jsx->SetJpath(g, path, false)) {
+ PUSH_WARNING(g->Message);
+ continue;
+ } // endif SetJpath
+
+ if (w) {
+ jsx->ReadValue(g);
+ b = jsx->GetValue()->IsNull();
+ b = (w == 1) ? b : !b;
+ } // endif w
+
+ if (b && jsx->WriteValue(gb, jvp))
+ PUSH_WARNING(g->Message);
+
+ } // endfor i
+
+ if (!(bsp = MakeBinResult(g, args, jsp, initid->max_length, INT_MAX32)))
+ *error = 1;
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Activityp = (PACTIVITY)bsp;
+
+ fin:
+ if (!bsp) {
+ *is_null = 1;
+ *res_length = 0;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_set_item
+
+void jbin_set_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_set_item_deinit
+
+/*********************************************************************************/
+/* Insert Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool jbin_insert_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_set_item_init(initid, args, message);
+} // end of jbin_insert_item_init
+
+char *jbin_insert_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *p)
+{
+ strcpy(result, "$insert");
+ return jbin_set_item(initid, args, result, res_length, is_null, p);
+} // end of jbin_insert_item
+
+void jbin_insert_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_insert_item_deinit
+
+/*********************************************************************************/
+/* Update Json items of a Json document according to path. */
+/*********************************************************************************/
+my_bool jbin_update_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ return json_set_item_init(initid, args, message);
+} // end of jbin_update_item_init
+
+char *jbin_update_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *p)
+{
+ strcpy(result, "$update");
+ return jbin_set_item(initid, args, result, res_length, is_null, p);
+} // end of jbin_update_item
+
+void jbin_update_item_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_update_item_deinit
+
+/*********************************************************************************/
+/* Returns a json file as a json item. */
+/*********************************************************************************/
+my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen, fl, more = 1024;
+
+ if (args->arg_count < 1 || args->arg_count > 4) {
+ strcpy(message, "This function only accepts 1 to 4 arguments");
+ return true;
+ } else if (args->arg_type[0] != STRING_RESULT || !args->args[0]) {
+ strcpy(message, "First argument must be a constant string (file name)");
+ return true;
+ } else if (args->arg_count > 1 && args->arg_type[1] != STRING_RESULT) {
+ strcpy(message, "Second argument is not a string (path)");
+ return true;
+ } else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
+ strcpy(message, "Third argument is not an integer (pretty)");
+ return true;
+ } else if (args->arg_count > 3) {
+ if (args->arg_type[3] != INT_RESULT) {
+ strcpy(message, "Fourth argument is not an integer (memory)");
+ return true;
+ } else
+ more += (ulong)*(longlong*)args->args[3];
+
+ } // endifs
+
+ initid->maybe_null = 1;
+ CalcLen(args, false, reslen, memlen);
+ fl = GetFileLength(args->args[0]);
+ reslen += fl;
+ more += fl * M;
+ memlen += more;
+ return JsonInit(initid, args, message, true, reslen, memlen);
+} // end of jbin_file_init
+
+char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *is_null, char *error)
+{
+ char *fn;
+ int pretty, len = 0, pty = 3;
+ PJSON jsp;
+ PJVAL jvp = NULL;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+ PBSON bsp = (PBSON)g->Xchk;
+
+ if (bsp && !bsp->Changed)
+ goto fin;
+
+ PlugSubSet(g, g->Sarea, g->Sarea_Size);
+ g->Xchk = NULL;
+ fn = MakePSZ(g, args, 0);
+ pretty = (args->arg_count > 2 && args->args[2]) ? (int)*(longlong*)args->args[2] : 3;
+
+ /*********************************************************************************/
+ /* Parse the json file and allocate its tree structure. */
+ /*********************************************************************************/
+ if (!(jsp = ParseJsonFile(g, fn, &pty, len))) {
+ PUSH_WARNING(g->Message);
+ *error = 1;
+ goto fin;
+ } // endif jsp
+
+ if (pty == 3)
+ PUSH_WARNING("File pretty format cannot be determined");
+ else if (pretty != 3 && pty != pretty)
+ PUSH_WARNING("File pretty format doesn't match the specified pretty value");
+ else if (pretty == 3)
+ pretty = pty;
+
+ if ((bsp = JbinAlloc(g, args, len, jsp))) {
+ strcat(bsp->Msg, " file");
+ bsp->Filename = fn;
+ bsp->Pretty = pretty;
+ } else {
+ *error = 1;
+ goto fin;
+ } // endif bsp
+
+ // Check whether a path was specified
+ if (CheckPath(g, args, jsp, jvp, 1)) {
+ PUSH_WARNING(g->Message);
+ bsp = NULL;
+ goto fin;
+ } else if (jvp)
+ bsp->Jsp = jvp->GetJsp();
+
+ if (initid->const_item)
+ // Keep result of constant function
+ g->Xchk = bsp;
+
+ fin:
+ if (!bsp) {
+ *res_length = 0;
+ *is_null = 1;
+ } else
+ *res_length = sizeof(BSON);
+
+ return (char*)bsp;
+} // end of jbin_file
+
+void jbin_file_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of jbin_file_deinit
+
+/*********************************************************************************/
+/* Serialize a Json document. . */
+/*********************************************************************************/
+my_bool json_serialize_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ unsigned long reslen, memlen;
+
+ if (args->arg_count != 1) {
+ strcpy(message, "This function must have 1 argument");
+ return true;
+ } else if (IsJson(args, 0) != 3) {
+ strcpy(message, "Argument must be a Jbin tree");
+ return true;
+ } else
+ CalcLen(args, false, reslen, memlen);
+
+ return JsonInit(initid, args, message, false, reslen, memlen);
+} // end of json_serialize_init
+
+char *json_serialize(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *, char *)
+{
+ char *str;
+ PGLOBAL g = (PGLOBAL)initid->ptr;
+
+ if (!g->Xchk) {
+ PBSON bsp = (PBSON)args->args[0];
+
+ JsonSubSet(g);
+
+ if (!(str = Serialize(g, bsp->Jsp, NULL, 0)))
+ str = strcpy(result, g->Message);
+
+ // Keep result of constant function
+ g->Xchk = (initid->const_item) ? str : NULL;
+ } else
+ str = (char*)g->Xchk;
+
+ *res_length = strlen(str);
+ return str;
+} // end of json_serialize
+
+void json_serialize_deinit(UDF_INIT* initid)
+{
+ JsonFreeMem((PGLOBAL)initid->ptr);
+} // end of json_serialize_deinit
diff --git a/storage/connect/jsonudf.h b/storage/connect/jsonudf.h
new file mode 100644
index 00000000000..ecbbb778214
--- /dev/null
+++ b/storage/connect/jsonudf.h
@@ -0,0 +1,292 @@
+/******************** tabjson H Declares Source Code File (.H) *******************/
+/* Name: jsonudf.h Version 1.2 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2015 */
+/* */
+/* This file contains the JSON UDF function and class declares. */
+/*********************************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "block.h"
+#include "osutil.h"
+#include "maputil.h"
+#include "json.h"
+
+#define UDF_EXEC_ARGS \
+ UDF_INIT*, UDF_ARGS*, char*, unsigned long*, char*, char*
+
+/*********************************************************************************/
+/* The JSON tree node. Can be an Object or an Array. */
+/*********************************************************************************/
+typedef struct _jnode {
+ PSZ Key; // The key used for object
+ OPVAL Op; // Operator used for this node
+ PVAL CncVal; // To cont value used for OP_CNC
+ PVAL Valp; // The internal array VALUE
+ int Rank; // The rank in array
+ int Rx; // Read row number
+ int Nx; // Next to read row number
+} JNODE, *PJNODE;
+
+typedef class JSNX *PJSNX;
+typedef class JOUTPATH *PJTP;
+typedef class JOUTALL *PJTA;
+
+extern "C" {
+ DllExport my_bool jsonvalue_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jsonvalue(UDF_EXEC_ARGS);
+ DllExport void jsonvalue_deinit(UDF_INIT*);
+
+ DllExport my_bool json_array_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_array(UDF_EXEC_ARGS);
+ DllExport void json_array_deinit(UDF_INIT*);
+
+ DllExport my_bool json_array_add_values_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_array_add_values(UDF_EXEC_ARGS);
+ DllExport void json_array_add_values_deinit(UDF_INIT*);
+
+ DllExport my_bool json_array_add_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_array_add(UDF_EXEC_ARGS);
+ DllExport void json_array_add_deinit(UDF_INIT*);
+
+ DllExport my_bool json_array_delete_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_array_delete(UDF_EXEC_ARGS);
+ DllExport void json_array_delete_deinit(UDF_INIT*);
+
+ DllExport my_bool json_object_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_object(UDF_EXEC_ARGS);
+ 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_EXEC_ARGS);
+ DllExport void json_object_nonull_deinit(UDF_INIT*);
+
+ DllExport my_bool json_object_key_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_object_key(UDF_EXEC_ARGS);
+ DllExport void json_object_key_deinit(UDF_INIT*);
+
+ DllExport my_bool json_object_add_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_object_add(UDF_EXEC_ARGS);
+ DllExport void json_object_add_deinit(UDF_INIT*);
+
+ DllExport my_bool json_object_delete_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_object_delete(UDF_EXEC_ARGS);
+ DllExport void json_object_delete_deinit(UDF_INIT*);
+
+ DllExport my_bool json_object_list_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_object_list(UDF_EXEC_ARGS);
+ DllExport void json_object_list_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_EXEC_ARGS);
+ 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_EXEC_ARGS);
+ DllExport void json_object_grp_clear(UDF_INIT *, char *, char *);
+ DllExport void json_object_grp_deinit(UDF_INIT*);
+
+ DllExport my_bool json_item_merge_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_item_merge(UDF_EXEC_ARGS);
+ DllExport void json_item_merge_deinit(UDF_INIT*);
+
+ DllExport my_bool json_get_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_get_item(UDF_EXEC_ARGS);
+ DllExport void json_get_item_deinit(UDF_INIT*);
+
+ DllExport my_bool jsonget_string_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jsonget_string(UDF_EXEC_ARGS);
+ DllExport void jsonget_string_deinit(UDF_INIT*);
+
+ DllExport my_bool jsonget_int_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport long long jsonget_int(UDF_INIT*, UDF_ARGS*, char*, char*);
+ DllExport void jsonget_int_deinit(UDF_INIT*);
+
+ DllExport my_bool jsonget_real_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport double jsonget_real(UDF_INIT*, UDF_ARGS*, char*, char*);
+ DllExport void jsonget_real_deinit(UDF_INIT*);
+
+ DllExport my_bool jsoncontains_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport long long jsoncontains(UDF_EXEC_ARGS);
+ DllExport void jsoncontains_deinit(UDF_INIT*);
+
+ DllExport my_bool jsonlocate_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jsonlocate(UDF_EXEC_ARGS);
+ DllExport void jsonlocate_deinit(UDF_INIT*);
+
+ DllExport my_bool json_locate_all_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_locate_all(UDF_EXEC_ARGS);
+ DllExport void json_locate_all_deinit(UDF_INIT*);
+
+ DllExport my_bool jsoncontains_path_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport long long jsoncontains_path(UDF_EXEC_ARGS);
+ DllExport void jsoncontains_path_deinit(UDF_INIT*);
+
+ DllExport my_bool json_set_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_set_item(UDF_EXEC_ARGS);
+ DllExport void json_set_item_deinit(UDF_INIT*);
+
+ DllExport my_bool json_insert_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_insert_item(UDF_EXEC_ARGS);
+ DllExport void json_insert_item_deinit(UDF_INIT*);
+
+ DllExport my_bool json_update_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_update_item(UDF_EXEC_ARGS);
+ DllExport void json_update_item_deinit(UDF_INIT*);
+
+ DllExport my_bool json_file_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_file(UDF_EXEC_ARGS);
+ DllExport void json_file_deinit(UDF_INIT*);
+
+ DllExport my_bool jfile_make_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jfile_make(UDF_EXEC_ARGS);
+ DllExport void jfile_make_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_array_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_array(UDF_EXEC_ARGS);
+ DllExport void jbin_array_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_array_add_values_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_array_add_values(UDF_EXEC_ARGS);
+ DllExport void jbin_array_add_values_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_array_add_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_array_add(UDF_EXEC_ARGS);
+ DllExport void jbin_array_add_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_array_delete_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_array_delete(UDF_EXEC_ARGS);
+ DllExport void jbin_array_delete_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_object_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_object(UDF_EXEC_ARGS);
+ DllExport void jbin_object_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_object_nonull_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_object_nonull(UDF_EXEC_ARGS);
+ DllExport void jbin_object_nonull_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_object_key_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_object_key(UDF_EXEC_ARGS);
+ DllExport void jbin_object_key_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_object_add_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_object_add(UDF_EXEC_ARGS);
+ DllExport void jbin_object_add_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_object_delete_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_object_delete(UDF_EXEC_ARGS);
+ DllExport void jbin_object_delete_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_object_list_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_object_list(UDF_EXEC_ARGS);
+ DllExport void jbin_object_list_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_get_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_get_item(UDF_EXEC_ARGS);
+ DllExport void jbin_get_item_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_item_merge_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_item_merge(UDF_EXEC_ARGS);
+ DllExport void jbin_item_merge_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_set_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_set_item(UDF_EXEC_ARGS);
+ DllExport void jbin_set_item_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_insert_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_insert_item(UDF_EXEC_ARGS);
+ DllExport void jbin_insert_item_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_update_item_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_update_item(UDF_EXEC_ARGS);
+ DllExport void jbin_update_item_deinit(UDF_INIT*);
+
+ DllExport my_bool jbin_file_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *jbin_file(UDF_EXEC_ARGS);
+ DllExport void jbin_file_deinit(UDF_INIT*);
+
+ DllExport my_bool json_serialize_init(UDF_INIT*, UDF_ARGS*, char*);
+ DllExport char *json_serialize(UDF_EXEC_ARGS);
+ DllExport void json_serialize_deinit(UDF_INIT*);
+} // extern "C"
+
+/*********************************************************************************/
+/* Structure JPN. Used to make the locate path. */
+/*********************************************************************************/
+typedef struct _jpn {
+ enum JTYP Type;
+ PSZ Key;
+ int N;
+} JPN, *PJPN;
+
+/*********************************************************************************/
+/* Class JSNX: JSON access method. */
+/*********************************************************************************/
+class JSNX : public BLOCK {
+public:
+ // Constructors
+ JSNX(PGLOBAL g, PJSON row, int type, int len = 64, int prec = 0, my_bool wr = false);
+
+ // Implementation
+ int GetPrecision(void) {return Prec;}
+ PVAL GetValue(void) {return Value;}
+
+ // Methods
+ my_bool SetJpath(PGLOBAL g, char *path, my_bool jb = false);
+ my_bool ParseJpath(PGLOBAL g);
+ void ReadValue(PGLOBAL g);
+ PJVAL GetRowValue(PGLOBAL g, PJSON row, int i, my_bool b = true);
+ PJVAL GetJson(PGLOBAL g);
+ my_bool CheckPath(PGLOBAL g);
+ my_bool WriteValue(PGLOBAL g, PJVAL jvalp);
+ char *Locate(PGLOBAL g, PJSON jsp, PJVAL jvp, int k = 1);
+ char *LocateAll(PGLOBAL g, PJSON jsp, PJVAL jvp, int mx = 10);
+
+protected:
+ my_bool SetArrayOptions(PGLOBAL g, char *p, int i, PSZ nm);
+ 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);
+ my_bool LocateArray(PJAR jarp);
+ my_bool LocateObject(PJOB jobp);
+ my_bool LocateValue(PJVAL jvp);
+ my_bool LocateArrayAll(PJAR jarp);
+ my_bool LocateObjectAll(PJOB jobp);
+ my_bool LocateValueAll(PJVAL jvp);
+ my_bool CompareTree(PJSON jp1, PJSON jp2);
+ my_bool AddPath(void);
+
+ // Default constructor not to be used
+ JSNX(void) {}
+
+ // Members
+ PJSON Row;
+ PJVAL Jvalp;
+ PJPN Jpnp;
+ JOUTSTR *Jp;
+ JNODE *Nodes; // The intermediate objects
+ PVAL Value;
+ PVAL MulVal; // To value used by multiple column
+ char *Jpath; // The json path
+ int Buf_Type;
+ int Long;
+ int Prec;
+ int Nod; // The number of intermediate objects
+ int Xnod; // Index of multiple values
+ int K; // Kth item to locate
+ int I; // Index of JPN
+ int Imax; // Max number of JPN's
+ int B; // Index base
+ my_bool Xpd; // True for expandable column
+ my_bool Parsed; // True when parsed
+ my_bool Found; // Item found by locate
+ my_bool Wr; // Write mode
+ my_bool Jb; // Must return json item
+}; // end of class JSNX
diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc
index 9c72e9cd665..2e9085b4c87 100644
--- a/storage/connect/mycat.cc
+++ b/storage/connect/mycat.cc
@@ -89,6 +89,7 @@
#if defined(XML_SUPPORT)
#include "tabxml.h"
#endif // XML_SUPPORT
+#include "mycat.h"
/***********************************************************************/
/* Extern static variables. */
@@ -299,13 +300,13 @@ int GetIndexType(TABTYPE type)
xtyp= 1;
break;
case TAB_MYSQL:
-// case TAB_ODBC:
+ case TAB_ODBC:
xtyp= 2;
break;
case TAB_VIR:
xtyp= 3;
break;
- case TAB_ODBC:
+// case TAB_ODBC:
default:
xtyp= 0;
break;
diff --git a/storage/connect/mycat.h b/storage/connect/mycat.h
index d4024e6b6c3..dfcbb2f6766 100644
--- a/storage/connect/mycat.h
+++ b/storage/connect/mycat.h
@@ -74,6 +74,7 @@ struct ha_table_option_struct {
typedef class ha_connect *PHC;
+char *GetPluginDir(void);
TABTYPE GetTypeID(const char *type);
bool IsFileType(TABTYPE type);
bool IsExactType(TABTYPE type);
diff --git a/storage/connect/mysql-test/connect/r/datest.result b/storage/connect/mysql-test/connect/r/datest.result
index 203a7419a8e..586741f09ad 100644
--- a/storage/connect/mysql-test/connect/r/datest.result
+++ b/storage/connect/mysql-test/connect/r/datest.result
@@ -30,3 +30,30 @@ SELECT id, TIME(tim) FROM t1 LIMIT 1;
id TIME(tim)
1 09:35:08.000000
DROP TABLE t1;
+#
+# Testing use of dates in where clause (MDEV-8926)
+#
+CREATE TABLE t1 (col1 DATE) ENGINE=CONNECT TABLE_TYPE=CSV;
+Warnings:
+Warning 1105 No file name. Table will use t1.csv
+INSERT INTO t1 VALUES('2015-01-01'),('2015-02-01'),('2015-03-01'),('2015-04-01');
+SELECT * FROM t1 WHERE col1 = '2015-02-01';
+col1
+2015-02-01
+SELECT * FROM t1 WHERE col1 > '2015-02-01';
+col1
+2015-03-01
+2015-04-01
+SELECT * FROM t1 WHERE col1 >= '2015-02-01';
+col1
+2015-02-01
+2015-03-01
+2015-04-01
+SELECT * FROM t1 WHERE col1 < '2015-02-01';
+col1
+2015-01-01
+SELECT * FROM t1 WHERE col1 <= '2015-02-01';
+col1
+2015-01-01
+2015-02-01
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/r/grant.result b/storage/connect/mysql-test/connect/r/grant.result
index 9a0a549fd1f..4e64b983ea7 100644
--- a/storage/connect/mysql-test/connect/r/grant.result
+++ b/storage/connect/mysql-test/connect/r/grant.result
@@ -1,3 +1,4 @@
+set sql_mode="";
#
# Testing FILE privilege
#
@@ -70,6 +71,7 @@ DROP USER user@localhost;
#
# Beginning of grant.inc
#
+CREATE USER user@localhost;
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
SELECT user();
@@ -164,6 +166,7 @@ DROP USER user@localhost;
#
# Beginning of grant.inc
#
+CREATE USER user@localhost;
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
SELECT user();
@@ -258,6 +261,7 @@ DROP USER user@localhost;
#
# Beginning of grant.inc
#
+CREATE USER user@localhost;
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
SELECT user();
@@ -352,6 +356,7 @@ DROP USER user@localhost;
#
# Beginning of grant.inc
#
+CREATE USER user@localhost;
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
SELECT user();
@@ -446,6 +451,7 @@ DROP USER user@localhost;
#
# Beginning of grant.inc
#
+CREATE USER user@localhost;
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
SELECT user();
@@ -537,3 +543,4 @@ DROP USER user@localhost;
#
# End of grant.inc
#
+set sql_mode=default;
diff --git a/storage/connect/mysql-test/connect/r/ini_grant.result b/storage/connect/mysql-test/connect/r/ini_grant.result
index 190114254b0..68330278183 100644
--- a/storage/connect/mysql-test/connect/r/ini_grant.result
+++ b/storage/connect/mysql-test/connect/r/ini_grant.result
@@ -1,8 +1,10 @@
#
# Checking FILE privileges
#
+set sql_mode="";
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
+set sql_mode=default;
SELECT user();
user()
user@localhost
diff --git a/storage/connect/mysql-test/connect/r/json.result b/storage/connect/mysql-test/connect/r/json.result
index acb74c38e26..aa6b04c58c7 100644
--- a/storage/connect/mysql-test/connect/r/json.result
+++ b/storage/connect/mysql-test/connect/r/json.result
@@ -171,6 +171,40 @@ line
]
DROP TABLE t1;
#
+# Testing a pretty=0 file
+#
+CREATE TABLE t1
+(
+ISBN CHAR(15) NOT NULL,
+Language CHAR(2) FIELD_FORMAT='LANG',
+Subject CHAR(32) FIELD_FORMAT='SUBJECT',
+AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:FIRSTNAME',
+AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:LASTNAME',
+Title CHAR(32) FIELD_FORMAT='TITLE',
+Translation CHAR(32) FIELD_FORMAT='TRANSLATED:PREFIX',
+TranslatorFN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:FIRSTNAME',
+TranslatorLN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:LASTNAME',
+Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
+Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
+Year int(4) FIELD_FORMAT='DATEPUB',
+INDEX IX(ISBN)
+)
+ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bib0.json' LRECL=320 OPTION_LIST='Pretty=0';
+SHOW INDEX FROM t1;
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
+t1 1 IX 1 ISBN A NULL NULL NULL XINDEX
+SELECT * FROM t1;
+ISBN Language Subject AuthorFN AuthorLN Title Translation TranslatorFN TranslatorLN Publisher Location Year
+9782212090819 fr applications Jean-Michel Bernadac Construire une application XML NULL NULL NULL Eyrolles Paris 1999
+9782212090819 fr applications François Knab Construire une application XML NULL NULL NULL Eyrolles Paris 1999
+9782840825685 fr applications William J. Pardi XML en Action adapté de l'anglais par James Guerin Microsoft Press Paris 2001
+DESCRIBE SELECT * FROM t1 WHERE ISBN = '9782212090819';
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref IX IX 15 const 1 Using where
+UPDATE t1 SET AuthorFN = 'Philippe' WHERE ISBN = '9782212090819';
+ERROR HY000: Got error 122 'Cannot write expanded column when Pretty is not 2' from CONNECT
+DROP TABLE t1;
+#
# A file with 2 arrays
#
CREATE TABLE t1 (
diff --git a/storage/connect/mysql-test/connect/r/json_udf.result b/storage/connect/mysql-test/connect/r/json_udf.result
index 1455bac9017..5089022c5ea 100644
--- a/storage/connect/mysql-test/connect/r/json_udf.result
+++ b/storage/connect/mysql-test/connect/r/json_udf.result
@@ -1,63 +1,177 @@
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=VIR BLOCK_SIZE=5;
#
# Test UDF's with constant arguments
#
+SELECT JsonValue(56, 3.1416, 'foo', NULL);
+ERROR HY000: Can't initialize function 'jsonvalue'; Cannot accept more than 1 argument
+SELECT JsonValue(3.1416);
+JsonValue(3.1416)
+3.141600
+SELECT JsonValue(-80);
+JsonValue(-80)
+-80
+SELECT JsonValue('foo');
+JsonValue('foo')
+"foo"
+SELECT JsonValue(9223372036854775807);
+JsonValue(9223372036854775807)
+9223372036854775807
+SELECT JsonValue(NULL);
+JsonValue(NULL)
+null
+SELECT JsonValue(TRUE);
+JsonValue(TRUE)
+true
+SELECT JsonValue(FALSE);
+JsonValue(FALSE)
+false
+SELECT JsonValue();
+JsonValue()
+null
+SELECT JsonValue('[11, 22, 33]' json_) FROM t1;
+JsonValue('[11, 22, 33]' json_)
+[11,22,33]
+[11,22,33]
+[11,22,33]
+[11,22,33]
+[11,22,33]
SELECT Json_Array();
Json_Array()
[]
-SELECT Json_Object(56,3.1416,'foo',NULL);
-Json_Object(56,3.1416,'foo',NULL)
-{"56":56,"3.1416":3.141600,"foo":"foo","NULL":null}
-SELECT Json_Object(56 qty,3.1416 price,'foo' truc, NULL garanty);
-Json_Object(56 qty,3.1416 price,'foo' truc, NULL garanty)
-{"qty":56,"price":3.141600,"truc":"foo","garanty":null}
-SELECT Json_Array(56,3.1416,'My name is "Foo"',NULL);
-Json_Array(56,3.1416,'My name is "Foo"',NULL)
+SELECT Json_Array(56, 3.1416, 'My name is "Foo"', NULL);
+Json_Array(56, 3.1416, 'My name is "Foo"', NULL)
[56,3.141600,"My name is \"Foo\"",null]
-SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL)) Array;
-ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Value_Add must have at least 2 arguments
-SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL),'One more') Array;
+SELECT Json_Array(Json_Array(56, 3.1416, 'foo'), TRUE);
+Json_Array(Json_Array(56, 3.1416, 'foo'), TRUE)
+[[56,3.141600,"foo"],true]
+SELECT Json_Array_Add(Json_Array(56, 3.1416, 'foo', NULL)) Array;
+ERROR HY000: Can't initialize function 'json_array_add'; This function must have at least 2 arguments
+SELECT Json_Array_Add(Json_Array(56, 3.1416, 'foo', NULL), 'One more') Array;
Array
[56,3.141600,"foo",null,"One more"]
-SELECT Json_Array_Add(Json_Value('one value'),'One more');
-Json_Array_Add(Json_Value('one value'),'One more')
-["one value","One more"]
-SELECT Json_Array_Add('one value','One more');
-ERROR HY000: Can't initialize function 'Json_Array_Add'; Json_Value_Add first argument must be a json item
-SELECT Json_Array_Add('one value' json_,'One more');
-Json_Array_Add('one value' json_,'One more')
-[null,"One more"]
+SELECT Json_Array_Add(JsonValue('one value'), 'One more');
+ERROR HY000: Can't initialize function 'json_array_add'; First argument must be a json item
+SELECT Json_Array_Add('one value', 'One more');
+ERROR HY000: Can't initialize function 'json_array_add'; First argument must be a json item
+SELECT Json_Array_Add('one value' json_, 'One more');
+Json_Array_Add('one value' json_, 'One more')
+one value
Warnings:
-Warning 1105 Bad 'o' character near one value
-SELECT Json_Value(56,3.1416,'foo',NULL);
-ERROR HY000: Can't initialize function 'Json_Value'; Json_Value cannot accept more than 1 argument
-SELECT Json_Value(3.1416);
-Json_Value(3.1416)
-3.141600
-SELECT Json_Value('foo');
-Json_Value('foo')
-"foo"
-SELECT Json_Value(NULL);
-Json_Value(NULL)
-null
-SELECT Json_Value();
-Json_Value()
-null
+Warning 1105 Error 2 opening one value
+Warning 1105 First argument target is not an array
+SELECT Json_Array_Add(5 json_, 'One more');
+ERROR HY000: Can't initialize function 'json_array_add'; First argument must be a json item
+SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 0);
+Json_Array_Add('[5,3,8,7,9]' json_, 4, 0)
+[4,5,3,8,7,9]
+SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 2) Array;
+Array
+[5,3,4,8,7,9]
+SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 9);
+Json_Array_Add('[5,3,8,7,9]' json_, 4, 9)
+[5,3,8,7,9,4]
+SELECT Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), '[2]', 33, 1);
+Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), '[2]', 33, 1)
+[1,2,[11,22],"[2]"]
+SELECT Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), 33, '[2]', 1);
+Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), 33, '[2]', 1)
+[1,2,[11,33,22]]
+SELECT Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), 33, 1, '[2]');
+Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), 33, 1, '[2]')
+[1,2,[11,33,22]]
+SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin', NULL), 'One more', 'Two more') Array;
+Array
+[56,3.141600,"machin",null,"One more","Two more"]
+SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), 'One more', 'Two more') Array FROM t1;
+Array
+[56,3.141600,"machin","One more","Two more"]
+[56,3.141600,"machin","One more","Two more"]
+[56,3.141600,"machin","One more","Two more"]
+[56,3.141600,"machin","One more","Two more"]
+[56,3.141600,"machin","One more","Two more"]
+SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), n) Array FROM t1;
+Array
+[56,3.141600,"machin",1]
+[56,3.141600,"machin",2]
+[56,3.141600,"machin",3]
+[56,3.141600,"machin",4]
+[56,3.141600,"machin",5]
+SELECT Json_Array_Add_Values(Json_Array(n, 3.1416, 'machin'), n) Array FROM t1;
+Array
+[1,3.141600,"machin",1]
+[2,3.141600,"machin",2]
+[3,3.141600,"machin",3]
+[4,3.141600,"machin",4]
+[5,3.141600,"machin",5]
+SELECT Json_Array_Add_Values('[56]', 3.1416, 'machin') Array;
+Array
+[56,3.141600,"machin"]
+SELECT Json_Array_Delete(Json_Array(56, 3.1416, 'My name is "Foo"', NULL), 0);
+Json_Array_Delete(Json_Array(56, 3.1416, 'My name is "Foo"', NULL), 0)
+[3.141600,"My name is \"Foo\"",null]
+SELECT Json_Array_Delete(Json_Object(56, 3.1416, 'My name is Foo', NULL), 2);
+Json_Array_Delete(Json_Object(56, 3.1416, 'My name is Foo', NULL), 2)
+{"56":56,"3.1416":3.141600,"My name is Foo":"My name is Foo","NULL":null}
+Warnings:
+Warning 1105 First argument target is not an array
+SELECT Json_Array_Delete(Json_Array(56, 3.1416, 'My name is "Foo"', NULL), '2');
+Json_Array_Delete(Json_Array(56, 3.1416, 'My name is "Foo"', NULL), '2')
+[56,3.141600,"My name is \"Foo\"",null]
+Warnings:
+Warning 1105 Missing or null array index
+SELECT Json_Array_Delete(json_array(56, 3.1416, 'My name is "Foo"', NULL), '2', 2);
+Json_Array_Delete(json_array(56, 3.1416, 'My name is "Foo"', NULL), '2', 2)
+[56,3.141600,"My name is \"Foo\"",null]
+Warnings:
+Warning 1105 No sub-item at '2'
+SELECT Json_Object(56, 3.1416, 'foo', NULL);
+Json_Object(56, 3.1416, 'foo', NULL)
+{"56":56,"3.1416":3.141600,"foo":"foo","NULL":null}
+SELECT Json_Object(56 qty, 3.1416 price, 'foo' truc, NULL garanty);
+Json_Object(56 qty, 3.1416 price, 'foo' truc, NULL garanty)
+{"qty":56,"price":3.141600,"truc":"foo","garanty":null}
SELECT Json_Object();
Json_Object()
{}
-SELECT Json_Object(Json_Array(56,3.1416,'foo'),NULL);
-Json_Object(Json_Array(56,3.1416,'foo'),NULL)
-{"Array(56,3.1416,'foo')":[56,3.141600,"foo"],"NULL":null}
-SELECT Json_Array(Json_Array(56,3.1416,'foo'),NULL);
-Json_Array(Json_Array(56,3.1416,'foo'),NULL)
-[[56,3.141600,"foo"],null]
-SELECT Json_Array(Json_Object(56 "qty",3.1416 "price",'foo'),NULL);
-Json_Array(Json_Object(56 "qty",3.1416 "price",'foo'),NULL)
+SELECT Json_Object(Json_Array(56, 3.1416, 'foo'), NULL);
+Json_Object(Json_Array(56, 3.1416, 'foo'), NULL)
+{"Array(56, 3.1416, 'foo')":[56,3.141600,"foo"],"NULL":null}
+SELECT Json_Array(Json_Object(56 "qty", 3.1416 "price", 'foo') ,NULL);
+Json_Array(Json_Object(56 "qty", 3.1416 "price", 'foo') ,NULL)
[{"qty":56,"price":3.141600,"foo":"foo"},null]
+SELECT Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL);
+Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL)
+{"qty":56,"price":3.141600,"truc":"machin","garanty":null}
+SELECT Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty');
+ERROR HY000: Can't initialize function 'json_object_key'; This function must have an even number of arguments
+SELECT Json_Object_Add(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color);
+Json_Object_Add(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color)
+{"qty":56,"price":3.141600,"truc":"machin","garanty":null,"color":"blue"}
+SELECT Json_Object_Add(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price);
+Json_Object_Add(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price)
+{"qty":56,"price":45.990000,"truc":"machin","garanty":null}
+SELECT Json_Object_Add(Json_File('notexist.json'), 'cheese' item, '[1]', 1);
+Json_Object_Add(Json_File('notexist.json'), 'cheese' item, '[1]', 1)
+NULL
+Warnings:
+Warning 1105 Error 2 opening notexist.json
+Warning 1105 First argument target is not an object
+SELECT Json_Object_Delete(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'truc');
+Json_Object_Delete(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'truc')
+{"qty":56,"price":3.141600,"garanty":null}
+SELECT Json_Object_Delete(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'chose');
+Json_Object_Delete(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'chose')
+{"qty":56,"price":3.141600,"truc":"machin","garanty":null}
+SELECT Json_Object_List(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty)) "Key List";
+Key List
+["qty","price","truc","garanty"]
+SELECT Json_Object_List('{"qty":56, "price":3.1416, "truc":"machin", "garanty":null}') "Key List";
+Key List
+["qty","price","truc","garanty"]
#
# Test UDF's with column arguments
#
-CREATE TABLE t1
+CREATE TABLE t2
(
ISBN CHAR(15),
LANG CHAR(2),
@@ -69,21 +183,20 @@ TRANSLATOR CHAR(80),
PUBLISHER CHAR(32),
DATEPUB int(4)
) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.json';
-SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t1;
+SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t2;
Json_Array(AUTHOR, TITLE, DATEPUB)
["Jean-Christophe Bernadac","Construire une application XML",1999]
["William J. Pardi","XML en Action",1999]
-SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t1;
+SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t2;
Json_Object(AUTHOR, TITLE, DATEPUB)
{"AUTHOR":"Jean-Christophe Bernadac","TITLE":"Construire une application XML","DATEPUB":1999}
{"AUTHOR":"William J. Pardi","TITLE":"XML en Action","DATEPUB":1999}
-SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t1;
-ERROR HY000: Can't initialize function 'Json_Array_Grp'; Json_Array_Grp can only accept 1 argument
-SELECT Json_Array_Grp(TITLE) FROM t1;
+SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t2;
+ERROR HY000: Can't initialize function 'json_array_grp'; This function can only accept 1 argument
+SELECT Json_Array_Grp(TITLE) FROM t2;
Json_Array_Grp(TITLE)
["Construire une application XML","XML en Action"]
-DROP TABLE t1;
-CREATE TABLE t1 (
+CREATE TABLE t3 (
SERIALNO CHAR(5) NOT NULL,
NAME VARCHAR(12) NOT NULL FLAG=6,
SEX SMALLINT(1) NOT NULL,
@@ -93,10 +206,10 @@ DEPARTMENT CHAr(4) NOT NULL FLAG=41,
SECRETARY CHAR(5) DEFAULT NULL FLAG=46,
SALARY DOUBLE(8,2) NOT NULL FLAG=52
) ENGINE=CONNECT TABLE_TYPE=FIX BLOCK_SIZE=8 FILE_NAME='employee.dat' ENDING=1;
-SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t1 WHERE NAME = 'MERCHANT';
+SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t3 WHERE NAME = 'MERCHANT';
Json_Object(SERIALNO, NAME, TITLE, SALARY)
{"SERIALNO":"78943","NAME":"MERCHANT","TITLE":"SALESMAN","SALARY":8700.000000}
-SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t1 GROUP BY DEPARTMENT;
+SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t3 GROUP BY DEPARTMENT;
DEPARTMENT Json_Array_Grp(NAME)
0021 ["STRONG","SHORTSIGHT"]
0318 ["BANCROFT","PLUMHEAD","HONEY","TONGHO","WALTER","SHRINKY","WERTHER","MERCHANT","WHEELFOR"]
@@ -104,26 +217,36 @@ DEPARTMENT Json_Array_Grp(NAME)
2452 ["BIGHEAD","ORELLY","BIGHORN","SMITH","CHERRY"]
Warnings:
Warning 1105 Result truncated to json_grp_size values
-set connect_json_grp_size=30;
-SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t1 GROUP BY DEPARTMENT;
+SET connect_json_grp_size=30;
+SELECT Json_Object(title, Json_Array_Grp(name) `json_names`) from t3 GROUP BY title;
+Json_Object(title, Json_Array_Grp(name) `json_names`)
+{"title":"ADMINISTRATOR","names":["GOOSEPEN","FUNNIGUY","SHRINKY"]}
+{"title":"DIRECTOR","names":["QUINN","WERTHER","STRONG"]}
+{"title":"ENGINEER","names":["BROWNY","ORELLY","MARTIN","TONGHO","WALTER","SMITH"]}
+{"title":"PROGRAMMER","names":["BUGHAPPY"]}
+{"title":"SALESMAN","names":["WHEELFOR","MERCHANT","BULLOZER","BANCROFT","FODDERMAN"]}
+{"title":"SCIENTIST","names":["BIGHEAD","BIGHORN"]}
+{"title":"SECRETARY","names":["MESSIFUL","HONEY","SHORTSIGHT","CHERRY","MONAPENNY"]}
+{"title":"TYPIST","names":["KITTY","PLUMHEAD"]}
+SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t3 GROUP BY DEPARTMENT;
Json_Array(DEPARTMENT, Json_Array_Grp(NAME))
["0021",["STRONG","SHORTSIGHT"]]
["0318",["BANCROFT","PLUMHEAD","HONEY","TONGHO","WALTER","SHRINKY","WERTHER","MERCHANT","WHEELFOR"]]
["0319",["BULLOZER","QUINN","BROWNY","KITTY","MONAPENNY","MARTIN","FUNNIGUY","BUGHAPPY","FODDERMAN","MESSIFUL","GOOSEPEN"]]
["2452",["BIGHEAD","ORELLY","BIGHORN","SMITH","CHERRY"]]
-SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t1 GROUP BY DEPARTMENT;
+SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t3 GROUP BY DEPARTMENT;
Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES)
{"DEPARTMENT":"0021","NAMES":["STRONG","SHORTSIGHT"]}
{"DEPARTMENT":"0318","NAMES":["BANCROFT","PLUMHEAD","HONEY","TONGHO","WALTER","SHRINKY","WERTHER","MERCHANT","WHEELFOR"]}
{"DEPARTMENT":"0319","NAMES":["BULLOZER","QUINN","BROWNY","KITTY","MONAPENNY","MARTIN","FUNNIGUY","BUGHAPPY","FODDERMAN","MESSIFUL","GOOSEPEN"]}
{"DEPARTMENT":"2452","NAMES":["BIGHEAD","ORELLY","BIGHORN","SMITH","CHERRY"]}
-SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT;
+SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT;
Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES)
{"DEPARTMENT":"0021","EMPLOYES":[{"SERIALNO":"87777","NAME":"STRONG","TITLE":"DIRECTOR","SALARY":23000.000000},{"SERIALNO":"22222","NAME":"SHORTSIGHT","TITLE":"SECRETARY","SALARY":5500.000000}]}
{"DEPARTMENT":"0318","EMPLOYES":[{"SERIALNO":"74200","NAME":"BANCROFT","TITLE":"SALESMAN","SALARY":9600.000000},{"SERIALNO":"24888","NAME":"PLUMHEAD","TITLE":"TYPIST","SALARY":2800.000000},{"SERIALNO":"27845","NAME":"HONEY","TITLE":"SECRETARY","SALARY":4900.000000},{"SERIALNO":"73452","NAME":"TONGHO","TITLE":"ENGINEER","SALARY":6800.000000},{"SERIALNO":"74234","NAME":"WALTER","TITLE":"ENGINEER","SALARY":7400.000000},{"SERIALNO":"77777","NAME":"SHRINKY","TITLE":"ADMINISTRATOR","SALARY":7500.000000},{"SERIALNO":"70012","NAME":"WERTHER","TITLE":"DIRECTOR","SALARY":14500.000000},{"SERIALNO":"78943","NAME":"MERCHANT","TITLE":"SALESMAN","SALARY":8700.000000},{"SERIALNO":"73111","NAME":"WHEELFOR","TITLE":"SALESMAN","SALARY":10030.000000}]}
{"DEPARTMENT":"0319","EMPLOYES":[{"SERIALNO":"76543","NAME":"BULLOZER","TITLE":"SALESMAN","SALARY":14800.000000},{"SERIALNO":"40567","NAME":"QUINN","TITLE":"DIRECTOR","SALARY":14000.000000},{"SERIALNO":"00137","NAME":"BROWNY","TITLE":"ENGINEER","SALARY":10500.000000},{"SERIALNO":"12345","NAME":"KITTY","TITLE":"TYPIST","SALARY":3000.450000},{"SERIALNO":"33333","NAME":"MONAPENNY","TITLE":"SECRETARY","SALARY":3800.000000},{"SERIALNO":"00023","NAME":"MARTIN","TITLE":"ENGINEER","SALARY":10000.000000},{"SERIALNO":"07654","NAME":"FUNNIGUY","TITLE":"ADMINISTRATOR","SALARY":8500.000000},{"SERIALNO":"45678","NAME":"BUGHAPPY","TITLE":"PROGRAMMER","SALARY":8500.000000},{"SERIALNO":"56789","NAME":"FODDERMAN","TITLE":"SALESMAN","SALARY":7000.000000},{"SERIALNO":"55555","NAME":"MESSIFUL","TITLE":"SECRETARY","SALARY":5000.500000},{"SERIALNO":"98765","NAME":"GOOSEPEN","TITLE":"ADMINISTRATOR","SALARY":4700.000000}]}
{"DEPARTMENT":"2452","EMPLOYES":[{"SERIALNO":"34567","NAME":"BIGHEAD","TITLE":"SCIENTIST","SALARY":8000.000000},{"SERIALNO":"31416","NAME":"ORELLY","TITLE":"ENGINEER","SALARY":13400.000000},{"SERIALNO":"36666","NAME":"BIGHORN","TITLE":"SCIENTIST","SALARY":11000.000000},{"SERIALNO":"02345","NAME":"SMITH","TITLE":"ENGINEER","SALARY":9000.000000},{"SERIALNO":"11111","NAME":"CHERRY","TITLE":"SECRETARY","SALARY":4500.000000}]}
-SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT, TITLE;
+SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT, TITLE;
Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES)
{"DEPARTMENT":"0021","TITLE":"DIRECTOR","EMPLOYES":[{"SERIALNO":"87777","NAME":"STRONG","SALARY":23000.000000}]}
{"DEPARTMENT":"0021","TITLE":"SECRETARY","EMPLOYES":[{"SERIALNO":"22222","NAME":"SHORTSIGHT","SALARY":5500.000000}]}
@@ -143,25 +266,382 @@ Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY
{"DEPARTMENT":"2452","TITLE":"ENGINEER","EMPLOYES":[{"SERIALNO":"31416","NAME":"ORELLY","SALARY":13400.000000},{"SERIALNO":"02345","NAME":"SMITH","SALARY":9000.000000}]}
{"DEPARTMENT":"2452","TITLE":"SCIENTIST","EMPLOYES":[{"SERIALNO":"34567","NAME":"BIGHEAD","SALARY":8000.000000},{"SERIALNO":"36666","NAME":"BIGHORN","SALARY":11000.000000}]}
{"DEPARTMENT":"2452","TITLE":"SECRETARY","EMPLOYES":[{"SERIALNO":"11111","NAME":"CHERRY","SALARY":4500.000000}]}
-SELECT Json_Object_Grp(SALARY) FROM t1;
-ERROR HY000: Can't initialize function 'Json_Object_Grp'; Json_Array_Grp can only accept 2 arguments
-SELECT Json_Object_Grp(SALARY, NAME) FROM t1;
-Json_Object_Grp(SALARY, NAME)
+SELECT Json_Object_Grp(SALARY) FROM t3;
+ERROR HY000: Can't initialize function 'json_object_grp'; This function requires 2 arguments (key, value)
+SELECT Json_Object_Grp(NAME, SALARY) FROM t3;
+Json_Object_Grp(NAME, SALARY)
{"BANCROFT":9600.000000,"SMITH":9000.000000,"MERCHANT":8700.000000,"FUNNIGUY":8500.000000,"BUGHAPPY":8500.000000,"BIGHEAD":8000.000000,"SHRINKY":7500.000000,"WALTER":7400.000000,"FODDERMAN":7000.000000,"TONGHO":6800.000000,"SHORTSIGHT":5500.000000,"MESSIFUL":5000.500000,"HONEY":4900.000000,"GOOSEPEN":4700.000000,"CHERRY":4500.000000,"MONAPENNY":3800.000000,"KITTY":3000.450000,"PLUMHEAD":2800.000000,"STRONG":23000.000000,"BULLOZER":14800.000000,"WERTHER":14500.000000,"QUINN":14000.000000,"ORELLY":13400.000000,"BIGHORN":11000.000000,"BROWNY":10500.000000,"WHEELFOR":10030.000000,"MARTIN":10000.000000}
-SELECT Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES") FROM t1 GROUP BY DEPARTMENT;
-Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES")
+SELECT Json_Object(DEPARTMENT, Json_Object_Grp(NAME, SALARY) "Json_SALARIES") FROM t3 GROUP BY DEPARTMENT;
+Json_Object(DEPARTMENT, Json_Object_Grp(NAME, SALARY) "Json_SALARIES")
{"DEPARTMENT":"0021","SALARIES":{"STRONG":23000.000000,"SHORTSIGHT":5500.000000}}
{"DEPARTMENT":"0318","SALARIES":{"BANCROFT":9600.000000,"PLUMHEAD":2800.000000,"HONEY":4900.000000,"TONGHO":6800.000000,"WALTER":7400.000000,"SHRINKY":7500.000000,"WERTHER":14500.000000,"MERCHANT":8700.000000,"WHEELFOR":10030.000000}}
{"DEPARTMENT":"0319","SALARIES":{"BULLOZER":14800.000000,"QUINN":14000.000000,"BROWNY":10500.000000,"KITTY":3000.450000,"MONAPENNY":3800.000000,"MARTIN":10000.000000,"FUNNIGUY":8500.000000,"BUGHAPPY":8500.000000,"FODDERMAN":7000.000000,"MESSIFUL":5000.500000,"GOOSEPEN":4700.000000}}
{"DEPARTMENT":"2452","SALARIES":{"BIGHEAD":8000.000000,"ORELLY":13400.000000,"BIGHORN":11000.000000,"SMITH":9000.000000,"CHERRY":4500.000000}}
-SELECT Json_Array_Grp(NAME) from t1;
+SELECT Json_Array_Grp(NAME) FROM t3;
Json_Array_Grp(NAME)
["BANCROFT","SMITH","MERCHANT","FUNNIGUY","BUGHAPPY","BIGHEAD","SHRINKY","WALTER","FODDERMAN","TONGHO","SHORTSIGHT","MESSIFUL","HONEY","GOOSEPEN","CHERRY","MONAPENNY","KITTY","PLUMHEAD","STRONG","BULLOZER","WERTHER","QUINN","ORELLY","BIGHORN","BROWNY","WHEELFOR","MARTIN"]
+SELECT Json_Object_Key(name, title) FROM t3 WHERE DEPARTMENT = 318;
+Json_Object_Key(name, title)
+{"BANCROFT":"SALESMAN"}
+{"MERCHANT":"SALESMAN"}
+{"SHRINKY":"ADMINISTRATOR"}
+{"WALTER":"ENGINEER"}
+{"TONGHO":"ENGINEER"}
+{"HONEY":"SECRETARY"}
+{"PLUMHEAD":"TYPIST"}
+{"WERTHER":"DIRECTOR"}
+{"WHEELFOR":"SALESMAN"}
+SELECT Json_Object_Grp(name, title) FROM t3 WHERE DEPARTMENT = 318;
+Json_Object_Grp(name, title)
+{"BANCROFT":"SALESMAN","MERCHANT":"SALESMAN","SHRINKY":"ADMINISTRATOR","WALTER":"ENGINEER","TONGHO":"ENGINEER","HONEY":"SECRETARY","PLUMHEAD":"TYPIST","WERTHER":"DIRECTOR","WHEELFOR":"SALESMAN"}
+#
+# Test value getting UDF's
+#
+SELECT JsonGet_String(Json_Array_Grp(name),'[#]') FROM t3;
+JsonGet_String(Json_Array_Grp(name),'[#]')
+27
+SELECT JsonGet_String(Json_Array_Grp(name),'[","]') FROM t3;
+JsonGet_String(Json_Array_Grp(name),'[","]')
+BANCROFT,SMITH,MERCHANT,FUNNIGUY,BUGHAPPY,BIGHEAD,SHRINKY,WALTER,FODDERMAN,TONGHO,SHORTSIGHT,MESSIFUL,HONEY,GOOSEPEN,CHERRY,MONAPENNY,KITTY,PLUMHEAD,STRONG,BULLOZER,WERTHER,QUINN,ORELLY,BIGHORN,BROWNY,WHEELFOR,MARTIN
+SELECT JsonGet_String(Json_Array_Grp(name),'[>]') FROM t3;
+JsonGet_String(Json_Array_Grp(name),'[>]')
+WHEELFOR
+SET @j1 = '[45,28,36,45,89]';
+SELECT JsonGet_String(@j1,'[1]');
+JsonGet_String(@j1,'[1]')
+28
+SELECT JsonGet_String(@j1 json_,'[3]');
+JsonGet_String(@j1 json_,'[3]')
+45
+SELECT JsonGet_String(Json_Array(45,28,36,45,89),'[3]');
+JsonGet_String(Json_Array(45,28,36,45,89),'[3]')
+45
+SELECT JsonGet_String(Json_Array(45,28,36,45,89),'["+"]') "list",'=' as "egal",JsonGet_String(Json_Array(45,28,36,45,89),'[+]') "sum";
+list egal sum
+45+28+36+45+89 = 243.00
+SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]');
+JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]')
+36
+SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:*');
+JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:*')
+[36,45,89]
+SELECT JsonGet_String(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'truc');
+JsonGet_String(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'truc')
+machin
+SET @j2 = '{"qty":56,"price":3.141600,"truc":"machin","garanty":null}';
+SELECT JsonGet_String(@j2 json_,'truc');
+JsonGet_String(@j2 json_,'truc')
+machin
+SELECT JsonGet_String(@j2,'truc');
+JsonGet_String(@j2,'truc')
+machin
+SELECT JsonGet_String(@j2,'chose');
+JsonGet_String(@j2,'chose')
+NULL
+SELECT JsonGet_String(NULL json_, NULL);
+JsonGet_String(NULL json_, NULL)
+NULL
+Warnings:
+Warning 1105
+SELECT department, JsonGet_String(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department;
+department Sumsal
+0021 28500.00
+0318 72230.00
+0319 89800.95
+2452 45900.00
+SELECT JsonGet_Int(@j1, '[4]');
+JsonGet_Int(@j1, '[4]')
+89
+SELECT JsonGet_Int(@j1, '[#]');
+JsonGet_Int(@j1, '[#]')
+5
+SELECT JsonGet_Int(@j1, '[+]');
+JsonGet_Int(@j1, '[+]')
+243
+SELECT JsonGet_Int(@j1 json_, '[3]');
+JsonGet_Int(@j1 json_, '[3]')
+45
+SELECT JsonGet_Int(Json_Array(45,28,36,45,89), '[3]');
+JsonGet_Int(Json_Array(45,28,36,45,89), '[3]')
+45
+SELECT JsonGet_Int(Json_Array(45,28,36,45,89), '["+"]');
+JsonGet_Int(Json_Array(45,28,36,45,89), '["+"]')
+45
+SELECT JsonGet_Int(Json_Array(45,28,36,45,89), '[+]');
+JsonGet_Int(Json_Array(45,28,36,45,89), '[+]')
+243
+SELECT JsonGet_Int(Json_Array(json_array(45,28), json_array(36,45,89)), '[1]:[0]');
+JsonGet_Int(Json_Array(json_array(45,28), json_array(36,45,89)), '[1]:[0]')
+36
+SELECT JsonGet_Int(Json_Array(json_array(45,28), json_array(36,45,89)), '[0]:[1]');
+JsonGet_Int(Json_Array(json_array(45,28), json_array(36,45,89)), '[0]:[1]')
+28
+SELECT JsonGet_Int(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'qty');
+JsonGet_Int(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'qty')
+56
+SELECT JsonGet_Int(@j2 json_, 'price');
+JsonGet_Int(@j2 json_, 'price')
+3
+SELECT JsonGet_Int(@j2, 'qty');
+JsonGet_Int(@j2, 'qty')
+56
+SELECT JsonGet_Int('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'chose');
+JsonGet_Int('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'chose')
+NULL
+SELECT JsonGet_Int(JsonGet_String(Json_Array(Json_Array(45,28),Json_Array(36,45,89)), '[1]:*'), '[+]') sum;
+sum
+170
+SELECT department, JsonGet_Int(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"), 'salaries:[+]') Sumsal FROM t3 GROUP BY department;
+department Sumsal
+0021 28500
+0318 72230
+0319 89800
+2452 45900
+SELECT JsonGet_Real(@j1, '[2]');
+JsonGet_Real(@j1, '[2]')
+36.000000000000000
+SELECT JsonGet_Real(@j1 json_, '[3]', 2);
+JsonGet_Real(@j1 json_, '[3]', 2)
+45.00
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '[3]');
+JsonGet_Real(Json_Array(45,28,36,45,89), '[3]')
+45.000000000000000
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '["+"]');
+JsonGet_Real(Json_Array(45,28,36,45,89), '["+"]')
+45.000000000000000
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '[+]');
+JsonGet_Real(Json_Array(45,28,36,45,89), '[+]')
+243.000000000000000
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '[!]');
+JsonGet_Real(Json_Array(45,28,36,45,89), '[!]')
+48.600000000000000
+SELECT JsonGet_Real(Json_Array(json_array(45,28), json_array(36,45,89)), '[1]:[0]');
+JsonGet_Real(Json_Array(json_array(45,28), json_array(36,45,89)), '[1]:[0]')
+36.000000000000000
+SELECT JsonGet_Real(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'price');
+JsonGet_Real(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'price')
+3.141600000000000
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}' json_, 'qty');
+JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}' json_, 'qty')
+56.000000000000000
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'price');
+JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'price')
+3.141600000000000
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'price', 4);
+JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'price', 4)
+3.1416
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'chose');
+JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'chose')
+NULL
+SELECT department, JsonGet_Real(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department;
+department Sumsal
+0021 28500.000000000000000
+0318 72230.000000000000000
+0319 89800.950000000000000
+2452 45900.000000000000000
+#
+# Documentation examples
+#
+SELECT
+JsonGet_Int(Json_Array(45,28,36,45,89), '[4]') "Rank",
+JsonGet_Int(Json_Array(45,28,36,45,89), '[#]') "Number",
+JsonGet_String(Json_Array(45,28,36,45,89), '[","]') "Concat",
+JsonGet_Int(Json_Array(45,28,36,45,89), '[+]') "Sum",
+JsonGet_Real(Json_Array(45,28,36,45,89), '[!]', 2) "Avg";
+Rank Number Concat Sum Avg
+89 5 45,28,36,45,89 243 48.60
+SELECT
+JsonGet_String('{"qty":7,"price":29.50,"garanty":null}', 'price') "String",
+JsonGet_Int('{"qty":7,"price":29.50,"garanty":null}', 'price') "Int",
+JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}', 'price') "Real";
+String Int Real
+29.50 29 29.500000000000000
+SELECT JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}', 'price', 3) "Real";
+Real
+29.500
+#
+# Testing Locate
+#
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'machin');
+JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'machin')
+truc
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),56);
+JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),56)
+qty
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),3.1416);
+JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),3.1416)
+price
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'chose');
+JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'chose')
+NULL
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'Jack') Path;
+Path
+AUTHORS:[1]:FN
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'jack' ci) Path;
+Path
+AUTHORS:[1]:FN
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"Jack", "LN":"London"}' json_) Path;
+Path
+AUTHORS:[1]
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"jack", "LN":"London"}' json_) Path;
+Path
+NULL
+SELECT JsonLocate('[45,28,36,45,89]',36);
+JsonLocate('[45,28,36,45,89]',36)
+[2]
+SELECT JsonLocate('[45,28,36,45,89]' json_,28.0);
+JsonLocate('[45,28,36,45,89]' json_,28.0)
+NULL
+SELECT Json_Locate_All('[45,28,36,45,89]',10);
+Json_Locate_All('[45,28,36,45,89]',10)
+[]
+SELECT Json_Locate_All('[45,28,36,45,89]',45);
+Json_Locate_All('[45,28,36,45,89]',45)
+["[0]","[3]"]
+SELECT Json_Locate_All('[[45,28],36,45,89]',45);
+Json_Locate_All('[[45,28],36,45,89]',45)
+["[0]:[0]","[2]"]
+SELECT Json_Locate_All('[[45,28,45],36,45,89]',45);
+Json_Locate_All('[[45,28,45],36,45,89]',45)
+["[0]:[0]","[0]:[2]","[2]"]
+SELECT Json_Locate_All('[[45,28,45],36,45,89]',JsonGet_Int('[3,45]','[1]'));
+Json_Locate_All('[[45,28,45],36,45,89]',JsonGet_Int('[3,45]','[1]'))
+["[0]:[0]","[0]:[2]","[2]"]
+SELECT JsonLocate('[[45,28,45],36,45,89]',45,n) from t1;
+JsonLocate('[[45,28,45],36,45,89]',45,n)
+[0]:[0]
+[0]:[2]
+[2]
+NULL
+NULL
+SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) FROM t1;
+JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']'))
+[0]:[0]
+[0]:[2]
+[2]
+NULL
+NULL
+SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) AS `Path` FROM t1 GROUP BY n HAVING `Path` IS NOT NULL;
+Path
+[0]:[0]
+[0]:[2]
+[2]
+SELECT Json_Locate_All('[45,28,[36,45,89]]',45);
+Json_Locate_All('[45,28,[36,45,89]]',45)
+["[0]","[2]:[1]"]
+SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',JsonValue(45.0));
+Json_Locate_All('[[45,28],[36,45.0,89]]',JsonValue(45.0))
+[]
+SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',45.0);
+Json_Locate_All('[[45,28],[36,45.0,89]]',45.0)
+["[1]:[1]"]
+SELECT JsonLocate('[[45,28],[36,45,89]]','[36,45,89]' json_);
+JsonLocate('[[45,28],[36,45,89]]','[36,45,89]' json_)
+[1]
+SELECT JsonLocate('[[45,28],[36,45,89]]','[45,28]' json_);
+JsonLocate('[[45,28],[36,45,89]]','[45,28]' json_)
+[0]
+SELECT Json_Locate_All('[[45,28],[[36,45],89]]','45') "All paths";
+All paths
+[]
+SELECT Json_Locate_All('[[45,28],[[36,45],89]]','[36,45]' json_);
+Json_Locate_All('[[45,28],[[36,45],89]]','[36,45]' json_)
+["[1]:[0]"]
+SELECT JsonGet_Int(Json_Locate_All('[[45,28],[[36,45],89]]',45), '[#]') "Nb of occurs";
+Nb of occurs
+2
+SELECT Json_Locate_All('[[45,28],[[36,45],89]]',45,2);
+Json_Locate_All('[[45,28],[[36,45],89]]',45,2)
+["[0]:[0]"]
+SELECT JsonGet_String(Json_Locate_All('[45,28,36,45,89]',45),'[0]');
+JsonGet_String(Json_Locate_All('[45,28,36,45,89]',45),'[0]')
+[0]
+SELECT JsonLocate(Json_File('test/biblio.json'), 'Knab');
+JsonLocate(Json_File('test/biblio.json'), 'Knab')
+[0]:AUTHOR:[1]:LASTNAME
+SELECT Json_Locate_All('test/biblio.json' jfile_, 'Knab');
+Json_Locate_All('test/biblio.json' jfile_, 'Knab')
+["[0]:AUTHOR:[1]:LASTNAME"]
+#
+# Testing json files
+#
+SELECT Jfile_Make('[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},
+{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},
+{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},
+{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}]', 'test/fx.json', 0) AS NewFile;
+NewFile
+test/fx.json
+SELECT Jfile_Make('test/fx.json', 1);
+Jfile_Make('test/fx.json', 1)
+test/fx.json
+SELECT Jfile_Make('test/fx.json' jfile_);
+Jfile_Make('test/fx.json' jfile_)
+test/fx.json
+SELECT Jfile_Make(Jbin_File('test/fx.json'), 0);
+Jfile_Make(Jbin_File('test/fx.json'), 0)
+test/fx.json
+SELECT Json_File('test/fx.json', 1);
+Json_File('test/fx.json', 1)
+[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}]
+Warnings:
+Warning 1105 File pretty format doesn't match the specified pretty value
+SELECT Json_File('test/fx.json', 2);
+Json_File('test/fx.json', 2)
+[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}]
+Warnings:
+Warning 1105 File pretty format doesn't match the specified pretty value
+SELECT Json_File('test/fx.json', 0);
+Json_File('test/fx.json', 0)
+[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}]
+SELECT Json_File('test/fx.json', '[0]');
+Json_File('test/fx.json', '[0]')
+{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]}
+SELECT Json_File('test/fx.json', '[?]');
+Json_File('test/fx.json', '[?]')
+NULL
+Warnings:
+Warning 1105 Invalid function specification ?
+SELECT JsonGet_String(Json_File('test/fx.json'), '[1]:*');
+JsonGet_String(Json_File('test/fx.json'), '[1]:*')
+{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]}
+SELECT JsonGet_String(Json_File('test/fx.json'), '[1]');
+JsonGet_String(Json_File('test/fx.json'), '[1]')
+6 car roadster 56000 ???
+SELECT JsonGet_Int(Json_File('test/fx.json'), '[1]:mileage') AS Mileage;
+Mileage
+56000
+SELECT JsonGet_Real(Json_File('test/fx.json'), '[0]:price', 2) AS Price;
+Price
+5.65
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings');
+Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings')
+{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4,6]}
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 1, 'ratings');
+Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 1, 'ratings')
+{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,6,4]}
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings', 1);
+Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings', 1)
+{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,6,4]}
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]:ratings'), 6, 0);
+Json_Array_Add(Json_File('test/fx.json', '[2]:ratings'), 6, 0)
+[6,2,4]
+SELECT Json_Array_Delete(Json_File('test/fx.json', '[2]'), 'ratings', 1);
+Json_Array_Delete(Json_File('test/fx.json', '[2]'), 'ratings', 1)
+{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2]}
+SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 'france' origin);
+Json_Object_Add(Json_File('test/fx.json', '[2]'), 'france' origin)
+{"_id":7,"type":"food","item":"meat","origin":"france","ratings":[2,4]}
+SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 70 H, 'size');
+Json_Object_Add(Json_File('test/fx.json', '[2]'), 70 H, 'size')
+{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]}
+Warnings:
+Warning 1105 No sub-item at 'size'
+SELECT Json_Object_Add(Json_File('test/fx.json', '[3]'), 70 H, 'size');
+Json_Object_Add(Json_File('test/fx.json', '[3]'), 70 H, 'size')
+{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":70},"ratings":[5,8,7]}
+SELECT Json_Object_List(Json_File('test/fx.json', '[3]:size'));
+Json_Object_List(Json_File('test/fx.json', '[3]:size'))
+["W","L","H"]
DROP TABLE t1;
-DROP FUNCTION Json_Array;
-DROP FUNCTION Json_Array_Add;
-DROP FUNCTION Json_Object;
-DROP FUNCTION Json_Object_Nonull;
-DROP FUNCTION Json_Value;
-DROP FUNCTION Json_Array_Grp;
-DROP FUNCTION Json_Object_Grp;
+DROP TABLE t2;
+DROP TABLE t3;
diff --git a/storage/connect/mysql-test/connect/r/json_udf_bin.result b/storage/connect/mysql-test/connect/r/json_udf_bin.result
new file mode 100644
index 00000000000..4e59b51c529
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/json_udf_bin.result
@@ -0,0 +1,588 @@
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=VIR BLOCK_SIZE=3;
+#
+# Test Jbin UDF's
+#
+SELECT Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), n) from t1;
+Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), n)
+[56,3.141600,"My name is \"Foo\"",null,1]
+[56,3.141600,"My name is \"Foo\"",null,2]
+[56,3.141600,"My name is \"Foo\"",null,3]
+SELECT Json_Array_Add(Jbin_Array(n, 3.1416, 'My name is "Foo"', NULL), n) from t1;
+Json_Array_Add(Jbin_Array(n, 3.1416, 'My name is "Foo"', NULL), n)
+[1,3.141600,"My name is \"Foo\"",null,1]
+[2,3.141600,"My name is \"Foo\"",null,2]
+[3,3.141600,"My name is \"Foo\"",null,3]
+SELECT Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), Jbin_Array('a','b',n)) from t1;
+Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), Jbin_Array('a','b',n))
+[56,3.141600,"My name is \"Foo\"",null,["a","b",1]]
+[56,3.141600,"My name is \"Foo\"",null,["a","b",2]]
+[56,3.141600,"My name is \"Foo\"",null,["a","b",3]]
+SELECT Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), JsonGet_String(Jbin_Array('a','b','c'), '[1]'));
+Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), JsonGet_String(Jbin_Array('a','b','c'), '[1]'))
+[56,3.141600,"My name is \"Foo\"",null,"b"]
+SELECT Json_Array_Delete(Jbin_Array_Add_Values(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), "One more", 2), 4);
+Json_Array_Delete(Jbin_Array_Add_Values(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), "One more", 2), 4)
+[56,3.141600,"My name is \"Foo\"",null,2]
+SELECT Json_Array_Delete(Jbin_Array(56, Jbin_Array(3.1416, 'My name is "Foo"'), NULL), '[1]', 1);
+Json_Array_Delete(Jbin_Array(56, Jbin_Array(3.1416, 'My name is "Foo"'), NULL), '[1]', 1)
+[56,[3.141600],null]
+SELECT Json_Array_Delete(Jbin_Array(56, Jbin_Array(3.1416, 'My name is "Foo"'), TRUE), 1, '[1]');
+Json_Array_Delete(Jbin_Array(56, Jbin_Array(3.1416, 'My name is "Foo"'), TRUE), 1, '[1]')
+[56,[3.141600],true]
+SELECT Json_Array(1, TRUE, 0, FALSE);
+Json_Array(1, TRUE, 0, FALSE)
+[1,true,0,false]
+SELECT Json_Serialize(Jbin_Array(TRUE, FALSE));
+Json_Serialize(Jbin_Array(TRUE, FALSE))
+[true,false]
+SELECT Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL);
+Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL)
+{"qty":56,"price":3.141600,"truc":"machin","garanty":null}
+SELECT Json_Serialize(Jbin_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL));
+Json_Serialize(Jbin_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL))
+{"qty":56,"price":3.141600,"truc":"machin","garanty":null}
+SELECT Jbin_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty');
+ERROR HY000: Can't initialize function 'jbin_object_key'; This function must have an even number of arguments
+SELECT Json_Object_Add(Jbin_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color);
+Json_Object_Add(Jbin_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color)
+{"qty":56,"price":3.141600,"truc":"machin","garanty":null,"color":"blue"}
+SELECT Json_Object_Add(Jbin_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price);
+Json_Object_Add(Jbin_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price)
+{"qty":56,"price":45.990000,"truc":"machin","garanty":null}
+SELECT Json_Object_Add(Jbin_Object_Nonull(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color);
+Json_Object_Add(Jbin_Object_Nonull(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color)
+{"qty":56,"price":3.141600,"truc":"machin","color":"blue"}
+SELECT Json_Object_Add(Jbin_Object_Nonull(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price);
+Json_Object_Add(Jbin_Object_Nonull(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price)
+{"qty":56,"price":45.990000,"truc":"machin"}
+#
+# Test Jbin file UDF's
+#
+SELECT Json_Serialize(Jbin_File('gloss.json'));
+Json_Serialize(Jbin_File('gloss.json'))
+{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}}}
+SELECT JsonLocate(Jbin_File('gloss.json'),'XML');
+JsonLocate(Jbin_File('gloss.json'),'XML')
+glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso:[1]
+SELECT Json_Object_Key('first', 'foo', 'second', Jbin_Array('a', 33));
+Json_Object_Key('first', 'foo', 'second', Jbin_Array('a', 33))
+{"first":"foo","second":["a",33]}
+SELECT Json_Get_Item(Json_Array('a','b','c'), '[1]');
+Json_Get_Item(Json_Array('a','b','c'), '[1]')
+NULL
+SELECT Json_Get_Item(Json_Object('foo' AS "first", Json_Array('a', 33) AS "json_second"), 'second') AS "item";
+item
+["a",33]
+SELECT Json_Get_Item(Jbin_Object('foo' first, Jbin_Array('a', 33) jbin_second), 'second:*') item;
+item
+["a",33]
+SELECT Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv');
+Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv')
+{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}
+SELECT Json_Serialize(Jbin_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv'));
+Json_Serialize(Jbin_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv'))
+{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}
+SELECT Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv:*');
+Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv:*')
+{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}
+SELECT JsonGet_String(Json_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso') lang;
+lang
+GML
+SELECT Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso') "See also";
+See also
+["GML","XML"]
+SELECT Json_Serialize(Jbin_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso')) "See also";
+See also
+["GML","XML"]
+SELECT JsonGet_String(Json_Get_Item(Json_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso'),'[0]') lang;
+lang
+GML
+#
+# Test Item Get/Set/Insert/Update UDF's
+#
+SELECT Json_Get_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), '[]');
+Json_Get_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), '[]')
+[1,2,{"trois":3,"quatre":4}]
+SELECT Json_Get_Item(Jbin_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), '[1]');
+Json_Get_Item(Jbin_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), '[1]')
+NULL
+SELECT Json_Get_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), '[1]');
+Json_Get_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), '[1]')
+NULL
+SELECT Json_Set_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)));
+Json_Set_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)))
+[1,2,{"trois":3,"quatre":4}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 'foo');
+ERROR HY000: Can't initialize function 'json_set_item'; This function must have an odd number of arguments
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq')
+[1,2,{"trois":3,"quatre":4,"cinq":5}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 7, '[1]');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 7, '[1]')
+[1,7,{"trois":3,"quatre":4}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 7, '[1]');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 7, '[1]')
+[1,7,{"trois":3,"quatre":4,"cinq":5}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Json_Array(7, 8, 9), '[1]');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Json_Array(7, 8, 9), '[1]')
+[1,[7,8,9],{"trois":3,"quatre":4}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[2]');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[2]')
+[1,2,[7,8,9]]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[2]:*');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[2]:*')
+[1,2,{"trois":3,"quatre":4}]
+Warnings:
+Warning 1105 Invalid specification * in a write path
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 3.1416, 'foo');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 3.1416, 'foo')
+[1,2,{"trois":3,"quatre":4}]
+SELECT Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 'toto', '[1]:[2]');
+Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 'toto', '[1]:[2]')
+[1,[7,8,"toto"],{"trois":3,"quatre":4}]
+SELECT Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 300, '[2]:nxt:total:[]');
+Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 300, '[2]:nxt:total:[]')
+[1,[7,8,9],{"trois":3,"quatre":4,"nxt":{"total":[300]}}]
+SELECT Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 300, '[2]:nxt:total:[]');
+Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 300, '[2]:nxt:total:[]')
+[1,[7,8,9,10],{"trois":3,"quatre":4,"cinq":5,"nxt":{"total":[300]}}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[1]', 5, '[2]:cinq', 10, '[1]:[]');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[1]', 5, '[2]:cinq', 10, '[1]:[]')
+[1,[7,8,9,10],{"trois":3,"quatre":4,"cinq":5}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 44, '[2]:quatre');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 44, '[2]:quatre')
+[1,2,{"trois":3,"quatre":44}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, 'truc');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, 'truc')
+[1,2,{"trois":3,"quatre":4}]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '')
+[1,2,{"trois":3,"quatre":4},5]
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '*');
+Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '*')
+[1,2,{"trois":3,"quatre":4}]
+Warnings:
+Warning 1105 Invalid specification * in a write path
+SELECT Json_Serialize(Jbin_Set_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq'));
+Json_Serialize(Jbin_Set_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq'))
+[1,2,{"trois":3,"quatre":4,"cinq":5}]
+SELECT Json_Insert_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq');
+Json_Insert_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq')
+[1,2,{"trois":3,"quatre":4,"cinq":5}]
+SELECT Json_Update_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq');
+Json_Update_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq')
+[1,2,{"trois":3,"quatre":4}]
+SELECT Json_Insert_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 44, '[2]:quatre');
+Json_Insert_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 44, '[2]:quatre')
+[1,[7,8,9,10],{"trois":3,"quatre":4,"cinq":5}]
+SELECT Json_Update_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 44, '[2]:quatre');
+Json_Update_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 44, '[2]:quatre')
+[1,[7,8,9],{"trois":3,"quatre":44}]
+SELECT Json_Insert_Item(Json_Array(1, Json_Array(7, 8, 9), Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[1]', 300, '[2]:nxt:total:[]');
+Json_Insert_Item(Json_Array(1, Json_Array(7, 8, 9), Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[1]', 300, '[2]:nxt:total:[]')
+[1,[7,8,9],{"trois":3,"quatre":4,"cinq":5,"nxt":{"total":[300]}}]
+SELECT Json_Update_Item(Json_Array(1, Json_Array(7, 8, 9), Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[1]', 300, '[2]:nxt:total:[]');
+Json_Update_Item(Json_Array(1, Json_Array(7, 8, 9), Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[1]', 300, '[2]:nxt:total:[]')
+[1,[7,10,9],{"trois":3,"quatre":4}]
+SELECT Json_Insert_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[]');
+Json_Insert_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[]')
+[1,2,{"trois":3,"quatre":4},5]
+SELECT Json_Update_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[]');
+Json_Update_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[]')
+[1,2,{"trois":3,"quatre":4}]
+#
+# Test merging items UDF's
+#
+SELECT Json_Item_Merge(Jbin_Array('a','b','c'), Jbin_Array('d','e','f'));
+Json_Item_Merge(Jbin_Array('a','b','c'), Jbin_Array('d','e','f'))
+["a","b","c","d","e","f"]
+SELECT Json_Item_Merge(Json_Array('a','b','c'), Json_Array('d','e','f')) AS "Result";
+Result
+["a","b","c","d","e","f"]
+SELECT Json_Array_Add(Jbin_Item_Merge(Jbin_Array('a','b','c'), Jbin_Array('d','e','f')), 'and', 3);
+Json_Array_Add(Jbin_Item_Merge(Jbin_Array('a','b','c'), Jbin_Array('d','e','f')), 'and', 3)
+["a","b","c","and","d","e","f"]
+SELECT Json_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "d",5 "e",6 "f"));
+Json_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "d",5 "e",6 "f"))
+{"a":1,"b":2,"c":3,"d":4,"e":5,"f":6}
+SELECT Json_Item_Merge(Jbin_Object(1 "a",2 "b",2 "c"), Jbin_Array('d','e','f'));
+Json_Item_Merge(Jbin_Object(1 "a",2 "b",2 "c"), Jbin_Array('d','e','f'))
+Binary Json object
+Warnings:
+Warning 1105 Second argument is not an object
+SELECT Json_Object_Add(Jbin_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "d",5 "e",6 "f")), 'x' AS "and");
+Json_Object_Add(Jbin_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "d",5 "e",6 "f")), 'x' AS "and")
+{"a":1,"b":2,"c":3,"d":4,"e":5,"f":6,"and":"x"}
+SELECT Json_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "a",5 "e",6 "f"));
+Json_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "a",5 "e",6 "f"))
+{"a":4,"b":2,"c":3,"e":5,"f":6}
+SELECT Json_Item_Merge('foo', Json_Array('d','e','f'));
+ERROR HY000: Can't initialize function 'json_item_merge'; First argument must be a json item
+#
+# Test making file UDF's
+#
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json')
+bt1.json
+SELECT Json_File('bt1.json');
+Json_File('bt1.json')
+[
+ "a",
+ "b",
+ "c"
+]
+
+SELECT Json_File(Jfile_Make(Jbin_File('bt1.json'), 0));
+Json_File(Jfile_Make(Jbin_File('bt1.json'), 0))
+"a"
+"b"
+"c"
+
+SELECT Json_File(Jfile_Make(Jbin_File('bt1.json'), 1));
+Json_File(Jfile_Make(Jbin_File('bt1.json'), 1))
+[
+ "a",
+ "b",
+ "c"
+]
+
+SELECT Json_File(Jfile_Make(Jbin_File('bt1.json'), 2));
+Json_File(Jfile_Make(Jbin_File('bt1.json'), 2))
+[
+ "a",
+ "b",
+ "c"
+]
+
+SELECT Json_File('bt1.json', 0);
+Json_File('bt1.json', 0)
+["a","b","c"]
+Warnings:
+Warning 1105 File pretty format doesn't match the specified pretty value
+SELECT Json_File('bt1.json', 1);
+Json_File('bt1.json', 1)
+["a","b","c"]
+Warnings:
+Warning 1105 File pretty format doesn't match the specified pretty value
+SELECT Json_File('bt1.json', 2);
+Json_File('bt1.json', 2)
+["a","b","c"]
+SELECT Json_Serialize(Jbin_Array('a','b','c'));
+Json_Serialize(Jbin_Array('a','b','c'))
+["a","b","c"]
+SELECT Json_Serialize(Jbin_Array_Add(Jbin_File('not_exist.json'), 'd'));
+Json_Serialize(Jbin_Array_Add(Jbin_File('not_exist.json'), 'd'))
+Null json tree
+Warnings:
+Warning 1105 Open(map) error 2 on not_exist.json
+Warning 1105 First argument is not an array
+# This does not modify the file
+SELECT Json_Serialize(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'));
+Json_Serialize(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'))
+["a","b","c","d"]
+SELECT Json_File('bt1.json', 2);
+Json_File('bt1.json', 2)
+["a","b","c"]
+# This does modify the file
+SELECT Json_Array_Add(Jbin_File('bt1.json'), 'd');
+Json_Array_Add(Jbin_File('bt1.json'), 'd')
+bt1.json
+SELECT Json_File('bt1.json', 2);
+Json_File('bt1.json', 2)
+["a","b","c","d"]
+# Back to the original file
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json')
+bt1.json
+SELECT Json_Object(Jbin_Array_Add(Jbin_Array('a','b','c'), 'd') "Jbin_foo") AS "Result";
+Result
+{"foo":["a","b","c","d"]}
+SELECT Json_Object(Jbin_Array_Add(Jbin_File('bt1.json'), 'd')) AS "Result";
+Result
+{"Array_Add(Jbin_File('bt1.json'), 'd')":["a","b","c","d"]}
+SELECT Json_Object(Jbin_Array_Add(Jbin_File('bt1.json'), 'd') "Jbin_bt1") AS "Result";
+Result
+{"bt1":["a","b","c","d"]}
+# This does modify the file
+SELECT Json_Object(Json_Array_Add(Jbin_File('bt1.json'), 'd') "Jfile_bt1") AS "Result";
+Result
+{"bt1":["a","b","c","d"]}
+SELECT Json_File('bt1.json');
+Json_File('bt1.json')
+[
+ "a",
+ "b",
+ "c",
+ "d"
+]
+
+SELECT Json_File(Json_Array_Delete(Jbin_File('bt1.json'), 3), 2);
+Json_File(Json_Array_Delete(Jbin_File('bt1.json'), 3), 2)
+["a","b","c"]
+SELECT Json_Object(Jbin_Array_Add(Jbin_File('bt1.json'), 'd') "Jbin_bt1", n "t1") AS "Result" from t1;
+Result
+{"bt1":["a","b","c","d"],"t1":1}
+{"bt1":["a","b","c","d"],"t1":2}
+{"bt1":["a","b","c","d"],"t1":3}
+SELECT Json_File(Json_Array_Add(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'), 'e')) AS "Result";
+Result
+[
+ "a",
+ "b",
+ "c",
+ "d",
+ "e"
+]
+
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json')
+bt1.json
+SELECT Json_File(Json_Array_Add(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'), 'e')) AS "Result" from t1;
+Result
+[
+ "a",
+ "b",
+ "c",
+ "d",
+ "e"
+]
+
+[
+ "a",
+ "b",
+ "c",
+ "d",
+ "e"
+]
+
+[
+ "a",
+ "b",
+ "c",
+ "d",
+ "e"
+]
+
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json')
+bt1.json
+SELECT Json_Array_Add(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'), n) AS "Result" from t1;
+Result
+bt1.json
+bt1.json
+bt1.json
+# Show modified file
+SELECT Json_File('bt1.json');
+Json_File('bt1.json')
+[
+ "a",
+ "b",
+ "c",
+ "d",
+ 1,
+ "d",
+ 2,
+ "d",
+ 3
+]
+
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json')
+bt1.json
+SELECT Json_Array_Add(Jbin_File('bt1.json'), n) AS "Result" from t1;
+Result
+bt1.json
+bt1.json
+bt1.json
+# Show modified file
+SELECT Json_File('bt1.json');
+Json_File('bt1.json')
+[
+ "a",
+ "b",
+ "c",
+ 1,
+ 2,
+ 3
+]
+
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json')
+bt1.json
+SELECT Json_File(Jbin_Item_Merge(Jbin_File('bt1.json'), Jbin_Array('d','e','f')));
+Json_File(Jbin_Item_Merge(Jbin_File('bt1.json'), Jbin_Array('d','e','f')))
+[
+ "a",
+ "b",
+ "c"
+]
+
+SELECT Json_File(Json_Item_Merge(Jbin_File('bt1.json'), Jbin_Array('d','e','f')));
+Json_File(Json_Item_Merge(Jbin_File('bt1.json'), Jbin_Array('d','e','f')))
+[
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f"
+]
+
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json')
+bt1.json
+# Test DELETE from file
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 1)) AS "Result";
+Result
+{"Array_Delete(Jbin_File('bt1.json'), 1)":["a","c"]}
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 2) "Jbin_bt1") AS "Result";
+Result
+{"bt1":["a","b"]}
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 0) "Jbin_bt1", n "t1") AS "Result" from t1;
+Result
+{"bt1":["b","c"],"t1":1}
+{"bt1":["b","c"],"t1":2}
+{"bt1":["b","c"],"t1":3}
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 3 - n) "Jbin_bt1") AS "Result" from t1;
+Result
+{"bt1":["a","b"]}
+{"bt1":["a"]}
+{"bt1":[]}
+SELECT Json_Object(Json_Array_Delete(Jbin_File('bt1.json'), 3 - n) "Jbin_bt1") AS "Result" from t1;
+Result
+{"bt1":["a","b"]}
+{"bt1":["a"]}
+{"bt1":[]}
+# Show modified file
+SELECT Json_File('bt1.json');
+Json_File('bt1.json')
+[
+
+]
+
+# Object file
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0)
+bt2.json
+SELECT Json_File('bt2.json', 0);
+Json_File('bt2.json', 0)
+{"a":1,"b":2,"c":3}
+SELECT Json_File('bt2.json');
+Json_File('bt2.json')
+{"a":1,"b":2,"c":3}
+
+SELECT Json_Serialize(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"));
+Json_Serialize(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"))
+{"a":1,"b":2,"c":3,"d":4}
+# First query (file not modified)
+SELECT Json_Object(Jbin_Object_Add(Jbin_File('bt2.json'), 4 AS "d") AS "Jbin_new") AS "Result";
+Result
+{"new":{"a":1,"b":2,"c":3,"d":4}}
+# First query (file modified)
+SELECT Json_Object(Json_Object_Add(Jbin_File('bt2.json'), 4 AS "d") AS "Jfile_new") AS "Result";
+Result
+{"new":{"a":1,"b":2,"c":3,"d":4}}
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0)
+bt2.json
+SELECT Json_Object(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d") "Jbin_new", n "t1") AS "Result" from t1;
+Result
+{"new":{"a":1,"b":2,"c":3,"d":4},"t1":1}
+{"new":{"a":1,"b":2,"c":3,"d":4},"t1":2}
+{"new":{"a":1,"b":2,"c":3,"d":4},"t1":3}
+SELECT Json_File(Json_Object_Add(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"), 5 "e")) AS "Result";
+Result
+{"a":1,"b":2,"c":3,"d":4,"e":5}
+
+SELECT Json_Object_Add(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"), 5 "e") AS "Result" from t1;
+Result
+bt2.json
+bt2.json
+bt2.json
+SELECT Json_Object_Add(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"), n "n") AS "Result" from t1;
+Result
+bt2.json
+bt2.json
+bt2.json
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0)
+bt2.json
+SELECT Json_Object_Add(Jbin_File('bt2.json'), n) AS "Result" from t1;
+Result
+bt2.json
+bt2.json
+bt2.json
+SELECT Json_File('bt2.json');
+Json_File('bt2.json')
+{"a":1,"b":2,"c":3,"n":3}
+
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0)
+bt2.json
+SELECT Json_Serialize(Jbin_Item_Merge(Jbin_File('bt2.json'), Jbin_Object(4 "d",5 "e",6 "f"))) AS "Result";
+Result
+{"a":1,"b":2,"c":3,"d":4,"e":5,"f":6}
+SELECT Json_File(Json_Item_Merge(Jbin_File('bt2.json'), Jbin_Object(4 "d",5 "e",6 "f"))) AS "Result";
+Result
+{"a":1,"b":2,"c":3,"d":4,"e":5,"f":6}
+
+SELECT Json_Item_Merge(Json_Object(1 "a", 2 "b", 3 "c"), Json_Object(4 "d",5 "b",6 "f")) AS "Result";
+Result
+{"a":1,"b":5,"c":3,"d":4,"f":6}
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'b')) AS "Result";
+Result
+{"Object_Delete(Jbin_File('bt2.json'), 'b')":{"a":1,"c":3,"d":4,"e":5,"f":6}}
+SELECT Json_Object(Jbin_Object_Delete(Jbin_File('bt2.json'), 'c') "Jbin_bt1") AS "Result";
+Result
+{"bt1":{"a":1,"d":4,"e":5,"f":6}}
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'c') "Jbin_bt1") AS "Result";
+Result
+{"bt1":{"a":1,"d":4,"e":5,"f":6}}
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'c') "Jfile_bt1") AS "Result";
+Result
+{"bt1":{"a":1,"d":4,"e":5,"f":6}}
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'a') "Jbin_bt1", n "t1") AS "Result" from t1;
+Result
+{"bt1":{"d":4,"e":5,"f":6},"t1":1}
+{"bt1":{"d":4,"e":5,"f":6},"t1":2}
+{"bt1":{"d":4,"e":5,"f":6},"t1":3}
+SELECT Json_Serialize(Jbin_Object_List(Jbin_File('bt2.json'))) "Key list";
+Key list
+["d","e","f"]
+SELECT Jfile_Make('{"a":1, "b":[44, 55]}' json_, 'bt3.json', 0);
+Jfile_Make('{"a":1, "b":[44, 55]}' json_, 'bt3.json', 0)
+bt3.json
+SELECT Json_Array_Add(Json_File('bt3.json', 'b'), 66);
+Json_Array_Add(Json_File('bt3.json', 'b'), 66)
+[44,55,66]
+SELECT Json_Array_Add(Json_File('bt3.json'), 66, 'b');
+Json_Array_Add(Json_File('bt3.json'), 66, 'b')
+{"a":1,"b":[44,55,66]}
+SELECT Json_Array_Add(Jbin_File('bt3.json', 'b'), 66);
+Json_Array_Add(Jbin_File('bt3.json', 'b'), 66)
+bt3.json
+SELECT Json_File('bt3.json', 3);
+Json_File('bt3.json', 3)
+{"a":1,"b":[44,55,66]}
+SELECT Jfile_Make('{"a":1, "b":[44, 55]}' json_, 'bt3.json', 0);
+Jfile_Make('{"a":1, "b":[44, 55]}' json_, 'bt3.json', 0)
+bt3.json
+CREATE TABLE t2 (
+n INT KEY,
+jfile_cols CHAR(12) NOT NULL)
+ENGINE= MYISAM;
+INSERT INTO t2 VALUES(1,'bt3.json');
+# In this table, the jfile_cols column just contains a file name
+UPDATE t2 SET jfile_cols = Json_Array_Add(Jbin_File('bt3.json', 'b'), 66) WHERE n = 1;
+SELECT JsonGet_String(jfile_cols, '*') FROM t2;
+JsonGet_String(jfile_cols, '*')
+{"a":1,"b":[44,55,66]}
+UPDATE t2 SET jfile_cols = Json_Insert_Item(jfile_cols, 77, 'b:[]') WHERE n = 1;
+SELECT JsonGet_String(jfile_cols, 'b:*') FROM t2;
+JsonGet_String(jfile_cols, 'b:*')
+[44,55,66,77]
+UPDATE t2 SET jfile_cols = Json_Insert_Item(Jbin_Insert_Item(jfile_cols, 88, 'b:') , 99, 'b:') WHERE n = 1;
+SELECT JsonGet_String(jfile_cols, '*') FROM t2;
+JsonGet_String(jfile_cols, '*')
+{"a":1,"b":[44,55,66,77,88,99]}
+DROP TABLE t1, t2;
diff --git a/storage/connect/mysql-test/connect/r/mysql_grant.result b/storage/connect/mysql-test/connect/r/mysql_grant.result
index 554e6f47ba1..5f630834392 100644
--- a/storage/connect/mysql-test/connect/r/mysql_grant.result
+++ b/storage/connect/mysql-test/connect/r/mysql_grant.result
@@ -1,8 +1,10 @@
#
# Testing FILE privilege
#
+set sql_mode="";
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
+set sql_mode=default;
SELECT user();
user()
user@localhost
diff --git a/storage/connect/mysql-test/connect/r/xml_grant.result b/storage/connect/mysql-test/connect/r/xml_grant.result
index f6dc725b259..9eb818bf32f 100644
--- a/storage/connect/mysql-test/connect/r/xml_grant.result
+++ b/storage/connect/mysql-test/connect/r/xml_grant.result
@@ -3,6 +3,7 @@ Warning 1105 No file name. Table will use t1.xml
#
# Beginning of grant.inc
#
+CREATE USER user@localhost;
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
SELECT user();
diff --git a/storage/connect/mysql-test/connect/std_data/bib0.json b/storage/connect/mysql-test/connect/std_data/bib0.json
new file mode 100644
index 00000000000..2dabf0fd4b3
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/bib0.json
@@ -0,0 +1,2 @@
+{"ISBN":"9782212090819","LANG":"fr","SUBJECT":"applications","AUTHOR":[{"FIRSTNAME":"Jean-Michel","LASTNAME":"Bernadac"},{"FIRSTNAME":"François","LASTNAME":"Knab"}],"TITLE":"Construire une application XML","PUBLISHER":{"NAME":"Eyrolles","PLACE":"Paris"},"DATEPUB":1999}
+{"ISBN":"9782840825685","LANG":"fr","SUBJECT":"applications","AUTHOR":[{"FIRSTNAME":"William J.","LASTNAME":"Pardi"}],"TITLE":"XML en Action","TRANSLATED":{"PREFIX":"adapté de l'anglais par","TRANSLATOR":{"FIRSTNAME":"James","LASTNAME":"Guerin"}},"PUBLISHER":{"NAME":"Microsoft Press","PLACE":"Paris"},"DATEPUB":2001}
diff --git a/storage/connect/mysql-test/connect/std_data/biblio.json b/storage/connect/mysql-test/connect/std_data/biblio.json
index cc24b162cd9..dcf9592296c 100644
--- a/storage/connect/mysql-test/connect/std_data/biblio.json
+++ b/storage/connect/mysql-test/connect/std_data/biblio.json
@@ -24,12 +24,10 @@
"ISBN": "9782840825685",
"LANG": "fr",
"SUBJECT": "applications",
- "AUTHOR": [
- {
- "FIRSTNAME": "William J.",
- "LASTNAME": "Pardi"
- }
- ],
+ "AUTHOR": {
+ "FIRSTNAME": "William J.",
+ "LASTNAME": "Pardi"
+ },
"TITLE": "XML en Action",
"TRANSLATION": "adapté de l'anglais par",
"TRANSLATOR": {
diff --git a/storage/connect/mysql-test/connect/std_data/gloss.json b/storage/connect/mysql-test/connect/std_data/gloss.json
new file mode 100644
index 00000000000..cfe3476cd00
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/gloss.json
@@ -0,0 +1,22 @@
+{
+ "glossary": {
+ "title": "example glossary",
+ "GlossDiv": {
+ "title": "S",
+ "GlossList": {
+ "GlossEntry": {
+ "ID": "SGML",
+ "SortAs": "SGML",
+ "GlossTerm": "Standard Generalized Markup Language",
+ "Acronym": "SGML",
+ "Abbrev": "ISO 8879:1986",
+ "GlossDef": {
+ "para": "A meta-markup language, used to create markup languages such as DocBook.",
+ "GlossSeeAlso": ["GML", "XML"]
+ },
+ "GlossSee": "markup"
+ }
+ }
+ }
+ }
+}
diff --git a/storage/connect/mysql-test/connect/t/datest.test b/storage/connect/mysql-test/connect/t/datest.test
index 7fe5fcea8ff..e9ee28ed550 100644
--- a/storage/connect/mysql-test/connect/t/datest.test
+++ b/storage/connect/mysql-test/connect/t/datest.test
@@ -14,3 +14,15 @@ SELECT id, DAYNAME(dat) FROM t1;
SELECT id, DAYNAME(datim) FROM t1 LIMIT 1;
SELECT id, TIME(tim) FROM t1 LIMIT 1;
DROP TABLE t1;
+
+--echo #
+--echo # Testing use of dates in where clause (MDEV-8926)
+--echo #
+CREATE TABLE t1 (col1 DATE) ENGINE=CONNECT TABLE_TYPE=CSV;
+INSERT INTO t1 VALUES('2015-01-01'),('2015-02-01'),('2015-03-01'),('2015-04-01');
+SELECT * FROM t1 WHERE col1 = '2015-02-01';
+SELECT * FROM t1 WHERE col1 > '2015-02-01';
+SELECT * FROM t1 WHERE col1 >= '2015-02-01';
+SELECT * FROM t1 WHERE col1 < '2015-02-01';
+SELECT * FROM t1 WHERE col1 <= '2015-02-01';
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/t/grant.inc b/storage/connect/mysql-test/connect/t/grant.inc
index 8f605a7c1d3..6580c845b56 100644
--- a/storage/connect/mysql-test/connect/t/grant.inc
+++ b/storage/connect/mysql-test/connect/t/grant.inc
@@ -1,6 +1,7 @@
--echo #
--echo # Beginning of grant.inc
--echo #
+CREATE USER user@localhost;
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
--connect(user,localhost,user,,)
diff --git a/storage/connect/mysql-test/connect/t/grant.test b/storage/connect/mysql-test/connect/t/grant.test
index 31a596ce078..738f156d8a4 100644
--- a/storage/connect/mysql-test/connect/t/grant.test
+++ b/storage/connect/mysql-test/connect/t/grant.test
@@ -1,4 +1,5 @@
-- source include/not_embedded.inc
+set sql_mode="";
let $MYSQLD_DATADIR= `select @@datadir`;
@@ -92,4 +93,4 @@ let $TABLE_OPTIONS=TABLE_TYPE=VEC MAX_ROWS=100;
let $FILE_EXT=VEC;
--source grant.inc
-
+set sql_mode=default;
diff --git a/storage/connect/mysql-test/connect/t/ini_grant.test b/storage/connect/mysql-test/connect/t/ini_grant.test
index ebc7a80b8f7..b0ddcb57979 100644
--- a/storage/connect/mysql-test/connect/t/ini_grant.test
+++ b/storage/connect/mysql-test/connect/t/ini_grant.test
@@ -5,8 +5,10 @@ let $MYSQLD_DATADIR= `select @@datadir`;
--echo #
--echo # Checking FILE privileges
--echo #
+set sql_mode="";
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
+set sql_mode=default;
--connect(user,localhost,user,,)
--connection user
SELECT user();
diff --git a/storage/connect/mysql-test/connect/t/json.test b/storage/connect/mysql-test/connect/t/json.test
index 1cc2c054db4..fa962d3b302 100644
--- a/storage/connect/mysql-test/connect/t/json.test
+++ b/storage/connect/mysql-test/connect/t/json.test
@@ -4,6 +4,7 @@
let $MYSQLD_DATADIR= `select @@datadir`;
--copy_file $MTR_SUITE_DIR/std_data/biblio.json $MYSQLD_DATADIR/test/biblio.json
+--copy_file $MTR_SUITE_DIR/std_data/bib0.json $MYSQLD_DATADIR/test/bib0.json
--copy_file $MTR_SUITE_DIR/std_data/expense.json $MYSQLD_DATADIR/test/expense.json
--copy_file $MTR_SUITE_DIR/std_data/mulexp3.json $MYSQLD_DATADIR/test/mulexp3.json
--copy_file $MTR_SUITE_DIR/std_data/mulexp4.json $MYSQLD_DATADIR/test/mulexp4.json
@@ -116,6 +117,33 @@ SELECT * FROM t1;
DROP TABLE t1;
--echo #
+--echo # Testing a pretty=0 file
+--echo #
+CREATE TABLE t1
+(
+ ISBN CHAR(15) NOT NULL,
+ Language CHAR(2) FIELD_FORMAT='LANG',
+ Subject CHAR(32) FIELD_FORMAT='SUBJECT',
+ AuthorFN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:FIRSTNAME',
+ AuthorLN CHAR(128) FIELD_FORMAT='AUTHOR:[X]:LASTNAME',
+ Title CHAR(32) FIELD_FORMAT='TITLE',
+ Translation CHAR(32) FIELD_FORMAT='TRANSLATED:PREFIX',
+ TranslatorFN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:FIRSTNAME',
+ TranslatorLN CHAR(80) FIELD_FORMAT='TRANSLATED:TRANSLATOR:LASTNAME',
+ Publisher CHAR(20) FIELD_FORMAT='PUBLISHER:NAME',
+ Location CHAR(16) FIELD_FORMAT='PUBLISHER:PLACE',
+ Year int(4) FIELD_FORMAT='DATEPUB',
+ INDEX IX(ISBN)
+)
+ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bib0.json' LRECL=320 OPTION_LIST='Pretty=0';
+SHOW INDEX FROM t1;
+SELECT * FROM t1;
+DESCRIBE SELECT * FROM t1 WHERE ISBN = '9782212090819';
+--error ER_GET_ERRMSG
+UPDATE t1 SET AuthorFN = 'Philippe' WHERE ISBN = '9782212090819';
+DROP TABLE t1;
+
+--echo #
--echo # A file with 2 arrays
--echo #
CREATE TABLE t1 (
@@ -258,6 +286,8 @@ DROP TABLE t1, t2, t3, t4;
# Clean up
#
--remove_file $MYSQLD_DATADIR/test/biblio.json
+--remove_file $MYSQLD_DATADIR/test/bib0.dnx
+--remove_file $MYSQLD_DATADIR/test/bib0.json
--remove_file $MYSQLD_DATADIR/test/expense.json
--remove_file $MYSQLD_DATADIR/test/mulexp3.json
--remove_file $MYSQLD_DATADIR/test/mulexp4.json
diff --git a/storage/connect/mysql-test/connect/t/json_udf.inc b/storage/connect/mysql-test/connect/t/json_udf.inc
index 098fff3663e..f17d59832f3 100644
--- a/storage/connect/mysql-test/connect/t/json_udf.inc
+++ b/storage/connect/mysql-test/connect/t/json_udf.inc
@@ -9,28 +9,50 @@ if (!$HA_CONNECT_SO) {
--skip Needs a dynamically built ha_connect.so
}
-let $is_win = `select convert(@@version_compile_os using latin1) IN ("Win32","Win64","Windows")`;
+--eval CREATE FUNCTION json_array RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_array_add RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_array_add_values RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_array_delete RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_object RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_object_nonull RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_object_key RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_object_add RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_object_delete RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_object_list RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jsonvalue RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE AGGREGATE FUNCTION json_array_grp RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE AGGREGATE FUNCTION json_object_grp RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jsonget_string RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jsonget_int RETURNS INTEGER SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jsonget_real RETURNS REAL SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jsonlocate RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_locate_all RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_file RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jfile_make RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jsoncontains RETURNS INTEGER SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jsoncontains_path RETURNS INTEGER SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_get_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_set_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_insert_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_update_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_item_merge RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION json_serialize RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_array RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_array_add_values RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_array_add RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_array_delete RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_object RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_object_nonull RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_object_key RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_object_add RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_object_delete RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_object_list RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_get_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_item_merge RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_set_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_insert_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_update_item RETURNS STRING SONAME '$HA_CONNECT_SO';
+--eval CREATE FUNCTION jbin_file RETURNS STRING SONAME '$HA_CONNECT_SO';
-if ($is_win)
-{
---eval CREATE FUNCTION Json_Array RETURNS STRING SONAME 'ha_connect.dll';
---eval CREATE FUNCTION Json_Array_Add RETURNS STRING SONAME 'ha_connect.dll';
---eval CREATE FUNCTION Json_Object RETURNS STRING SONAME 'ha_connect.dll';
---eval CREATE FUNCTION Json_Object_Nonull RETURNS STRING SONAME 'ha_connect.dll';
---eval CREATE FUNCTION Json_Value returns STRING SONAME 'ha_connect.dll';
---eval CREATE AGGREGATE FUNCTION Json_Array_Grp RETURNS STRING SONAME 'ha_connect.dll';
---eval CREATE AGGREGATE FUNCTION Json_Object_Grp RETURNS STRING SONAME 'ha_connect.dll';
-}
-
-if (!$is_win)
-{
---eval CREATE FUNCTION Json_Array RETURNS STRING SONAME 'ha_connect.so';
---eval CREATE FUNCTION Json_Array_Add RETURNS STRING SONAME 'ha_connect.so';
---eval CREATE FUNCTION Json_Object RETURNS STRING SONAME 'ha_connect.so';
---eval CREATE FUNCTION Json_Object_Nonull RETURNS STRING SONAME 'ha_connect.so';
---eval CREATE FUNCTION Json_Value returns STRING SONAME 'ha_connect.so';
---eval CREATE AGGREGATE FUNCTION Json_Array_Grp RETURNS STRING SONAME 'ha_connect.so';
---eval CREATE AGGREGATE FUNCTION Json_Object_Grp RETURNS STRING SONAME 'ha_connect.so';
-}
--enable_query_log
diff --git a/storage/connect/mysql-test/connect/t/json_udf.test b/storage/connect/mysql-test/connect/t/json_udf.test
index b4427517ca5..cfd1fdae258 100644
--- a/storage/connect/mysql-test/connect/t/json_udf.test
+++ b/storage/connect/mysql-test/connect/t/json_udf.test
@@ -5,35 +5,78 @@ let $MYSQLD_DATADIR= `select @@datadir`;
--copy_file $MTR_SUITE_DIR/std_data/biblio.json $MYSQLD_DATADIR/test/biblio.json
--copy_file $MTR_SUITE_DIR/std_data/employee.dat $MYSQLD_DATADIR/test/employee.dat
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=VIR BLOCK_SIZE=5;
+
--echo #
--echo # Test UDF's with constant arguments
--echo #
+--error ER_CANT_INITIALIZE_UDF
+SELECT JsonValue(56, 3.1416, 'foo', NULL);
+SELECT JsonValue(3.1416);
+SELECT JsonValue(-80);
+SELECT JsonValue('foo');
+SELECT JsonValue(9223372036854775807);
+SELECT JsonValue(NULL);
+SELECT JsonValue(TRUE);
+SELECT JsonValue(FALSE);
+SELECT JsonValue();
+SELECT JsonValue('[11, 22, 33]' json_) FROM t1;
+#
SELECT Json_Array();
-SELECT Json_Object(56,3.1416,'foo',NULL);
-SELECT Json_Object(56 qty,3.1416 price,'foo' truc, NULL garanty);
-SELECT Json_Array(56,3.1416,'My name is "Foo"',NULL);
+SELECT Json_Array(56, 3.1416, 'My name is "Foo"', NULL);
+SELECT Json_Array(Json_Array(56, 3.1416, 'foo'), TRUE);
+#
--error ER_CANT_INITIALIZE_UDF
-SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL)) Array;
-SELECT Json_Array_Add(Json_Array(56,3.1416,'foo',NULL),'One more') Array;
-SELECT Json_Array_Add(Json_Value('one value'),'One more');
+SELECT Json_Array_Add(Json_Array(56, 3.1416, 'foo', NULL)) Array;
+SELECT Json_Array_Add(Json_Array(56, 3.1416, 'foo', NULL), 'One more') Array;
--error ER_CANT_INITIALIZE_UDF
-SELECT Json_Array_Add('one value','One more');
-SELECT Json_Array_Add('one value' json_,'One more');
+SELECT Json_Array_Add(JsonValue('one value'), 'One more');
--error ER_CANT_INITIALIZE_UDF
-SELECT Json_Value(56,3.1416,'foo',NULL);
-SELECT Json_Value(3.1416);
-SELECT Json_Value('foo');
-SELECT Json_Value(NULL);
-SELECT Json_Value();
+SELECT Json_Array_Add('one value', 'One more');
+SELECT Json_Array_Add('one value' json_, 'One more');
+--error ER_CANT_INITIALIZE_UDF
+SELECT Json_Array_Add(5 json_, 'One more');
+SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 0);
+SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 2) Array;
+SELECT Json_Array_Add('[5,3,8,7,9]' json_, 4, 9);
+SELECT Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), '[2]', 33, 1);
+SELECT Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), 33, '[2]', 1);
+SELECT Json_Array_Add(Json_Array(1, 2, Json_Array(11, 22)), 33, 1, '[2]');
+#
+SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin', NULL), 'One more', 'Two more') Array;
+SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), 'One more', 'Two more') Array FROM t1;
+SELECT Json_Array_Add_Values(Json_Array(56, 3.1416, 'machin'), n) Array FROM t1;
+SELECT Json_Array_Add_Values(Json_Array(n, 3.1416, 'machin'), n) Array FROM t1;
+SELECT Json_Array_Add_Values('[56]', 3.1416, 'machin') Array;
+#
+SELECT Json_Array_Delete(Json_Array(56, 3.1416, 'My name is "Foo"', NULL), 0);
+SELECT Json_Array_Delete(Json_Object(56, 3.1416, 'My name is Foo', NULL), 2);
+SELECT Json_Array_Delete(Json_Array(56, 3.1416, 'My name is "Foo"', NULL), '2');
+SELECT Json_Array_Delete(json_array(56, 3.1416, 'My name is "Foo"', NULL), '2', 2);
+#
+SELECT Json_Object(56, 3.1416, 'foo', NULL);
+SELECT Json_Object(56 qty, 3.1416 price, 'foo' truc, NULL garanty);
SELECT Json_Object();
-SELECT Json_Object(Json_Array(56,3.1416,'foo'),NULL);
-SELECT Json_Array(Json_Array(56,3.1416,'foo'),NULL);
-SELECT Json_Array(Json_Object(56 "qty",3.1416 "price",'foo'),NULL);
+SELECT Json_Object(Json_Array(56, 3.1416, 'foo'), NULL);
+SELECT Json_Array(Json_Object(56 "qty", 3.1416 "price", 'foo') ,NULL);
+SELECT Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL);
+--error ER_CANT_INITIALIZE_UDF
+SELECT Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty');
+#
+SELECT Json_Object_Add(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color);
+SELECT Json_Object_Add(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price);
+SELECT Json_Object_Add(Json_File('notexist.json'), 'cheese' item, '[1]', 1);
+#
+SELECT Json_Object_Delete(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'truc');
+SELECT Json_Object_Delete(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'chose');
+#
+SELECT Json_Object_List(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty)) "Key List";
+SELECT Json_Object_List('{"qty":56, "price":3.1416, "truc":"machin", "garanty":null}') "Key List";
--echo #
--echo # Test UDF's with column arguments
--echo #
-CREATE TABLE t1
+CREATE TABLE t2
(
ISBN CHAR(15),
LANG CHAR(2),
@@ -46,14 +89,13 @@ CREATE TABLE t1
DATEPUB int(4)
) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='biblio.json';
-SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t1;
-SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t1;
+SELECT Json_Array(AUTHOR, TITLE, DATEPUB) FROM t2;
+SELECT Json_Object(AUTHOR, TITLE, DATEPUB) FROM t2;
--error ER_CANT_INITIALIZE_UDF
-SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t1;
-SELECT Json_Array_Grp(TITLE) FROM t1;
-DROP TABLE t1;
+SELECT Json_Array_Grp(TITLE, DATEPUB) FROM t2;
+SELECT Json_Array_Grp(TITLE) FROM t2;
-CREATE TABLE t1 (
+CREATE TABLE t3 (
SERIALNO CHAR(5) NOT NULL,
NAME VARCHAR(12) NOT NULL FLAG=6,
SEX SMALLINT(1) NOT NULL,
@@ -64,30 +106,160 @@ CREATE TABLE t1 (
SALARY DOUBLE(8,2) NOT NULL FLAG=52
) ENGINE=CONNECT TABLE_TYPE=FIX BLOCK_SIZE=8 FILE_NAME='employee.dat' ENDING=1;
-SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t1 WHERE NAME = 'MERCHANT';
-SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t1 GROUP BY DEPARTMENT;
-set connect_json_grp_size=30;
-SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t1 GROUP BY DEPARTMENT;
-SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t1 GROUP BY DEPARTMENT;
-SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT;
-SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t1 GROUP BY DEPARTMENT, TITLE;
+SELECT Json_Object(SERIALNO, NAME, TITLE, SALARY) FROM t3 WHERE NAME = 'MERCHANT';
+SELECT DEPARTMENT, Json_Array_Grp(NAME) FROM t3 GROUP BY DEPARTMENT;
+SET connect_json_grp_size=30;
+SELECT Json_Object(title, Json_Array_Grp(name) `json_names`) from t3 GROUP BY title;
+SELECT Json_Array(DEPARTMENT, Json_Array_Grp(NAME)) FROM t3 GROUP BY DEPARTMENT;
+SELECT Json_Object(DEPARTMENT, Json_Array_Grp(NAME) json_NAMES) FROM t3 GROUP BY DEPARTMENT;
+SELECT Json_Object(DEPARTMENT, Json_Array_Grp(Json_Object(SERIALNO, NAME, TITLE, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT;
+SELECT Json_Object(DEPARTMENT, TITLE, Json_Array_Grp(Json_Object(SERIALNO, NAME, SALARY)) json_EMPLOYES) FROM t3 GROUP BY DEPARTMENT, TITLE;
--error ER_CANT_INITIALIZE_UDF
-SELECT Json_Object_Grp(SALARY) FROM t1;
-SELECT Json_Object_Grp(SALARY, NAME) FROM t1;
-SELECT Json_Object(DEPARTMENT, Json_Object_Grp(SALARY, NAME) "Json_SALARIES") FROM t1 GROUP BY DEPARTMENT;
-SELECT Json_Array_Grp(NAME) from t1;
-DROP TABLE t1;
+SELECT Json_Object_Grp(SALARY) FROM t3;
+SELECT Json_Object_Grp(NAME, SALARY) FROM t3;
+SELECT Json_Object(DEPARTMENT, Json_Object_Grp(NAME, SALARY) "Json_SALARIES") FROM t3 GROUP BY DEPARTMENT;
+SELECT Json_Array_Grp(NAME) FROM t3;
+#
+SELECT Json_Object_Key(name, title) FROM t3 WHERE DEPARTMENT = 318;
+SELECT Json_Object_Grp(name, title) FROM t3 WHERE DEPARTMENT = 318;
-DROP FUNCTION Json_Array;
-DROP FUNCTION Json_Array_Add;
-DROP FUNCTION Json_Object;
-DROP FUNCTION Json_Object_Nonull;
-DROP FUNCTION Json_Value;
-DROP FUNCTION Json_Array_Grp;
-DROP FUNCTION Json_Object_Grp;
+--echo #
+--echo # Test value getting UDF's
+--echo #
+SELECT JsonGet_String(Json_Array_Grp(name),'[#]') FROM t3;
+SELECT JsonGet_String(Json_Array_Grp(name),'[","]') FROM t3;
+SELECT JsonGet_String(Json_Array_Grp(name),'[>]') FROM t3;
+SET @j1 = '[45,28,36,45,89]';
+SELECT JsonGet_String(@j1,'[1]');
+SELECT JsonGet_String(@j1 json_,'[3]');
+SELECT JsonGet_String(Json_Array(45,28,36,45,89),'[3]');
+SELECT JsonGet_String(Json_Array(45,28,36,45,89),'["+"]') "list",'=' as "egal",JsonGet_String(Json_Array(45,28,36,45,89),'[+]') "sum";
+SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:[0]');
+SELECT JsonGet_String(Json_Array(json_array(45,28),json_array(36,45,89)),'[1]:*');
+SELECT JsonGet_String(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'truc');
+SET @j2 = '{"qty":56,"price":3.141600,"truc":"machin","garanty":null}';
+SELECT JsonGet_String(@j2 json_,'truc');
+SELECT JsonGet_String(@j2,'truc');
+SELECT JsonGet_String(@j2,'chose');
+SELECT JsonGet_String(NULL json_, NULL);
+SELECT department, JsonGet_String(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department;
+#
+SELECT JsonGet_Int(@j1, '[4]');
+SELECT JsonGet_Int(@j1, '[#]');
+SELECT JsonGet_Int(@j1, '[+]');
+SELECT JsonGet_Int(@j1 json_, '[3]');
+SELECT JsonGet_Int(Json_Array(45,28,36,45,89), '[3]');
+SELECT JsonGet_Int(Json_Array(45,28,36,45,89), '["+"]');
+SELECT JsonGet_Int(Json_Array(45,28,36,45,89), '[+]');
+SELECT JsonGet_Int(Json_Array(json_array(45,28), json_array(36,45,89)), '[1]:[0]');
+SELECT JsonGet_Int(Json_Array(json_array(45,28), json_array(36,45,89)), '[0]:[1]');
+SELECT JsonGet_Int(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'qty');
+SELECT JsonGet_Int(@j2 json_, 'price');
+SELECT JsonGet_Int(@j2, 'qty');
+SELECT JsonGet_Int('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'chose');
+SELECT JsonGet_Int(JsonGet_String(Json_Array(Json_Array(45,28),Json_Array(36,45,89)), '[1]:*'), '[+]') sum;
+SELECT department, JsonGet_Int(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"), 'salaries:[+]') Sumsal FROM t3 GROUP BY department;
+#
+SELECT JsonGet_Real(@j1, '[2]');
+SELECT JsonGet_Real(@j1 json_, '[3]', 2);
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '[3]');
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '["+"]');
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '[+]');
+SELECT JsonGet_Real(Json_Array(45,28,36,45,89), '[!]');
+SELECT JsonGet_Real(Json_Array(json_array(45,28), json_array(36,45,89)), '[1]:[0]');
+SELECT JsonGet_Real(Json_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'price');
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}' json_, 'qty');
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'price');
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'price', 4);
+SELECT JsonGet_Real('{"qty":56,"price":3.141600,"truc":"machin","garanty":null}', 'chose');
+SELECT department, JsonGet_Real(Json_Object(department, Json_Array_Grp(salary) "Json_salaries"),'salaries:[+]') Sumsal FROM t3 GROUP BY department;
+
+--echo #
+--echo # Documentation examples
+--echo #
+SELECT
+ JsonGet_Int(Json_Array(45,28,36,45,89), '[4]') "Rank",
+ JsonGet_Int(Json_Array(45,28,36,45,89), '[#]') "Number",
+ JsonGet_String(Json_Array(45,28,36,45,89), '[","]') "Concat",
+ JsonGet_Int(Json_Array(45,28,36,45,89), '[+]') "Sum",
+ JsonGet_Real(Json_Array(45,28,36,45,89), '[!]', 2) "Avg";
+SELECT
+ JsonGet_String('{"qty":7,"price":29.50,"garanty":null}', 'price') "String",
+ JsonGet_Int('{"qty":7,"price":29.50,"garanty":null}', 'price') "Int",
+ JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}', 'price') "Real";
+SELECT JsonGet_Real('{"qty":7,"price":29.50,"garanty":null}', 'price', 3) "Real";
+
+--echo #
+--echo # Testing Locate
+--echo #
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'machin');
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),56);
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),3.1416);
+SELECT JsonLocate(Json_Object(56 qty,3.1416 price,'machin' truc, NULL garanty),'chose');
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'Jack') Path;
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, 'jack' ci) Path;
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"Jack", "LN":"London"}' json_) Path;
+SELECT JsonLocate('{"AUTHORS":[{"FN":"Jules", "LN":"Verne"}, {"FN":"Jack", "LN":"London"}]}' json_, '{"FN":"jack", "LN":"London"}' json_) Path;
+SELECT JsonLocate('[45,28,36,45,89]',36);
+SELECT JsonLocate('[45,28,36,45,89]' json_,28.0);
+SELECT Json_Locate_All('[45,28,36,45,89]',10);
+SELECT Json_Locate_All('[45,28,36,45,89]',45);
+SELECT Json_Locate_All('[[45,28],36,45,89]',45);
+SELECT Json_Locate_All('[[45,28,45],36,45,89]',45);
+SELECT Json_Locate_All('[[45,28,45],36,45,89]',JsonGet_Int('[3,45]','[1]'));
+SELECT JsonLocate('[[45,28,45],36,45,89]',45,n) from t1;
+SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) FROM t1;
+SELECT JsonGet_String(Json_Locate_All('[[45,28,45],36,45,89]',45),concat('[',n-1,']')) AS `Path` FROM t1 GROUP BY n HAVING `Path` IS NOT NULL;
+SELECT Json_Locate_All('[45,28,[36,45,89]]',45);
+SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',JsonValue(45.0));
+SELECT Json_Locate_All('[[45,28],[36,45.0,89]]',45.0);
+SELECT JsonLocate('[[45,28],[36,45,89]]','[36,45,89]' json_);
+SELECT JsonLocate('[[45,28],[36,45,89]]','[45,28]' json_);
+SELECT Json_Locate_All('[[45,28],[[36,45],89]]','45') "All paths";
+SELECT Json_Locate_All('[[45,28],[[36,45],89]]','[36,45]' json_);
+SELECT JsonGet_Int(Json_Locate_All('[[45,28],[[36,45],89]]',45), '[#]') "Nb of occurs";
+SELECT Json_Locate_All('[[45,28],[[36,45],89]]',45,2);
+SELECT JsonGet_String(Json_Locate_All('[45,28,36,45,89]',45),'[0]');
+SELECT JsonLocate(Json_File('test/biblio.json'), 'Knab');
+SELECT Json_Locate_All('test/biblio.json' jfile_, 'Knab');
+
+--echo #
+--echo # Testing json files
+--echo #
+SELECT Jfile_Make('[{"_id":5,"type":"food","item":"beer","taste":"light","price":5.65,"ratings":[5,8,9]},
+{"_id":6,"type":"car","item":"roadster","mileage":56000,"ratings":[6,9]},
+{"_id":7,"type":"food","item":"meat","origin":"argentina","ratings":[2,4]},
+{"_id":8,"type":"furniture","item":"table","size":{"W":60,"L":80,"H":40},"ratings":[5,8,7]}]', 'test/fx.json', 0) AS NewFile;
+SELECT Jfile_Make('test/fx.json', 1);
+SELECT Jfile_Make('test/fx.json' jfile_);
+SELECT Jfile_Make(Jbin_File('test/fx.json'), 0);
+SELECT Json_File('test/fx.json', 1);
+SELECT Json_File('test/fx.json', 2);
+SELECT Json_File('test/fx.json', 0);
+SELECT Json_File('test/fx.json', '[0]');
+SELECT Json_File('test/fx.json', '[?]');
+SELECT JsonGet_String(Json_File('test/fx.json'), '[1]:*');
+SELECT JsonGet_String(Json_File('test/fx.json'), '[1]');
+SELECT JsonGet_Int(Json_File('test/fx.json'), '[1]:mileage') AS Mileage;
+SELECT JsonGet_Real(Json_File('test/fx.json'), '[0]:price', 2) AS Price;
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings');
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 1, 'ratings');
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]'), 6, 'ratings', 1);
+SELECT Json_Array_Add(Json_File('test/fx.json', '[2]:ratings'), 6, 0);
+SELECT Json_Array_Delete(Json_File('test/fx.json', '[2]'), 'ratings', 1);
+SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 'france' origin);
+SELECT Json_Object_Add(Json_File('test/fx.json', '[2]'), 70 H, 'size');
+SELECT Json_Object_Add(Json_File('test/fx.json', '[3]'), 70 H, 'size');
+SELECT Json_Object_List(Json_File('test/fx.json', '[3]:size'));
+
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
#
# Clean up
#
+--source json_udf2.inc
--remove_file $MYSQLD_DATADIR/test/biblio.json
--remove_file $MYSQLD_DATADIR/test/employee.dat
+--remove_file $MYSQLD_DATADIR/test/fx.json
diff --git a/storage/connect/mysql-test/connect/t/json_udf2.inc b/storage/connect/mysql-test/connect/t/json_udf2.inc
new file mode 100644
index 00000000000..f62b178b003
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/json_udf2.inc
@@ -0,0 +1,49 @@
+--disable_query_log
+
+DROP FUNCTION json_array;
+DROP FUNCTION json_array_add;
+DROP FUNCTION json_array_add_values;
+DROP FUNCTION json_array_delete;
+DROP FUNCTION json_object;
+DROP FUNCTION json_object_nonull;
+DROP FUNCTION json_object_key;
+DROP FUNCTION json_object_add;
+DROP FUNCTION json_object_delete;
+DROP FUNCTION json_object_list;
+DROP FUNCTION jsonvalue;
+DROP FUNCTION json_array_grp;
+DROP FUNCTION json_object_grp;
+DROP FUNCTION jsonget_string;
+DROP FUNCTION jsonget_int;
+DROP FUNCTION jsonget_real;
+DROP FUNCTION jsonlocate;
+DROP FUNCTION json_locate_all;
+DROP FUNCTION json_file;
+DROP FUNCTION jfile_make;
+DROP FUNCTION json_get_item;
+DROP FUNCTION json_item_merge;
+DROP FUNCTION jsoncontains;
+DROP FUNCTION jsoncontains_path;
+DROP FUNCTION json_set_item;
+DROP FUNCTION json_insert_item;
+DROP FUNCTION json_update_item;
+DROP FUNCTION json_serialize;
+DROP FUNCTION jbin_array;
+DROP FUNCTION jbin_array_add_values;
+DROP FUNCTION jbin_array_add;
+DROP FUNCTION jbin_array_delete;
+DROP FUNCTION jbin_object;
+DROP FUNCTION jbin_object_nonull;
+DROP FUNCTION jbin_object_key;
+DROP FUNCTION jbin_object_add;
+DROP FUNCTION jbin_object_delete;
+DROP FUNCTION jbin_object_list;
+DROP FUNCTION jbin_get_item;
+DROP FUNCTION jbin_item_merge;
+DROP FUNCTION jbin_set_item;
+DROP FUNCTION jbin_insert_item;
+DROP FUNCTION jbin_update_item;
+DROP FUNCTION jbin_file;
+
+--enable_query_log
+
diff --git a/storage/connect/mysql-test/connect/t/json_udf_bin.test b/storage/connect/mysql-test/connect/t/json_udf_bin.test
new file mode 100644
index 00000000000..e4ee422c263
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/json_udf_bin.test
@@ -0,0 +1,212 @@
+--source json_udf.inc
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+--copy_file $MTR_SUITE_DIR/std_data/gloss.json $MYSQLD_DATADIR/gloss.json
+
+CREATE TABLE t1 ENGINE=CONNECT TABLE_TYPE=VIR BLOCK_SIZE=3;
+
+--echo #
+--echo # Test Jbin UDF's
+--echo #
+SELECT Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), n) from t1;
+SELECT Json_Array_Add(Jbin_Array(n, 3.1416, 'My name is "Foo"', NULL), n) from t1;
+SELECT Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), Jbin_Array('a','b',n)) from t1;
+SELECT Json_Array_Add(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), JsonGet_String(Jbin_Array('a','b','c'), '[1]'));
+SELECT Json_Array_Delete(Jbin_Array_Add_Values(Jbin_Array(56, 3.1416, 'My name is "Foo"', NULL), "One more", 2), 4);
+SELECT Json_Array_Delete(Jbin_Array(56, Jbin_Array(3.1416, 'My name is "Foo"'), NULL), '[1]', 1);
+SELECT Json_Array_Delete(Jbin_Array(56, Jbin_Array(3.1416, 'My name is "Foo"'), TRUE), 1, '[1]');
+SELECT Json_Array(1, TRUE, 0, FALSE);
+SELECT Json_Serialize(Jbin_Array(TRUE, FALSE));
+#
+SELECT Json_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL);
+SELECT Json_Serialize(Jbin_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty', NULL));
+--error ER_CANT_INITIALIZE_UDF
+SELECT Jbin_Object_Key('qty', 56, 'price', 3.1416, 'truc', 'machin', 'garanty');
+SELECT Json_Object_Add(Jbin_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color);
+SELECT Json_Object_Add(Jbin_Object(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price);
+SELECT Json_Object_Add(Jbin_Object_Nonull(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 'blue' color);
+SELECT Json_Object_Add(Jbin_Object_Nonull(56 qty, 3.1416 price, 'machin' truc, NULL garanty), 45.99 price);
+
+--echo #
+--echo # Test Jbin file UDF's
+--echo #
+SELECT Json_Serialize(Jbin_File('gloss.json'));
+SELECT JsonLocate(Jbin_File('gloss.json'),'XML');
+#
+SELECT Json_Object_Key('first', 'foo', 'second', Jbin_Array('a', 33));
+SELECT Json_Get_Item(Json_Array('a','b','c'), '[1]');
+SELECT Json_Get_Item(Json_Object('foo' AS "first", Json_Array('a', 33) AS "json_second"), 'second') AS "item";
+SELECT Json_Get_Item(Jbin_Object('foo' first, Jbin_Array('a', 33) jbin_second), 'second:*') item;
+SELECT Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv');
+SELECT Json_Serialize(Jbin_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv'));
+SELECT Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv:*');
+SELECT JsonGet_String(Json_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso') lang;
+SELECT Json_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso') "See also";
+SELECT Json_Serialize(Jbin_Get_Item(Jbin_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso')) "See also";
+SELECT JsonGet_String(Json_Get_Item(Json_File('gloss.json'),'glossary:GlossDiv:GlossList:GlossEntry:GlossDef:GlossSeeAlso'),'[0]') lang;
+
+--echo #
+--echo # Test Item Get/Set/Insert/Update UDF's
+--echo #
+SELECT Json_Get_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), '[]');
+SELECT Json_Get_Item(Jbin_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), '[1]');
+SELECT Json_Get_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), '[1]');
+#
+SELECT Json_Set_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)));
+--error ER_CANT_INITIALIZE_UDF
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 'foo');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 7, '[1]');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 7, '[1]');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Json_Array(7, 8, 9), '[1]');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[2]');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[2]:*');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 3.1416, 'foo');
+SELECT Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 'toto', '[1]:[2]');
+SELECT Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 300, '[2]:nxt:total:[]');
+SELECT Json_Set_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 300, '[2]:nxt:total:[]');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), Jbin_Array(7, 8, 9), '[1]', 5, '[2]:cinq', 10, '[1]:[]');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 44, '[2]:quatre');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, 'truc');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '');
+SELECT Json_Set_Item(Jbin_Array(1, 2, Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '*');
+SELECT Json_Serialize(Jbin_Set_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq'));
+#
+SELECT Json_Insert_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq');
+SELECT Json_Update_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq');
+SELECT Json_Insert_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 44, '[2]:quatre');
+SELECT Json_Update_Item(Jbin_Array(1, Jbin_Array(7, 8, 9), Jbin_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[]', 44, '[2]:quatre');
+SELECT Json_Insert_Item(Json_Array(1, Json_Array(7, 8, 9), Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[1]', 300, '[2]:nxt:total:[]');
+SELECT Json_Update_Item(Json_Array(1, Json_Array(7, 8, 9), Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[2]:cinq', 10, '[1]:[1]', 300, '[2]:nxt:total:[]');
+SELECT Json_Insert_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[]');
+SELECT Json_Update_Item(Json_Array(1, 2, Json_Object_Key('trois', 3, 'quatre', 4)), 5, '[]');
+
+--echo #
+--echo # Test merging items UDF's
+--echo #
+SELECT Json_Item_Merge(Jbin_Array('a','b','c'), Jbin_Array('d','e','f'));
+SELECT Json_Item_Merge(Json_Array('a','b','c'), Json_Array('d','e','f')) AS "Result";
+SELECT Json_Array_Add(Jbin_Item_Merge(Jbin_Array('a','b','c'), Jbin_Array('d','e','f')), 'and', 3);
+SELECT Json_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "d",5 "e",6 "f"));
+SELECT Json_Item_Merge(Jbin_Object(1 "a",2 "b",2 "c"), Jbin_Array('d','e','f'));
+SELECT Json_Object_Add(Jbin_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "d",5 "e",6 "f")), 'x' AS "and");
+SELECT Json_Item_Merge(Jbin_Object(1 "a",2 "b",3 "c"), Jbin_Object(4 "a",5 "e",6 "f"));
+--error ER_CANT_INITIALIZE_UDF
+SELECT Json_Item_Merge('foo', Json_Array('d','e','f'));
+
+--echo #
+--echo # Test making file UDF's
+--echo #
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+SELECT Json_File('bt1.json');
+SELECT Json_File(Jfile_Make(Jbin_File('bt1.json'), 0));
+SELECT Json_File(Jfile_Make(Jbin_File('bt1.json'), 1));
+SELECT Json_File(Jfile_Make(Jbin_File('bt1.json'), 2));
+SELECT Json_File('bt1.json', 0);
+SELECT Json_File('bt1.json', 1);
+SELECT Json_File('bt1.json', 2);
+SELECT Json_Serialize(Jbin_Array('a','b','c'));
+SELECT Json_Serialize(Jbin_Array_Add(Jbin_File('not_exist.json'), 'd'));
+--echo # This does not modify the file
+SELECT Json_Serialize(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'));
+SELECT Json_File('bt1.json', 2);
+--echo # This does modify the file
+SELECT Json_Array_Add(Jbin_File('bt1.json'), 'd');
+SELECT Json_File('bt1.json', 2);
+--echo # Back to the original file
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+SELECT Json_Object(Jbin_Array_Add(Jbin_Array('a','b','c'), 'd') "Jbin_foo") AS "Result";
+SELECT Json_Object(Jbin_Array_Add(Jbin_File('bt1.json'), 'd')) AS "Result";
+SELECT Json_Object(Jbin_Array_Add(Jbin_File('bt1.json'), 'd') "Jbin_bt1") AS "Result";
+--echo # This does modify the file
+SELECT Json_Object(Json_Array_Add(Jbin_File('bt1.json'), 'd') "Jfile_bt1") AS "Result";
+SELECT Json_File('bt1.json');
+SELECT Json_File(Json_Array_Delete(Jbin_File('bt1.json'), 3), 2);
+SELECT Json_Object(Jbin_Array_Add(Jbin_File('bt1.json'), 'd') "Jbin_bt1", n "t1") AS "Result" from t1;
+SELECT Json_File(Json_Array_Add(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'), 'e')) AS "Result";
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+SELECT Json_File(Json_Array_Add(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'), 'e')) AS "Result" from t1;
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+SELECT Json_Array_Add(Jbin_Array_Add(Jbin_File('bt1.json'), 'd'), n) AS "Result" from t1;
+--echo # Show modified file
+SELECT Json_File('bt1.json');
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+SELECT Json_Array_Add(Jbin_File('bt1.json'), n) AS "Result" from t1;
+--echo # Show modified file
+SELECT Json_File('bt1.json');
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+SELECT Json_File(Jbin_Item_Merge(Jbin_File('bt1.json'), Jbin_Array('d','e','f')));
+SELECT Json_File(Json_Item_Merge(Jbin_File('bt1.json'), Jbin_Array('d','e','f')));
+SELECT Jfile_Make(Jbin_Array('a','b','c'), 'bt1.json');
+--echo # Test DELETE from file
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 1)) AS "Result";
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 2) "Jbin_bt1") AS "Result";
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 0) "Jbin_bt1", n "t1") AS "Result" from t1;
+SELECT Json_Object(Jbin_Array_Delete(Jbin_File('bt1.json'), 3 - n) "Jbin_bt1") AS "Result" from t1;
+SELECT Json_Object(Json_Array_Delete(Jbin_File('bt1.json'), 3 - n) "Jbin_bt1") AS "Result" from t1;
+--echo # Show modified file
+SELECT Json_File('bt1.json');
+--echo # Object file
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+SELECT Json_File('bt2.json', 0);
+SELECT Json_File('bt2.json');
+SELECT Json_Serialize(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"));
+--echo # First query (file not modified)
+SELECT Json_Object(Jbin_Object_Add(Jbin_File('bt2.json'), 4 AS "d") AS "Jbin_new") AS "Result";
+--echo # First query (file modified)
+SELECT Json_Object(Json_Object_Add(Jbin_File('bt2.json'), 4 AS "d") AS "Jfile_new") AS "Result";
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+SELECT Json_Object(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d") "Jbin_new", n "t1") AS "Result" from t1;
+SELECT Json_File(Json_Object_Add(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"), 5 "e")) AS "Result";
+SELECT Json_Object_Add(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"), 5 "e") AS "Result" from t1;
+SELECT Json_Object_Add(Jbin_Object_Add(Jbin_File('bt2.json'), 4 "d"), n "n") AS "Result" from t1;
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+SELECT Json_Object_Add(Jbin_File('bt2.json'), n) AS "Result" from t1;
+SELECT Json_File('bt2.json');
+SELECT Jfile_Make(Jbin_Object(1 "a", 2 "b", 3 "c"), 'bt2.json', 0);
+SELECT Json_Serialize(Jbin_Item_Merge(Jbin_File('bt2.json'), Jbin_Object(4 "d",5 "e",6 "f"))) AS "Result";
+SELECT Json_File(Json_Item_Merge(Jbin_File('bt2.json'), Jbin_Object(4 "d",5 "e",6 "f"))) AS "Result";
+SELECT Json_Item_Merge(Json_Object(1 "a", 2 "b", 3 "c"), Json_Object(4 "d",5 "b",6 "f")) AS "Result";
+#
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'b')) AS "Result";
+SELECT Json_Object(Jbin_Object_Delete(Jbin_File('bt2.json'), 'c') "Jbin_bt1") AS "Result";
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'c') "Jbin_bt1") AS "Result";
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'c') "Jfile_bt1") AS "Result";
+SELECT Json_Object(Json_Object_Delete(Jbin_File('bt2.json'), 'a') "Jbin_bt1", n "t1") AS "Result" from t1;
+#
+SELECT Json_Serialize(Jbin_Object_List(Jbin_File('bt2.json'))) "Key list";
+
+#
+# Test documentation examples
+#
+SELECT Jfile_Make('{"a":1, "b":[44, 55]}' json_, 'bt3.json', 0);
+SELECT Json_Array_Add(Json_File('bt3.json', 'b'), 66);
+SELECT Json_Array_Add(Json_File('bt3.json'), 66, 'b');
+SELECT Json_Array_Add(Jbin_File('bt3.json', 'b'), 66);
+SELECT Json_File('bt3.json', 3);
+SELECT Jfile_Make('{"a":1, "b":[44, 55]}' json_, 'bt3.json', 0);
+#
+CREATE TABLE t2 (
+ n INT KEY,
+ jfile_cols CHAR(12) NOT NULL)
+ENGINE= MYISAM;
+INSERT INTO t2 VALUES(1,'bt3.json');
+--echo # In this table, the jfile_cols column just contains a file name
+UPDATE t2 SET jfile_cols = Json_Array_Add(Jbin_File('bt3.json', 'b'), 66) WHERE n = 1;
+SELECT JsonGet_String(jfile_cols, '*') FROM t2;
+UPDATE t2 SET jfile_cols = Json_Insert_Item(jfile_cols, 77, 'b:[]') WHERE n = 1;
+SELECT JsonGet_String(jfile_cols, 'b:*') FROM t2;
+UPDATE t2 SET jfile_cols = Json_Insert_Item(Jbin_Insert_Item(jfile_cols, 88, 'b:') , 99, 'b:') WHERE n = 1;
+SELECT JsonGet_String(jfile_cols, '*') FROM t2;
+
+DROP TABLE t1, t2;
+
+#
+# Clean up
+#
+--source json_udf2.inc
+--remove_file $MYSQLD_DATADIR/gloss.json
+--remove_file $MYSQLD_DATADIR/bt1.json
+--remove_file $MYSQLD_DATADIR/bt2.json
+--remove_file $MYSQLD_DATADIR/bt3.json
diff --git a/storage/connect/mysql-test/connect/t/mysql_grant.test b/storage/connect/mysql-test/connect/t/mysql_grant.test
index 05337efb3f2..7d3d05cb8fd 100644
--- a/storage/connect/mysql-test/connect/t/mysql_grant.test
+++ b/storage/connect/mysql-test/connect/t/mysql_grant.test
@@ -19,8 +19,10 @@ DROP TABLE t1;
--echo #
--echo # Testing FILE privilege
--echo #
+set sql_mode="";
GRANT ALL PRIVILEGES ON *.* TO user@localhost;
REVOKE FILE ON *.* FROM user@localhost;
+set sql_mode=default;
--connect(user,localhost,user,,)
--connection user
SELECT user();
diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c
index c0e249adf12..38e28a171b2 100644
--- a/storage/connect/plugutil.c
+++ b/storage/connect/plugutil.c
@@ -143,7 +143,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize)
fprintf(stderr, MSG(GLOBAL_ERROR), (int)sizeof(GLOBAL));
return NULL;
} else {
- g->Sarea_Size = worksize;
+ g->Sarea = NULL;
g->Createas = 0;
g->Alchecked = 0;
g->Mrr = 0;
@@ -155,7 +155,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize)
/*******************************************************************/
/* Allocate the main work segment. */
/*******************************************************************/
- if (!(g->Sarea = PlugAllocMem(g, worksize))) {
+ if (worksize && !(g->Sarea = PlugAllocMem(g, worksize))) {
char errmsg[256];
sprintf(errmsg, MSG(WORK_AREA), g->Message);
strcpy(g->Message, errmsg);
@@ -163,7 +163,7 @@ PGLOBAL PlugInit(LPCSTR Language, uint worksize)
} else
g->Sarea_Size = worksize;
- } /* endif g */
+ } /* endif g */
g->jump_level = -1; /* New setting to allow recursive call of Plug */
return(g);
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index 60d515a61ca..8f9328c0b2f 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -47,6 +47,7 @@
#include "valblk.h"
#include "tabmul.h"
#include "ha_connect.h"
+#include "mycat.h"
#if !defined(__WIN__)
extern handlerton *connect_hton;
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index 527fe55dd89..7906f6c9219 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -2019,7 +2019,7 @@ int TDBDOS::EstimatedLength(void)
dep = 1 + cdp->GetLong() / 20; // Why 20 ?????
} else for (; cdp; cdp = cdp->GetNext())
if (!(cdp->Flags & (U_VIRTUAL|U_SPECIAL)))
- dep = MY_MAX(dep, cdp->GetOffset());
+ dep = MY_MAX(dep, cdp->GetOffset());
return (int)dep;
} // end of Estimated Length
@@ -2204,7 +2204,7 @@ bool TDBDOS::PrepareWriting(PGLOBAL)
} // endif Mode
return false;
- } // end of WriteDB
+ } // end of PrepareWriting
/***********************************************************************/
/* WriteDB: Data Base write routine for DOS access method. */
@@ -2216,7 +2216,7 @@ int TDBDOS::WriteDB(PGLOBAL g)
// Make the line to write
if (PrepareWriting(g))
- return true;
+ return RC_FX;
if (trace > 1)
htrc("Write: line is='%s'\n", To_Line);
diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp
index 211a58f0344..17260836371 100644
--- a/storage/connect/tabjson.cpp
+++ b/storage/connect/tabjson.cpp
@@ -38,6 +38,7 @@
/***********************************************************************/
#define MAXCOL 200 /* Default max column nb in result */
#define TYPE_UNKNOWN 12 /* Must be greater than other types */
+#define USE_G 1 /* Use recoverable memory if 1 */
/***********************************************************************/
/* External function. */
@@ -411,9 +412,23 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
// Txfp must be set for TDBDOS
tdbp = new(g) TDBJSN(this, txfp);
- } else {
+
+#if USE_G
+ // Allocate the parse work memory
+ PGLOBAL G = (PGLOBAL)PlugSubAlloc(g, NULL, sizeof(GLOBAL));
+ memset(G, 0, sizeof(GLOBAL));
+ G->Sarea_Size = Lrecl * 10;
+ G->Sarea = PlugSubAlloc(g, NULL, G->Sarea_Size);
+ PlugSubSet(G, G->Sarea, G->Sarea_Size);
+ G->jump_level = -1;
+ ((TDBJSN*)tdbp)->G = G;
+#else
+ ((TDBJSN*)tdbp)->G = g;
+#endif
+ } else {
txfp = new(g) MAPFAM(this);
tdbp = new(g) TDBJSON(this, txfp);
+ ((TDBJSON*)tdbp)->G = g;
} // endif Pretty
if (Multiple)
@@ -462,6 +477,7 @@ TDBJSN::TDBJSN(PJDEF tdp, PTXF txfp) : TDBDOS(tdp, txfp)
TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
{
+ G = NULL;
Top = tdbp->Top;
Row = tdbp->Row;
Val = tdbp->Val;
@@ -485,6 +501,7 @@ TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
// Used for update
PTDB TDBJSN::CopyOne(PTABS t)
{
+ G = NULL;
PTDB tp;
PJCOL cp1, cp2;
PGLOBAL g = t->G;
@@ -578,7 +595,7 @@ PJSON TDBJSN::FindRow(PGLOBAL g)
} // end of FindRow
/***********************************************************************/
-/* OpenDB: Data Base open routine for JSN access method. */
+/* OpenDB: Data Base open routine for JSN access method. */
/***********************************************************************/
bool TDBJSN::OpenDB(PGLOBAL g)
{
@@ -603,7 +620,7 @@ bool TDBJSN::OpenDB(PGLOBAL g)
return true;
} // endswitch Jmode
- } // endif Use
+ } // endif Use
return TDBDOS::OpenDB(g);
} // end of OpenDB
@@ -655,19 +672,31 @@ int TDBJSN::ReadDB(PGLOBAL g)
NextSame = 0;
M++;
return RC_OK;
- } else if ((rc = TDBDOS::ReadDB(g)) == RC_OK)
- if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK)) {
- // Deferred reading failed
- } else if (!(Row = ParseJson(g, To_Line,
- strlen(To_Line), Pretty, &Comma))) {
- rc = (Pretty == 1 && !strcmp(To_Line, "]")) ? RC_EF : RC_FX;
- } else {
- Row = FindRow(g);
- SameRow = 0;
- Fpos++;
- M = 1;
- rc = RC_OK;
- } // endif's
+ } else if ((rc = TDBDOS::ReadDB(g)) == RC_OK) {
+ if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK))
+ // Deferred reading failed
+ return rc;
+
+#if USE_G
+ // Recover the memory used for parsing
+ PlugSubSet(G, G->Sarea, G->Sarea_Size);
+#endif
+
+ if ((Row = ParseJson(G, To_Line, strlen(To_Line), &Pretty, &Comma))) {
+ Row = FindRow(g);
+ SameRow = 0;
+ Fpos++;
+ M = 1;
+ rc = RC_OK;
+ } else if (Pretty != 1 || strcmp(To_Line, "]")) {
+#if USE_G
+ strcpy(g->Message, G->Message);
+#endif
+ rc = RC_FX;
+ } else
+ rc = RC_EF;
+
+ } // endif ReadDB
return rc;
} // end of ReadDB
@@ -744,7 +773,7 @@ int TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp)
if (MakeTopTree(g, Row))
return true;
- if ((s = Serialize(g, Top, NULL, Pretty))) {
+ if ((s = Serialize(G, Top, NULL, Pretty))) {
if (Comma)
strcat(s, ",");
@@ -755,22 +784,39 @@ int TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp)
} else
strcpy(To_Line, s);
-// Row->Clear();
return false;
} else
return true;
} // end of PrepareWriting
-/* ---------------------------- JSONCOL ------------------------------ */
+ /***********************************************************************/
+ /* WriteDB: Data Base write routine for DOS access method. */
+ /***********************************************************************/
+ int TDBJSN::WriteDB(PGLOBAL g)
+{
+ int rc = TDBDOS::WriteDB(g);
+
+#if USE_G
+ if (rc == RC_FX)
+ strcpy(g->Message, G->Message);
+
+ PlugSubSet(G, G->Sarea, G->Sarea_Size);
+#endif
+ Row->Clear();
+ return rc;
+} // end of WriteDB
+
+ /* ---------------------------- JSONCOL ------------------------------ */
/***********************************************************************/
/* JSONCOL public constructor. */
/***********************************************************************/
JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
: DOSCOL(g, cdp, tdbp, cprec, i, "DOS")
- {
+{
Tjp = (TDBJSN *)(tdbp->GetOrig() ? tdbp->GetOrig() : tdbp);
+ G = Tjp->G;
Jpath = cdp->GetFmt();
MulVal = NULL;
Nodes = NULL;
@@ -778,14 +824,15 @@ JSONCOL::JSONCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
Xnod = -1;
Xpd = false;
Parsed = false;
- } // end of JSONCOL constructor
+} // end of JSONCOL constructor
/***********************************************************************/
/* JSONCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
JSONCOL::JSONCOL(JSONCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
- {
+{
+ G = col1->G;
Tjp = col1->Tjp;
Jpath = col1->Jpath;
MulVal = col1->MulVal;
@@ -794,7 +841,7 @@ JSONCOL::JSONCOL(JSONCOL *col1, PTDB tdbp) : DOSCOL(col1, tdbp)
Xnod = col1->Xnod;
Xpd = col1->Xpd;
Parsed = col1->Parsed;
- } // end of JSONCOL copy constructor
+} // end of JSONCOL copy constructor
/***********************************************************************/
/* SetBuffer: prepare a column block for write operation. */
@@ -809,6 +856,7 @@ bool JSONCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
return true;
Tjp = (TDBJSN*)To_Tdb;
+ G = Tjp->G;
return false;
} // end of SetBuffer
@@ -980,7 +1028,7 @@ bool JSONCOL::ParseJpath(PGLOBAL g)
Nod = colp->Nod;
Nodes = colp->Nodes;
Xpd = colp->Xpd;
- goto fin;
+ goto fin;
} // endif Name
sprintf(g->Message, "Cannot parse updated column %s", Name);
@@ -1046,7 +1094,8 @@ void JSONCOL::SetJsonValue(PGLOBAL g, PVAL vp, PJVAL val, int n)
switch (val->GetValType()) {
case TYPE_STRG:
case TYPE_INTG:
- case TYPE_DBL:
+ case TYPE_BINT:
+ case TYPE_DBL:
vp->SetValue_pval(val->GetValue());
break;
case TYPE_BOOL:
@@ -1103,15 +1152,15 @@ PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i)
Value->SetValue(row->GetType() == TYPE_JAR ? row->size() : 1);
return(Value);
} else if (Nodes[i].Op == OP_XX) {
- return MakeJson(g, row);
+ return MakeJson(G, row);
} else switch (row->GetType()) {
case TYPE_JOB:
if (!Nodes[i].Key) {
- // Expected Array was not there
+ // Expected Array was not there, wrap the value
if (i < Nod-1)
continue;
else
- val = new(g) JVALUE(row);
+ val = new(G) JVALUE(row);
} else
val = ((PJOB)row)->GetValue(Nodes[i].Key);
@@ -1128,11 +1177,11 @@ PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i)
else
return CalculateArray(g, arp, i);
- } else if (i < Nod-1) {
- strcpy(g->Message, "Unexpected array");
- val = NULL; // Not an expected array
- } else
- val = arp->GetValue(0);
+ } else {
+ // Unexpected array, unwrap it as [0]
+ val = arp->GetValue(0);
+ i--;
+ } // endif's
break;
case TYPE_JVAL:
@@ -1275,30 +1324,31 @@ PJSON JSONCOL::GetRow(PGLOBAL g)
PJAR arp;
PJSON nwr, row = Tjp->Row;
- for (int i = 0; i < Nod-1 && row; i++) {
- if (Nodes[i+1].Op == OP_XX)
+ for (int i = 0; i < Nod && row; i++) {
+ if (Nodes[i+1].Op == OP_XX)
break;
else switch (row->GetType()) {
case TYPE_JOB:
if (!Nodes[i].Key)
- // Expected Array was not there
+ // Expected Array was not there, wrap the value
continue;
val = ((PJOB)row)->GetValue(Nodes[i].Key);
break;
case TYPE_JAR:
- if (!Nodes[i].Key) {
- arp = (PJAR)row;
+ arp = (PJAR)row;
+ if (!Nodes[i].Key) {
if (Nodes[i].Op == OP_EQ)
val = arp->GetValue(Nodes[i].Rank);
else
val = arp->GetValue(Nodes[i].Rx);
} else {
- strcpy(g->Message, "Unexpected array");
- val = NULL; // Not an expected array
- } // endif Nodes
+ // Unexpected array, unwrap it as [0]
+ val = arp->GetValue(0);
+ i--;
+ } // endif Nodes
break;
case TYPE_JVAL:
@@ -1318,15 +1368,15 @@ PJSON JSONCOL::GetRow(PGLOBAL g)
break;
else if (!Nodes[i].Key)
// Construct intermediate array
- nwr = new(g) JARRAY;
+ nwr = new(G) JARRAY;
else
- nwr = new(g) JOBJECT;
+ nwr = new(G) JOBJECT;
if (row->GetType() == TYPE_JOB) {
- ((PJOB)row)->SetValue(g, new(g) JVALUE(nwr), Nodes[i-1].Key);
+ ((PJOB)row)->SetValue(G, new(G) JVALUE(nwr), Nodes[i-1].Key);
} else if (row->GetType() == TYPE_JAR) {
- ((PJAR)row)->AddValue(g, new(g) JVALUE(nwr));
- ((PJAR)row)->InitArray(g);
+ ((PJAR)row)->AddValue(G, new(G) JVALUE(nwr));
+ ((PJAR)row)->InitArray(G);
} else {
strcpy(g->Message, "Wrong type when writing new row");
nwr = NULL;
@@ -1353,7 +1403,7 @@ void JSONCOL::WriteColumn(PGLOBAL g)
longjmp(g->jumper[g->jump_level], 666);
} // endif Xpd
- /*********************************************************************/
+ /*********************************************************************/
/* Check whether this node must be written. */
/*********************************************************************/
if (Value != To_Val)
@@ -1370,7 +1420,6 @@ void JSONCOL::WriteColumn(PGLOBAL g)
PJAR arp = NULL;
PJVAL jvp = NULL;
PJSON jsp, row = GetRow(g);
- JTYP type = row->GetType();
switch (row->GetType()) {
case TYPE_JOB: objp = (PJOB)row; break;
@@ -1384,21 +1433,21 @@ void JSONCOL::WriteColumn(PGLOBAL g)
if (Nodes[Nod-1].Op == OP_XX) {
s = Value->GetCharValue();
- if (!(jsp = ParseJson(g, s, (int)strlen(s), 0))) {
+ if (!(jsp = ParseJson(G, s, (int)strlen(s)))) {
strcpy(g->Message, s);
longjmp(g->jumper[g->jump_level], 666);
} // endif jsp
if (arp) {
if (Nod > 1 && Nodes[Nod-2].Op == OP_EQ)
- arp->SetValue(g, new(g) JVALUE(jsp), Nodes[Nod-2].Rank);
+ arp->SetValue(G, new(G) JVALUE(jsp), Nodes[Nod-2].Rank);
else
- arp->AddValue(g, new(g) JVALUE(jsp));
+ arp->AddValue(G, new(G) JVALUE(jsp));
- arp->InitArray(g);
+ arp->InitArray(G);
} else if (objp) {
if (Nod > 1 && Nodes[Nod-2].Key)
- objp->SetValue(g, new(g) JVALUE(jsp), Nodes[Nod-2].Key);
+ objp->SetValue(G, new(G) JVALUE(jsp), Nodes[Nod-2].Key);
} else if (jvp)
jvp->SetValue(jsp);
@@ -1409,17 +1458,19 @@ void JSONCOL::WriteColumn(PGLOBAL g)
// Passthru
case TYPE_DATE:
case TYPE_INT:
- case TYPE_DOUBLE:
+ case TYPE_SHORT:
+ case TYPE_BIGINT:
+ case TYPE_DOUBLE:
if (arp) {
if (Nodes[Nod-1].Op == OP_EQ)
- arp->SetValue(g, new(g) JVALUE(g, Value), Nodes[Nod-1].Rank);
+ arp->SetValue(G, new(G) JVALUE(G, Value), Nodes[Nod-1].Rank);
else
- arp->AddValue(g, new(g) JVALUE(g, Value));
+ arp->AddValue(G, new(G) JVALUE(G, Value));
- arp->InitArray(g);
+ arp->InitArray(G);
} else if (objp) {
if (Nodes[Nod-1].Key)
- objp->SetValue(g, new(g) JVALUE(g, Value), Nodes[Nod-1].Key);
+ objp->SetValue(G, new(G) JVALUE(G, Value), Nodes[Nod-1].Key);
} else if (jvp)
jvp->SetValue(Value);
@@ -1522,7 +1573,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
/* Parse the json file and allocate its tree structure. */
/*********************************************************************/
g->Message[0] = 0;
- jsp = Top = ParseJson(g, memory, len, Pretty);
+ jsp = Top = ParseJson(g, memory, len, &Pretty);
Txfp->CloseTableFile(g, false);
Mode = mode; // Restore saved Mode
@@ -1540,7 +1591,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
if (*objpath != '[') { // objpass is a key
if (jsp->GetType() != TYPE_JOB) {
- strcpy(g->Message, "Table path does no match json file");
+ strcpy(g->Message, "Table path does not match the json file");
return RC_FX;
} // endif Type
@@ -1556,7 +1607,7 @@ int TDBJSON::MakeDocument(PGLOBAL g)
} else if (objpath[strlen(objpath)-1] == ']') {
if (jsp->GetType() != TYPE_JAR) {
- strcpy(g->Message, "Table path does no match json file");
+ strcpy(g->Message, "Table path does not match the json file");
return RC_FX;
} // endif Type
@@ -1835,9 +1886,7 @@ void TDBJSON::CloseDB(PGLOBAL g)
return;
// Save the modified document
- char filename[_MAX_PATH];
- PSZ msg;
- FILE *fop;
+ char filename[_MAX_PATH];
Doc->InitArray(g);
@@ -1845,12 +1894,8 @@ void TDBJSON::CloseDB(PGLOBAL g)
PlugSetPath(filename, ((PJDEF)To_Def)->Fn, GetPath());
// Serialize the modified table
- if (!(fop = fopen(filename, "wb"))) {
- sprintf(g->Message, MSG(OPEN_MODE_ERROR),
- "w", (int)errno, filename);
- strcat(strcat(g->Message, ": "), strerror(errno));
- } else if ((msg = Serialize(g, Top, fop, Pretty)))
- puts(msg);
+ if (!Serialize(g, Top, filename, Pretty))
+ puts(g->Message);
} // end of CloseDB
diff --git a/storage/connect/tabjson.h b/storage/connect/tabjson.h
index 4505d30a21c..f7cb74c3c4d 100644
--- a/storage/connect/tabjson.h
+++ b/storage/connect/tabjson.h
@@ -68,7 +68,8 @@ class JSONDEF : public DOSDEF { /* Table description */
/***********************************************************************/
class TDBJSN : public TDBDOS {
friend class JSONCOL;
- public:
+ friend class JSONDEF;
+public:
// Constructor
TDBJSN(PJDEF tdp, PTXF txfp);
TDBJSN(TDBJSN *tdbp);
@@ -90,32 +91,34 @@ class TDBJSN : public TDBDOS {
virtual int Cardinality(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
- virtual bool PrepareWriting(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
+ virtual bool PrepareWriting(PGLOBAL g);
+ virtual int WriteDB(PGLOBAL g);
protected:
PJSON FindRow(PGLOBAL g);
int MakeTopTree(PGLOBAL g, PJSON jsp);
// Members
- PJSON Top; // The top JSON tree
- PJSON Row; // The current row
- PJSON Val; // The value of the current row
- PJCOL Colp; // The multiple column
- JMODE Jmode; // MODE_OBJECT by default
- char *Objname; // The table object name
- char *Xcol; // Name of expandable column
- int Fpos; // The current row index
- int N; // The current Rownum
- int M; // Index of multiple value
- int Limit; // Limit of multiple values
- int Pretty; // Depends on file structure
- int NextSame; // Same next row
- int SameRow; // Same row nb
- int Xval; // Index of expandable array
- int B; // Array index base
- bool Strict; // Strict syntax checking
- bool Comma; // Row has final comma
+ PGLOBAL G; // Support of parse memory
+ PJSON Top; // The top JSON tree
+ PJSON Row; // The current row
+ PJSON Val; // The value of the current row
+ PJCOL Colp; // The multiple column
+ JMODE Jmode; // MODE_OBJECT by default
+ char *Objname; // The table object name
+ char *Xcol; // Name of expandable column
+ int Fpos; // The current row index
+ int N; // The current Rownum
+ int M; // Index of multiple value
+ int Limit; // Limit of multiple values
+ int Pretty; // Depends on file structure
+ int NextSame; // Same next row
+ int SameRow; // Same row nb
+ int Xval; // Index of expandable array
+ int B; // Array index base
+ bool Strict; // Strict syntax checking
+ bool Comma; // Row has final comma
}; // end of class TDBJSN
/* -------------------------- JSONCOL class -------------------------- */
@@ -154,7 +157,8 @@ class JSONCOL : public DOSCOL {
JSONCOL(void) {}
// Members
- TDBJSN *Tjp; // To the JSN table block
+ PGLOBAL G; // Support of parse memory
+ TDBJSN *Tjp; // To the JSN table block
PVAL MulVal; // To value used by multiple column
char *Jpath; // The json path
JNODE *Nodes; // The intermediate objects
@@ -170,7 +174,8 @@ class JSONCOL : public DOSCOL {
/* This is the JSON Access Method class declaration. */
/***********************************************************************/
class TDBJSON : public TDBJSN {
- friend class JSONCOL;
+ friend class JSONDEF;
+ friend class JSONCOL;
public:
// Constructor
TDBJSON(PJDEF tdp, PTXF txfp);
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp
index f82cca3b514..658f3513b07 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -1054,32 +1054,14 @@ int TDBMYSQL::SendCommand(PGLOBAL g)
/***********************************************************************/
/* Data Base indexed read routine for MYSQL access method. */
/***********************************************************************/
-bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
+bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
{
- bool oom;
int oldlen = Query->GetLength();
PHC hc = To_Def->GetHandler();
- if (op == OP_FIRST && hc->end_range) {
-#ifdef _DEBUG
- assert(!key);
-#endif
- key_range *end_key = &hc->save_end_range;
-
- key = end_key->key;
- len = end_key->length;
-
- switch (end_key->flag) {
- case HA_READ_BEFORE_KEY: op = OP_LT; break;
- case HA_READ_AFTER_KEY: op = OP_LE; break;
- default: key = NULL;
- } // endswitch flag
-
- } // endif OP_FIRST
-
- if (!key || op == OP_NEXT ||
- Mode == MODE_UPDATE || Mode == MODE_DELETE) {
- if (!key && Mode == MODE_READX) {
+ if (!(kr || hc->end_range) || op == OP_NEXT ||
+ Mode == MODE_UPDATE || Mode == MODE_DELETE) {
+ if (!kr && Mode == MODE_READX) {
// This is a false indexed read
m_Rc = Myc.ExecSQL(g, Query->GetStr());
Mode = MODE_READ;
@@ -1091,23 +1073,35 @@ bool TDBMYSQL::ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
if (Myc.m_Res)
Myc.FreeResult();
- if (hc->MakeKeyWhere(g, Query, op, '`', key, len))
+ if (hc->MakeKeyWhere(g, Query, op, '`', kr))
return true;
if (To_CondFil) {
- oom = Query->Append(" AND (");
- oom |= Query->Append(To_CondFil->Body);
+ if (To_CondFil->Idx != hc->active_index) {
+ To_CondFil->Idx = hc->active_index;
+ To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
+ *To_CondFil->Body= 0;
- if ((oom |= Query->Append(')'))) {
- strcpy(g->Message, "Readkey: Out of memory");
- return true;
- } // endif oom
+ if ((To_CondFil = hc->CheckCond(g, To_CondFil, To_CondFil->Cond)))
+ PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
+
+ } // endif active_index
+
+ if (To_CondFil)
+ if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
+ strcpy(g->Message, "Readkey: Out of memory");
+ return true;
+ } // endif Append
+
+ } // endif To_Condfil
- } // endif To_Condfil
+ Mode = MODE_READ;
+ } // endif's op
- } // endif's op
+ if (trace)
+ htrc("MYSQL ReadKey: Query=%s\n", Query->GetStr());
- m_Rc = Myc.ExecSQL(g, Query->GetStr());
+ m_Rc = Myc.ExecSQL(g, Query->GetStr());
Query->Truncate(oldlen);
return (m_Rc == RC_FX) ? true : false;
} // end of ReadKey
diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h
index 17d7b190340..edb15b5cca6 100644
--- a/storage/connect/tabmysql.h
+++ b/storage/connect/tabmysql.h
@@ -99,7 +99,7 @@ class TDBMYSQL : public TDBASE {
virtual int WriteDB(PGLOBAL g);
virtual int DeleteDB(PGLOBAL g, int irc);
virtual void CloseDB(PGLOBAL g);
- virtual bool ReadKey(PGLOBAL g, OPVAL op, const void *key, int len);
+ virtual bool ReadKey(PGLOBAL g, OPVAL op, const key_range *kr);
// Specific routines
bool SetColumnRanks(PGLOBAL g);
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index 31854870ed2..fd9a049a05a 100644
--- a/storage/connect/tabodbc.cpp
+++ b/storage/connect/tabodbc.cpp
@@ -1,7 +1,7 @@
/************* Tabodbc C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABODBC */
/* ------------- */
-/* Version 2.9 */
+/* Version 3.0 */
/* */
/* COPYRIGHT: */
/* ---------- */
@@ -35,6 +35,7 @@
/* Include relevant MariaDB header file. */
/***********************************************************************/
#include "my_global.h"
+#include "sql_class.h"
#if defined(__WIN__)
#include <io.h>
#include <fcntl.h>
@@ -72,6 +73,7 @@
#include "reldef.h"
#include "tabcol.h"
#include "valblk.h"
+#include "ha_connect.h"
#include "sql_string.h"
@@ -322,7 +324,7 @@ PSZ TDBODBC::GetFile(PGLOBAL g)
if (Connect) {
char *p1, *p2;
int i;
- size_t n;
+ size_t n;
if (!(p1 = strstr(Connect, "DBQ="))) {
char *p, *lc = strlwr(PlugDup(g, Connect));
@@ -334,8 +336,8 @@ PSZ TDBODBC::GetFile(PGLOBAL g)
} else
i = 4;
- if (p1) {
- p1 += i; // Beginning of file name
+ if (p1) {
+ p1 += i; // Beginning of file name
p2 = strchr(p1, ';'); // End of file path/name
// Make the File path/name from the connect string
@@ -397,176 +399,209 @@ int TDBODBC::Decode(char *txt, char *buf, size_t n)
/* Note: when implementing EOM filtering, column only used in local */
/* filter should be removed from column list. */
/***********************************************************************/
-char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
+bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
{
- char *colist, *tabname, *sql, buf[NAM_LEN * 3];
- LPCSTR schmp = NULL, catp = NULL;
- int len, ncol = 0;
- bool first = true;
- PTABLE tablep = To_Table;
- PCOL colp;
-
- if (Srcdef)
- return Srcdef;
-
- if (!cnt) {
- // Normal SQL statement to retrieve results
- for (colp = Columns; colp; colp = colp->GetNext())
- if (!colp->IsSpecial())
- ncol++;
-
- if (ncol) {
- colist = (char*)PlugSubAlloc(g, NULL, (NAM_LEN + 4) * ncol);
-
- for (colp = Columns; colp; colp = colp->GetNext())
- if (!colp->IsSpecial()) {
- // Column name can be in UTF-8 encoding
- /*rc=*/ Decode(colp->GetName(), buf, sizeof(buf));
-
- if (Quote) {
- if (first) {
- strcat(strcat(strcpy(colist, Quote), buf), Quote);
- first = false;
- } else
- strcat(strcat(strcat(strcat(colist, ", "),
- Quote), buf), Quote);
-
- } else {
- if (first) {
- strcpy(colist, buf);
- first = false;
- } else
- strcat(strcat(colist, ", "), buf);
-
- } // endif Quote
-
- } // endif !Special
+ char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
+ int len;
+ bool oom = false, first = true;
+ PTABLE tablep = To_Table;
+ PCOL colp;
+
+ if (Srcdef) {
+ Query = new(g)STRING(g, 0, Srcdef);
+ return false;
+ } // endif Srcdef
+
+ // Allocate the string used to contain the Query
+ Query = new(g)STRING(g, 1023, "SELECT ");
+
+ if (!cnt) {
+ if (Columns) {
+ // Normal SQL statement to retrieve results
+ for (colp = Columns; colp; colp = colp->GetNext())
+ if (!colp->IsSpecial()) {
+ if (!first)
+ oom |= Query->Append(", ");
+ else
+ first = false;
+
+ // Column name can be encoded in UTF-8
+ Decode(colp->GetName(), buf, sizeof(buf));
+
+ if (Quote) {
+ // Put column name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ } // endif colp
- } else {
- // ncol == 0 can occur for queries such that sql count(*) from...
+ } else
+ // !Columns can occur for queries such that sql count(*) from...
// for which we will count the rows from sql * from...
- colist = (char*)PlugSubAlloc(g, NULL, 2);
- strcpy(colist, "*");
- } // endif ncol
+ oom |= Query->Append('*');
- } else {
+ } else
// SQL statement used to retrieve the size of the result
- colist = (char*)PlugSubAlloc(g, NULL, 9);
- strcpy(colist, "count(*)");
- } // endif cnt
+ oom |= Query->Append("count(*)");
- // Table name can be encoded in UTF-8
- /*rc = */Decode(TableName, buf, sizeof(buf));
-
- // Put table name between identifier quotes in case in contains blanks
- tabname = (char*)PlugSubAlloc(g, NULL, strlen(buf) + 3);
-
- if (Quote)
- strcat(strcat(strcpy(tabname, Quote), buf), Quote);
- else
- strcpy(tabname, buf);
-
- // Below 14 is length of 'select ' + length of ' from ' + 1
- len = (strlen(colist) + strlen(buf) + 14);
- len += (To_CondFil ? strlen(To_CondFil->Body) + 7 : 0);
+ oom |= Query->Append(" FROM ");
if (Catalog && *Catalog)
catp = Catalog;
- if (catp)
- len += (strlen(catp) + 2);
-
if (tablep->GetSchema())
- schmp = tablep->GetSchema();
+ schmp = (char*)tablep->GetSchema();
else if (Schema && *Schema)
schmp = Schema;
- if (schmp)
- len += (strlen(schmp) + 1);
-
- sql = (char*)PlugSubAlloc(g, NULL, len);
- strcat(strcat(strcpy(sql, "SELECT "), colist), " FROM ");
-
if (catp) {
- strcat(sql, catp);
-
- if (schmp)
- strcat(strcat(sql, "."), schmp);
- else
- strcat(sql, ".");
+ oom |= Query->Append(catp);
+
+ if (schmp) {
+ oom |= Query->Append('.');
+ oom |= Query->Append(schmp);
+ } // endif schmp
+
+ oom |= Query->Append('.');
+ } else if (schmp) {
+ oom |= Query->Append(schmp);
+ oom |= Query->Append('.');
+ } // endif schmp
+
+ // Table name can be encoded in UTF-8
+ Decode(TableName, buf, sizeof(buf));
+
+ if (Quote) {
+ // Put table name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ len = Query->GetLength();
+
+ if (To_CondFil) {
+ if (Mode == MODE_READ) {
+ oom |= Query->Append(" WHERE ");
+ oom |= Query->Append(To_CondFil->Body);
+ len = Query->GetLength() + 1;
+ } else
+ len += (strlen(To_CondFil->Body) + 256);
- strcat(sql, ".");
- } else if (schmp)
- strcat(strcat(sql, schmp), ".");
+ } else
+ len += ((Mode == MODE_READX) ? 256 : 1);
- strcat(sql, tabname);
+ if (oom || Query->Resize(len)) {
+ strcpy(g->Message, "MakeSQL: Out of memory");
+ return true;
+ } // endif oom
- if (To_CondFil)
- strcat(strcat(sql, " WHERE "), To_CondFil->Body);
-
- if (trace)
- htrc("sql: '%s'\n", sql);
+ if (trace)
+ htrc("Query=%s\n", Query->GetStr());
- return sql;
+ return false;
} // end of MakeSQL
/***********************************************************************/
/* MakeInsert: make the Insert statement used with ODBC connection. */
/***********************************************************************/
-char *TDBODBC::MakeInsert(PGLOBAL g)
+bool TDBODBC::MakeInsert(PGLOBAL g)
{
- char *stmt, *colist, *valist, buf[NAM_LEN * 3];
-// char *tk = "`";
- int len = 0;
- bool b = FALSE;
- PCOL colp;
+ char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
+ int len = 0;
+ bool b = false, oom = false;
+ PTABLE tablep = To_Table;
+ PCOL colp;
for (colp = Columns; colp; colp = colp->GetNext())
if (colp->IsSpecial()) {
strcpy(g->Message, MSG(NO_ODBC_SPECOL));
- return NULL;
+ return true;
} else {
- len += (strlen(colp->GetName()) + 4);
+ // Column name can be encoded in UTF-8
+ Decode(colp->GetName(), buf, sizeof(buf));
+ len += (strlen(buf) + 6); // comma + quotes + valist
((PODBCCOL)colp)->Rank = ++Nparm;
} // endif colp
- colist = (char*)PlugSubAlloc(g, NULL, len);
- *colist = '\0';
- valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm);
- *valist = '\0';
+ // Below 32 is enough to contain the fixed part of the query
+ if (Catalog && *Catalog)
+ catp = Catalog;
- for (colp = Columns; colp; colp = colp->GetNext()) {
- if (b) {
- strcat(colist, ", ");
- strcat(valist, ",");
- } else
- b = true;
+ if (catp)
+ len += strlen(catp) + 1;
- // Column name can be in UTF-8 encoding
- Decode(colp->GetName(), buf, sizeof(buf));
+ if (tablep->GetSchema())
+ schmp = (char*)tablep->GetSchema();
+ else if (Schema && *Schema)
+ schmp = Schema;
- if (Quote)
- strcat(strcat(strcat(colist, Quote), buf), Quote);
- else
- strcat(colist, buf);
+ if (schmp)
+ len += strlen(schmp) + 1;
- strcat(valist, "?"); // Parameter marker
- } // endfor colp
+ // Column name can be encoded in UTF-8
+ Decode(TableName, buf, sizeof(buf));
+ len += (strlen(buf) + 32);
+ Query = new(g) STRING(g, len, "INSERT INTO ");
- // Below 32 is enough to contain the fixed part of the query
- len = (strlen(TableName) + strlen(colist) + strlen(valist) + 32);
- stmt = (char*)PlugSubAlloc(g, NULL, len);
- strcpy(stmt, "INSERT INTO ");
+ if (catp) {
+ oom |= Query->Append(catp);
- if (Quote)
- strcat(strcat(strcat(stmt, Quote), TableName), Quote);
- else
- strcat(stmt, TableName);
+ if (schmp) {
+ oom |= Query->Append('.');
+ oom |= Query->Append(schmp);
+ } // endif schmp
- strcat(strcat(strcat(stmt, " ("), colist), ") VALUES (");
- strcat(strcat(stmt, valist), ")");
+ oom |= Query->Append('.');
+ } else if (schmp) {
+ oom |= Query->Append(schmp);
+ oom |= Query->Append('.');
+ } // endif schmp
- return stmt;
+ if (Quote) {
+ // Put table name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ oom |= Query->Append('(');
+
+ for (colp = Columns; colp; colp = colp->GetNext()) {
+ if (b)
+ oom |= Query->Append(", ");
+ else
+ b = true;
+
+ // Column name can be in UTF-8 encoding
+ Decode(colp->GetName(), buf, sizeof(buf));
+
+ if (Quote) {
+ // Put column name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ } // endfor colp
+
+ oom |= Query->Append(") VALUES (");
+
+ for (int i = 0; i < Nparm; i++)
+ oom |= Query->Append("?,");
+
+ if (oom)
+ strcpy(g->Message, "MakeInsert: Out of memory");
+ else
+ Query->RepLast(')');
+
+ return oom;
} // end of MakeInsert
/***********************************************************************/
@@ -591,7 +626,7 @@ bool TDBODBC::BindParameters(PGLOBAL g)
/* MakeCommand: make the Update or Delete statement to send to the */
/* MySQL server. Limited to remote values and filtering. */
/***********************************************************************/
-char *TDBODBC::MakeCommand(PGLOBAL g)
+bool TDBODBC::MakeCommand(PGLOBAL g)
{
char *p, *stmt, name[68], *body = NULL, *qc = Ocp->GetQuoteChar();
char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
@@ -649,7 +684,8 @@ char *TDBODBC::MakeCommand(PGLOBAL g)
return NULL;
} // endif p
- return stmt;
+ Query = new(g) STRING(g, 0, stmt);
+ return (!Query->GetSize());
} // end of MakeCommand
#if 0
@@ -826,10 +862,12 @@ bool TDBODBC::OpenDB(PGLOBAL g)
if (Memory < 3) {
// Method will depend on cursor type
- if ((Rbuf = Ocp->Rewind(Query, (PODBCCOL)Columns)) < 0) {
- Ocp->Close();
- return true;
- } // endif Rewind
+ if ((Rbuf = Ocp->Rewind(Query->GetStr(), (PODBCCOL)Columns)) < 0)
+ if (Mode != MODE_READX) {
+ Ocp->Close();
+ return true;
+ } else
+ Rbuf = 0;
} else
Rbuf = Qrp->Nblin;
@@ -864,15 +902,14 @@ bool TDBODBC::OpenDB(PGLOBAL g)
/*********************************************************************/
if (Mode == MODE_READ || Mode == MODE_READX) {
if (Memory > 1 && !Srcdef) {
- char *Sql;
- int n;
+ int n;
- if ((Sql = MakeSQL(g, true))) {
+ if (!MakeSQL(g, true)) {
// Allocate a Count(*) column
Cnp = new(g) ODBCCOL;
Cnp->InitValue(g);
- if ((n = Ocp->GetResultSize(Sql, Cnp)) < 0) {
+ if ((n = Ocp->GetResultSize(Query->GetStr(), Cnp)) < 0) {
strcpy(g->Message, "Cannot get result size");
return true;
} // endif n
@@ -882,36 +919,36 @@ bool TDBODBC::OpenDB(PGLOBAL g)
if ((Qrp = Ocp->AllocateResult(g)))
Memory = 2; // Must be filled
else {
- strcpy(g->Message, "Memory allocation failed");
+ strcpy(g->Message, "Result set memory allocation failed");
return true;
} // endif n
Ocp->m_Rows = 0;
- } else {
- strcpy(g->Message, "MakeSQL failed");
+ } else
return true;
- } // endif Sql
} // endif Memory
- if ((Query = MakeSQL(g, false))) {
+ if (!(rc = MakeSQL(g, false))) {
for (PODBCCOL colp = (PODBCCOL)Columns; colp;
colp = (PODBCCOL)colp->GetNext())
if (!colp->IsSpecial())
colp->AllocateBuffers(g, Rows);
- rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0);
- } // endif Query
+ rc = (Mode == MODE_READ)
+ ? ((Rows = Ocp->ExecDirectSQL(Query->GetStr(), (PODBCCOL)Columns)) < 0)
+ : false;
+ } // endif rc
} else if (Mode == MODE_INSERT) {
- if ((Query = MakeInsert(g))) {
- if (Nparm != Ocp->PrepareSQL(Query)) {
+ if (!(rc = MakeInsert(g))) {
+ if (Nparm != Ocp->PrepareSQL(Query->GetStr())) {
strcpy(g->Message, MSG(PARM_CNT_MISS));
rc = true;
} else
rc = BindParameters(g);
- } // endif Query
+ } // endif rc
} else if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
rc = false; // wait for CheckCond before calling MakeCommand(g);
@@ -970,6 +1007,59 @@ bool TDBODBC::SetRecpos(PGLOBAL g, int recpos)
} // end of SetRecpos
/***********************************************************************/
+/* Data Base indexed read routine for MYSQL access method. */
+/***********************************************************************/
+bool TDBODBC::ReadKey(PGLOBAL g, OPVAL op, const key_range *kr)
+{
+ char c = Quote ? *Quote : 0;
+ int oldlen = Query->GetLength();
+ PHC hc = To_Def->GetHandler();
+
+ if (!(kr || hc->end_range) || op == OP_NEXT ||
+ Mode == MODE_UPDATE || Mode == MODE_DELETE) {
+ if (!kr && Mode == MODE_READX) {
+ // This is a false indexed read
+ Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns);
+ Mode = MODE_READ;
+ return (Rows < 0);
+ } // endif key
+
+ return false;
+ } else {
+ if (To_Def->GetHandler()->MakeKeyWhere(g, Query, op, c, kr))
+ return true;
+
+ if (To_CondFil) {
+ if (To_CondFil->Idx != hc->active_index) {
+ To_CondFil->Idx = hc->active_index;
+ To_CondFil->Body= (char*)PlugSubAlloc(g, NULL, 0);
+ *To_CondFil->Body= 0;
+
+ if ((To_CondFil = hc->CheckCond(g, To_CondFil, To_CondFil->Cond)))
+ PlugSubAlloc(g, NULL, strlen(To_CondFil->Body) + 1);
+
+ } // endif active_index
+
+ if (To_CondFil)
+ if (Query->Append(" AND ") || Query->Append(To_CondFil->Body)) {
+ strcpy(g->Message, "Readkey: Out of memory");
+ return true;
+ } // endif Append
+
+ } // endif To_Condfil
+
+ Mode = MODE_READ;
+ } // endif's op
+
+ if (trace)
+ htrc("ODBC ReadKey: Query=%s\n", Query->GetStr());
+
+ Rows = Ocp->ExecDirectSQL((char*)Query->GetStr(), (PODBCCOL)Columns);
+ Query->Truncate(oldlen);
+ return (Rows < 0);
+} // end of ReadKey
+
+/***********************************************************************/
/* VRDNDOS: Data Base read routine for odbc access method. */
/***********************************************************************/
int TDBODBC::ReadDB(PGLOBAL g)
@@ -981,11 +1071,11 @@ 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)))
+ if (!Query && MakeCommand(g))
return RC_FX;
// Send the UPDATE/DELETE command to the remote table
- if (!Ocp->ExecSQLcommand(Query)) {
+ if (!Ocp->ExecSQLcommand(Query->GetStr())) {
sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
if (trace)
@@ -1063,11 +1153,11 @@ int TDBODBC::WriteDB(PGLOBAL g)
int TDBODBC::DeleteDB(PGLOBAL g, int irc)
{
if (irc == RC_FX) {
- if (!Query && !(Query = MakeCommand(g)))
+ if (!Query && MakeCommand(g))
return RC_FX;
// Send the DELETE (all) command to the remote table
- if (!Ocp->ExecSQLcommand(Query)) {
+ if (!Ocp->ExecSQLcommand(Query->GetStr())) {
sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
if (trace)
@@ -1279,12 +1369,7 @@ void ODBCCOL::ReadColumn(PGLOBAL g)
} // endif Buf_Type
- // Nulls are handled by StrLen[n] == SQL_NULL_DATA
- // MDEV-8561
-//if (Value->IsZero())
-// Value->SetNull(Nullable);
-
- if (trace) {
+ if (trace > 1) {
char buf[64];
htrc("ODBC Column %s: rows=%d buf=%p type=%d value=%s\n",
@@ -1572,9 +1657,12 @@ bool TDBXDBC::OpenDB(PGLOBAL g)
int TDBXDBC::ReadDB(PGLOBAL g)
{
if (Cmdlist) {
- Query = Cmdlist->Cmd;
+ if (!Query)
+ Query = new(g)STRING(g, 0, Cmdlist->Cmd);
+ else
+ Query->Set(Cmdlist->Cmd);
- if (Ocp->ExecSQLcommand(Query))
+ if (Ocp->ExecSQLcommand(Query->GetStr()))
Nerr++;
Fpos++; // Used for progress info
@@ -1632,10 +1720,10 @@ void XSRCCOL::ReadColumn(PGLOBAL g)
PTDBXDBC tdbp = (PTDBXDBC)To_Tdb;
switch (Flag) {
- case 0: Value->SetValue_psz(tdbp->Query); break;
- case 1: Value->SetValue(tdbp->AftRows); break;
- case 2: Value->SetValue_psz(g->Message); break;
- default: Value->SetValue_psz("Invalid Flag"); break;
+ case 0: Value->SetValue_psz(tdbp->Query->GetStr()); break;
+ case 1: Value->SetValue(tdbp->AftRows); break;
+ case 2: Value->SetValue_psz(g->Message); break;
+ default: Value->SetValue_psz("Invalid Flag"); break;
} // endswitch Flag
} // end of ReadColumn
diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h
index b8c9d85aae5..6440dee830d 100644
--- a/storage/connect/tabodbc.h
+++ b/storage/connect/tabodbc.h
@@ -42,7 +42,8 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */
int GetOptions(void) {return Options;}
// Methods
- virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+ virtual int Indexable(void) {return 2;}
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE m);
protected:
@@ -111,15 +112,14 @@ class TDBODBC : public TDBASE {
virtual int WriteDB(PGLOBAL g);
virtual int DeleteDB(PGLOBAL g, int irc);
virtual void CloseDB(PGLOBAL g);
- virtual bool ReadKey(PGLOBAL g, OPVAL op, const void *key, int len)
- {return true;}
+ virtual bool ReadKey(PGLOBAL g, OPVAL op, const key_range *kr);
protected:
// Internal functions
int Decode(char *utf, char *buf, size_t n);
- char *MakeSQL(PGLOBAL g, bool cnt);
- char *MakeInsert(PGLOBAL g);
- char *MakeCommand(PGLOBAL g);
+ bool MakeSQL(PGLOBAL g, bool cnt);
+ bool MakeInsert(PGLOBAL g);
+ bool MakeCommand(PGLOBAL g);
//bool MakeFilter(PGLOBAL g, bool c);
bool BindParameters(PGLOBAL g);
//char *MakeUpdate(PGLOBAL g);
@@ -129,14 +129,14 @@ class TDBODBC : public TDBASE {
ODBConn *Ocp; // Points to an ODBC connection class
ODBCCOL *Cnp; // Points to count(*) column
ODBCPARM Ops; // Additional parameters
- char *Connect; // Points to connection string
+ PSTRG Query; // Constructed SQL query
+ 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
char *Count; // Points to count(*) SQL statement
//char *Where; // Points to local where clause
char *Quote; // The identifier quoting character
diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp
index a5ca96e40c4..57d204a4286 100644
--- a/storage/connect/tabxml.cpp
+++ b/storage/connect/tabxml.cpp
@@ -60,6 +60,7 @@ extern "C" char version[];
#endif // !__WIN__
#define TYPE_UNKNOWN 12 /* Must be greater than other types */
+#define XLEN(M) sizeof(M) - strlen(M) - 1 /* To avoid overflow*/
/***********************************************************************/
/* Class and structure used by XMLColumns. */
@@ -225,30 +226,30 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info)
more:
if (vp->atp) {
strncpy(colname, vp->atp->GetName(g), sizeof(colname));
- strncat(xcol->Name, colname, sizeof(xcol->Name)-1);
+ strncat(xcol->Name, colname, XLEN(xcol->Name));
switch (vp->atp->GetText(g, buf, sizeof(buf))) {
case RC_INFO:
PushWarning(g, txmp);
case RC_OK:
- strncat(fmt, "@", sizeof(fmt));
+ strncat(fmt, "@", XLEN(fmt));
break;
default:
goto err;
} // enswitch rc
if (j)
- strncat(fmt, colname, sizeof(fmt)-1);
+ strncat(fmt, colname, XLEN(fmt));
} else {
if (tdp->Usedom && node->GetType() != 1)
continue;
strncpy(colname, node->GetName(g), sizeof(colname));
- strncat(xcol->Name, colname, sizeof(xcol->Name)-1);
+ strncat(xcol->Name, colname, XLEN(xcol->Name));
if (j)
- strncat(fmt, colname, sizeof(fmt)-1);
+ strncat(fmt, colname, XLEN(fmt));
if (j < lvl && ok) {
vp = lvlp[j+1];
@@ -266,9 +267,10 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info)
if (!vp->atp)
node = vp->nl->GetItem(g, vp->k++, node);
- strncat(strncat(fmt, colname, 125), "/", 125);
- strncat(xcol->Name, "_", 64);
- j++;
+ strncat(fmt, colname, XLEN(fmt));
+ strncat(fmt, "/", XLEN(fmt));
+ strncat(xcol->Name, "_", XLEN(xcol->Name));
+ j++;
vp->n = (int)strlen(xcol->Name);
vp->m = (int)strlen(fmt);
goto more;
diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp
index ed380c46043..f9597cb842b 100644
--- a/storage/connect/value.cpp
+++ b/storage/connect/value.cpp
@@ -340,7 +340,7 @@ PVAL AllocateValue(PGLOBAL g, void *value, short type, short prec)
switch (type) {
case TYPE_STRING:
- valp = new(g) TYPVAL<PSZ>((PSZ)value);
+ valp = new(g) TYPVAL<PSZ>((PSZ)value, prec);
break;
case TYPE_SHORT:
valp = new(g) TYPVAL<short>(*(short*)value, TYPE_SHORT);
@@ -1209,12 +1209,12 @@ void TYPVAL<TYPE>::Print(PGLOBAL g, char *ps, uint z)
/***********************************************************************/
/* STRING public constructor from a constant string. */
/***********************************************************************/
-TYPVAL<PSZ>::TYPVAL(PSZ s) : VALUE(TYPE_STRING)
+TYPVAL<PSZ>::TYPVAL(PSZ s, short c) : VALUE(TYPE_STRING)
{
Strp = s;
Len = strlen(s);
Clen = Len;
- Ci = false;
+ Ci = (c == 1);
} // end of STRING constructor
/***********************************************************************/
diff --git a/storage/connect/value.h b/storage/connect/value.h
index 471da851423..c5a381e89da 100644
--- a/storage/connect/value.h
+++ b/storage/connect/value.h
@@ -236,7 +236,7 @@ template <>
class DllExport TYPVAL<PSZ>: public VALUE {
public:
// Constructors
- TYPVAL(PSZ s);
+ TYPVAL(PSZ s, short c = 0);
TYPVAL(PGLOBAL g, PSZ s, int n, int c);
// Implementation
diff --git a/storage/connect/xobject.cpp b/storage/connect/xobject.cpp
index a6faebf3c2b..a0b7849543d 100644
--- a/storage/connect/xobject.cpp
+++ b/storage/connect/xobject.cpp
@@ -291,14 +291,14 @@ bool STRING::Set(char *s, uint n)
} // end of Set
/***********************************************************************/
-/* Append a char* to a STRING. */
+/* Append a char* to a STRING. */
/***********************************************************************/
-bool STRING::Append(const char *s, uint ln)
+bool STRING::Append(const char *s, uint ln, bool nq)
{
if (!s)
return false;
- uint len = Length + ln + 1;
+ uint i, len = Length + ln + 1;
if (len > Size) {
char *p = Realloc(len);
@@ -312,8 +312,22 @@ bool STRING::Append(const char *s, uint ln)
} // endif n
- strncpy(Strp + Length, s, ln);
- Length = len - 1;
+ if (nq) {
+ for (i = 0; i < ln; i++)
+ switch (s[i]) {
+ case '\\': Strp[Length++] = '\\'; Strp[Length++] = '\\'; break;
+ case '\0': Strp[Length++] = '\\'; Strp[Length++] = '0'; break;
+ case '\'': Strp[Length++] = '\\'; Strp[Length++] = '\''; break;
+ case '\n': Strp[Length++] = '\\'; Strp[Length++] = 'n'; break;
+ case '\r': Strp[Length++] = '\\'; Strp[Length++] = 'r'; break;
+ case '\032': Strp[Length++] = '\\'; Strp[Length++] = 'Z'; break;
+ default: Strp[Length++] = s[i];
+ } // endswitch s[i]
+
+ } else
+ for (i = 0; i < ln && s[i]; i++)
+ Strp[Length++] = s[i];
+
Strp[Length] = 0;
return false;
} // end of Append
diff --git a/storage/connect/xobject.h b/storage/connect/xobject.h
index 82ff9e21225..d78cd09f9a4 100644
--- a/storage/connect/xobject.h
+++ b/storage/connect/xobject.h
@@ -134,7 +134,7 @@ class DllExport STRING : public BLOCK {
inline void Reset(void) {*Strp = 0;}
bool Set(PSZ s);
bool Set(char *s, uint n);
- bool Append(const char *s, uint ln);
+ bool Append(const char *s, uint ln, bool nq = false);
bool Append(PSZ s);
bool Append(STRING &str);
bool Append(char c);
diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h
index 1a75d97bafa..e18a08a54b8 100644
--- a/storage/connect/xtable.h
+++ b/storage/connect/xtable.h
@@ -19,6 +19,7 @@
#include "m_ctype.h"
typedef class CMD *PCMD;
+typedef struct st_key_range key_range;
// Commands executed by XDBC and MYX tables
class CMD : public BLOCK {
@@ -32,12 +33,24 @@ class CMD : public BLOCK {
}; // end of class CMD
// Condition filter structure
-typedef struct _cond_filter {
- char *Body;
- OPVAL Op;
- PCMD Cmds;
-} CONDFIL, *PCFIL;
-
+class CONDFIL : public BLOCK {
+ public:
+ // Constructor
+ CONDFIL(const Item *cond, uint idx, AMT type)
+ {
+ Cond = cond; Idx = idx; Type = type; Body = NULL; Op = OP_XX; Cmds = NULL;
+ }
+
+ // Members
+ const Item *Cond;
+ AMT Type;
+ uint Idx;
+ char *Body;
+ OPVAL Op;
+ PCMD Cmds;
+}; // end of class CONDFIL
+
+typedef class CONDFIL *PCFIL;
typedef class TDBCAT *PTDBCAT;
typedef class CATCOL *PCATCOL;
@@ -109,7 +122,7 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
virtual int DeleteDB(PGLOBAL, int) = 0;
virtual void CloseDB(PGLOBAL) = 0;
virtual int CheckWrite(PGLOBAL) {return 0;}
- virtual bool ReadKey(PGLOBAL, OPVAL, const void *, int) = 0;
+ virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *) = 0;
protected:
// Members
@@ -188,7 +201,7 @@ class DllExport TDBASE : public TDB {
virtual void MarkDB(PGLOBAL g, PTDB tdb2);
virtual int MakeIndex(PGLOBAL g, PIXDEF, bool)
{strcpy(g->Message, "Remote index"); return RC_INFO;}
- virtual bool ReadKey(PGLOBAL, OPVAL, const void *, int)
+ virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *)
{assert(false); return true;}
protected: