From 670f482f470eefd6356fc164ca8eb4d53f2f34d5 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Mon, 19 Aug 2013 23:42:08 +0200 Subject: [PATCH 01/20] - Fix a bug causing wrong charset used when inserting an attibute in an XML table with DOMDOC. modified: storage/connect/domdoc.cpp - Add the flag HA_MUST_USE_TABLE_CONDITION_PUSHDOWN. modified: storage/connect/ha_connect.cc - Update version number modified: storage/connect/ha_connect.cc storage/connect/mysql-test/connect/r/xml.result --- storage/connect/domdoc.cpp | 4 ++-- storage/connect/ha_connect.cc | 2 +- storage/connect/ha_connect.h | 3 ++- storage/connect/mysql-test/connect/r/xml.result | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp index 95426be97bc..ebdaeba57b5 100644 --- a/storage/connect/domdoc.cpp +++ b/storage/connect/domdoc.cpp @@ -618,8 +618,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/ha_connect.cc b/storage/connect/ha_connect.cc index 57770567cd6..bca4a6efc73 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 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/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) - + ÀÁÂÃ From 6bef50bbac42978d464c5ca614325e0e0fdad98d Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Tue, 20 Aug 2013 13:52:01 +0200 Subject: [PATCH 02/20] - Suppress a gcc compiler warning modified: storage/connect/tabutil.h --- storage/connect/tabutil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h index 56c4667b258..15078ca3dee 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() : "?";} + virtual PSZ GetServer(void) {return (Tdbp) ? Tdbp->GetServer() : (PSZ)"?";} // Database routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); From dd0872eb9cb1068da7f148865a2eab49955762a5 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 25 Aug 2013 11:12:54 +0200 Subject: [PATCH 03/20] - Handle TINY in ODBC type conversion modified: storage/connect/odbconn.cpp - Enhance TYPBLK::GetMaxLength modified: storage/connect/valblk.cpp --- storage/connect/odbconn.cpp | 8 ++++++-- storage/connect/valblk.cpp | 9 ++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp index 6129c93d388..397b7c2a2f4 100644 --- a/storage/connect/odbconn.cpp +++ b/storage/connect/odbconn.cpp @@ -76,8 +76,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 +98,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 +129,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 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::Find(PVAL vp) template int TYPBLK::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; From b344a49f7e54f93f7b4815441d0b51b5838b84af Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Thu, 29 Aug 2013 12:01:27 +0200 Subject: [PATCH 04/20] - Fix a bug causing a crash when an XCOL table was the source of a PROXY or PROXY based table. modified: storage/connect/colblk.cpp storage/connect/tabutil.cpp storage/connect/tabutil.h storage/connect/tabxcl.cpp storage/connect/tabxcl.h --- storage/connect/colblk.cpp | 2 +- storage/connect/tabutil.cpp | 13 ++++++++++++- storage/connect/tabutil.h | 1 + storage/connect/tabxcl.cpp | 6 ++++-- storage/connect/tabxcl.h | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) 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/tabutil.cpp b/storage/connect/tabutil.cpp index 110c53c33b2..674a8acf45a 100644 --- a/storage/connect/tabutil.cpp +++ b/storage/connect/tabutil.cpp @@ -591,6 +591,17 @@ bool PRXCOL::Init(PGLOBAL g) return FALSE; } // 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: */ /***********************************************************************/ @@ -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 15078ca3dee..a6202950390 100644 --- a/storage/connect/tabutil.h +++ b/storage/connect/tabutil.h @@ -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: From e57fbef12e29bdb13b185c9c8592b432d1f80eab Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Thu, 29 Aug 2013 15:12:03 +0200 Subject: [PATCH 05/20] - Get rid of a compiler warning modified: storage/connect/tabfmt.cpp --- storage/connect/tabfmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From c4405fef23645ad6c4e7d703932a76d1806934e9 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Mon, 16 Sep 2013 14:59:25 +0200 Subject: [PATCH 06/20] - Fix compilation error on some platforms modified: storage/connect/os.h - Uniforms line endings modified: storage/connect/tabutil.cpp --- storage/connect/os.h | 2 +- storage/connect/tabutil.cpp | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/storage/connect/os.h b/storage/connect/os.h index 792d1668a91..e1b973e1a17 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/tabutil.cpp b/storage/connect/tabutil.cpp index 674a8acf45a..2405b1853a2 100644 --- a/storage/connect/tabutil.cpp +++ b/storage/connect/tabutil.cpp @@ -591,17 +591,17 @@ bool PRXCOL::Init(PGLOBAL g) return FALSE; } // 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 - +/***********************************************************************/ +/* Reset the column descriptor to non evaluated yet. */ +/***********************************************************************/ +void PRXCOL::Reset(void) + { + if (Colp) + Colp->Reset(); + + Status &= ~BUF_READ; + } // end of Reset + /***********************************************************************/ /* ReadColumn: */ /***********************************************************************/ From bd37d644d6a93e595d98ec20172cad527c3e23bd Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 16 Sep 2013 17:14:44 +0400 Subject: [PATCH 07/20] Fixing a typo in the previous commit defined(OpenBSD) -> defined(__OpenBSD__) modified: storage/connect/os.h --- storage/connect/os.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/connect/os.h b/storage/connect/os.h index e1b973e1a17..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(__NetBSD__) || defined(OpenBSD) || defined(__DragonFly__) || 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)) From 65b0e5455b547a3d574fa77b34cce23ae3bea0a0 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 22 Sep 2013 13:40:31 +0200 Subject: [PATCH 08/20] - Fix several bugs causing memory leak or invalid access detected by Valgrind. This concerns the XML libxml2 support. modified: storage/connect/domdoc.cpp storage/connect/domdoc.h storage/connect/ha_connect.cc storage/connect/libdoc.cpp storage/connect/plgdbsem.h storage/connect/plgxml.h storage/connect/tabxml.cpp storage/connect/tabxml.h --- storage/connect/domdoc.cpp | 12 +++ storage/connect/domdoc.h | 3 +- storage/connect/ha_connect.cc | 8 +- storage/connect/libdoc.cpp | 169 ++++++++++++++++++++++++++++------ storage/connect/plgdbsem.h | 1 + storage/connect/plgxml.h | 1 + storage/connect/tabxml.cpp | 38 +++++--- storage/connect/tabxml.h | 4 +- 8 files changed, 191 insertions(+), 45 deletions(-) diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp index ebdaeba57b5..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 proprit n'a pas de mthode 'set' + return false; + } // end of DeleteItem + /* ----------------------- class DOMATTR ------------------------ */ /******************************************************************/ 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 bca4a6efc73..a44310fd52b 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -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))) @@ -2658,8 +2659,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)) { diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp index 45f379350f5..01a9f858fb0 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,40 @@ 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); + + } // endif Nlist + + if (Xop) { xmlXPathFreeObject(Xop); - if (Ctxp) + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + } // endif Xop + + if (NlXop) { + xmlXPathFreeObject(NlXop); + + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + } // endif NlXop + + if (Ctxp) { xmlXPathFreeContext(Ctxp); - } // endif Count + if ((Xerr = xmlGetLastError())) + xmlResetError(Xerr); + + } // endif Ctxp + +// } // endif Count CloseXML2File(g, xp, false); } // end of Close @@ -560,18 +619,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 +1060,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 +1071,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 +1144,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/plgdbsem.h b/storage/connect/plgdbsem.h index 11fe5694bc6..0d2dfde3eb3 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 */ 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/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); From dd930e109e037139a4e245f8f6f6759e089ddb6c Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 23 Sep 2013 17:35:12 +0400 Subject: [PATCH 09/20] ConnectSE: suppressing some "still reachable" errors from unixODBC. modified: mysql-test/valgrind.supp --- mysql-test/valgrind.supp | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 39748edd476..faa2318f38a 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1105,3 +1105,74 @@ 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 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* +} From 5a8a8822d0b3473bfa902d51b2d31c90b12aed42 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 23 Sep 2013 17:59:09 +0400 Subject: [PATCH 10/20] ConnectSE: adding more unixODBC "still reachable" suppressions. --- mysql-test/valgrind.supp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index faa2318f38a..8f15f419e6c 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1126,6 +1126,27 @@ 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 From ce397d8cd23a486be7994f0cc4a5198efcd0c000 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 24 Sep 2013 22:26:44 +0400 Subject: [PATCH 11/20] Activating connect suite --- mysql-test/mysql-test-run.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index d369114ee47..d60ad741f54 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -378,6 +378,7 @@ sub main { mtr_report("Logging: $0 ", join(" ", @ARGV)); $DEFAULT_SUITES.= ',' . join(',', qw( + connect query_response_time sequence spider From 069edfe125f6734fe17baa0c6af49107affb8343 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Wed, 25 Sep 2013 18:41:20 +0200 Subject: [PATCH 12/20] - Reset some variables when closing just in case the XML tre is re-used. modified: storage/connect/libdoc.cpp --- storage/connect/libdoc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp index 01a9f858fb0..31aa90da076 100644 --- a/storage/connect/libdoc.cpp +++ b/storage/connect/libdoc.cpp @@ -540,6 +540,7 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) if ((Xerr = xmlGetLastError())) xmlResetError(Xerr); + Nlist = NULL; } // endif Nlist if (Xop) { @@ -548,6 +549,7 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) if ((Xerr = xmlGetLastError())) xmlResetError(Xerr); + Xop = NULL; } // endif Xop if (NlXop) { @@ -556,6 +558,7 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) if ((Xerr = xmlGetLastError())) xmlResetError(Xerr); + NlXop = NULL; } // endif NlXop if (Ctxp) { @@ -564,6 +567,7 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) if ((Xerr = xmlGetLastError())) xmlResetError(Xerr); + Ctxp = NULL; } // endif Ctxp // } // endif Count From 8619da05d94410bd57254e52dbdcb372f989e355 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Wed, 2 Oct 2013 19:58:49 +0200 Subject: [PATCH 13/20] - Fix the default length for DOUBLE to 255 (was 256, max is 255) Add a trace in MakeSQL modified: storage/connect/myconn.cpp storage/connect/tabmysql.cpp --- storage/connect/myconn.cpp | 2 +- storage/connect/tabmysql.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp index ea0425d3fae..5f121cf1d9d 100644 --- a/storage/connect/myconn.cpp +++ b/storage/connect/myconn.cpp @@ -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/tabmysql.cpp b/storage/connect/tabmysql.cpp index 6d91a444d5e..2ccb594439f 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -507,6 +507,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; From ca4c54aaeed00e5a0b9f09c0e94ee12bf77cbf9d Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Fri, 11 Oct 2013 13:57:56 +0200 Subject: [PATCH 14/20] - Fix bug when closing some table types modified: storage/connect/tabmul.cpp storage/connect/tabmysql.cpp storage/connect/tabodbc.cpp - Add Insert support for ODBC table Add the send command feature to ODBC tables (not documented yet) modified: storage/connect/ha_connect.cc storage/connect/myconn.cpp storage/connect/odbccat.h storage/connect/odbconn.cpp storage/connect/odbconn.h storage/connect/tabodbc.cpp storage/connect/tabodbc.h --- storage/connect/ha_connect.cc | 19 +- storage/connect/myconn.cpp | 2 +- storage/connect/odbccat.h | 1 + storage/connect/odbconn.cpp | 288 +++++++++++++++++++++--- storage/connect/odbconn.h | 8 +- storage/connect/tabmul.cpp | 8 +- storage/connect/tabmysql.cpp | 22 +- storage/connect/tabodbc.cpp | 411 ++++++++++++++++++++++++++++++---- storage/connect/tabodbc.h | 93 +++++++- 9 files changed, 746 insertions(+), 106 deletions(-) diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index a44310fd52b..00ab4d8124e 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -1133,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; @@ -1374,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)) @@ -3464,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); @@ -3948,7 +3950,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) @@ -3966,7 +3968,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); diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp index 5f121cf1d9d..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; diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h index 3665d1b283e..b3a170f4efe 100644 --- a/storage/connect/odbccat.h +++ b/storage/connect/odbccat.h @@ -4,5 +4,6 @@ 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 397b7c2a2f4..990a212117e 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 */ /* */ @@ -229,7 +229,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) @@ -318,6 +317,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. */ @@ -804,6 +814,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. */ /***********************************************************************/ @@ -822,7 +843,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 @@ -1208,8 +1229,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; @@ -1244,26 +1264,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 (!Check(rc)) - ThrowDBX(rc, "SQLExecDirect", hstmt); - - do { - rc = SQLNumResultCols(hstmt, &ncol); - } while (rc == SQL_STILL_EXECUTING); - - if (ncol == 0) { - // Update or Delete statement - rc = SQLRowCount(hstmt, &afrw); + 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, "SQLRowCount", hstmt); + ThrowDBX(rc, "SQLPrepare", hstmt); - return afrw; - } // endif ncol + 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 (!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, "SQLExecDirect", hstmt); + + 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()) @@ -1411,10 +1449,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; @@ -1454,13 +1515,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 @@ -1469,27 +1536,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 /***********************************************************************/ @@ -1541,6 +1640,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; + ULONG n; + SWORD ncol; + RETCODE rc; + HSTMT hstmt; + + if (Open(dsn, 2) < 1) // 2 is openReadOnly + 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. */ /***********************************************************************/ @@ -1844,6 +2069,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/tabmul.cpp b/storage/connect/tabmul.cpp index 146e2891ec7..14ebd8d891a 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 2ccb594439f..b1353a47013 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -1043,18 +1043,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); diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp index ae6817344b8..34157a19f10 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 /***********************************************************************/ @@ -107,9 +108,11 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) 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); Pseudo = 2; // FILID is Ok but not ROWID return false; } // end of DefineAM @@ -125,7 +128,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 +166,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 +208,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 +307,6 @@ void TDBODBC::SetFile(PGLOBAL g, PSZ fn) DBQ = fn; } // end of SetFile - /******************************************************************/ /* Convert an UTF-8 string to latin characters. */ /******************************************************************/ @@ -314,7 +321,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 +335,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()) @@ -430,6 +439,83 @@ char *TDBODBC::MakeSQL(PGLOBAL g, bool cnt) return sql; } // 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. */ /***********************************************************************/ @@ -448,6 +534,12 @@ void TDBODBC::ResetSize(void) int TDBODBC::GetMaxSize(PGLOBAL g) { if (MaxSize < 0) { + if (Srcdef) { + // Give a reasonable guess + MaxSize = 100; + return MaxSize; + } // endif Srcdef + if (!Ocp) Ocp = new(g) ODBConn(g, this); @@ -473,12 +565,11 @@ int TDBODBC::GetMaxSize(PGLOBAL g) } // 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 +637,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 +693,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 +718,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 +748,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 +977,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 +988,242 @@ 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 = sscanf(To_Filter, "%s = '%[^\0]", 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,16 +161,85 @@ 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. */ /***********************************************************************/ From c0907d57b1493e75ac16d8b24cb91a644f078750 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sat, 12 Oct 2013 00:31:09 +0200 Subject: [PATCH 15/20] - Fix compile error on some plarforms (64bits?) modified: storage/connect/odbconn.cpp --- storage/connect/odbconn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp index 990a212117e..a9610c86f18 100644 --- a/storage/connect/odbconn.cpp +++ b/storage/connect/odbconn.cpp @@ -1657,7 +1657,7 @@ PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src) PQRYRES qrp = NULL; PCOLRES crp; USHORT i; - ULONG n; + SQLULEN n; SWORD ncol; RETCODE rc; HSTMT hstmt; From ba3f4a2cc9ec5337f7677def0a1366cbb7332922 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sat, 26 Oct 2013 00:43:03 +0200 Subject: [PATCH 16/20] - Add new features to ODBC table type Srcdef definition Execute command tables uncomplete connect string modified: storage/connect/ha_connect.cc storage/connect/odbccat.h storage/connect/odbconn.cpp storage/connect/odbconn.h storage/connect/plgdbsem.h storage/connect/plgdbutl.cpp storage/connect/tabodbc.cpp --- storage/connect/ha_connect.cc | 52 +++++++++++++++++++++++------------ storage/connect/odbccat.h | 1 + storage/connect/odbconn.cpp | 37 +++++++++++++++++++++---- storage/connect/odbconn.h | 2 +- storage/connect/plgdbsem.h | 1 + storage/connect/plgdbutl.cpp | 28 +++++++++++++++++++ storage/connect/tabodbc.cpp | 12 ++++---- 7 files changed, 105 insertions(+), 28 deletions(-) diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 00ab4d8124e..ee2c9a7eea6 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -3616,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; @@ -3674,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) @@ -3737,13 +3740,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, *prt, *sep, *tbl, *src, *cnp; 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; @@ -3764,7 +3767,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= prt= tbl= src= col= ocl= pic= fcl= rnk= cnp= dsn= NULL; // Get the useful create options ttp= GetTypeID(topt->type); @@ -3782,23 +3785,25 @@ 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); + 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; #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 mxr= atoi(GetListOption(g,"maxerr", topt->oplist, "0")); + cnp= GetListOption(g, "createopt", topt->oplist); + cop= (cnp) ? atoi(cnp) : 0; } else { host= "localhost"; user= "root"; @@ -3854,8 +3859,20 @@ 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 != 2) { + if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) { + create_info->connect_string.str= dsn; + create_info->connect_string.length= strlen(dsn); + ok= true; + + } // endif dsn + + } else if (!dsn) sprintf(g->Message, "Missing %s connection string", topt->type); else ok= true; @@ -4139,6 +4156,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; diff --git a/storage/connect/odbccat.h b/storage/connect/odbccat.h index b3a170f4efe..ba4074bf276 100644 --- a/storage/connect/odbccat.h +++ b/storage/connect/odbccat.h @@ -1,6 +1,7 @@ /***********************************************************************/ /* 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); diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp index a9610c86f18..8dcf71c2953 100644 --- a/storage/connect/odbconn.cpp +++ b/storage/connect/odbconn.cpp @@ -13,6 +13,7 @@ #if defined(WIN32) //nclude //nclude +#include // for getcwd #if defined(__BORLANDC__) #define __MFC_COMPAT__ // To define min/max as macro #endif @@ -172,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. */ @@ -254,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 @@ -1662,7 +1689,7 @@ PQRYRES ODBConn::GetMetaData(PGLOBAL g, char *dsn, char *src) RETCODE rc; HSTMT hstmt; - if (Open(dsn, 2) < 1) // 2 is openReadOnly + if (Open(dsn, 10) < 1) // openReadOnly + noOdbcDialog return NULL; try { diff --git a/storage/connect/odbconn.h b/storage/connect/odbconn.h index 448ce2d428f..311a6ee6406 100644 --- a/storage/connect/odbconn.h +++ b/storage/connect/odbconn.h @@ -115,7 +115,7 @@ class ODBConn : public BLOCK { public: ODBConn(PGLOBAL g, TDBODBC *tdbp); - enum DOP { // Db Open oPtions + static enum DOP { // Db Open oPtions traceSQL = 0x0001, // Trace SQL calls openReadOnly = 0x0002, // Open database read only useCursorLib = 0x0004, // Use ODBC cursor lib diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h index 0d2dfde3eb3..550153921a5 100644 --- a/storage/connect/plgdbsem.h +++ b/storage/connect/plgdbsem.h @@ -583,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 @@ -731,6 +731,34 @@ bool EvalLikePattern(LPCSTR sp, LPCSTR tp) return (b); } /* 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. */ /***********************************************************************/ diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp index 38699b9d8e5..d9d794ef4e4 100644 --- a/storage/connect/tabodbc.cpp +++ b/storage/connect/tabodbc.cpp @@ -100,8 +100,6 @@ 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); @@ -111,8 +109,8 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) 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 @@ -534,8 +532,12 @@ 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) { - // Give a reasonable guess + // Return a reasonable guess MaxSize = 100; return MaxSize; } // endif Srcdef @@ -558,7 +560,7 @@ int TDBODBC::GetMaxSize(PGLOBAL g) if ((MaxSize = Ocp->GetResultSize(Count, Cnp)) < 0) return -3; - +#endif // 0 } // endif MaxSize return MaxSize; From 0f6bcf73de25040c442cec62ddcdbe7f1d43040b Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sat, 26 Oct 2013 17:14:58 +0200 Subject: [PATCH 17/20] - Implement the "exec source" feature for table type MYSQL. modified: storage/connect/ha_connect.cc storage/connect/myconn.h storage/connect/tabmysql.cpp storage/connect/tabmysql.h --- storage/connect/ha_connect.cc | 7 +- storage/connect/myconn.h | 1 + storage/connect/tabmysql.cpp | 212 +++++++++++++++++++++++++++++++++- storage/connect/tabmysql.h | 86 +++++++++++++- 4 files changed, 300 insertions(+), 6 deletions(-) diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index ee2c9a7eea6..b3bafa5a7d9 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -3740,7 +3740,7 @@ 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, *cnp; + const char *user, *fn, *db, *host, *pwd, *prt, *sep, *tbl, *src; const char *col, *ocl, *rnk, *pic, *fcl; char *tab, *dsn; #if defined(WIN32) @@ -3767,7 +3767,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= cnp= dsn= NULL; + user= host= pwd= prt= tbl= src= col= ocl= pic= fcl= rnk= dsn= NULL; // Get the useful create options ttp= GetTypeID(topt->type); @@ -3802,8 +3802,7 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, cls= GetListOption(g, "class", topt->oplist); #endif // WIN32 mxr= atoi(GetListOption(g,"maxerr", topt->oplist, "0")); - cnp= GetListOption(g, "createopt", topt->oplist); - cop= (cnp) ? atoi(cnp) : 0; + cop= atoi(GetListOption(g, "createopt", topt->oplist, "0")); } else { host= "localhost"; user= "root"; 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/tabmysql.cpp b/storage/connect/tabmysql.cpp index b1353a47013..28da47d2d33 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 /***********************************************************************/ @@ -347,6 +348,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 +357,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); @@ -1280,6 +1284,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..f4a234526ff 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 --------------------------- */ @@ -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 /***********************************************************************/ @@ -158,6 +161,87 @@ class MYSQLCOL : public COLBLK { int Rank; // Rank (position) number in the query }; // 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. */ /***********************************************************************/ From bb597a765dff8da39c3c4a907eee6a9e3a75489a Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 27 Oct 2013 10:37:12 +0100 Subject: [PATCH 18/20] - Add test on MYSQL table self reference during CREATE TABLE Fix option other ignored when parsing URL modified: storage/connect/ha_connect.cc storage/connect/mysql-test/connect/t/mysql.test storage/connect/mysql-test/connect/t/mysql_grant.test storage/connect/tabmysql.cpp storage/connect/tabmysql.h --- storage/connect/ha_connect.cc | 123 +++++++++++--- .../connect/mysql-test/connect/t/mysql.test | 2 +- .../mysql-test/connect/t/mysql_grant.test | 156 +++++++++--------- storage/connect/tabmysql.cpp | 31 ++-- storage/connect/tabmysql.h | 2 +- 5 files changed, 200 insertions(+), 114 deletions(-) diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index b3bafa5a7d9..4fecbe9be84 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -3722,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 != 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. @@ -3740,7 +3759,7 @@ 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) @@ -3767,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); @@ -3795,14 +3814,13 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, 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; #if defined(WIN32) 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, "createopt", topt->oplist, "0")); + cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0")); } else { host= "localhost"; user= "root"; @@ -3848,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); @@ -3863,12 +3881,10 @@ static int connect_assisted_discovery(handlerton *hton, THD* thd, if (fnc & (FNC_DSN | FNC_DRIVER)) ok= true; else if (!stricmp(thd->main_security_ctx.host, "localhost") - && cop != 2) { + && cop == 1) { if ((dsn = ODBCCheckConnection(g, dsn, cop)) != NULL) { - create_info->connect_string.str= dsn; - create_info->connect_string.length= strlen(dsn); + thd->make_lex_string(&create_info->connect_string, dsn, strlen(dsn)); ok= true; - } // endif dsn } else if (!dsn) @@ -3893,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) @@ -4276,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/mysql-test/connect/t/mysql.test b/storage/connect/mysql-test/connect/t/mysql.test index bb4b2e78e56..271e3f07465 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' 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/tabmysql.cpp b/storage/connect/tabmysql.cpp index 28da47d2d33..f37cb6d349e 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -172,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 @@ -249,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 diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h index f4a234526ff..bcac10dcaa7 100644 --- a/storage/connect/tabmysql.h +++ b/storage/connect/tabmysql.h @@ -40,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: From 89e71ffb40e5f58b7ec29c3b74db6a8a9bcc9a20 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 27 Oct 2013 14:32:54 +0100 Subject: [PATCH 19/20] - Fix some GCC compiler eroors and warnings modified: storage/connect/ha_connect.cc storage/connect/odbconn.cpp storage/connect/odbconn.h --- storage/connect/ha_connect.cc | 2 +- storage/connect/odbconn.cpp | 2 +- storage/connect/odbconn.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 4fecbe9be84..a066c48bf0e 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -3734,7 +3734,7 @@ static bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host, return false; else if (tab && stricmp(tab, s->table_name.str)) return false; - else if (port && port != GetDefaultPort()) + else if (port && port != (signed)GetDefaultPort()) return false; strcpy(g->Message, "This MySQL table is defined on itself"); diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp index 8dcf71c2953..0a19d90a422 100644 --- a/storage/connect/odbconn.cpp +++ b/storage/connect/odbconn.cpp @@ -1113,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); diff --git a/storage/connect/odbconn.h b/storage/connect/odbconn.h index 311a6ee6406..448ce2d428f 100644 --- a/storage/connect/odbconn.h +++ b/storage/connect/odbconn.h @@ -115,7 +115,7 @@ class ODBConn : public BLOCK { public: ODBConn(PGLOBAL g, TDBODBC *tdbp); - static enum DOP { // Db Open oPtions + enum DOP { // Db Open oPtions traceSQL = 0x0001, // Trace SQL calls openReadOnly = 0x0002, // Open database read only useCursorLib = 0x0004, // Use ODBC cursor lib From 727c1f62a59131b1f33f9931cb6dcba031e35847 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 29 Oct 2013 10:09:11 +0400 Subject: [PATCH 20/20] MDEV-4877 mysqldump dumps all data from a connect table --- client/mysqldump.c | 1 + .../connect/mysql-test/connect/r/mysql.result | 21 +++++++++++++++++++ .../connect/mysql-test/connect/t/mysql.test | 14 +++++++++++++ 3 files changed, 36 insertions(+) diff --git a/client/mysqldump.c b/client/mysqldump.c index f9fcbb65515..1f897352d49 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -5144,6 +5144,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/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/t/mysql.test b/storage/connect/mysql-test/connect/t/mysql.test index 271e3f07465..673df3fcb54 100644 --- a/storage/connect/mysql-test/connect/t/mysql.test +++ b/storage/connect/mysql-test/connect/t/mysql.test @@ -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;