From 27f9d2f9291efaca2a96fbda1c68e410abd94767 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Fri, 4 Dec 2015 22:38:16 +0100 Subject: Commit updating CONNECT from the 10.1 version --- storage/connect/CMakeLists.txt | 8 +- storage/connect/connect.cc | 28 +- storage/connect/connect.h | 4 +- storage/connect/filamzip.cpp | 7 +- storage/connect/ha_connect.cc | 343 +- storage/connect/ha_connect.h | 11 +- storage/connect/json.cpp | 375 +- storage/connect/json.h | 91 +- storage/connect/jsonudf.cpp | 4954 ++++++++++++++++++-- storage/connect/jsonudf.h | 292 ++ storage/connect/mycat.cc | 5 +- storage/connect/mycat.h | 1 + storage/connect/mysql-test/connect/r/datest.result | 27 + storage/connect/mysql-test/connect/r/grant.result | 19 +- .../connect/mysql-test/connect/r/ini_grant.result | 4 +- storage/connect/mysql-test/connect/r/json.result | 34 + .../connect/mysql-test/connect/r/json_udf.result | 622 ++- .../mysql-test/connect/r/json_udf_bin.result | 588 +++ .../mysql-test/connect/r/mysql_grant.result | 4 +- .../mysql-test/connect/r/odbc_sqlite3_grant.result | 16 +- .../connect/mysql-test/connect/r/xml_grant.result | 3 +- .../connect/mysql-test/connect/std_data/bib0.json | 2 + .../mysql-test/connect/std_data/biblio.json | 10 +- .../connect/mysql-test/connect/std_data/gloss.json | 22 + storage/connect/mysql-test/connect/t/datest.test | 12 + storage/connect/mysql-test/connect/t/grant.inc | 3 +- storage/connect/mysql-test/connect/t/grant.test | 5 +- .../connect/mysql-test/connect/t/ini_grant.test | 4 +- storage/connect/mysql-test/connect/t/json.test | 30 + storage/connect/mysql-test/connect/t/json_udf.inc | 66 +- storage/connect/mysql-test/connect/t/json_udf.test | 256 +- storage/connect/mysql-test/connect/t/json_udf2.inc | 49 + .../connect/mysql-test/connect/t/json_udf_bin.test | 212 + .../connect/mysql-test/connect/t/mysql_grant.test | 4 +- .../mysql-test/connect/t/odbc_postgresql.test | 9 + .../mysql-test/connect/t/odbc_sqlite3_grant.test | 14 +- storage/connect/plugutil.c | 6 +- storage/connect/reldef.cpp | 1 + storage/connect/tabdos.cpp | 6 +- storage/connect/tabjson.cpp | 57 +- storage/connect/tabmysql.cpp | 58 +- storage/connect/tabmysql.h | 2 +- storage/connect/tabodbc.cpp | 426 +- storage/connect/tabodbc.h | 16 +- storage/connect/tabxml.cpp | 20 +- storage/connect/value.cpp | 6 +- storage/connect/value.h | 2 +- storage/connect/xobject.cpp | 25 +- storage/connect/xobject.h | 2 +- storage/connect/xtable.h | 29 +- 50 files changed, 7733 insertions(+), 1057 deletions(-) create mode 100644 storage/connect/jsonudf.h create mode 100644 storage/connect/mysql-test/connect/r/json_udf_bin.result create mode 100644 storage/connect/mysql-test/connect/std_data/bib0.json create mode 100644 storage/connect/mysql-test/connect/std_data/gloss.json create mode 100644 storage/connect/mysql-test/connect/t/json_udf2.inc create mode 100644 storage/connect/mysql-test/connect/t/json_udf_bin.test 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 e6510c3c82a..84a78f7dee1 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 November 20, 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= '/'; @@ -1114,7 +1114,7 @@ int GetIntegerTableOption(PGLOBAL g, PTOS options, char *opname, int idef) else if (!stricmp(opname, "Compressed")) opval= (options->compressed); - if (opval == NO_IVAL) { + if (opval == (ulonglong)NO_IVAL) { char *pv; if ((pv= GetListOption(g, opname, options->oplist))) @@ -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) - 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; - - 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 + if (active_index < MAX_KEY) { + KEY_PART_INFO *kpart; + KEY *kfp= &table->key_info[active_index]; + uint rem= kfp->user_defined_key_parts; - if (nq) - oom|= qry->Append('\''); + for (kpart= kfp->key_part; rem; rem--, kpart++) + if (kpart->field == fp) + return true; - if (kpart->key_part_flag & HA_VAR_LENGTH_PART) { - String varchar; - uint var_length= uint2korr(ptr); + } // endif active_index - 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 + return false; +} // end of IsIndexed - 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,9 +2543,11 @@ PFIL ha_connect::CondFilter(PGLOBAL g, Item *cond) case MYSQL_TYPE_YEAR: case MYSQL_TYPE_NEWDATE: return NULL; + 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 @@ -2562,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); @@ -2596,7 +2659,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, AMT tty, Item *cond) List* arglist= cond_item->argument_list(); List_iterator li(*arglist); - Item *subitem; + const Item *subitem; p1= body + strlen(body); strcpy(p1, "("); @@ -2604,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 @@ -2626,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; @@ -2658,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++) { @@ -2679,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); @@ -2712,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 @@ -2901,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); @@ -3372,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); @@ -3444,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 @@ -3750,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; @@ -4034,7 +4107,27 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn) case TAB_MAC: case TAB_WMI: case TAB_OEM: - return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0); +#ifdef NO_EMBEDDED_ACCESS_CHECKS + return false; +#endif + /* + If table or table->mdl_ticket is NULL - it's a DLL, e.g. CREATE TABLE. + if the table has an MDL_EXCLUSIVE lock - it's a DDL too, e.g. the + insert step of CREATE ... SELECT. + + Otherwise it's a DML, the table was normally opened, locked, + privilege were already checked, and table->grant.privilege is set. + With SQL SECURITY DEFINER, table->grant.privilege has definer's privileges. + */ + if (!table || !table->mdl_ticket || table->mdl_ticket->get_type() == MDL_EXCLUSIVE) + return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0); + if (table->grant.privilege & FILE_ACL) + return false; + status_var_increment(thd->status_var.access_denied_errors); + my_error(access_denied_error_code(thd->password), MYF(0), + thd->security_ctx->priv_user, thd->security_ctx->priv_host, + (thd->password ? ER(ER_YES) : ER(ER_NO))); + return true; // This is temporary until a solution is found case TAB_TBL: @@ -4421,15 +4514,16 @@ int ha_connect::external_lock(THD *thd, int lock_type) xmod= MODE_ANY; // For info commands DBUG_RETURN(rc); } // endif MODE_ANY - - DBUG_ASSERT(table && table->s); - + else if (check_privileges(thd, options, table->s->db.str)) { strcpy(g->Message, "This operation requires the FILE privilege"); htrc("%s\n", g->Message); DBUG_RETURN(HA_ERR_INTERNAL_ERROR); } // endif check_privileges + + DBUG_ASSERT(table && table->s); + // Table mode depends on the query type newmode= CheckMode(g, thd, newmode, &xcheck, &cras); @@ -5068,7 +5162,6 @@ static int connect_assisted_discovery(handlerton *, THD* thd, hdr= (int)topt->header; tbl= topt->tablist; col= topt->colist; - lrecl= (int)topt->lrecl; if (topt->oplist) { host= GetListOption(g, "host", topt->oplist, "localhost"); @@ -5684,6 +5777,14 @@ int ha_connect::create(const char *name, TABLE *table_arg, PGLOBAL g= xp->g; DBUG_ENTER("ha_connect::create"); + /* + This assignment fixes test failures if some + "ALTER TABLE t1 ADD KEY(a)" query exits on ER_ACCESS_DENIED_ERROR + (e.g. on missing FILE_ACL). All following "CREATE TABLE" failed with + "ERROR 1105: CONNECT index modification should be in-place" + TODO: check with Olivier. + */ + g->Xchk= NULL; int sqlcom= thd_sql_command(table_arg->in_use); PTOS options= GetTableOptionStruct(table_arg->s); @@ -6173,10 +6274,6 @@ bool ha_connect::FileExists(const char *fn, bool bf) int n; struct stat info; - if (check_access(ha_thd(), FILE_ACL, table->s->db.str, - NULL, NULL, 0, 0)) - return true; - #if defined(__WIN__) s= "\\"; #else // !__WIN__ @@ -6662,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.0003", /* 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..638999540bb 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,44 @@ 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) { bool b = false, err = true; JOUT *jp; + FILE *fs = NULL; - g->Message[0] = 0; + g->Message[0] = 0; if (!jsp) { strcpy(g->Message, "Null json tree"); return NULL; - } else if (!fs) { + } 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)); + return g->Message; + } 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,7 +580,7 @@ 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; } else if (!err) { @@ -565,29 +605,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 +698,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 -------------------------- */ @@ -866,6 +917,20 @@ PJPR JOBJECT::AddPair(PGLOBAL g, PSZ key) return jpp; } // 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. */ /***********************************************************************/ @@ -903,12 +968,30 @@ PSZ JOBJECT::GetText(PGLOBAL g, PSZ text) return text + n; } // 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)) { @@ -923,6 +1006,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. */ /***********************************************************************/ @@ -943,23 +1043,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 +1077,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; @@ -1048,6 +1185,17 @@ JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON() Del = false; } // 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. */ /***********************************************************************/ @@ -1092,6 +1240,14 @@ int JVALUE::GetInteger(void) return (Value) ? Value->GetIntValue() : 0; } // 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. */ /***********************************************************************/ @@ -1128,13 +1284,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 +1329,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..2437f7e7476 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 #include #include #include +#include -#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); + +/* ----------------------------------- JSNX ------------------------------------ */ + +/*********************************************************************************/ +/* 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 + +/*********************************************************************************/ +/* ReadValue: */ +/*********************************************************************************/ +void JSNX::ReadValue(PGLOBAL g) +{ + Value->SetValue_pval(GetColumnValue(g, Row, 0)); +} // end of ReadValue -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*); +/*********************************************************************************/ +/* 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; -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*); + 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_Array_Add_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Array_Add(UDF_EXEC_ARGS); -DllExport void Json_Array_Add_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_Array_Delete_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Array_Delete(UDF_EXEC_ARGS); -DllExport void Json_Array_Delete_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_Object_init(UDF_INIT*, UDF_ARGS*, char*); -DllExport char *Json_Object(UDF_EXEC_ARGS); -DllExport void Json_Object_deinit(UDF_INIT*); + if (i < Nod-1) + if (!(row = (val) ? val->GetJsp() : NULL)) + val = NULL; -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*); + } // endfor i -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*); + return (val != NULL); +} // end of CheckPath -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" +/***********************************************************************/ +/* GetRow: Set the complete path of the object to be set. */ +/***********************************************************************/ +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 /***********************************************************************/ -/* Allocate and initialise the memory area. */ +/* WriteValue: */ /***********************************************************************/ -static my_bool JsonInit(UDF_INIT *initid, char *message, +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,229 @@ 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 + char *msg; + 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 ((msg = Serialize(g, top, MakePSZ(g, args, 0), pretty))) + PUSH_WARNING(msg); + + str = NULL; + } else if (IsJson(args, 0) == 3) { + PBSON bsp = (PBSON)args->args[0]; + + if (bsp->Filename) { + // Make the change in the json file + char *msg; + + if ((msg = Serialize(g, top, bsp->Filename, bsp->Pretty))) + PUSH_WARNING(msg); + + 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 IsJson + +/*********************************************************************************/ +/* 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 +1312,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 +1342,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 +1349,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 +1364,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); break; case INT_RESULT: @@ -167,7 +1395,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 +1402,54 @@ 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 obj, my_bool mod = false) +{ + unsigned long rl, ml; + + n = MY_MIN(n, args->arg_count); + + for (uint i = 0; i < n; i++) + if (IsJson(args, i) == 2) { + if (CalcLen(args, obj, rl, ml, mod)) + return true; + else 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 +1461,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 *sap = (args->arg_count > (unsigned)i) ? args->args[i] : NULL; + 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 > 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 +1669,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 +1676,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, false)) { + 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, 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"); + 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_Delete first argument must be a json item"); - 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, 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, 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, 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, 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, false, 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)) { + 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, false, 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)) { + 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, false)) { + 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 +2410,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 +2420,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 +2430,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 +2439,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 +2483,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 +2503,2450 @@ 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, 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 *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; -void Json_Object_Grp_deinit(UDF_INIT* initid) + if (!g->Xchk) { + 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 (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; + + } // endif CheckMemory + + 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 *) { - PlugExit((PGLOBAL)initid->ptr); -} // end of Json_Object_Grp_deinit + int rc; + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + if (g->N) { + str = (char*)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + // 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) { + 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--; + } // endif CheckMemory + +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) +{ + 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 (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + long long n; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + 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()) { +// PUSH_WARNING("Value not found"); + *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; + } // endif CheckMemory + + if (g->Mrr) *error = 1; + *is_null = 1; + return 0LL; +} // 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) +{ + 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 (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + double d; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + 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()) { +// PUSH_WARNING("Value not found"); + *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; + } // endif CheckMemory + + if (g->Mrr) *error = 1; + *is_null = 1; + return 0.0; +} // 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 *path = NULL; + 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; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p; + int k, rc; + PJVAL jvp, jvp2; + PJSON jsp; + PJSNX jsx; + + // 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) { + 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; + } // endif CheckMemory + + *error = 1; + *is_null = 1; + return NULL; +} // 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 *path = NULL; + 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; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p; + int rc, mx = 10; + PJVAL jvp, jvp2; + PJSON jsp; + PJSNX jsx; + + // 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) { + 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; + } // endif CheckMemory + + *error = 1; + *is_null = 1; + return NULL; +} // 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) +{ + 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 (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + long long n; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + 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 + + 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; + } // endif CheckMemory + + 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) +{ + int w, rc; + my_bool b = true; + char *str = NULL; + PGLOBAL g = (PGLOBAL)initid->ptr; + + 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; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PGLOBAL gb = GetMemPtr(g, args, 0); + + // 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) { + 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--; + } // endif CheckMemory + +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, *msg, *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; + + PlugSubSet(g, g->Sarea, g->Sarea_Size); + + 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) { + 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); + return NULL; + } else + fn = jvp->GetString(); + + } // endif p + + if (!(jsp = ParseJson(g, p, strlen(p)))) { + PUSH_WARNING(g->Message); + return NULL; + } // 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 ((msg = Serialize(g, jvp->GetJson(), fn, pretty))) + PUSH_WARNING(msg); + } 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 + +/*********************************************************************************/ +/* Add one or several values to a Json array. */ +/*********************************************************************************/ +my_bool jbin_array_add_values_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + 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, false)) { + 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, 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, 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, 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, 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)) { + 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)) { + 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, false)) { + 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) +{ + PGLOBAL g = (PGLOBAL)initid->ptr; + PBSON bsp = NULL; + + if (g->N) { + bsp = (PBSON)g->Activityp; + goto fin; + } else if (initid->const_item) + g->N = 1; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + + if (!g->Xchk) { + 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, false)) { + PUSH_WARNING(g->Message); + *is_null = 1; + return NULL; + } // 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; + + } // endif CheckMemory + +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, 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) +{ + int w; + my_bool b = true; + 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 (!strcmp(result, "$insert")) + w = 1; + else if (!strcmp(result, "$update")) + w = 2; + else + w = 0; + + if (!CheckMemory(g, initid, args, 1, false)) { + char *p, *path; + PJSON jsp; + PJSNX jsx; + PJVAL jvp; + PGLOBAL gb = GetMemPtr(g, args, 0); + + if (!g->Xchk) { + 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; + + 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; + + } // endif CheckMemory + +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 ba5728703a5..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 # @@ -46,7 +47,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -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(); @@ -130,7 +132,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -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(); @@ -224,7 +227,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -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(); @@ -318,7 +322,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -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(); @@ -412,7 +417,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -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(); @@ -506,7 +512,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -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 c3acf7c8dfc..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 @@ -59,7 +61,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; 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 f8d0ee6ad6f..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 @@ -40,7 +42,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost diff --git a/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result b/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result index 364f340eddf..06b4239bd69 100644 --- a/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result +++ b/storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result @@ -49,10 +49,11 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) CREATE VIEW v1 AS SELECT * FROM t1; ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) # Testing a VIEW created with FILE privileges but accessed with no FILE +# using SQL SECIRITY INVOKER SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost @@ -64,6 +65,19 @@ UPDATE v1 SET a=123; ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) DELETE FROM v1; ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) +# Testing a VIEW created with FILE privileges but accessed with no FILE +# using SQL SECIRITY DEFINER +DROP VIEW v1; +SELECT user(); +user() +root@localhost +CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1; +SELECT user(); +user() +user@localhost +SELECT * FROM v1 WHERE a='test1'; +a +test1 SELECT user(); user() root@localhost diff --git a/storage/connect/mysql-test/connect/r/xml_grant.result b/storage/connect/mysql-test/connect/r/xml_grant.result index ea38e57af86..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(); @@ -63,7 +64,7 @@ ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO) SELECT user(); user() root@localhost -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; SELECT user(); user() user@localhost 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 7bb214dc9fd..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,,) @@ -53,7 +54,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR diff --git a/storage/connect/mysql-test/connect/t/grant.test b/storage/connect/mysql-test/connect/t/grant.test index 909bb4117a1..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`; @@ -49,7 +50,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR @@ -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 30678645692..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(); @@ -54,7 +56,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR 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 @@ -115,6 +116,33 @@ ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='biblio.json'; 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 # @@ -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 7c75103ed3b..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(); @@ -54,7 +56,7 @@ CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR diff --git a/storage/connect/mysql-test/connect/t/odbc_postgresql.test b/storage/connect/mysql-test/connect/t/odbc_postgresql.test index f634b34323f..7fc16130713 100644 --- a/storage/connect/mysql-test/connect/t/odbc_postgresql.test +++ b/storage/connect/mysql-test/connect/t/odbc_postgresql.test @@ -27,6 +27,15 @@ #Servername=localhost #Port=5432 # +# 5. Allow user "mtr" to connect to the database "mtr" +# Add this line into the begginning of pg_hba.conf +# (usually /var/lib/pgsql/data/pg_hba.conf on Linux): +#host mtr mtr 127.0.0.1/32 password +# +# 6. Restart the server: +# sudo service postgresql restart +# +# SET NAMES utf8; diff --git a/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test b/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test index 7664a4473ba..887385af2dc 100644 --- a/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test +++ b/storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test @@ -56,9 +56,10 @@ ALTER TABLE t1 READONLY=1; CREATE VIEW v1 AS SELECT * FROM t1; --echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--echo # using SQL SECIRITY INVOKER --connection default SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; +CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1; --connection user SELECT user(); --error ER_ACCESS_DENIED_ERROR @@ -70,6 +71,17 @@ UPDATE v1 SET a=123; --error ER_ACCESS_DENIED_ERROR DELETE FROM v1; +--echo # Testing a VIEW created with FILE privileges but accessed with no FILE +--echo # using SQL SECIRITY DEFINER +--connection default +DROP VIEW v1; +SELECT user(); +CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1; +--connection user +SELECT user(); +SELECT * FROM v1 WHERE a='test1'; + + --disconnect user --connection default 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..61436ff88e5 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -578,7 +578,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) { @@ -659,7 +659,7 @@ int TDBJSN::ReadDB(PGLOBAL g) if (!IsRead() && ((rc = ReadBuffer(g)) != RC_OK)) { // Deferred reading failed } else if (!(Row = ParseJson(g, To_Line, - strlen(To_Line), Pretty, &Comma))) { + strlen(To_Line), &Pretty, &Comma))) { rc = (Pretty == 1 && !strcmp(To_Line, "]")) ? RC_EF : RC_FX; } else { Row = FindRow(g); @@ -755,7 +755,6 @@ int TDBJSN::MakeTopTree(PGLOBAL g, PJSON jsp) } else strcpy(To_Line, s); -// Row->Clear(); return false; } else return true; @@ -980,7 +979,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 +1045,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: @@ -1107,7 +1107,7 @@ PVAL JSONCOL::GetColumnValue(PGLOBAL g, PJSON row, int i) } 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 @@ -1128,11 +1128,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 +1275,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: @@ -1353,7 +1354,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 +1371,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,7 +1384,7 @@ 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 @@ -1522,7 +1522,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 +1540,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 +1556,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 @@ -1837,7 +1837,6 @@ void TDBJSON::CloseDB(PGLOBAL g) // Save the modified document char filename[_MAX_PATH]; PSZ msg; - FILE *fop; Doc->InitArray(g); @@ -1845,11 +1844,7 @@ 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))) + if ((msg = Serialize(g, Top, filename, Pretty))) puts(msg); } // end of CloseDB 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 #include @@ -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); @@ -969,6 +1006,59 @@ bool TDBODBC::SetRecpos(PGLOBAL g, int recpos) return false; } // 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. */ /***********************************************************************/ @@ -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 282e02b900e..57d204a4286 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -60,7 +60,7 @@ extern "C" char version[]; #endif // !__WIN__ #define TYPE_UNKNOWN 12 /* Must be greater than other types */ -#define XSTR(M) sizeof(M) - strlen(M) - 1 /* To avoid overflow*/ +#define XLEN(M) sizeof(M) - strlen(M) - 1 /* To avoid overflow*/ /***********************************************************************/ /* Class and structure used by XMLColumns. */ @@ -226,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, XSTR(xcol->Name)); + 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, "@", XSTR(fmt)); + strncat(fmt, "@", XLEN(fmt)); break; default: goto err; } // enswitch rc if (j) - strncat(fmt, colname, XSTR(fmt)); + strncat(fmt, colname, XLEN(fmt)); } else { if (tdp->Usedom && node->GetType() != 1) continue; strncpy(colname, node->GetName(g), sizeof(colname)); - strncat(xcol->Name, colname, XSTR(xcol->Name)); + strncat(xcol->Name, colname, XLEN(xcol->Name)); if (j) - strncat(fmt, colname, XSTR(fmt)); + strncat(fmt, colname, XLEN(fmt)); if (j < lvl && ok) { vp = lvlp[j+1]; @@ -267,10 +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(fmt, colname, XSTR(fmt)); - strncat(fmt, "/", XSTR(fmt)); - strncat(xcol->Name, "_", XSTR(xcol->Name)); - 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 884ce976a52..60471c9b0d8 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)value); + valp = new(g) TYPVAL((PSZ)value, prec); break; case TYPE_SHORT: valp = new(g) TYPVAL(*(short*)value, TYPE_SHORT); @@ -1209,12 +1209,12 @@ void TYPVAL::Print(PGLOBAL g, char *ps, uint z) /***********************************************************************/ /* STRING public constructor from a constant string. */ /***********************************************************************/ -TYPVAL::TYPVAL(PSZ s) : VALUE(TYPE_STRING) +TYPVAL::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 780917c9962..0cd0846ac7c 100644 --- a/storage/connect/value.h +++ b/storage/connect/value.h @@ -216,7 +216,7 @@ template <> class DllExport TYPVAL: 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 92bf039c07c..a0b7849543d 100644 --- a/storage/connect/xobject.cpp +++ b/storage/connect/xobject.cpp @@ -11,6 +11,7 @@ /* Include mariaDB header file. */ /***********************************************************************/ #include "my_global.h" +#include "m_string.h" /***********************************************************************/ /* Include required application header files */ @@ -290,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); @@ -311,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: -- cgit v1.2.1