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