diff options
35 files changed, 1703 insertions, 303 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c index 2c84b89b3e2..1d57b29c897 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -5147,6 +5147,7 @@ char check_if_ignore_table(const char *table_name, char *table_type) if (!opt_no_data && (!my_strcasecmp(&my_charset_latin1, table_type, "MRG_MyISAM") || !strcmp(table_type,"MRG_ISAM") || + !strcmp(table_type,"CONNECT") || !strcmp(table_type,"FEDERATED"))) result= IGNORE_DATA; } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 1400f088870..a7a53166563 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -379,6 +379,7 @@ sub main { mtr_report("Logging: $0 ", join(" ", @ARGV)); $DEFAULT_SUITES.= ',' . join(',', qw( + connect query_response_time sequence spider diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index b09ab0904e7..754991e9014 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1117,3 +1117,95 @@ fun:_nss_dns_gethostbyaddr_r fun:gethostbyaddr_r } + + +{ + ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers + Memcheck:Leak + fun:malloc + fun:strdup + ... + obj:*/libodbc.so* + fun:_ZN7ODBConn10GetDriversEP7_qryres +} + +{ + ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers + Memcheck:Leak + fun:calloc + ... + obj:*/libodbc.so* + fun:_ZN7ODBConn10GetDriversEP7_qryres +} + +{ + ConnectSE: unixODBC SQLAllocEnv leavs some "still reachable" pointers + Memcheck:Leak + fun:malloc + fun:strdup + ... + obj:*/libodbc.so* + fun:_ZN7ODBConn14GetDataSourcesEP7_qryres +} + + +{ + ConnectSE: unixODBC SQLAllocEnv leavs some "still reachable" pointers + Memcheck:Leak + fun:calloc + ... + obj:*/libodbc.so* + fun:_ZN7ODBConn14GetDataSourcesEP7_qryres +} + + +{ + ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers + Memcheck:Leak + fun:malloc + fun:strdup + ... + obj:*/libodbc.so* + fun:SQLDriverConnect + fun:_ZN7ODBConn7ConnectEj + fun:_ZN7ODBConn4OpenEPcj + fun:_Z11ODBCColumnsP7_globalPcS1_S1_b + fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO +} + +{ + ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers + Memcheck:Leak + fun:calloc + ... + obj:*/libodbc.so* + fun:SQLDriverConnect + fun:_ZN7ODBConn7ConnectEj + fun:_ZN7ODBConn4OpenEPcj + fun:_Z11ODBCColumnsP7_globalPcS1_S1_b + fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO +} + +{ + ConnectSE: unixODBC SQLDriverConnect leaves some "still reachable" pointers + Memcheck:Leak + fun:malloc + ... + obj:*/libodbc.so* + fun:SQLDriverConnect + fun:_ZN7ODBConn7ConnectEj + fun:_ZN7ODBConn4OpenEPcj + fun:_Z11ODBCColumnsP7_globalPcS1_S1_b + fun:_ZL26connect_assisted_discoveryP10handlertonP3THDP11TABLE_SHAREP14HA_CREATE_INFO +} + +{ + ConnectSE: unixODBC dlopen leaves some "still reachable" + Memcheck:Leak + fun:malloc + fun:expand_dynamic_string_token + ... + obj:*/libltdl.so* + ... + obj:*/libodbc.so* +} diff --git a/storage/connect/colblk.cpp b/storage/connect/colblk.cpp index a3206243cc1..64cccfced7f 100644 --- a/storage/connect/colblk.cpp +++ b/storage/connect/colblk.cpp @@ -178,7 +178,7 @@ bool COLBLK::InitValue(PGLOBAL g) (To_Tdb) ? To_Tdb->GetCat() : NULL))) return true; - Status = BUF_READY; + AddStatus(BUF_READY); Value->SetNullable(Nullable); #ifdef DEBTRACE diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp index 95426be97bc..0272a1d709a 100644 --- a/storage/connect/domdoc.cpp +++ b/storage/connect/domdoc.cpp @@ -592,6 +592,18 @@ PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np) } // end of GetItem +/******************************************************************/ +/* Reset the pointer on the deleted item. */ +/******************************************************************/ +bool DOMNODELIST::DropItem(PGLOBAL g, int n) + { + if (Listp == NULL || Listp->length <= n) + return true; + +//Listp->item[n] = NULL; La propriété n'a pas de méthode 'set' + return false; + } // end of DeleteItem + /* ----------------------- class DOMATTR ------------------------ */ /******************************************************************/ @@ -618,8 +630,8 @@ bool DOMATTR::SetText(PGLOBAL g, char *txtp, int len) Len = len; } // endif len - if (!MultiByteToWideChar(CP_ACP, 0, txtp, strlen(txtp) + 1, - Ws, Len + 1)) { + if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1, + Ws, Len + 1)) { sprintf(g->Message, MSG(WS_CONV_ERR), txtp); return true; } // endif diff --git a/storage/connect/domdoc.h b/storage/connect/domdoc.h index 0fd0a58ffdb..b0bcc1478a5 100644 --- a/storage/connect/domdoc.h +++ b/storage/connect/domdoc.h @@ -104,8 +104,9 @@ class DOMNODELIST : public XMLNODELIST { friend class DOMNODE; public: // Methods - virtual int GetLength(void) {return Listp->length;} + virtual int GetLength(void) {return Listp->length;} virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np); + virtual bool DropItem(PGLOBAL g, int n); protected: // Constructor diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 57770567cd6..a066c48bf0e 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -165,7 +165,7 @@ extern "C" char nmfile[]; extern "C" char pdebug[]; extern "C" { - char version[]= "Version 1.01.0007 July 26, 2013"; + char version[]= "Version 1.01.0008 August 18, 2013"; #if defined(XMSG) char msglang[]; // Default message language @@ -1093,7 +1093,8 @@ PTDB ha_connect::GetTDB(PGLOBAL g) if (!xp->CheckQuery(valid_query_id) && tdbp && !stricmp(tdbp->GetName(), table_name) - && tdbp->GetMode() == xmod) { + && (tdbp->GetMode() == xmod + || tdbp->GetAmType() == TYPE_AM_XML)) { tp= tdbp; tp->SetMode(xmod); } else if ((tp= CntGetTDB(g, table_name, xmod, this))) @@ -1132,13 +1133,14 @@ bool ha_connect::OpenTable(PGLOBAL g, bool del) break; } // endswitch xmode - if (xmod != MODE_INSERT) { + if (xmod != MODE_INSERT || tdbp->GetAmType() == TYPE_AM_ODBC + || tdbp->GetAmType() == TYPE_AM_MYSQL) { // Get the list of used fields (columns) char *p; unsigned int k1, k2, n1, n2; Field* *field; Field* fp; - MY_BITMAP *map= table->read_set; + MY_BITMAP *map= (xmod == MODE_INSERT) ? table->write_set : table->read_set; MY_BITMAP *ump= (xmod == MODE_UPDATE) ? table->write_set : NULL; k1= k2= 0; @@ -1373,7 +1375,8 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *buf) fp->option_struct->special) continue; // Is a virtual column possible here ??? - if (xmod == MODE_INSERT || + if ((xmod == MODE_INSERT && tdbp->GetAmType() != TYPE_AM_MYSQL + && tdbp->GetAmType() != TYPE_AM_ODBC) || bitmap_is_set(table->write_set, fp->field_index)) { for (colp= tp->GetSetCols(); colp; colp= colp->GetNext()) if (!stricmp(colp->GetName(), fp->field_name)) @@ -2658,8 +2661,9 @@ int ha_connect::delete_all_rows() PGLOBAL g= xp->g; DBUG_ENTER("ha_connect::delete_all_rows"); - // Close and reopen the table so it will be deleted - rc= CloseTable(g); + if (tdbp && tdbp->GetAmType() != TYPE_AM_XML) + // Close and reopen the table so it will be deleted + rc= CloseTable(g); if (!(OpenTable(g))) { if (CntDeleteRow(g, tdbp, true)) { @@ -3462,7 +3466,7 @@ static bool add_field(String *sql, const char *field_name, int typ, int len, { bool error= false; const char *type= PLGtoMYSQLtype(typ, dbf); - type= PLGtoMYSQLtype(typ, true); +// type= PLGtoMYSQLtype(typ, true); ????? error|= sql->append('`'); error|= sql->append(field_name); @@ -3612,6 +3616,7 @@ static int init_table_share(THD *thd, static int init_table_share(THD* thd, TABLE_SHARE *table_s, HA_CREATE_INFO *create_info, +// char *dsn, String *sql) { bool oom= false; @@ -3670,10 +3675,12 @@ static int init_table_share(THD* thd, } // endfor opt if (create_info->connect_string.length) { +//if (dsn) { oom|= sql->append(' '); oom|= sql->append("CONNECTION='"); oom|= sql->append_for_single_quote(create_info->connect_string.str, create_info->connect_string.length); +// oom|= sql->append_for_single_quote(dsn, strlen(dsn)); oom|= sql->append('\''); if (oom) @@ -3715,6 +3722,25 @@ static void add_option(THD* thd, HA_CREATE_INFO *create_info, #endif // NEW_WAY } // end of add_option +// Used to check whether a MYSQL table is created on itself +static bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host, + const char *db, char *tab, const char *src, int port) +{ + if (src) + return false; + else if (host && stricmp(host, "localhost") && strcmp(host, "127.0.0.1")) + return false; + else if (db && stricmp(db, s->db.str)) + return false; + else if (tab && stricmp(tab, s->table_name.str)) + return false; + else if (port && port != (signed)GetDefaultPort()) + return false; + + strcpy(g->Message, "This MySQL table is defined on itself"); + return true; +} // end of CheckSelf + /** @brief connect_assisted_discovery() is called when creating a table with no columns. @@ -3733,13 +3759,13 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, { char spc= ',', qch= 0; const char *fncn= "?"; - const char *user, *fn, *db, *host, *pwd, *prt, *sep, *tbl, *src; + const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src; const char *col, *ocl, *rnk, *pic, *fcl; char *tab, *dsn; #if defined(WIN32) char *nsp= NULL, *cls= NULL; #endif // WIN32 - int port= 0, hdr= 0, mxr= 0, rc= 0; + int port= 0, hdr= 0, mxr= 0, rc= 0, cop= 0; uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL); bool bif, ok= false, dbf= false; TABTYPE ttp= TAB_UNDEF; @@ -3760,7 +3786,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, if (!g) return HA_ERR_INTERNAL_ERROR; - user= host= pwd= prt= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL; + user= host= pwd= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL; // Get the useful create options ttp= GetTypeID(topt->type); @@ -3778,23 +3804,23 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, col= topt->colist; if (topt->oplist) { - host= GetListOption(g,"host", topt->oplist, "localhost"); - user= GetListOption(g,"user", topt->oplist, "root"); + host= GetListOption(g, "host", topt->oplist, "localhost"); + user= GetListOption(g, "user", topt->oplist, "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); - ocl= GetListOption(g,"occurcol", topt->oplist, NULL); - pic= GetListOption(g,"pivotcol", topt->oplist, NULL); - fcl= GetListOption(g,"fnccol", topt->oplist, NULL); - rnk= GetListOption(g,"rankcol", topt->oplist, NULL); - pwd= GetListOption(g,"password", topt->oplist); - prt= GetListOption(g,"port", topt->oplist); - port= (prt) ? atoi(prt) : 0; + db= GetListOption(g, "database", topt->oplist, db); + col= GetListOption(g, "colist", topt->oplist, col); + ocl= GetListOption(g, "occurcol", topt->oplist, NULL); + pic= GetListOption(g, "pivotcol", topt->oplist, NULL); + fcl= GetListOption(g, "fnccol", topt->oplist, NULL); + rnk= GetListOption(g, "rankcol", topt->oplist, NULL); + pwd= GetListOption(g, "password", topt->oplist); #if defined(WIN32) - nsp= GetListOption(g,"namespace", topt->oplist); - cls= GetListOption(g,"class", topt->oplist); + nsp= GetListOption(g, "namespace", topt->oplist); + cls= GetListOption(g, "class", topt->oplist); #endif // WIN32 + port= atoi(GetListOption(g, "port", topt->oplist, "0")); mxr= atoi(GetListOption(g,"maxerr", topt->oplist, "0")); + cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0")); } else { host= "localhost"; user= "root"; @@ -3840,7 +3866,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, } // endif p } else if (ttp != TAB_ODBC || !(fnc & (FNC_TABLE | FNC_COL))) - tab= (char*)create_info->alias; + tab= table_s->table_name.str; // Default value #if defined(NEW_WAY) add_option(thd, create_info, "tabname", tab); @@ -3850,8 +3876,18 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, switch (ttp) { #if defined(ODBC_SUPPORT) case TAB_ODBC: - if (!(dsn= create_info->connect_string.str) - && !(fnc & (FNC_DSN | FNC_DRIVER))) + dsn= create_info->connect_string.str; + + if (fnc & (FNC_DSN | FNC_DRIVER)) + ok= true; + else if (!stricmp(thd->main_security_ctx.host, "localhost") + && cop == 1) { + if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) { + thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn)); + ok= true; + } // endif dsn + + } else if (!dsn) sprintf(g->Message, "Missing %s connection string", topt->type); else ok= true; @@ -3873,31 +3909,46 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, case TAB_MYSQL: ok= true; - if ((dsn= create_info->connect_string.str)) { + if (create_info->connect_string.str) { + int len= create_info->connect_string.length; PMYDEF mydef= new(g) MYSQLDEF(); PDBUSER dup= PlgGetUser(g); PCATLG cat= (dup) ? dup->Catalog : NULL; - dsn= (char*)PlugSubAlloc(g, NULL, strlen(dsn) + 1); - strncpy(dsn, create_info->connect_string.str, - create_info->connect_string.length); - dsn[create_info->connect_string.length]= 0; + dsn= (char*)PlugSubAlloc(g, NULL, len + 1); + strncpy(dsn, create_info->connect_string.str, len); + dsn[len]= 0; mydef->SetName(create_info->alias); mydef->SetCat(cat); - if (!mydef->ParseURL(g, dsn)) { - host= mydef->GetHostname(); - user= mydef->GetUsername(); - pwd= mydef->GetPassword(); - db= mydef->GetDatabase(); - tab= mydef->GetTabname(); - port= mydef->GetPortnumber(); + if (!mydef->ParseURL(g, dsn, false)) { + if (mydef->GetHostname()) + host= mydef->GetHostname(); + + if (mydef->GetUsername()) + user= mydef->GetUsername(); + + if (mydef->GetPassword()) + pwd= mydef->GetPassword(); + + if (mydef->GetDatabase()) + db= mydef->GetDatabase(); + + if (mydef->GetTabname()) + tab= mydef->GetTabname(); + + if (mydef->GetPortnumber()) + port= mydef->GetPortnumber(); + } else ok= false; } else if (!user) user= "root"; + if (CheckSelf(g, table_s, host, db, tab, src, port)) + ok= false; + break; #endif // MYSQL_SUPPORT #if defined(WIN32) @@ -3946,7 +3997,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, else return HA_ERR_INTERNAL_ERROR; // Should never happen - if (src && ttp != TAB_PIVOT) { + if (src && ttp != TAB_PIVOT && ttp != TAB_ODBC) { qrp= SrcColumns(g, host, db, user, pwd, src, port); if (qrp && ttp == TAB_OCCUR) @@ -3964,7 +4015,12 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, switch (fnc) { case FNC_NO: case FNC_COL: - qrp= ODBCColumns(g, dsn, (char *) tab, NULL, fnc == FNC_COL); + if (src) { + qrp= ODBCSrcCols(g, dsn, (char*)src); + src= NULL; // for next tests + } else + qrp= ODBCColumns(g, dsn, (char *) tab, NULL, fnc == FNC_COL); + break; case FNC_TABLE: qrp= ODBCTables(g, dsn, (char *) tab, true); @@ -4130,6 +4186,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, #else // !NEW_WAY if (!rc) rc= init_table_share(thd, table_s, create_info, &sql); +// rc= init_table_share(thd, table_s, create_info, dsn, &sql); #endif // !NEW_WAY return rc; @@ -4250,6 +4307,54 @@ int ha_connect::create(const char *name, TABLE *table_arg, DBUG_RETURN(HA_ERR_INTERNAL_ERROR); } // endif tabname + case TAB_MYSQL: + {const char *src= options->srcdef; + char *host, *db, *tab= (char*)options->tabname; + int port; + + host= GetListOption(g, "host", options->oplist, NULL); + db= GetListOption(g, "database", options->oplist, NULL); + port= atoi(GetListOption(g, "port", options->oplist, "0")); + + if (create_info->connect_string.str) { + char *dsn; + int len= create_info->connect_string.length; + PMYDEF mydef= new(g) MYSQLDEF(); + PDBUSER dup= PlgGetUser(g); + PCATLG cat= (dup) ? dup->Catalog : NULL; + + dsn= (char*)PlugSubAlloc(g, NULL, len + 1); + strncpy(dsn, create_info->connect_string.str, len); + dsn[len]= 0; + mydef->SetName(create_info->alias); + mydef->SetCat(cat); + + if (!mydef->ParseURL(g, dsn, false)) { + if (mydef->GetHostname()) + host= mydef->GetHostname(); + + if (mydef->GetDatabase()) + db= mydef->GetDatabase(); + + if (mydef->GetTabname()) + tab= mydef->GetTabname(); + + if (mydef->GetPortnumber()) + port= mydef->GetPortnumber(); + + } else { + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endif ParseURL + + } // endif connect_string + + if (CheckSelf(g, table_arg->s, host, db, tab, src, port)) { + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endif CheckSelf + + }break; default: /* do nothing */; break; } // endswitch ttp diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h index 8fc88098794..35daf2e5c19 100644 --- a/storage/connect/ha_connect.h +++ b/storage/connect/ha_connect.h @@ -208,7 +208,8 @@ public: return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_HAS_RECORDS | HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS | HA_NO_COPY_ON_ALTER | HA_CAN_VIRTUAL_COLUMNS | - /*HA_NULL_IN_KEY |*/ HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE); + HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE | + /*HA_NULL_IN_KEY |*/ HA_MUST_USE_TABLE_CONDITION_PUSHDOWN); } /** @brief diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp index 45f379350f5..31aa90da076 100644 --- a/storage/connect/libdoc.cpp +++ b/storage/connect/libdoc.cpp @@ -14,6 +14,10 @@ #include "my_global.h" //#endif // !WIN32 +#if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED) +#error "tree support not compiled in" +#endif + #if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED) #error "XPath not supported" #endif @@ -47,8 +51,6 @@ typedef struct _x2block { /* Loaded XML file block */ short Type; /* TYPE_FB_XML */ int Retcode; /* Return code from Load */ xmlDocPtr Docp; /* Document interface pointer */ -// xmlXPathContextPtr Ctxp; -// xmlXPathObjectPtr Xop; } X2BLOCK, *PX2BLOCK; /******************************************************************/ @@ -91,6 +93,8 @@ class LIBXMLDOC : public XMLDOCUMENT { xmlNodeSetPtr Nlist; xmlXPathContextPtr Ctxp; xmlXPathObjectPtr Xop; + xmlXPathObjectPtr NlXop; + xmlErrorPtr Xerr; char *Buf; // Temporary bool Nofreelist; }; // end of class LIBXMLDOC @@ -141,6 +145,7 @@ class XML2NODELIST : public XMLNODELIST { // Methods virtual int GetLength(void); virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np); + virtual bool DropItem(PGLOBAL g, int n); protected: // Constructor @@ -180,6 +185,23 @@ extern int trace; } // "C" #if defined(MEMORY_TRACE) +static int m = 0; +static char s[500]; +/**************************************************************************/ +/* Tracing output function. */ +/**************************************************************************/ +void xtrc(char const *fmt, ...) + { + va_list ap; + va_start (ap, fmt); + +//vfprintf(stderr, fmt, ap); + vsprintf(s, fmt, ap); + if (s[strlen(s)-1] == '\n') + s[strlen(s)-1] = 0; + va_end (ap); + } // end of htrc + static xmlFreeFunc Free; static xmlMallocFunc Malloc; static xmlMallocFunc MallocA; @@ -188,42 +210,53 @@ static xmlStrdupFunc Strdup; void xmlMyFree(void *mem) { - if (trace) - htrc("Freeing at %p\n", mem); + if (trace) { + htrc("%.4d Freeing at %p %s\n", ++m, mem, s); + *s = 0; + } // endif trace Free(mem); } // end of xmlMyFree void *xmlMyMalloc(size_t size) { void *p = Malloc(size); - if (trace) - htrc("Allocating %.5d at %p\n", size, p); + if (trace) { + htrc("%.4d Allocating %.5d at %p %s\n", ++m, size, p, s); + *s = 0; + } // endif trace return p; } // end of xmlMyMalloc void *xmlMyMallocAtomic(size_t size) { void *p = MallocA(size); - if (trace) - htrc("Atom alloc %.5d at %p\n", size, p); + if (trace) { + htrc("%.4d Atom alloc %.5d at %p %s\n", ++m, size, p, s); + *s = 0; + } // endif trace return p; } // end of xmlMyMallocAtomic void *xmlMyRealloc(void *mem, size_t size) { void *p = Realloc(mem, size); - if (trace) - htrc("ReAlloc %.5d to %p from %p\n", size, p, mem); + if (trace) { + htrc("%.4d ReAlloc %.5d to %p from %p %s\n", ++m, size, p, mem, s); + *s = 0; + } // endif trace return p; } // end of xmlMyRealloc char *xmlMyStrdup(const char *str) { char *p = Strdup(str); - if (trace) - htrc("Duplicating to %p from %p %s\n", p, str, str); + if (trace) { + htrc("%.4d Duplicating to %p from %p %s %s\n", ++m, p, str, str, s); + *s = 0; + } // endif trace return p; } // end of xmlMyStrdup +#define htrc xtrc #endif // MEMORY_TRACE /******************************************************************/ @@ -295,6 +328,8 @@ LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) Nlist = NULL; Ctxp = NULL; Xop = NULL; + NlXop = NULL; + Xerr = NULL; Buf = NULL; Nofreelist = false; } // end of LIBXMLDOC constructor @@ -321,9 +356,10 @@ bool LIBXMLDOC::ParseFile(char *fn) Encoding = (char*)Docp->encoding; return false; - } else - return true; + } else if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + return true; } // end of ParseFile /******************************************************************/ @@ -344,8 +380,6 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn) xp->Length = (m == MODE_READ) ? 1 : 0; xp->Retcode = rc; xp->Docp = Docp; -// xp->Ctxp = Ctxp; -// xp->Xop = Xop; // Return xp as a fp return (PFBLOCK)xp; @@ -356,6 +390,9 @@ PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn) /******************************************************************/ bool LIBXMLDOC::NewDoc(PGLOBAL g, char *ver) { + if (trace) + htrc("NewDoc\n"); + return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL); } // end of NewDoc @@ -462,8 +499,7 @@ int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn) if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) { xmlErrorPtr err = xmlGetLastError(); - strcpy(g->Message, (err) ? err->message : "Error saving XML doc" - ); + strcpy(g->Message, (err) ? err->message : "Error saving XML doc"); rc = -1; } // endif Save // rc = xmlDocDump(of, Docp); @@ -497,17 +533,44 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) if (trace) htrc("CloseDoc: xp=%p count=%d\n", xp, (xp) ? xp->Count : 0); - if (xp && xp->Count == 1) { - if (Nlist) +//if (xp && xp->Count == 1) { + if (Nlist) { xmlXPathFreeNodeSet(Nlist); - if (Xop) + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + Nlist = NULL; + } // endif Nlist + + if (Xop) { xmlXPathFreeObject(Xop); - if (Ctxp) + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + Xop = NULL; + } // endif Xop + + if (NlXop) { + xmlXPathFreeObject(NlXop); + + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + NlXop = NULL; + } // endif NlXop + + if (Ctxp) { xmlXPathFreeContext(Ctxp); - } // endif Count + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + Ctxp = NULL; + } // endif Ctxp + +// } // endif Count CloseXML2File(g, xp, false); } // end of Close @@ -560,18 +623,29 @@ xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp) } // endfor nsp - } else { + } // endif Ctxp + + if (Xop) { if (trace) - htrc("Calling xmlXPathFreeNodeSetList Xop=%p\n", Xop); + htrc("Calling xmlXPathFreeNodeSetList Xop=%p NOFREE=%d\n", + Xop, Nofreelist); if (Nofreelist) { // Making Nlist that must not be freed yet - xmlXPathFreeNodeSetList(Xop); // Caused memory leak +// xmlXPathFreeNodeSetList(Xop); // Caused memory leak + assert(!NlXop); + NlXop = Xop; // Freed on closing Nofreelist = false; } else xmlXPathFreeObject(Xop); // Caused node not found - } // endif Ctxp + if ((Xerr = xmlGetLastError())) { + strcpy(g->Message, Xerr->message); + xmlResetError(Xerr); + return NULL; + } // endif Xerr + + } // endif Xop // Set the context to the calling node Ctxp->node = np; @@ -990,6 +1064,8 @@ void XML2NODE::AddText(PGLOBAL g, char *txtp) /******************************************************************/ void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp) { + xmlErrorPtr xerr; + if (trace) htrc("DeleteChild: node=%p\n", dnp); @@ -999,12 +1075,39 @@ void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp) // This is specific to row nodes if (text && text->type == XML_TEXT_NODE) { xmlUnlinkNode(text); + + if ((xerr = xmlGetLastError())) + goto err; + xmlFreeNode(text); + + if ((xerr = xmlGetLastError())) + goto err; + } // endif type xmlUnlinkNode(np); + + if ((xerr = xmlGetLastError())) + goto err; + xmlFreeNode(np); + + if ((xerr = xmlGetLastError())) + goto err; + Delete(dnp); + + if ((xerr = xmlGetLastError())) + goto err; + + return; + +err: + if (trace) + htrc("DeleteChild: errmsg=%s\n", xerr->message); + + xmlResetError(xerr); } // end of DeleteChild /* -------------------- class XML2NODELIST ---------------------- */ @@ -1045,6 +1148,22 @@ PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np) } // end of GetItem +/******************************************************************/ +/* Reset the pointer on the deleted item. */ +/******************************************************************/ +bool XML2NODELIST::DropItem(PGLOBAL g, int n) + { + if (trace) + htrc("DropItem: n=%d\n", n); + + // We should do something here + if (!Listp || Listp->nodeNr <= n) + return true; + + Listp->nodeTab[n] = NULL; // This was causing Valgrind warning + return false; + } // end of DropItem + /* ---------------------- class XML2ATTR ------------------------ */ /******************************************************************/ diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp index ea0425d3fae..e2de93e8fc8 100644 --- a/storage/connect/myconn.cpp +++ b/storage/connect/myconn.cpp @@ -89,7 +89,7 @@ PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db, static unsigned int length[] = {0, 4, 16, 4, 4, 4, 4, 4, 256, 32, 32}; char *fld, *fmt, cmd[128]; int i, n, nf, ncol = sizeof(buftyp) / sizeof(int); - int len, type, prec, rc, k = 0; + int len, type, prec, rc, k = 0; PQRYRES qrp; PCOLRES crp; MYSQLC myc; @@ -166,7 +166,7 @@ PQRYRES MyColumns(PGLOBAL g, const char *host, const char *db, // Get type, type name, and precision fld = myc.GetCharField(1); prec = 0; - len = 256; // Default for text or blob + len = 255; // Default for text or blob if ((nf = sscanf(fld, "%[^(](%d,%d", cmd, &len, &prec)) < 1) { sprintf(g->Message, MSG(BAD_FIELD_TYPE), fld); diff --git a/storage/connect/myconn.h b/storage/connect/myconn.h index f8c8c3dcbae..10ff76c3273 100644 --- a/storage/connect/myconn.h +++ b/storage/connect/myconn.h @@ -54,6 +54,7 @@ uint GetDefaultPort(void); class DllItem MYSQLC { friend class TDBMYSQL; friend class MYSQLCOL; + friend class TDBMYEXC; // Construction public: MYSQLC(void); diff --git a/storage/connect/mysql-test/connect/r/mysql.result b/storage/connect/mysql-test/connect/r/mysql.result index 784162cae33..c0ef487c111 100644 --- a/storage/connect/mysql-test/connect/r/mysql.result +++ b/storage/connect/mysql-test/connect/r/mysql.result @@ -217,3 +217,24 @@ t2 CREATE TABLE `t2` ( SELECT * FROM t2; a DROP TABLE t2, t1; +# +# MDEV-4877 mysqldump dumps all data from a connect table +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10),(20),(30); +CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL CONNECTION='mysql://root@localhost:PORT/test/t1'; +SELECT * FROM t2; +a +10 +20 +30 +# Start of mysqldump ------ +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL +) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='mysql://root@localhost:PORT/test/t1' `TABLE_TYPE`='MYSQL'; +/*!40101 SET character_set_client = @saved_cs_client */; +# End of mysqldump ------ +DROP TABLE t2; +DROP TABLE t1; diff --git a/storage/connect/mysql-test/connect/r/xml.result b/storage/connect/mysql-test/connect/r/xml.result index 19b5c936e6c..ea915234203 100644 --- a/storage/connect/mysql-test/connect/r/xml.result +++ b/storage/connect/mysql-test/connect/r/xml.result @@ -413,7 +413,7 @@ DROP TABLE t1; SET @a=LOAD_FILE('MYSQLD_DATADIR/test/t1.xml'); SELECT CAST(@a AS CHAR CHARACTER SET latin1); CAST(@a AS CHAR CHARACTER SET latin1) <?xml version="1.0" encoding="iso-8859-1"?> -<!-- Created by CONNECT Version 1.01.0007 July 26, 2013 --> +<!-- Created by CONNECT Version 1.01.0008 August 18, 2013 --> <t1> <line> <node>ÀÃÂÃ</node> diff --git a/storage/connect/mysql-test/connect/t/mysql.test b/storage/connect/mysql-test/connect/t/mysql.test index bb4b2e78e56..673df3fcb54 100644 --- a/storage/connect/mysql-test/connect/t/mysql.test +++ b/storage/connect/mysql-test/connect/t/mysql.test @@ -10,7 +10,7 @@ let $PORT= `select @@port`; --disable_query_log
--replace_result $PORT PORT
--error 0,ER_UNKNOWN_ERROR
---eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT'
+--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='tx1' OPTION_LIST='host=localhost,user=root,port=$PORT'
if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
AND ENGINE='CONNECT'
@@ -420,3 +420,17 @@ DROP TABLE t2, t1; # TODO: add test for YEAR
# TODO: add tests for fractional seconds
+--echo #
+--echo # MDEV-4877 mysqldump dumps all data from a connect table
+--echo #
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (10),(20),(30);
+--replace_result $PORT PORT
+--eval CREATE TABLE t2 ENGINE=CONNECT TABLE_TYPE=MYSQL CONNECTION='mysql://root@localhost:$PORT/test/t1'
+SELECT * FROM t2;
+--echo # Start of mysqldump ------
+--replace_result $PORT PORT
+--exec $MYSQL_DUMP --compact test t2
+--echo # End of mysqldump ------
+DROP TABLE t2;
+DROP TABLE t1;
diff --git a/storage/connect/mysql-test/connect/t/mysql_grant.test b/storage/connect/mysql-test/connect/t/mysql_grant.test index e825e70a84b..8eb6a90c917 100644 --- a/storage/connect/mysql-test/connect/t/mysql_grant.test +++ b/storage/connect/mysql-test/connect/t/mysql_grant.test @@ -1,78 +1,78 @@ --- source include/not_embedded.inc - -let $PORT= `select @@port`; - ---disable_query_log ---replace_result $PORT PORT ---error 0,ER_UNKNOWN_ERROR ---eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='t1' OPTION_LIST='host=localhost,user=root,port=$PORT' -if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1' - AND ENGINE='CONNECT' - AND CREATE_OPTIONS LIKE '%`table_type`=MySQL%'`) -{ - Skip Need MySQL support; -} -DROP TABLE t1; ---enable_query_log - ---echo # ---echo # Testing FILE privilege ---echo # -GRANT ALL PRIVILEGES ON *.* TO user@localhost; -REVOKE FILE ON *.* FROM user@localhost; ---connect(user,localhost,user,,) ---connection user -SELECT user(); ---replace_result $PORT PORT ---error ER_ACCESS_DENIED_ERROR ---eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT' ---connection default -SELECT user(); -CREATE TABLE t1remote (a INT NOT NULL); -INSERT INTO t1remote VALUES (10),(20),(30); ---replace_result $PORT PORT ---eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT' -SELECT * FROM t1; ---connection user -SELECT user(); ---error ER_ACCESS_DENIED_ERROR -SELECT * FROM t1; ---error ER_ACCESS_DENIED_ERROR -INSERT INTO t1 VALUES ('xxx'); ---error ER_ACCESS_DENIED_ERROR -DELETE FROM t1 WHERE a='xxx'; ---error ER_ACCESS_DENIED_ERROR -UPDATE t1 SET a='yyy' WHERE a='xxx'; ---error ER_ACCESS_DENIED_ERROR -TRUNCATE TABLE t1; ---error ER_ACCESS_DENIED_ERROR -ALTER TABLE t1 READONLY=1; ---error ER_ACCESS_DENIED_ERROR -CREATE VIEW v1 AS SELECT * FROM t1; - ---echo # Testing a VIEW created with FILE privileges but accessed with no FILE ---connection default -SELECT user(); -CREATE VIEW v1 AS SELECT * FROM t1; ---connection user -SELECT user(); ---error ER_ACCESS_DENIED_ERROR -SELECT * FROM v1; ---error ER_ACCESS_DENIED_ERROR -INSERT INTO v1 VALUES (2); ---error ER_ACCESS_DENIED_ERROR -UPDATE v1 SET a=123; ---error ER_ACCESS_DENIED_ERROR -DELETE FROM v1; - ---disconnect user ---connection default -SELECT user(); -DROP VIEW v1; -DROP TABLE t1, t1remote; -DROP USER user@localhost; ---echo # ---echo # Testing FILE privileges done ---echo # - +-- source include/not_embedded.inc
+
+let $PORT= `select @@port`;
+
+--disable_query_log
+--replace_result $PORT PORT
+--error 0,ER_UNKNOWN_ERROR
+--eval CREATE TABLE t1 (a INT) ENGINE=CONNECT TABLE_TYPE=MYSQL TABNAME='tx1' OPTION_LIST='host=localhost,user=root,port=$PORT'
+if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
+ WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
+ AND ENGINE='CONNECT'
+ AND CREATE_OPTIONS LIKE '%`table_type`=MySQL%'`)
+{
+ Skip Need MySQL support;
+}
+DROP TABLE t1;
+--enable_query_log
+
+--echo #
+--echo # Testing FILE privilege
+--echo #
+GRANT ALL PRIVILEGES ON *.* TO user@localhost;
+REVOKE FILE ON *.* FROM user@localhost;
+--connect(user,localhost,user,,)
+--connection user
+SELECT user();
+--replace_result $PORT PORT
+--error ER_ACCESS_DENIED_ERROR
+--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL OPTION_LIST='host=localhost,user=root1,port=$PORT'
+--connection default
+SELECT user();
+CREATE TABLE t1remote (a INT NOT NULL);
+INSERT INTO t1remote VALUES (10),(20),(30);
+--replace_result $PORT PORT
+--eval CREATE TABLE t1 (a INT NOT NULL) ENGINE=CONNECT TABLE_TYPE=MySQL TABNAME=t1remote OPTION_LIST='host=localhost,user=root,port=$PORT'
+SELECT * FROM t1;
+--connection user
+SELECT user();
+--error ER_ACCESS_DENIED_ERROR
+SELECT * FROM t1;
+--error ER_ACCESS_DENIED_ERROR
+INSERT INTO t1 VALUES ('xxx');
+--error ER_ACCESS_DENIED_ERROR
+DELETE FROM t1 WHERE a='xxx';
+--error ER_ACCESS_DENIED_ERROR
+UPDATE t1 SET a='yyy' WHERE a='xxx';
+--error ER_ACCESS_DENIED_ERROR
+TRUNCATE TABLE t1;
+--error ER_ACCESS_DENIED_ERROR
+ALTER TABLE t1 READONLY=1;
+--error ER_ACCESS_DENIED_ERROR
+CREATE VIEW v1 AS SELECT * FROM t1;
+
+--echo # Testing a VIEW created with FILE privileges but accessed with no FILE
+--connection default
+SELECT user();
+CREATE VIEW v1 AS SELECT * FROM t1;
+--connection user
+SELECT user();
+--error ER_ACCESS_DENIED_ERROR
+SELECT * FROM v1;
+--error ER_ACCESS_DENIED_ERROR
+INSERT INTO v1 VALUES (2);
+--error ER_ACCESS_DENIED_ERROR
+UPDATE v1 SET a=123;
+--error ER_ACCESS_DENIED_ERROR
+DELETE FROM v1;
+
+--disconnect user
+--connection default
+SELECT user();
+DROP VIEW v1;
+DROP TABLE t1, t1remote;
+DROP USER user@localhost;
+--echo #
+--echo # Testing FILE privileges done
+--echo #
+
diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h index 3665d1b283e..ba4074bf276 100644 --- a/storage/connect/odbccat.h +++ b/storage/connect/odbccat.h @@ -1,8 +1,10 @@ /***********************************************************************/ /* ODBC catalog function prototypes. */ /***********************************************************************/ +char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop); PQRYRES ODBCDataSources(PGLOBAL g, bool info); PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table, char *colpat, bool info); +PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src); PQRYRES ODBCTables(PGLOBAL g, char *dsn, char *tabpat, bool info); PQRYRES ODBCDrivers(PGLOBAL g, bool info); diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp index 6129c93d388..0a19d90a422 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 1.6 */ +/* Name: ODBCONN.CPP Version 1.7 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 1998-2013 */ /* */ @@ -13,6 +13,7 @@ #if defined(WIN32) //nclude <io.h> //nclude <fcntl.h> +#include <direct.h> // for getcwd #if defined(__BORLANDC__) #define __MFC_COMPAT__ // To define min/max as macro #endif @@ -76,8 +77,9 @@ static short GetSQLType(int type) case TYPE_SHORT: tp = SQL_SMALLINT; break; case TYPE_INT: tp = SQL_INTEGER; break; case TYPE_DATE: tp = SQL_TIMESTAMP; break; - case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5) + case TYPE_BIGINT: tp = SQL_BIGINT; break; // (-5) case TYPE_FLOAT: tp = SQL_DOUBLE; break; + case TYPE_TINY : tp = SQL_TINYINT; break; } // endswitch type return tp; @@ -97,6 +99,7 @@ static int GetSQLCType(int type) case TYPE_DATE: tp = SQL_C_TIMESTAMP; break; case TYPE_BIGINT: tp = SQL_C_SBIGINT; break; case TYPE_FLOAT: tp = SQL_C_DOUBLE; break; + case TYPE_TINY : tp = SQL_C_TINYINT; break; } // endswitch type return tp; @@ -127,9 +130,11 @@ int TranslateSQLType(int stp, int prec, int& len) type = TYPE_INT; break; case SQL_SMALLINT: // 5 + type = TYPE_SHORT; + break; case SQL_TINYINT: // (-6) case SQL_BIT: // (-7) - type = TYPE_SHORT; + type = TYPE_TINY; break; case SQL_FLOAT: // 6 case SQL_REAL: // 7 @@ -168,10 +173,36 @@ int TranslateSQLType(int stp, int prec, int& len) } // end of TranslateSQLType /***********************************************************************/ -/* ODBConn static members initialization. */ +/* ODBCCheckConnection: Check completeness of connection string. */ /***********************************************************************/ -//HENV ODBConn::m_henv = SQL_NULL_HENV; -//int ODBConn::m_nAlloc = 0; // per-Appl reference to HENV above +char *ODBCCheckConnection(PGLOBAL g, char *dsn, int cop) + { + char *newdsn, dir[_MAX_PATH], buf[_MAX_PATH]; + int rc; + DWORD options = ODBConn::openReadOnly; + ODBConn *ocp = new(g) ODBConn(g, NULL); + + (void) getcwd(dir, sizeof(dir) - 1); + + switch (cop) { + case 1: options |= ODBConn::forceOdbcDialog; break; + case 2: options |= ODBConn::noOdbcDialog; break; + } // endswitch cop + + if (ocp->Open(dsn, options) < 1) + newdsn = NULL; + else + newdsn = ocp->GetConnect(); + + (void) getcwd(buf, sizeof(buf) - 1); + + // Some data sources change the current directory + if (strcmp(dir, buf)) + rc = chdir(dir); + + ocp->Close(); + return newdsn; // Return complete connection string + } // end of ODBCCheckConnection /***********************************************************************/ /* Allocate the structure used to refer to the result set. */ @@ -225,7 +256,6 @@ static void ResetNullValues(CATPARM *cap) /***********************************************************************/ /* ODBCColumns: constructs the result blocks containing all columns */ /* of an ODBC table that will be retrieved by GetData commands. */ -/* Note: The first two columns (Qualifier, Owner) are ignored. */ /***********************************************************************/ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table, char *colpat, bool info) @@ -251,7 +281,7 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table, if (!info) { ocp = new(g) ODBConn(g, NULL); - if (ocp->Open(dsn, 2) < 1) // 2 is openReadOnly + if (ocp->Open(dsn, 10) < 1) // openReadOnly + noODBCdialog return NULL; // We fix a MySQL limit because some data sources return 32767 @@ -314,6 +344,17 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *table, return qrp; } // end of ODBCColumns +/**************************************************************************/ +/* ODBCSrcCols: constructs the result blocks containing the */ +/* description of all the columns of a Srcdef option. */ +/**************************************************************************/ +PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src) + { + ODBConn *ocp = new(g) ODBConn(g, NULL); + + return ocp->GetMetaData(g, dsn, src); + } // end of ODBCSrcCols + #if 0 /**************************************************************************/ /* MyODBCCols: returns column info as required by ha_connect::pre_create. */ @@ -800,6 +841,17 @@ void DBX::BuildErrorMessage(ODBConn* pdb, HSTMT hstmt) } // end of BuildErrorMessage +const char *DBX::GetErrorMessage(int i) + { + if (i < 0 || i >= MAX_NUM_OF_MSG) + return "No ODBC error"; + else if (m_ErrMsg[i]) + return m_ErrMsg[i]; + else + return (m_Msg) ? m_Msg : "Unknown error"; + + } // end of GetErrorMessage + /***********************************************************************/ /* ODBConn construction/destruction. */ /***********************************************************************/ @@ -818,7 +870,7 @@ ODBConn::ODBConn(PGLOBAL g, TDBODBC *tdbp) m_Catver = (tdbp) ? tdbp->Catver : 0; m_Connect = NULL; m_Updatable = true; -//m_Transactions = false; + m_Transact = false; m_IDQuoteChar = '\''; //*m_ErrMsg = '\0'; } // end of ODBConn @@ -1061,7 +1113,7 @@ bool ODBConn::Connect(DWORD Options) if (hWnd == NULL) hWnd = GetDesktopWindow(); #else // !WIN32 - HWND hWnd = NULL; + HWND hWnd = 1; #endif // !WIN32 PGLOBAL& g = m_G; PDBUSER dup = PlgGetUser(g); @@ -1204,8 +1256,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols) void *buffer; bool b; UWORD n; - SWORD ncol, len, tp; - SQLLEN afrw; + SWORD len, tp, ncol = 0; ODBCCOL *colp; RETCODE rc; HSTMT hstmt; @@ -1240,26 +1291,44 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols) if (trace) htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql); - do { - rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS); - } while (rc == SQL_STILL_EXECUTING); + if (m_Tdb->Srcdef) { + // Be sure this is a query returning a result set + do { + rc = SQLPrepare(hstmt, (PUCHAR)sql, SQL_NTS); + } while (rc == SQL_STILL_EXECUTING); - if (!Check(rc)) - ThrowDBX(rc, "SQLExecDirect", hstmt); + if (!Check(rc)) + ThrowDBX(rc, "SQLPrepare", hstmt); - do { - rc = SQLNumResultCols(hstmt, &ncol); - } while (rc == SQL_STILL_EXECUTING); + if (!Check(rc = SQLNumResultCols(hstmt, &ncol))) + ThrowDBX(rc, "SQLNumResultCols", hstmt); + + if (ncol == 0) { + strcpy(g->Message, "This Srcdef does not return a result set"); + return -1; + } // endif ncol + + // Ok, now we can proceed + do { + rc = SQLExecute(hstmt); + } while (rc == SQL_STILL_EXECUTING); - if (ncol == 0) { - // Update or Delete statement - rc = SQLRowCount(hstmt, &afrw); + if (!Check(rc)) + ThrowDBX(rc, "SQLExecute", hstmt); + + } else { + do { + rc = SQLExecDirect(hstmt, (PUCHAR)sql, SQL_NTS); + } while (rc == SQL_STILL_EXECUTING); if (!Check(rc)) - ThrowDBX(rc, "SQLRowCount", hstmt); + ThrowDBX(rc, "SQLExecDirect", hstmt); - return afrw; - } // endif ncol + do { + rc = SQLNumResultCols(hstmt, &ncol); + } while (rc == SQL_STILL_EXECUTING); + + } // endif Srcdef for (n = 0, colp = tocols; colp; colp = (PODBCCOL)colp->GetNext()) if (!colp->IsSpecial()) @@ -1407,10 +1476,33 @@ int ODBConn::PrepareSQL(char *sql) { PGLOBAL& g = m_G; bool b; + UINT txn = 0; SWORD nparm; RETCODE rc; HSTMT hstmt; + if (m_Tdb->GetMode() != MODE_READ) { + // Does the data source support transactions + rc = SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &txn, 0, NULL); + + if (Check(rc) && txn != SQL_TC_NONE) try { + rc = SQLSetConnectAttr(m_hdbc, SQL_ATTR_AUTOCOMMIT, + SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER); + + if (!Check(rc)) + ThrowDBX(SQL_INVALID_HANDLE, "SQLSetConnectAttr"); + + m_Transact = true; + } 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)); + } // end try/catch + + } // endif Mode + try { b = false; @@ -1450,13 +1542,19 @@ 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(m_G->Message, x->GetErrorMessage(0)); + strcpy(g->Message, x->GetErrorMessage(0)); if (b) SQLCancel(hstmt); rc = SQLFreeStmt(hstmt, SQL_DROP); m_hstmt = NULL; + + if (m_Transact) { + rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK); + m_Transact = false; + } // endif m_Transact + return -1; } // end try/catch @@ -1465,27 +1563,59 @@ int ODBConn::PrepareSQL(char *sql) } // end of PrepareSQL /***********************************************************************/ -/* Bind a parameter for inserting. */ +/* Execute a prepared statement. */ /***********************************************************************/ -bool ODBConn::ExecuteSQL(void) +int ODBConn::ExecuteSQL(bool x) { - RETCODE rc; + PGLOBAL& g = m_G; + SWORD ncol = 0; + RETCODE rc; + SQLLEN afrw = -1; try { - rc = SQLExecute(m_hstmt); + do { + rc = SQLExecute(m_hstmt); + } while (rc == SQL_STILL_EXECUTING); if (!Check(rc)) ThrowDBX(rc, "SQLExecute", m_hstmt); + if (!Check(SQLNumResultCols(m_hstmt, &ncol))) + ThrowDBX(rc, "SQLNumResultCols", m_hstmt); + + if (ncol) { + if (x) { + afrw = ncol; + strcpy(g->Message, "Result set column number"); + } else { + // This should never happen while inserting + strcpy(g->Message, "Logical error while inserting"); + } // endif ncol + + } else { + // Insert, Update or Delete statement + if (!Check(SQLRowCount(m_hstmt, &afrw))) + ThrowDBX(rc, "SQLRowCount", m_hstmt); + + if (x) + strcpy(g->Message, "Affected rows"); + + } // endif ncol + } catch(DBX *x) { strcpy(m_G->Message, x->GetErrorMessage(0)); SQLCancel(m_hstmt); rc = SQLFreeStmt(m_hstmt, SQL_DROP); m_hstmt = NULL; - return true; + + if (m_Transact) { + rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_ROLLBACK); + m_Transact = false; + } // endif m_Transact + } // end try/catch - return false; + return (int)afrw; } // end of ExecuteSQL /***********************************************************************/ @@ -1537,6 +1667,132 @@ bool ODBConn::BindParam(ODBCCOL *colp) return false; } // end of BindParam +/**************************************************************************/ +/* GetMetaData: constructs the result blocks containing the */ +/* description of all the columns of an SQL command. */ +/**************************************************************************/ +PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src) + { + static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_INT, + TYPE_SHORT, TYPE_SHORT}; + static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_PREC, + FLD_SCALE, FLD_NULL}; + static unsigned int length[] = {0, 6, 10, 6, 6}; + unsigned char cn[60]; + int qcol = 5; + short nl, type, prec, nul, cns = (short)sizeof(cn); + PQRYRES qrp = NULL; + PCOLRES crp; + USHORT i; + SQLULEN n; + SWORD ncol; + RETCODE rc; + HSTMT hstmt; + + if (Open(dsn, 10) < 1) // openReadOnly + noOdbcDialog + return NULL; + + try { + rc = SQLAllocStmt(m_hdbc, &hstmt); + + if (!Check(rc)) + ThrowDBX(SQL_INVALID_HANDLE, "SQLAllocStmt"); + + OnSetOptions(hstmt); + + do { + rc = SQLPrepare(hstmt, (PUCHAR)src, SQL_NTS); +// rc = SQLExecDirect(hstmt, (PUCHAR)src, SQL_NTS); + } while (rc == SQL_STILL_EXECUTING); + + if (!Check(rc)) + ThrowDBX(rc, "SQLExecDirect", hstmt); + + do { + rc = SQLNumResultCols(hstmt, &ncol); + } while (rc == SQL_STILL_EXECUTING); + + if (!Check(rc)) + ThrowDBX(rc, "SQLNumResultCols", hstmt); + + if (ncol) for (i = 1; i <= ncol; i++) { + do { + rc = SQLDescribeCol(hstmt, i, NULL, 0, &nl, NULL, NULL, NULL, NULL); + } while (rc == SQL_STILL_EXECUTING); + + if (!Check(rc)) + ThrowDBX(rc, "SQLDescribeCol", hstmt); + + length[0] = max(length[0], (UINT)nl); + } // endfor i + + } catch(DBX *x) { + strcpy(g->Message, x->GetErrorMessage(0)); + goto err; + } // end try/catch + + if (!ncol) { + strcpy(g->Message, "Invalid Srcdef"); + goto err; + } // endif ncol + + /************************************************************************/ + /* Allocate the structures used to refer to the result set. */ + /************************************************************************/ + qrp = PlgAllocResult(g, qcol, ncol, IDS_COLUMNS + 3, + buftyp, fldtyp, length, false, true); + + // Some columns must be renamed + for (i = 0, crp = qrp->Colresp; crp; crp = crp->Next) + switch (++i) { + case 3: crp->Name = "Precision"; break; + case 4: crp->Name = "Scale"; break; + case 5: crp->Name = "Nullable"; break; + } // endswitch i + + /************************************************************************/ + /* Now get the results into blocks. */ + /************************************************************************/ + try { + for (i = 0; i < ncol; i++) { + do { + rc = SQLDescribeCol(hstmt, i+1, cn, cns, &nl, &type, &n, &prec, &nul); + } while (rc == SQL_STILL_EXECUTING); + + if (!Check(rc)) + ThrowDBX(rc, "SQLDescribeCol", hstmt); + else + qrp->Nblin++; + + crp = qrp->Colresp; // Column_Name + crp->Kdata->SetValue((char*)cn, i); + crp = crp->Next; // Data_Type + crp->Kdata->SetValue(type, i); + crp = crp->Next; // Precision (length) + crp->Kdata->SetValue((int)n, i); + crp = crp->Next; // Scale + crp->Kdata->SetValue(prec, i); + crp = crp->Next; // Nullable + crp->Kdata->SetValue(nul, i); + } // endfor i + + } catch(DBX *x) { + strcpy(g->Message, x->GetErrorMessage(0)); + qrp = NULL; + } // end try/catch + + /* Cleanup */ + err: + SQLCancel(hstmt); + rc = SQLFreeStmt(hstmt, SQL_DROP); + Close(); + + /************************************************************************/ + /* Return the result pointer for use by GetData routines. */ + /************************************************************************/ + return qrp; + } // end of GetMetaData + /***********************************************************************/ /* Get the list of Data Sources and set it in qrp. */ /***********************************************************************/ @@ -1840,6 +2096,11 @@ void ODBConn::Close() } // endif m_hstmt if (m_hdbc != SQL_NULL_HDBC) { + if (m_Transact) { + rc = SQLEndTran(SQL_HANDLE_DBC, m_hdbc, SQL_COMMIT); + m_Transact = false; + } // endif m_Transact + rc = SQLDisconnect(m_hdbc); if (trace && rc != SQL_SUCCESS) diff --git a/storage/connect/odbconn.h b/storage/connect/odbconn.h index a84cb19b485..448ce2d428f 100644 --- a/storage/connect/odbconn.h +++ b/storage/connect/odbconn.h @@ -91,8 +91,7 @@ class DBX : public BLOCK { // Implementation (use ThrowDBX to create) RETCODE GetRC(void) {return m_RC;} PSZ GetMsg(void) {return m_Msg;} - const char *GetErrorMessage(int i) - {return (i >=0 && i < MAX_NUM_OF_MSG) ? m_ErrMsg[i] : "No ODBC error";} + const char *GetErrorMessage(int i); protected: void BuildErrorMessage(ODBConn* pdb, HSTMT hstmt = SQL_NULL_HSTMT); @@ -107,6 +106,7 @@ class DBX : public BLOCK { /* ODBConn class. */ /***********************************************************************/ class ODBConn : public BLOCK { + friend class TDBODBC; friend class DBX; friend PQRYRES GetColumnInfo(PGLOBAL, char*&, char *, int, PVBLK&); private: @@ -142,11 +142,12 @@ class ODBConn : public BLOCK { int ExecDirectSQL(char *sql, ODBCCOL *tocols); int Fetch(void); int PrepareSQL(char *sql); - bool ExecuteSQL(void); + int ExecuteSQL(bool x); bool BindParam(ODBCCOL *colp); int GetCatInfo(CATPARM *cap); bool GetDataSources(PQRYRES qrp); bool GetDrivers(PQRYRES qrp); + PQRYRES GetMetaData(PGLOBAL g, char *dsn, char *src); public: // Set special options @@ -185,5 +186,6 @@ class ODBConn : public BLOCK { int m_Catver; PSZ m_Connect; bool m_Updatable; + bool m_Transact; char m_IDQuoteChar; }; // end of ODBConn class definition diff --git a/storage/connect/os.h b/storage/connect/os.h index 792d1668a91..e3d452bf7b8 100644 --- a/storage/connect/os.h +++ b/storage/connect/os.h @@ -1,7 +1,7 @@ #ifndef _OS_H_INCLUDED #define _OS_H_INCLUDED -#if defined(__FreeBSD__) || defined(__APPLE__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) typedef off_t off64_t; #define lseek64(fd, offset, whence) lseek((fd), (offset), (whence)) #define open64(path, flags, mode) open((path), (flags), (mode)) diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h index 11fe5694bc6..550153921a5 100644 --- a/storage/connect/plgdbsem.h +++ b/storage/connect/plgdbsem.h @@ -111,6 +111,7 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */ TYPE_AM_PIVOT = 120, /* PIVOT access method type no */ TYPE_AM_SRC = 121, /* PIVOT multiple column type no */ TYPE_AM_FNC = 122, /* PIVOT source column type no */ + TYPE_AM_XML = 127, /* XML access method type no */ TYPE_AM_XTB = 130, /* SYS table access method type */ TYPE_AM_MAC = 137, /* MAC table access method type */ TYPE_AM_WMI = 139, /* WMI table access method type */ @@ -582,5 +583,6 @@ FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode); int global_open(GLOBAL *g, int msgid, const char *filename, int flags); int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode); DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir); +char *MakeEscape(PGLOBAL g, char* str, char q); bool PushWarning(PGLOBAL, PTDBASE); diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp index 5ea8457f25e..270a0381bad 100644 --- a/storage/connect/plgdbutl.cpp +++ b/storage/connect/plgdbutl.cpp @@ -732,6 +732,34 @@ bool EvalLikePattern(LPCSTR sp, LPCSTR tp) } /* end of EvalLikePattern */ /***********************************************************************/ +/* MakeEscape: Escape some characters in a string. */ +/***********************************************************************/ +char *MakeEscape(PGLOBAL g, char* str, char q) + { + char *bufp; + int i, k, n = 0, len = (int)strlen(str); + + for (i = 0; i < len; i++) + if (str[i] == q || str[i] == '\\') + n++; + + if (!n) + return str; + else + bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1); + + for (i = k = 0; i < len; i++) { + if (str[i] == q || str[i] == '\\') + bufp[k++] = '\\'; + + bufp[k++] = str[i]; + } // endfor i + + bufp[k] = 0; + return bufp; + } /* end of MakeEscape */ + +/***********************************************************************/ /* PlugConvertConstant: convert a Plug constant to an Xobject. */ /***********************************************************************/ void PlugConvertConstant(PGLOBAL g, void* & value, short& type) diff --git a/storage/connect/plgxml.h b/storage/connect/plgxml.h index b8352c36c14..74a16fd4823 100644 --- a/storage/connect/plgxml.h +++ b/storage/connect/plgxml.h @@ -147,6 +147,7 @@ class XMLNODELIST : public BLOCK { // Properties virtual int GetLength(void) = 0; virtual PXNODE GetItem(PGLOBAL, int, PXNODE = NULL) = 0; + virtual bool DropItem(PGLOBAL, int) = 0; protected: // Constructor diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index b538383b766..1955597bb76 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -93,7 +93,7 @@ PQRYRES CSVColumns(PGLOBAL g, const char *fn, char sep, char q, PCOLRES crp; if (info) { - imax = 0; + imax = hmax = 0; length[0] = 128; goto skipit; } // endif info diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp index 146e2891ec7..6f68cd5381a 100755 --- a/storage/connect/tabmul.cpp +++ b/storage/connect/tabmul.cpp @@ -884,9 +884,11 @@ void TDBDIR::CloseDB(PGLOBAL g) _findclose(Hsearch); Hsearch = -1; #else // !WIN32 - // Close the DIR handle. - closedir(Dir); - Dir = NULL; + // Close the DIR handle + if (Dir) { + closedir(Dir); + Dir = NULL; + } // endif dir #endif // !WIN32 iFile = 0; } // end of CloseDB diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index 6d91a444d5e..f37cb6d349e 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -86,6 +86,7 @@ MYSQLDEF::MYSQLDEF(void) Isview = FALSE; Bind = FALSE; Delayed = FALSE; + Xsrc = FALSE; } // end of MYSQLDEF constructor /***********************************************************************/ @@ -171,7 +172,7 @@ bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name) /* true error */ /* */ /***********************************************************************/ -bool MYSQLDEF::ParseURL(PGLOBAL g, char *url) +bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b) { if ((!strstr(url, "://") && (!strchr(url, '@')))) { // No :// or @ in connection string. Must be a straight @@ -248,34 +249,41 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url) if ((Database = strchr(Hostname, '/'))) { *Database++ = 0; - if ((Tabname = strchr(Database, '/'))) + if ((Tabname = strchr(Database, '/'))) { *Tabname++ = 0; - // Make sure there's not an extra / - if ((strchr(Tabname, '/'))) { - strcpy(g->Message, "Syntax error in URL"); - return true; - } // endif / + // Make sure there's not an extra / + if ((strchr(Tabname, '/'))) { + strcpy(g->Message, "Syntax error in URL"); + return true; + } // endif / + + } // endif Tabname } // endif database if ((sport = strchr(Hostname, ':'))) *sport++ = 0; - Portnumber = (sport && sport[0]) ? atoi(sport) : GetDefaultPort(); + // For unspecified values, get the values of old style options + // but only if called from MYSQLDEF, else set them to NULL + Portnumber = (sport && sport[0]) ? atoi(sport) + : (b) ? Cat->GetIntCatInfo("Port", GetDefaultPort()) : 0; if (Username[0] == 0) - Username = Cat->GetStringCatInfo(g, "User", "*"); + Username = (b) ? Cat->GetStringCatInfo(g, "User", "*") : NULL; if (Hostname[0] == 0) - Hostname = Cat->GetStringCatInfo(g, "Host", "localhost"); + Hostname = (b) ? Cat->GetStringCatInfo(g, "Host", "localhost") : NULL; if (!Database || !*Database) - Database = Cat->GetStringCatInfo(g, "Database", "*"); + Database = (b) ? Cat->GetStringCatInfo(g, "Database", "*") : NULL; if (!Tabname || !*Tabname) - Tabname = Name; + Tabname = (b) ? Cat->GetStringCatInfo(g, "Tabname", Name) : NULL; + if (!Password) + Password = (b) ? Cat->GetStringCatInfo(g, "Password", NULL) : NULL; } // endif URL #if 0 @@ -347,6 +355,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) if ((Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL))) Isview = TRUE; + Xsrc = Cat->GetBoolCatInfo("Execsrc", FALSE); return FALSE; } // end of DefineAM @@ -355,7 +364,9 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) /***********************************************************************/ PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE m) { - if (Catfunc == FNC_COL) + if (Xsrc) + return new(g) TDBMYEXC(this); + else if (Catfunc == FNC_COL) return new(g) TDBMCL(this); else return new(g) TDBMYSQL(this); @@ -507,6 +518,9 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g) if (To_Filter) strcat(strcat(Query, " WHERE "), To_Filter); + if (trace) + htrc("Query=%s\n", Query); + // Now we know how much to suballocate PlugSubAlloc(g, NULL, strlen(Query) + 1); return FALSE; @@ -1040,18 +1054,20 @@ int TDBMYSQL::DeleteDB(PGLOBAL g, int irc) /***********************************************************************/ void TDBMYSQL::CloseDB(PGLOBAL g) { - if (Mode == MODE_INSERT) { - char cmd[64]; - int w; - PDBUSER dup = PlgGetUser(g); + if (Myc.Connected()) { + if (Mode == MODE_INSERT) { + char cmd[64]; + int w; + PDBUSER dup = PlgGetUser(g); - dup->Step = "Enabling indexes"; - sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname); - Myc.m_Rows = -1; // To execute the query - m_Rc = Myc.ExecSQL(g, cmd, &w); - } // endif m_Rc + dup->Step = "Enabling indexes"; + sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname); + Myc.m_Rows = -1; // To execute the query + m_Rc = Myc.ExecSQL(g, cmd, &w); + } // endif m_Rc - Myc.Close(); + Myc.Close(); + } // endif Myc if (trace) htrc("MySQL CloseDB: closing %s rc=%d\n", Name, m_Rc); @@ -1275,6 +1291,212 @@ void MYSQLCOL::WriteColumn(PGLOBAL g) } // end of WriteColumn +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBMYSQL class. */ +/***********************************************************************/ + +// Is this really useful ??? +PTDB TDBMYEXC::CopyOne(PTABS t) + { + PTDB tp; + PCOL cp1, cp2; + PGLOBAL g = t->G; + + tp = new(g) TDBMYEXC(g, this); + + for (cp1 = Columns; cp1; cp1 = cp1->GetNext()) { + cp2 = new(g) MYXCOL((PMYXCOL)cp1, tp); + + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate MYSQL column description block. */ +/***********************************************************************/ +PCOL TDBMYEXC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + PMYXCOL colp = new(g) MYXCOL(cdp, this, cprec, n); + + if (!colp->Flag) + Cmdcol = colp->GetName(); + + return colp; + } // end of MakeCol + +/***********************************************************************/ +/* MakeCMD: make the SQL statement to send to MYSQL connection. */ +/***********************************************************************/ +char *TDBMYEXC::MakeCMD(PGLOBAL g) + { + char *xcmd = NULL; + + if (To_Filter) { + if (Cmdcol) { + char col[128], cmd[1024]; + int n; + + memset(cmd, 0, sizeof(cmd)); + n = sscanf(To_Filter, "%s = '%1023c", col, cmd); + + if (n == 2 && !stricmp(col, Cmdcol)) { + xcmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1); + + strcpy(xcmd, cmd); + xcmd[strlen(xcmd) - 1] = 0; + } else + strcpy(g->Message, "Invalid command specification filter"); + + } else + strcpy(g->Message, "No command column in select list"); + + } else if (!Srcdef) + strcpy(g->Message, "No Srcdef default command"); + else + xcmd = Srcdef; + + return xcmd; + } // end of MakeCMD + +/***********************************************************************/ +/* EXC GetMaxSize: returns the maximum number of rows in the table. */ +/***********************************************************************/ +int TDBMYEXC::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) { + MaxSize = 1; + } // endif MaxSize + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* MySQL Exec Access Method opening routine. */ +/***********************************************************************/ +bool TDBMYEXC::OpenDB(PGLOBAL g) + { + int rc; + + if (Use == USE_OPEN) { + strcpy(g->Message, "Multiple execution is not allowed"); + return true; + } // endif use + + /*********************************************************************/ + /* Open a MySQL connection for this table. */ + /* Note: this may not be the proper way to do. Perhaps it is better */ + /* to test whether a connection is already open for this server */ + /* and if so to allocate just a new result set. But this only for */ + /* servers allowing concurency in getting results ??? */ + /*********************************************************************/ + if (!Myc.Connected()) + if (Myc.Open(g, Host, Database, User, Pwd, Port)) + return true; + + Use = USE_OPEN; // Do it now in case we are recursively called + + if (Mode != MODE_READ) { + strcpy(g->Message, "No INSERT/DELETE/UPDATE of MYSQL EXEC tables"); + return true; + } // endif Mode + + /*********************************************************************/ + /* Get the command to execute. */ + /*********************************************************************/ + if (!(Query = MakeCMD(g))) { + Myc.Close(); + return true; + } // endif Query + + if ((rc = Myc.ExecSQL(g, Query)) == RC_NF) { + strcpy(g->Message, "Affected rows"); + AftRows = Myc.m_Rows; + } else if (rc == RC_OK) { + sprintf(g->Message, "Columns and %d rows", Myc.m_Rows); + AftRows = Myc.m_Fields; + } else + return true; + + return false; + } // end of OpenDB + +/***********************************************************************/ +/* Data Base read routine for MYSQL access method. */ +/***********************************************************************/ +int TDBMYEXC::ReadDB(PGLOBAL g) + { + return (++N) ? RC_EF : RC_OK; + } // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for Exec MYSQL access methods. */ +/***********************************************************************/ +int TDBMYEXC::WriteDB(PGLOBAL g) + { + strcpy(g->Message, "EXEC MYSQL tables are read only"); + return RC_FX; + } // end of WriteDB + +// ------------------------- MYXCOL functions --------------------------- + +/***********************************************************************/ +/* MYXCOL public constructor. */ +/***********************************************************************/ +MYXCOL::MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : MYSQLCOL(cdp, tdbp, cprec, i, am) + { + // Set additional EXEC MYSQL access method information for column. + Flag = cdp->GetOffset(); + } // end of MYSQLCOL constructor + +/***********************************************************************/ +/* MYSQLCOL public constructor. */ +/***********************************************************************/ +MYXCOL::MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am) + : MYSQLCOL(fld, tdbp, i, am) + { + if (trace) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + } // end of MYSQLCOL constructor + +/***********************************************************************/ +/* MYXCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +MYXCOL::MYXCOL(MYXCOL *col1, PTDB tdbp) : MYSQLCOL(col1, tdbp) + { + Flag = col1->Flag; + } // end of MYXCOL copy constructor + +/***********************************************************************/ +/* ReadColumn: */ +/***********************************************************************/ +void MYXCOL::ReadColumn(PGLOBAL g) + { + PTDBMYX tdbp = (PTDBMYX)To_Tdb; + + switch (Flag) { + case 0: Value->SetValue_psz(tdbp->Query); break; + case 1: Value->SetValue(tdbp->AftRows); break; + case 2: Value->SetValue_psz(g->Message); break; + default: Value->SetValue_psz("Invalid Flag"); break; + } // endswitch Flag + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: should never be called. */ +/***********************************************************************/ +void MYXCOL::WriteColumn(PGLOBAL g) + { + assert(false); + } // end of WriteColumn + /* ---------------------------TDBMCL class --------------------------- */ /***********************************************************************/ diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h index 2573259ec3c..bcac10dcaa7 100644 --- a/storage/connect/tabmysql.h +++ b/storage/connect/tabmysql.h @@ -3,8 +3,10 @@ typedef class MYSQLDEF *PMYDEF; typedef class TDBMYSQL *PTDBMY; -typedef class MYSQLC *PMYC; typedef class MYSQLCOL *PMYCOL; +typedef class TDBMYEXC *PTDBMYX; +typedef class MYXCOL *PMYXCOL; +typedef class MYSQLC *PMYC; /* ------------------------- MYSQL classes --------------------------- */ @@ -38,7 +40,7 @@ class MYSQLDEF : public TABDEF {/* Logical table description */ // Methods virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); virtual PTDB GetTable(PGLOBAL g, MODE m); - bool ParseURL(PGLOBAL g, char *url); + bool ParseURL(PGLOBAL g, char *url, bool b = true); bool GetServerInfo(PGLOBAL g, const char *server_name); protected: @@ -54,6 +56,7 @@ class MYSQLDEF : public TABDEF {/* Logical table description */ bool Isview; /* TRUE if this table is a MySQL view */ bool Bind; /* Use prepared statement on insert */ bool Delayed; /* Delayed insert */ + bool Xsrc; /* Execution type */ }; // end of MYSQLDEF /***********************************************************************/ @@ -159,6 +162,87 @@ class MYSQLCOL : public COLBLK { }; // end of class MYSQLCOL /***********************************************************************/ +/* This is the class declaration for the exec command MYSQL table. */ +/***********************************************************************/ +class TDBMYEXC : public TDBMYSQL { + friend class MYXCOL; + public: + // Constructor + TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp) {Cmdcol = NULL;} + TDBMYEXC(PGLOBAL g, PTDBMYX tdbp) : TDBMYSQL(g, tdbp) + {Cmdcol = tdbp->Cmdcol;} + + // Implementation +//virtual AMT GetAmType(void) {return TYPE_AM_MYSQL;} + virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYEXC(g, this);} + + // Methods + virtual PTDB CopyOne(PTABS t); +//virtual int GetAffectedRows(void) {return AftRows;} +//virtual int GetRecpos(void) {return N;} +//virtual int GetProgMax(PGLOBAL g); +//virtual void ResetDB(void) {N = 0;} +//virtual int RowNumber(PGLOBAL g, bool b = FALSE); + virtual bool IsView(void) {return Isview;} +//virtual PSZ GetServer(void) {return Server;} +// void SetDatabase(LPCSTR db) {Database = (char*)db;} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); +//virtual int DeleteDB(PGLOBAL g, int irc); +//virtual void CloseDB(PGLOBAL g); + + // Specific routines +// bool SetColumnRanks(PGLOBAL g); +// PCOL MakeFieldColumn(PGLOBAL g, char *name); +// PSZ FindFieldColumn(char *name); + + protected: + // Internal functions + char *MakeCMD(PGLOBAL g); +//bool MakeSelect(PGLOBAL g); +//bool MakeInsert(PGLOBAL g); +//int BindColumns(PGLOBAL g); + + // Members + char *Cmdcol; // The name of the Xsrc command column + }; // end of class TDBMYEXC + +/***********************************************************************/ +/* Class MYXCOL: MySQL exec command table column. */ +/***********************************************************************/ +class MYXCOL : public MYSQLCOL { + friend class TDBMYEXC; + public: + // Constructors + MYXCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "MYSQL"); + MYXCOL(MYSQL_FIELD *fld, PTDB tdbp, int i, PSZ am = "MYSQL"); + MYXCOL(MYXCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation +//virtual int GetAmType(void) {return TYPE_AM_MYSQL;} +// void InitBind(PGLOBAL g); + + // Methods +//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); +// bool FindRank(PGLOBAL g); + + protected: + // Default constructor not to be used + MYXCOL(void) {} + + // Members + char *Buffer; // To get returned message + int Flag; // Column content desc + }; // end of class MYXCOL + +/***********************************************************************/ /* This is the class declaration for the MYSQL column catalog table. */ /***********************************************************************/ class TDBMCL : public TDBCAT { diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp index ae6817344b8..d9d794ef4e4 100644 --- a/storage/connect/tabodbc.cpp +++ b/storage/connect/tabodbc.cpp @@ -1,7 +1,7 @@ /************* Tabodbc C++ Program Source Code File (.CPP) *************/ /* PROGRAM NAME: TABODBC */ /* ------------- */ -/* Version 2.5 */ +/* Version 2.6 */ /* */ /* COPYRIGHT: */ /* ---------- */ @@ -90,8 +90,9 @@ extern int num_read, num_there, num_eq[2]; // Statistics /***********************************************************************/ ODBCDEF::ODBCDEF(void) { - Connect = Tabname = Tabowner = Tabqual = Qchar = NULL; - Catver = Options = 0; + Connect = Tabname = Tabowner = Tabqual = Srcdef = Qchar = NULL; + Catver = Options = 0; + Xsrc = false; } // end of ODBCDEF constructor /***********************************************************************/ @@ -99,17 +100,17 @@ ODBCDEF::ODBCDEF(void) /***********************************************************************/ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { - int dop = ODBConn::noOdbcDialog; // Default for options - Desc = Connect = Cat->GetStringCatInfo(g, "Connect", ""); Tabname = Cat->GetStringCatInfo(g, "Name", (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); Tabname = Cat->GetStringCatInfo(g, "Tabname", Tabname); Tabowner = Cat->GetStringCatInfo(g, "Owner", ""); Tabqual = Cat->GetStringCatInfo(g, "Qualifier", ""); + Srcdef = Cat->GetStringCatInfo(g, "Srcdef", NULL); Qchar = Cat->GetStringCatInfo(g, "Qchar", ""); Catver = Cat->GetIntCatInfo("Catver", 2); - Options = Cat->GetIntCatInfo("Options", dop); + Xsrc = Cat->GetBoolCatInfo("Execsrc", FALSE); + Options = ODBConn::noOdbcDialog; Pseudo = 2; // FILID is Ok but not ROWID return false; } // end of DefineAM @@ -125,7 +126,9 @@ PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m) /* Allocate a TDB of the proper type. */ /* Column blocks will be allocated only when needed. */ /*********************************************************************/ - switch (Catfunc) { + if (Xsrc) + tdbp = new(g) TDBXDBC(this); + else switch (Catfunc) { case FNC_COL: tdbp = new(g) TDBOCL(this); break; @@ -161,19 +164,21 @@ TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp) Cnp = NULL; if (tdp) { - Connect = tdp->GetConnect(); - TableName = tdp->GetTabname(); - Owner = tdp->GetTabowner(); - Qualifier = tdp->GetTabqual(); + Connect = tdp->Connect; + TableName = tdp->Tabname; + Owner = tdp->Tabowner; + Qualifier = tdp->Tabqual; + Srcdef = tdp->Srcdef; Quote = tdp->GetQchar(); - Options = tdp->GetOptions(); + Options = tdp->Options; Rows = tdp->GetElemt(); - Catver = tdp->GetCatver(); + Catver = tdp->Catver; } else { Connect = NULL; TableName = NULL; Owner = NULL; Qualifier = NULL; + Srcdef = NULL; Quote = NULL; Options = 0; Rows = 0; @@ -201,6 +206,7 @@ TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp) TableName = tdbp->TableName; Owner = tdbp->Owner; Qualifier = tdbp->Qualifier; + Srcdef = tdbp->Srcdef; Quote = tdbp->Quote; Query = tdbp->Query; Count = tdbp->Count; @@ -299,7 +305,6 @@ void TDBODBC::SetFile(PGLOBAL g, PSZ fn) DBQ = fn; } // end of SetFile - /******************************************************************/ /* Convert an UTF-8 string to latin characters. */ /******************************************************************/ @@ -314,7 +319,6 @@ int TDBODBC::Decode(char *txt, char *buf, size_t n) return 0; } // end of Decode - /***********************************************************************/ /* MakeSQL: make the SQL statement use with ODBC connection. */ /* Note: when implementing EOM filtering, column only used in local */ @@ -329,6 +333,9 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt) PTABLE tablep = To_Table; PCOL colp; + if (Srcdef) + return Srcdef; + if (!cnt) { // Normal SQL statement to retrieve results for (colp = Columns; colp; colp = colp->GetNext()) @@ -431,6 +438,83 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt) } // end of MakeSQL /***********************************************************************/ +/* MakeInsert: make the Insert statement used with ODBC connection. */ +/***********************************************************************/ +bool TDBODBC::MakeInsert(PGLOBAL g) + { + char *colist, *valist; +// char *tk = "`"; + int len = 0; + bool b = FALSE; + PCOL colp; + + if (Query) + return false; // already done + + for (colp = Columns; colp; colp = colp->GetNext()) + if (colp->IsSpecial()) { + strcpy(g->Message, MSG(NO_ODBC_SPECOL)); + return true; + } else { + len += (strlen(colp->GetName()) + 4); + ((PODBCCOL)colp)->Rank = ++Nparm; + } // endif colp + + colist = (char*)PlugSubAlloc(g, NULL, len); + *colist = '\0'; + valist = (char*)PlugSubAlloc(g, NULL, 2 * Nparm); + *valist = '\0'; + + for (colp = Columns; colp; colp = colp->GetNext()) { + if (b) { + strcat(colist, ", "); + strcat(valist, ","); + } else + b = true; + + if (Quote) + strcat(strcat(strcat(colist, Quote), colp->GetName()), Quote); + else + strcat(colist, colp->GetName()); + + strcat(valist, "?"); // Parameter marker + } // endfor colp + + // Below 32 is enough to contain the fixed part of the query + len = (strlen(TableName) + strlen(colist) + strlen(valist) + 32); + Query = (char*)PlugSubAlloc(g, NULL, len); + strcpy(Query, "INSERT INTO "); + + if (Quote) + strcat(strcat(strcat(Query, Quote), TableName), Quote); + else + strcat(Query, TableName); + + strcat(strcat(strcat(Query, " ("), colist), ") VALUES ("); + strcat(strcat(Query, valist), ")"); + + return false; + } // end of MakeInsert + +/***********************************************************************/ +/* ODBC Bind Parameter function. */ +/***********************************************************************/ +bool TDBODBC::BindParameters(PGLOBAL g) + { + PODBCCOL colp; + + for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) { + colp->AllocateBuffers(g, 0); + + if (Ocp->BindParam(colp)) + return true; + + } // endfor colp + + return false; + } // end of BindParameters + +/***********************************************************************/ /* ResetSize: call by TDBMUL when calculating size estimate. */ /***********************************************************************/ void TDBODBC::ResetSize(void) @@ -448,6 +532,16 @@ void TDBODBC::ResetSize(void) int TDBODBC::GetMaxSize(PGLOBAL g) { if (MaxSize < 0) { + // Make MariaDB happy + MaxSize = 100; +#if 0 + // This is unuseful and takes time + if (Srcdef) { + // Return a reasonable guess + MaxSize = 100; + return MaxSize; + } // endif Srcdef + if (!Ocp) Ocp = new(g) ODBConn(g, this); @@ -466,19 +560,18 @@ int TDBODBC::GetMaxSize(PGLOBAL g) if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0) return -3; - +#endif // 0 } // endif MaxSize return MaxSize; } // end of GetMaxSize /***********************************************************************/ -/* Return 0 in mode DELETE or UPDATE to tell that it is done. */ +/* Return max size value. */ /***********************************************************************/ int TDBODBC::GetProgMax(PGLOBAL g) { - return (Mode == MODE_DELETE || Mode == MODE_UPDATE) ? 0 - : GetMaxSize(g); + return GetMaxSize(g); } // end of GetProgMax /***********************************************************************/ @@ -546,14 +639,24 @@ bool TDBODBC::OpenDB(PGLOBAL g) if (!colp->IsSpecial()) colp->AllocateBuffers(g, Rows); - } else - rc = true; + } else { + Ocp->Close(); + return true; + } // endif Query if (!rc) rc = ((Rows = Ocp->ExecDirectSQL(Query, (PODBCCOL)Columns)) < 0); + } else if (Mode == MODE_INSERT) { + if (!(rc = MakeInsert(g))) + if (Nparm != Ocp->PrepareSQL(Query)) { + strcpy(g->Message, MSG(PARM_CNT_MISS)); + rc = true; + } else + rc = BindParameters(g); + } else { - strcpy(g->Message, "ODBC tables are read only in this version"); + strcpy(g->Message, "No DELETE/UPDATE of ODBC tablesd"); return true; } // endelse @@ -592,30 +695,6 @@ int TDBODBC::ReadDB(PGLOBAL g) // Direct access of ODBC tables is not implemented yet strcpy(g->Message, MSG(NO_ODBC_DIRECT)); longjmp(g->jumper[g->jump_level], GetAmType()); - -#if 0 - /*******************************************************************/ - /* Reading is by an index table. */ - /*******************************************************************/ - int recpos = To_Kindex->Fetch(g); - - switch (recpos) { - case -1: // End of file reached - return RC_EF; - case -2: // No match for join - return RC_NF; - case -3: // Same record as current one - num_there++; - return RC_OK; - default: - /***************************************************************/ - /* Set the cursor position according to record to read. */ - /***************************************************************/ -//--------------------------------- TODO -------------------------------- - break; - } // endswitch recpos -#endif // 0 - } // endif To_Kindex /*********************************************************************/ @@ -641,8 +720,15 @@ int TDBODBC::ReadDB(PGLOBAL g) /***********************************************************************/ int TDBODBC::WriteDB(PGLOBAL g) { - strcpy(g->Message, "ODBC tables are read only"); - return RC_FX; + int n = Ocp->ExecuteSQL(false); + + if (n < 0) { + AftRows = n; + return RC_FX; + } else + AftRows += n; + + return RC_OK; } // end of WriteDB /***********************************************************************/ @@ -664,7 +750,8 @@ void TDBODBC::CloseDB(PGLOBAL g) // To_Kindex = NULL; // } // endif - Ocp->Close(); + if (Ocp) + Ocp->Close(); if (trace) htrc("ODBC CloseDB: closing %s\n", Name); @@ -892,7 +979,7 @@ void ODBCCOL::WriteColumn(PGLOBAL g) /* Do convert the column value if necessary. */ /*********************************************************************/ if (Value != To_Val) - Value->SetValue_pval(To_Val, false); // Convert the inserted value + Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value if (Buf_Type == TYPE_DATE) { struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm); @@ -903,8 +990,245 @@ void ODBCCOL::WriteColumn(PGLOBAL g) Sqlbuf->day = dbtime->tm_mday; Sqlbuf->month = dbtime->tm_mon + 1; Sqlbuf->year = dbtime->tm_year + 1900; + Sqlbuf->fraction = 0; } // endif Buf_Type + if (Nullable) + *StrLen = (Value->IsNull()) ? SQL_NULL_DATA : + (IsTypeNum(Buf_Type)) ? 0 : SQL_NTS; + + } // end of WriteColumn + +/* -------------------------- Class TDBXDBC -------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBODBC class. */ +/***********************************************************************/ +PTDB TDBXDBC::CopyOne(PTABS t) + { + PTDB tp; + PXSRCCOL cp1, cp2; + PGLOBAL g = t->G; // Is this really useful ??? + + tp = new(g) TDBXDBC(this); + + for (cp1 = (PXSRCCOL)Columns; cp1; cp1 = (PXSRCCOL)cp1->GetNext()) { + cp2 = new(g) XSRCCOL(cp1, tp); // Make a copy + NewPointer(t, cp1, cp2); + } // endfor cp1 + + return tp; + } // end of CopyOne + +/***********************************************************************/ +/* Allocate XSRC column description block. */ +/***********************************************************************/ +PCOL TDBXDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) + { + PXSRCCOL colp = new(g) XSRCCOL(cdp, this, cprec, n); + + if (!colp->Flag) + Cmdcol = colp->GetName(); + + return colp; + } // end of MakeCol + +/***********************************************************************/ +/* MakeCMD: make the SQL statement to send to ODBC connection. */ +/***********************************************************************/ +char *TDBXDBC::MakeCMD(PGLOBAL g) + { + char *xcmd = NULL; + + if (To_Filter) { + if (Cmdcol) { + char col[128], cmd[1024]; + int n; + + memset(cmd, 0, sizeof(cmd)); + n = sscanf(To_Filter, "%s = '%1023c", col, cmd); + + if (n == 2 && !stricmp(col, Cmdcol)) { + xcmd = (char*)PlugSubAlloc(g, NULL, strlen(cmd) + 1); + + strcpy(xcmd, cmd); + xcmd[strlen(xcmd) - 1] = 0; + } else + strcpy(g->Message, "Invalid command specification filter"); + + } else + strcpy(g->Message, "No command column in select list"); + + } else if (!Srcdef) + strcpy(g->Message, "No Srcdef default command"); + else + xcmd = Srcdef; + + return xcmd; + } // end of MakeCMD + +#if 0 +/***********************************************************************/ +/* ODBC Bind Parameter function. */ +/***********************************************************************/ +bool TDBXDBC::BindParameters(PGLOBAL g) + { + PODBCCOL colp; + + for (colp = (PODBCCOL)Columns; colp; colp = (PODBCCOL)colp->Next) { + colp->AllocateBuffers(g, 0); + + if (Ocp->BindParam(colp)) + return true; + + } // endfor colp + + return false; + } // end of BindParameters +#endif // 0 + +/***********************************************************************/ +/* XDBC GetMaxSize: returns table size (always one row). */ +/***********************************************************************/ +int TDBXDBC::GetMaxSize(PGLOBAL g) + { + if (MaxSize < 0) + MaxSize = 1; + + return MaxSize; + } // end of GetMaxSize + +/***********************************************************************/ +/* ODBC Access Method opening routine. */ +/* New method now that this routine is called recursively (last table */ +/* first in reverse order): index blocks are immediately linked to */ +/* join block of next table if it exists or else are discarted. */ +/***********************************************************************/ +bool TDBXDBC::OpenDB(PGLOBAL g) + { + bool rc = false; + + if (g->Trace) + htrc("ODBC OpenDB: tdbp=%p tdb=R%d use=%dmode=%d\n", + this, Tdb_No, Use, Mode); + + if (Use == USE_OPEN) { + strcpy(g->Message, "Multiple execution is not allowed"); + return true; + } // endif use + + /*********************************************************************/ + /* Open an ODBC connection for this table. */ + /* Note: this may not be the proper way to do. Perhaps it is better */ + /* to test whether a connection is already open for this datasource */ + /* and if so to allocate just a new result set. But this only for */ + /* drivers allowing concurency in getting results ??? */ + /*********************************************************************/ + if (!Ocp) + Ocp = new(g) ODBConn(g, this); + else if (Ocp->IsOpen()) + Ocp->Close(); + + if (Ocp->Open(Connect, Options) < 1) + return true; + + Use = USE_OPEN; // Do it now in case we are recursively called + + if (Mode != MODE_READ) { + strcpy(g->Message, "No INSERT/DELETE/UPDATE of XDBC tables"); + return true; + } // endif Mode + + /*********************************************************************/ + /* Get the command to execute. */ + /*********************************************************************/ + if (!(Query = MakeCMD(g))) { + Ocp->Close(); + return true; + } // endif Query + + Rows = 1; + + if (Ocp->PrepareSQL(Query)) { + strcpy(g->Message, "Parameters not supported"); + AftRows = -1; + } else + AftRows = 0; + + return false; + } // end of OpenDB + +/***********************************************************************/ +/* ReadDB: Data Base read routine for xdbc access method. */ +/***********************************************************************/ +int TDBXDBC::ReadDB(PGLOBAL g) + { + if (trace) + htrc("XDBC ReadDB: query=%s\n", SVP(Query)); + + if (Rows--) { + if (!AftRows) + AftRows = Ocp->ExecuteSQL(true); + + } else + return RC_EF; + + Fpos++; // Used for progress info + return RC_OK; + } // end of ReadDB + +/***********************************************************************/ +/* Data Base delete line routine for ODBC access method. */ +/***********************************************************************/ +int TDBXDBC::WriteDB(PGLOBAL g) + { + strcpy(g->Message, "Execsrc tables are read only"); + return RC_FX; + } // end of DeleteDB + +/* --------------------------- XSRCCOL ------------------------------- */ + +/***********************************************************************/ +/* XSRCCOL public constructor. */ +/***********************************************************************/ +XSRCCOL::XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : ODBCCOL(cdp, tdbp, cprec, i, am) + { + // Set additional ODBC access method information for column. + Flag = cdp->GetOffset(); + } // end of XSRCCOL constructor + +/***********************************************************************/ +/* XSRCCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +XSRCCOL::XSRCCOL(XSRCCOL *col1, PTDB tdbp) : ODBCCOL(col1, tdbp) + { + Flag = col1->Flag; + } // end of XSRCCOL copy constructor + +/***********************************************************************/ +/* ReadColumn: set column value according to Flag. */ +/***********************************************************************/ +void XSRCCOL::ReadColumn(PGLOBAL g) + { + PTDBXDBC tdbp = (PTDBXDBC)To_Tdb; + + switch (Flag) { + case 0: Value->SetValue_psz(tdbp->Query); break; + case 1: Value->SetValue(tdbp->AftRows); break; + case 2: Value->SetValue_psz(g->Message); break; + default: Value->SetValue_psz("Invalid Flag"); break; + } // endswitch Flag + + } // end of ReadColumn + +/***********************************************************************/ +/* WriteColumn: Should never be called. */ +/***********************************************************************/ +void XSRCCOL::WriteColumn(PGLOBAL g) + { + // Should never be called } // end of WriteColumn /* ---------------------------TDBSRC class --------------------------- */ diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h index a470655bd11..b3577bce5be 100644 --- a/storage/connect/tabodbc.h +++ b/storage/connect/tabodbc.h @@ -1,5 +1,5 @@ /*************** Tabodbc H Declares Source Code File (.H) **************/ -/* Name: TABODBC.H Version 1.5 */ +/* Name: TABODBC.H Version 1.6 */ /* */ /* (C) Copyright to the author Olivier BERTRAND 2000-2013 */ /* */ @@ -11,6 +11,8 @@ typedef class ODBCDEF *PODEF; typedef class TDBODBC *PTDBODBC; typedef class ODBCCOL *PODBCCOL; +typedef class TDBXDBC *PTDBXDBC; +typedef class XSRCCOL *PXSRCCOL; typedef class TDBOIF *PTDBOIF; typedef class OIFCOL *POIFCOL; typedef class TDBSRC *PTDBSRC; @@ -19,6 +21,8 @@ typedef class TDBSRC *PTDBSRC; /* ODBC table. */ /***********************************************************************/ class DllExport ODBCDEF : public TABDEF { /* Logical table description */ + friend class TDBODBC; + friend class TDBXDBC; public: // Constructor ODBCDEF(void); @@ -29,6 +33,7 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */ PSZ GetTabname(void) {return Tabname;} PSZ GetTabowner(void) {return Tabowner;} PSZ GetTabqual(void) {return Tabqual;} + PSZ GetSrcdef(void) {return Srcdef;} PSZ GetQchar(void) {return (Qchar && *Qchar) ? Qchar : NULL;} int GetCatver(void) {return Catver;} int GetOptions(void) {return Options;} @@ -43,9 +48,11 @@ class DllExport ODBCDEF : public TABDEF { /* Logical table description */ PSZ Tabname; /* External table name */ PSZ Tabowner; /* External table owner */ PSZ Tabqual; /* External table qualifier */ + PSZ Srcdef; /* The source table SQL definition */ PSZ Qchar; /* Identifier quoting character */ int Catver; /* ODBC version for catalog functions */ int Options; /* Open connection options */ + bool Xsrc; /* Execution type */ }; // end of ODBCDEF #if !defined(NODBC) @@ -92,10 +99,10 @@ class TDBODBC : public TDBASE { int Decode(char *utf, char *buf, size_t n); char *MakeSQL(PGLOBAL g, bool cnt); //bool MakeUpdate(PGLOBAL g, PSELECT selist); -//bool MakeInsert(PGLOBAL g); + bool MakeInsert(PGLOBAL g); //bool MakeDelete(PGLOBAL g); //bool MakeFilter(PGLOBAL g, bool c); -//bool BindParameters(PGLOBAL g); + bool BindParameters(PGLOBAL g); // Members ODBConn *Ocp; // Points to an ODBC connection class @@ -104,6 +111,7 @@ class TDBODBC : public TDBASE { char *TableName; // Points to ODBC table name char *Owner; // Points to ODBC table Owner char *Qualifier; // Points to ODBC table Qualifier + char *Srcdef; // The source table SQL definition char *Query; // Points to SQL statement char *Count; // Points to count(*) SQL statement //char *Where; // Points to local where clause @@ -122,7 +130,7 @@ class TDBODBC : public TDBASE { }; // end of class TDBODBC /***********************************************************************/ -/* Class ODBCCOL: DOS access method column descriptor. */ +/* Class ODBCCOL: ODBC access method column descriptor. */ /* This A.M. is used for ODBC tables. */ /***********************************************************************/ class ODBCCOL : public COLBLK { @@ -153,17 +161,86 @@ class ODBCCOL : public COLBLK { ODBCCOL(void); // Members - TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's - void *Bufp; // To extended buffer + TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's + void *Bufp; // To extended buffer PVBLK Blkp; // To Value Block //char F_Date[12]; // Internal Date format PVAL To_Val; // To value used for Insert SQLLEN *StrLen; // As returned by ODBC - SQLLEN Slen; // Used with Fetch - int Rank; // Rank (position) number in the query + SQLLEN Slen; // Used with Fetch + int Rank; // Rank (position) number in the query }; // end of class ODBCCOL /***********************************************************************/ +/* This is the ODBC Access Method class declaration that send */ +/* commands to be executed by other DB ODBC drivers. */ +/***********************************************************************/ +class TDBXDBC : public TDBODBC { + friend class XSRCCOL; + friend class ODBConn; + public: + // Constructor + TDBXDBC(PODEF tdp = NULL) : TDBODBC(tdp) {Cmdcol = NULL;} + TDBXDBC(PTDBXDBC tdbp) : TDBODBC(tdbp) {Cmdcol = tdbp->Cmdcol;} + + // Implementation +//virtual AMT GetAmType(void) {return TYPE_AM_ODBC;} + virtual PTDB Duplicate(PGLOBAL g) + {return (PTDB)new(g) TDBXDBC(this);} + + // Methods + virtual PTDB CopyOne(PTABS t); +//virtual int GetRecpos(void); +//virtual PSZ GetFile(PGLOBAL g); +//virtual void SetFile(PGLOBAL g, PSZ fn); +//virtual void ResetSize(void); +//virtual int GetAffectedRows(void) {return AftRows;} +//virtual PSZ GetServer(void) {return "ODBC";} + + // Database routines + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); +//virtual int GetProgMax(PGLOBAL g); + virtual int GetMaxSize(PGLOBAL g); + virtual bool OpenDB(PGLOBAL g); + virtual int ReadDB(PGLOBAL g); + virtual int WriteDB(PGLOBAL g); +//virtual int DeleteDB(PGLOBAL g, int irc); +//virtual void CloseDB(PGLOBAL g); + + protected: + // Internal functions + char *MakeCMD(PGLOBAL g); +//bool BindParameters(PGLOBAL g); + + // Members + char *Cmdcol; // The name of the Xsrc command column + }; // end of class TDBXDBC + +/***********************************************************************/ +/* Used by table in source execute mode. */ +/***********************************************************************/ +class XSRCCOL : public ODBCCOL { + friend class TDBXDBC; + public: + // Constructors + XSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "ODBC"); + XSRCCOL(XSRCCOL *colp, PTDB tdbp); // Constructor used in copy process + + // Implementation +//virtual int GetAmType(void) {return TYPE_AM_ODBC;} + + // Methods + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); +// void Print(PGLOBAL g, FILE *, uint); + + protected: + // Members + char *Buffer; // To get returned message + int Flag; // Column content desc + }; // end of class XSRCCOL + +/***********************************************************************/ /* This is the class declaration for the Data Sources catalog table. */ /***********************************************************************/ class TDBSRC : public TDBCAT { diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp index 110c53c33b2..2405b1853a2 100644 --- a/storage/connect/tabutil.cpp +++ b/storage/connect/tabutil.cpp @@ -592,6 +592,17 @@ bool PRXCOL::Init(PGLOBAL g) } // end of Init /***********************************************************************/ +/* Reset the column descriptor to non evaluated yet. */ +/***********************************************************************/ +void PRXCOL::Reset(void) + { + if (Colp) + Colp->Reset(); + + Status &= ~BUF_READ; + } // end of Reset + +/***********************************************************************/ /* ReadColumn: */ /***********************************************************************/ void PRXCOL::ReadColumn(PGLOBAL g) @@ -600,7 +611,7 @@ void PRXCOL::ReadColumn(PGLOBAL g) htrc("PRX ReadColumn: name=%s\n", Name); if (Colp) { - Colp->ReadColumn(g); + Colp->Eval(g); Value->SetValue_pval(To_Val); // Set null when applicable diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h index 9bfa4b726d1..a6202950390 100644 --- a/storage/connect/tabutil.h +++ b/storage/connect/tabutil.h @@ -70,7 +70,7 @@ class DllExport TDBPRX : public TDBASE { virtual int GetRecpos(void) {return Tdbp->GetRecpos();} virtual void ResetDB(void) {Tdbp->ResetDB();} virtual int RowNumber(PGLOBAL g, bool b = FALSE); - virtual PSZ GetServer(void) {return (Tdbp) ? Tdbp->GetServer() : (char*) "?";} + virtual PSZ GetServer(void) {return (Tdbp) ? Tdbp->GetServer() : (PSZ)"?";} // Database routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); @@ -105,6 +105,7 @@ class DllExport PRXCOL : public COLBLK { virtual int GetAmType(void) {return TYPE_AM_PRX;} // Methods + virtual void Reset(void); virtual bool IsSpecial(void) {return Pseudo;} virtual void ReadColumn(PGLOBAL g); bool Init(PGLOBAL g); diff --git a/storage/connect/tabxcl.cpp b/storage/connect/tabxcl.cpp index eb9e9c0e4dc..33ec8984219 100644 --- a/storage/connect/tabxcl.cpp +++ b/storage/connect/tabxcl.cpp @@ -253,7 +253,7 @@ XCLCOL::XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i) void XCLCOL::ReadColumn(PGLOBAL g) { if (((PTDBXCL)To_Tdb)->New) { - Colp->ReadColumn(g); + Colp->Eval(g); strcpy(Cbuf, To_Val->GetCharValue()); Cp = Cbuf; } // endif New @@ -272,9 +272,11 @@ void XCLCOL::ReadColumn(PGLOBAL g) } else if (Nullable) { Value->Reset(); Value->SetNull(true); - } else + } else { // Skip that row ((PTDBXCL)To_Tdb)->RowFlag = 2; + Colp->Reset(); + } // endif Cp if (Cp && *Cp) // More to come from the same row diff --git a/storage/connect/tabxcl.h b/storage/connect/tabxcl.h index 28fbf85d509..0189775cd8d 100644 --- a/storage/connect/tabxcl.h +++ b/storage/connect/tabxcl.h @@ -90,7 +90,7 @@ class XCLCOL : public PRXCOL { XCLCOL(PGLOBAL g, PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i); // Methods - virtual void Reset(void) {} // Evaluated only by TDBXCL + virtual void Reset(void) {Colp->Reset();} // Evaluated only by TDBXCL virtual void ReadColumn(PGLOBAL g); protected: diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp index 69ad6711638..3feba14ca5e 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -78,7 +78,9 @@ XMLDEF::XMLDEF(void) DefNs = NULL; Attrib = NULL; Hdattr = NULL; + Coltype = 1; Limit = 0; + Header = 0; Xpand = false; Usedom = false; } // end of XMLDEF constructor @@ -338,17 +340,14 @@ PCOL TDBXML::InsertSpecialColumn(PGLOBAL g, PCOL colp) /***********************************************************************/ /* LoadTableFile: Load and parse an XML file. */ /***********************************************************************/ -int TDBXML::LoadTableFile(PGLOBAL g) +int TDBXML::LoadTableFile(PGLOBAL g, char *filename) { - char filename[_MAX_PATH]; int rc = RC_OK, type = (Usedom) ? TYPE_FB_XML : TYPE_FB_XML2; PFBLOCK fp = NULL; PDBUSER dup = (PDBUSER)g->Activityp->Aptr; - /*********************************************************************/ - /* We used the file name relative to recorded datapath. */ - /*********************************************************************/ - PlugSetPath(filename, Xfile, GetPath()); + if (Docp) + return rc; // Already done if (trace) htrc("TDBXML: loading %s\n", filename); @@ -397,6 +396,7 @@ int TDBXML::LoadTableFile(PGLOBAL g) } else rc = (errno == ENOENT) ? RC_NF : RC_INFO; + // Cannot make a Xblock until document is made return rc; } // endif Docp @@ -418,9 +418,8 @@ int TDBXML::LoadTableFile(PGLOBAL g) /***********************************************************************/ bool TDBXML::Initialize(PGLOBAL g) { - char tabpath[64]; - int rc; - PXMLCOL colp; + int rc; + PXMLCOL colp; if (Void) return false; @@ -440,8 +439,13 @@ bool TDBXML::Initialize(PGLOBAL g) #else if (!Root) { #endif + char tabpath[64], filename[_MAX_PATH]; + + // We used the file name relative to recorded datapath + PlugSetPath(filename, Xfile, GetPath()); + // Load or re-use the table file - rc = LoadTableFile(g); + rc = LoadTableFile(g, filename); if (rc == RC_OK) { // Get root node @@ -503,6 +507,9 @@ bool TDBXML::Initialize(PGLOBAL g) goto error; } // endif NewDoc + // Now we can link the Xblock + To_Xb = Docp->LinkXblock(g, Mode, rc, filename); + // Add a CONNECT comment node // sprintf(buf, MSG(CREATED_PLUGDB), version); sprintf(buf, " Created by CONNECT %s ", version); @@ -893,12 +900,21 @@ int TDBXML::DeleteDB(PGLOBAL g, int irc) if ((RowNode = Nlist->GetItem(g, Irow, RowNode)) == NULL) { sprintf(g->Message, MSG(MISSING_ROWNODE), Irow); return RC_FX; - } else + } else { TabNode->DeleteChild(g, RowNode); + if (Nlist->DropItem(g, Irow)) + return RC_FX; + + } // endif RowNode + Changed = true; } else if (irc != RC_EF) { TabNode->DeleteChild(g, RowNode); + + if (Nlist->DropItem(g, Irow)) + return RC_FX; + Changed = true; } // endif's irc diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h index 1bce0824e49..5aa038530c7 100644 --- a/storage/connect/tabxml.h +++ b/storage/connect/tabxml.h @@ -6,8 +6,6 @@ /* */ /* This file contains the XML table classes declares. */ /***********************************************************************/ -#define TYPE_AM_XML (AMT)127 - typedef class XMLDEF *PXMLDEF; typedef class TDBXML *PTDBXML; typedef class XMLCOL *PXMLCOL; @@ -81,7 +79,7 @@ class DllExport TDBXML : public TDBASE { virtual void ResetDB(void) {N = 0;} virtual void ResetSize(void) {MaxSize = -1;} virtual int RowNumber(PGLOBAL g, bool b = false); - int LoadTableFile(PGLOBAL g); + int LoadTableFile(PGLOBAL g, char *filename); bool Initialize(PGLOBAL g); bool SetTabNode(PGLOBAL g); void SetNodeAttr(PGLOBAL g, char *attr, PXNODE node); diff --git a/storage/connect/valblk.cpp b/storage/connect/valblk.cpp index 55f9da7571b..06c7776d2f5 100644 --- a/storage/connect/valblk.cpp +++ b/storage/connect/valblk.cpp @@ -447,13 +447,12 @@ int TYPBLK<TYPE>::Find(PVAL vp) template <class TYPE> int TYPBLK<TYPE>::GetMaxLength(void) { - char buf[12]; - int i, n; + char buf[32]; + int i, n, m; for (i = n = 0; i < Nval; i++) { - sprintf(buf, Fmt, Typp[i]); - - n = max(n, (signed)strlen(buf)); + m = sprintf(buf, Fmt, Typp[i]); + n = max(n, m); } // endfor i return n; |