From 39750cd4dbe9b79a100d45aae010f39f71ec7ddf Mon Sep 17 00:00:00 2001 From: Olivier Bertrand <bertrandop@gmail.com> Date: Tue, 22 Apr 2014 19:15:08 +0200 Subject: [PATCH] - FIX a bug causing libxml2 not retrieving expanded multiple column values. This was working but the cause probably comes from freeing Xop object to handle memory leaks reported by Valgrind. Also add a test case on XML multiple tables. added: storage/connect/mysql-test/connect/r/xml_mult.result storage/connect/mysql-test/connect/std_data/bookstore.xml storage/connect/mysql-test/connect/t/xml_mult.test modified: storage/connect/domdoc.cpp storage/connect/tabxml.cpp storage/connect/tabxml.h - Enhance the index types and flages returning functions. modified: storage/connect/ha_connect.cc storage/connect/ha_connect.h - Suppress irrelevant warning message (MDEV-6117) modified: storage/connect/ha_connect.cc --- storage/connect/domdoc.cpp | 24 ++-- storage/connect/ha_connect.cc | 78 ++++++++--- storage/connect/ha_connect.h | 10 +- .../mysql-test/connect/r/xml_mult.result | 102 ++++++++++++++ .../mysql-test/connect/std_data/bookstore.xml | 31 +++++ .../mysql-test/connect/t/xml_mult.test | 64 +++++++++ storage/connect/tabxml.cpp | 125 +++++++++++------- storage/connect/tabxml.h | 1 + 8 files changed, 351 insertions(+), 84 deletions(-) create mode 100644 storage/connect/mysql-test/connect/r/xml_mult.result create mode 100644 storage/connect/mysql-test/connect/std_data/bookstore.xml create mode 100644 storage/connect/mysql-test/connect/t/xml_mult.test diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp index 251b4f1798..518c806c65 100644 --- a/storage/connect/domdoc.cpp +++ b/storage/connect/domdoc.cpp @@ -416,18 +416,24 @@ PXLIST DOMNODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp) /******************************************************************/ PXNODE DOMNODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np) { - MSXML2::IXMLDOMNodePtr dnp = Nodep->selectSingleNode(xp); + try { + MSXML2::IXMLDOMNodePtr dnp = Nodep->selectSingleNode(xp); - if (dnp) { - if (np) { - ((PDOMNODE)np)->Nodep = dnp; - return np; - } else - return new(g) DOMNODE(Doc, dnp); + if (dnp) { + if (np) { + ((PDOMNODE)np)->Nodep = dnp; + return np; + } else + return new(g) DOMNODE(Doc, dnp); - } else - return NULL; + } // endif dnp + + } catch(_com_error e) { + sprintf(g->Message, "%s: %s", MSG(COM_ERROR), + _com_util::ConvertBSTRToString(e.Description())); + } catch(...) {} + return NULL; } // end of SelectSingleNode /******************************************************************/ diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 4788b669cb..71d263511a 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -621,7 +621,7 @@ TABTYPE ha_connect::GetRealType(PTOS pos) { TABTYPE type; - if (pos || (pos= GetTableOptionStruct(table))) { + if (pos || (pos= GetTableOptionStruct())) { type= GetTypeID(pos->type); if (type == TAB_UNDEF) @@ -633,6 +633,50 @@ TABTYPE ha_connect::GetRealType(PTOS pos) return type; } // end of GetRealType +/** @brief + The name of the index type that will be used for display. + Don't implement this method unless you really have indexes. + */ +const char *ha_connect::index_type(uint inx) +{ + switch (GetIndexType(GetRealType())) { + case 1: return "XPLUG"; + case 2: return "REMOTE"; + } // endswitch + + return "Unknown"; +} // end of index_type + +/** @brief + This is a bitmap of flags that indicates how the storage engine + implements indexes. The current index flags are documented in + handler.h. If you do not implement indexes, just return zero here. + + @details + part is the key part to check. First key part is 0. + If all_parts is set, MySQL wants to know the flags for the combined + index, up to and including 'part'. +*/ +ulong ha_connect::index_flags(uint inx, uint part, bool all_parts) const +{ + ulong flags= HA_READ_NEXT | HA_READ_RANGE | + HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR; + ha_connect *hp= (ha_connect*)this; + PTOS pos= hp->GetTableOptionStruct(); + + if (pos) { + TABTYPE type= hp->GetRealType(pos); + + switch (GetIndexType(type)) { + case 1: flags|= (HA_READ_ORDER | HA_READ_PREV); break; + case 2: flags|= HA_READ_AFTER_KEY; break; + } // endswitch + + } // endif pos + + return flags; +} // end of index_flags + /** @brief This is a list of flags that indicate what functionality the storage engine implements. The current table flags are documented in handler.h @@ -641,14 +685,14 @@ ulonglong ha_connect::table_flags() const { ulonglong flags= HA_CAN_VIRTUAL_COLUMNS | HA_REC_NOT_IN_SEQ | HA_NO_AUTO_INCREMENT | HA_NO_PREFIX_CHAR_KEYS | -// HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE | + HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE | HA_PARTIAL_COLUMN_READ | HA_FILE_BASED | // HA_NULL_IN_KEY | not implemented yet // HA_FAST_KEY_READ | causes error when sorting (???) HA_NO_TRANSACTIONS | HA_DUPLICATE_KEY_NOT_IN_ORDER | HA_NO_BLOBS | HA_MUST_USE_TABLE_CONDITION_PUSHDOWN; ha_connect *hp= (ha_connect*)this; - PTOS pos= hp->GetTableOptionStruct(table); + PTOS pos= hp->GetTableOptionStruct(); if (pos) { TABTYPE type= hp->GetRealType(pos); @@ -719,10 +763,11 @@ char *GetListOption(PGLOBAL g, const char *opname, /****************************************************************************/ /* Return the table option structure. */ /****************************************************************************/ -PTOS ha_connect::GetTableOptionStruct(TABLE *tab) +PTOS ha_connect::GetTableOptionStruct(TABLE_SHARE *s) { - return (tshp) ? tshp->option_struct : - (tab) ? tab->s->option_struct : NULL; + TABLE_SHARE *tsp= (tshp) ? tshp : (s) ? s : table_share; + + return (tsp) ? tsp->option_struct : NULL; } // end of GetTableOptionStruct /****************************************************************************/ @@ -731,7 +776,7 @@ PTOS ha_connect::GetTableOptionStruct(TABLE *tab) char *ha_connect::GetStringOption(char *opname, char *sdef) { char *opval= NULL; - PTOS options= GetTableOptionStruct(table); + PTOS options= GetTableOptionStruct(); if (!options) ; @@ -803,10 +848,10 @@ bool ha_connect::GetBooleanOption(char *opname, bool bdef) { bool opval= bdef; char *pv; - PTOS options= GetTableOptionStruct(table); + PTOS options= GetTableOptionStruct(); if (!stricmp(opname, "View")) - opval= (tshp) ? tshp->is_view : table->s->is_view; + opval= (tshp) ? tshp->is_view : table_share->is_view; else if (!options) ; else if (!stricmp(opname, "Mapped")) @@ -834,7 +879,7 @@ bool ha_connect::GetBooleanOption(char *opname, bool bdef) /****************************************************************************/ bool ha_connect::SetBooleanOption(char *opname, bool b) { - PTOS options= GetTableOptionStruct(table); + PTOS options= GetTableOptionStruct(); if (!options) return true; @@ -854,7 +899,7 @@ int ha_connect::GetIntegerOption(char *opname) { ulonglong opval= NO_IVAL; char *pv; - PTOS options= GetTableOptionStruct(table); + PTOS options= GetTableOptionStruct(); if (!options) ; @@ -891,7 +936,7 @@ int ha_connect::GetIntegerOption(char *opname) /****************************************************************************/ bool ha_connect::SetIntegerOption(char *opname, int n) { - PTOS options= GetTableOptionStruct(table); + PTOS options= GetTableOptionStruct(); if (!options) return true; @@ -1153,7 +1198,7 @@ const char *ha_connect::GetDBName(const char* name) const char *ha_connect::GetTableName(void) { - return (tshp) ? tshp->table_name.str : table->s->table_name.str; + return (tshp) ? tshp->table_name.str : table_share->table_name.str; } // end of GetTableName #if 0 @@ -3310,7 +3355,7 @@ int ha_connect::external_lock(THD *thd, int lock_type) int rc= 0; bool xcheck=false, cras= false; MODE newmode; - PTOS options= GetTableOptionStruct(table); + PTOS options= GetTableOptionStruct(); PGLOBAL g= GetPlug(thd, xp); DBUG_ENTER("ha_connect::external_lock"); @@ -4550,7 +4595,7 @@ int ha_connect::create(const char *name, TABLE *table_arg, DBUG_ENTER("ha_connect::create"); int sqlcom= thd_sql_command(table_arg->in_use); - PTOS options= GetTableOptionStruct(table_arg); + PTOS options= GetTableOptionStruct(table_arg->s); table= table_arg; // Used by called functions @@ -4885,7 +4930,7 @@ int ha_connect::create(const char *name, TABLE *table_arg, } else ::close(h); - if (type == TAB_FMT || options->readonly) + if ((type == TAB_FMT || options->readonly) && sqlcom == SQLCOM_CREATE_TABLE) push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, "Congratulation, you just created a read-only void table!"); @@ -5123,7 +5168,6 @@ ha_connect::check_if_supported_inplace_alter(TABLE *altered_table, int sqlcom= thd_sql_command(thd); TABTYPE newtyp, type= TAB_UNDEF; HA_CREATE_INFO *create_info= ha_alter_info->create_info; -//PTOS pos= GetTableOptionStruct(table); PTOS newopt, oldopt; xp= GetUser(thd, xp); PGLOBAL g= xp->g; diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h index ae7ae010c7..a8d0be4c03 100644 --- a/storage/connect/ha_connect.h +++ b/storage/connect/ha_connect.h @@ -167,7 +167,7 @@ public: static bool connect_end(void); TABTYPE GetRealType(PTOS pos= NULL); char *GetStringOption(char *opname, char *sdef= NULL); - PTOS GetTableOptionStruct(TABLE *table_arg); + PTOS GetTableOptionStruct(TABLE_SHARE *s= NULL); bool GetBooleanOption(char *opname, bool bdef); bool SetBooleanOption(char *opname, bool b); int GetIntegerOption(char *opname); @@ -210,7 +210,7 @@ public: The name of the index type that will be used for display. Don't implement this method unless you really have indexes. */ - const char *index_type(uint inx) { return "XPLUG"; } + const char *index_type(uint inx); /** @brief The file extensions. @@ -241,11 +241,7 @@ public: If all_parts is set, MySQL wants to know the flags for the combined index, up to and including 'part'. */ - ulong index_flags(uint inx, uint part, bool all_parts) const - { - return HA_READ_NEXT | HA_READ_RANGE | HA_READ_ORDER | - HA_READ_PREV | HA_KEYREAD_ONLY | HA_KEY_SCAN_NOT_ROR; - } // end of index_flags + ulong index_flags(uint inx, uint part, bool all_parts) const; /** @brief unireg.cc will call max_supported_record_length(), max_supported_keys(), diff --git a/storage/connect/mysql-test/connect/r/xml_mult.result b/storage/connect/mysql-test/connect/r/xml_mult.result new file mode 100644 index 0000000000..a9592e986c --- /dev/null +++ b/storage/connect/mysql-test/connect/r/xml_mult.result @@ -0,0 +1,102 @@ +Warnings: +Warning 1105 No file name. Table will use t1.xml +SET NAMES utf8; +# +# Testing expanded values +# +CREATE TABLE `bookstore` ( +`category` CHAR(16) NOT NULL FIELD_FORMAT='@', +`title` VARCHAR(50) NOT NULL, +`lang` char(2) NOT NULL FIELD_FORMAT='title/@', +`author` VARCHAR(24) NOT NULL, +`year` INT(4) NOT NULL, +`price` DOUBLE(8,2) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='bookstore.xml' OPTION_LIST='expand=1,mulnode=author,limit=6,xmlsup=libxml2'; +SELECT * FROM bookstore; +category title lang author year price +COOKING Everyday Italian en Giada De Laurentiis 2005 30.00 +CHILDREN Harry Potter en J K. Rowling 2005 29.99 +WEB XQuery Kick Start en James McGovern 2003 49.99 +WEB XQuery Kick Start en Per Bothner 2003 49.99 +WEB XQuery Kick Start en Kurt Cagle 2003 49.99 +WEB XQuery Kick Start en James Linn 2003 49.99 +WEB XQuery Kick Start en Vaidyanathan Nagarajan 2003 49.99 +WEB Learning XML en Erik T. Ray 2003 39.95 +SELECT category, title, price FROM bookstore; +category title price +COOKING Everyday Italian 30.00 +CHILDREN Harry Potter 29.99 +WEB XQuery Kick Start 49.99 +WEB Learning XML 39.95 +SELECT category, title, author, price FROM bookstore WHERE author LIKE '%K%'; +category title author price +CHILDREN Harry Potter J K. Rowling 29.99 +WEB XQuery Kick Start Kurt Cagle 49.99 +WEB Learning XML Erik T. Ray 39.95 +SELECT category, title, price FROM bookstore WHERE author LIKE 'J%'; +category title price +CHILDREN Harry Potter 29.99 +WEB XQuery Kick Start 49.99 +WEB XQuery Kick Start 49.99 +# +# Limiting expanded values +# +ALTER TABLE bookstore OPTION_LIST='expand=1,mulnode=author,limit=3,xmlsup=libxml2'; +SELECT * FROM bookstore; +category title lang author year price +COOKING Everyday Italian en Giada De Laurentiis 2005 30.00 +CHILDREN Harry Potter en J K. Rowling 2005 29.99 +WEB XQuery Kick Start en James McGovern 2003 49.99 +WEB XQuery Kick Start en Per Bothner 2003 49.99 +WEB XQuery Kick Start en Kurt Cagle 2003 49.99 +WEB Learning XML en Erik T. Ray 2003 39.95 +Warnings: +Warning 1105 Mutiple values limited to 3 +# One line lost because the where clause is applied only on the first 3 rows +SELECT category, title, author, price FROM bookstore WHERE author LIKE 'J%'; +category title author price +CHILDREN Harry Potter J K. Rowling 29.99 +WEB XQuery Kick Start James McGovern 49.99 +Warnings: +Warning 1105 Mutiple values limited to 3 +# +# Testing concatenated values +# +ALTER TABLE bookstore OPTION_LIST='mulnode=author,limit=6,xmlsup=libxml2'; +# truncated +SELECT * FROM bookstore; +category title lang author year price +COOKING Everyday Italian en Giada De Laurentiis 2005 30.00 +CHILDREN Harry Potter en J K. Rowling 2005 29.99 +WEB XQuery Kick Start en James McGovern, Per Both 2003 49.99 +WEB Learning XML en Erik T. Ray 2003 39.95 +Warnings: +Warning 1105 Truncated author content +# increase author size +ALTER TABLE bookstore MODIFY `author` VARCHAR(128) NOT NULL; +SELECT * FROM bookstore; +category title lang author year price +COOKING Everyday Italian en Giada De Laurentiis 2005 30.00 +CHILDREN Harry Potter en J K. Rowling 2005 29.99 +WEB XQuery Kick Start en James McGovern, Per Bothner, Kurt Cagle, James Linn, Vaidyanathan Nagarajan 2003 49.99 +WEB Learning XML en Erik T. Ray 2003 39.95 +# +# Limiting concatenated values +# +ALTER TABLE bookstore OPTION_LIST='mulnode=author,limit=4,xmlsup=libxml2'; +SELECT * FROM bookstore; +category title lang author year price +COOKING Everyday Italian en Giada De Laurentiis 2005 30.00 +CHILDREN Harry Potter en J K. Rowling 2005 29.99 +WEB XQuery Kick Start en James McGovern, Per Bothner, Kurt Cagle, James Linn 2003 49.99 +WEB Learning XML en Erik T. Ray 2003 39.95 +Warnings: +Warning 1105 Mutiple values limited to 4 +# The where clause is applied on the concatenated column result +SELECT category, title, author, price FROM bookstore WHERE author LIKE 'J%'; +category title author price +CHILDREN Harry Potter J K. Rowling 29.99 +WEB XQuery Kick Start James McGovern, Per Bothner, Kurt Cagle, James Linn 49.99 +Warnings: +Warning 1105 Mutiple values limited to 4 +DROP TABLE bookstore; diff --git a/storage/connect/mysql-test/connect/std_data/bookstore.xml b/storage/connect/mysql-test/connect/std_data/bookstore.xml new file mode 100644 index 0000000000..0aebbcd243 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/bookstore.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bookstore> + <book category="COOKING"> + <title lang="en">Everyday Italian</title> + <author>Giada De Laurentiis</author> + <year>2005</year> + <price>30.00</price> + </book> + <book category="CHILDREN"> + <title lang="en">Harry Potter</title> + <author>J K. Rowling</author> + <year>2005</year> + <price>29.99</price> + </book> + <book category="WEB"> + <title lang="en">XQuery Kick Start</title> + <author>James McGovern</author> + <author>Per Bothner</author> + <author>Kurt Cagle</author> + <author>James Linn</author> + <author>Vaidyanathan Nagarajan</author> + <year>2003</year> + <price>49.99</price> + </book> + <book category="WEB"> + <title lang="en">Learning XML</title> + <author>Erik T. Ray</author> + <year>2003</year> + <price>39.95</price> + </book> +</bookstore> diff --git a/storage/connect/mysql-test/connect/t/xml_mult.test b/storage/connect/mysql-test/connect/t/xml_mult.test new file mode 100644 index 0000000000..cd83827fe3 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/xml_mult.test @@ -0,0 +1,64 @@ +--source have_libxml2.inc + +let $MYSQLD_DATADIR= `select @@datadir`; + +SET NAMES utf8; + +--copy_file $MTR_SUITE_DIR/std_data/bookstore.xml $MYSQLD_DATADIR/test/bookstore.xml + +#--echo $MYSQL_TEST_DIR +#--exec pwd +#SELECT LOAD_FILE('test/bookstore.xml'); + + +--echo # +--echo # Testing expanded values +--echo # +CREATE TABLE `bookstore` ( + `category` CHAR(16) NOT NULL FIELD_FORMAT='@', + `title` VARCHAR(50) NOT NULL, + `lang` char(2) NOT NULL FIELD_FORMAT='title/@', + `author` VARCHAR(24) NOT NULL, + `year` INT(4) NOT NULL, + `price` DOUBLE(8,2) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='bookstore.xml' OPTION_LIST='expand=1,mulnode=author,limit=6,xmlsup=libxml2'; +SELECT * FROM bookstore; +SELECT category, title, price FROM bookstore; +SELECT category, title, author, price FROM bookstore WHERE author LIKE '%K%'; +SELECT category, title, price FROM bookstore WHERE author LIKE 'J%'; + + +--echo # +--echo # Limiting expanded values +--echo # +ALTER TABLE bookstore OPTION_LIST='expand=1,mulnode=author,limit=3,xmlsup=libxml2'; +SELECT * FROM bookstore; +--echo # One line lost because the where clause is applied only on the first 3 rows +SELECT category, title, author, price FROM bookstore WHERE author LIKE 'J%'; + + +--echo # +--echo # Testing concatenated values +--echo # +ALTER TABLE bookstore OPTION_LIST='mulnode=author,limit=6,xmlsup=libxml2'; +--echo # truncated +SELECT * FROM bookstore; +--echo # increase author size +ALTER TABLE bookstore MODIFY `author` VARCHAR(128) NOT NULL; +SELECT * FROM bookstore; + + +--echo # +--echo # Limiting concatenated values +--echo # +ALTER TABLE bookstore OPTION_LIST='mulnode=author,limit=4,xmlsup=libxml2'; +SELECT * FROM bookstore; +--echo # The where clause is applied on the concatenated column result +SELECT category, title, author, price FROM bookstore WHERE author LIKE 'J%'; +DROP TABLE bookstore; + + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/bookstore.xml diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp index c7c61f0dcb..1e9c172cdb 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -145,7 +145,7 @@ bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) XmlDB = GetStringCatInfo(g, "XmlDB", ""); Nslist = GetStringCatInfo(g, "Nslist", ""); DefNs = GetStringCatInfo(g, "DefNs", ""); - Limit = GetIntCatInfo("Limit", 2); + Limit = GetIntCatInfo("Limit", 10); Xpand = (GetIntCatInfo("Expand", 0) != 0); Header = GetIntCatInfo("Header", 0); GetCharCatInfo("Xmlsup", "*", buf, sizeof(buf)); @@ -1038,12 +1038,13 @@ XMLCOL::XMLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) Type = Tdbp->Coltype; Nx = -1; Sx = -1; + N = 0; Valbuf = NULL; To_Val = NULL; } // end of XMLCOL constructor /***********************************************************************/ -/* XMLCOL constructor used for copying columns. */ +/* XMLCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/ XMLCOL::XMLCOL(XMLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) @@ -1068,6 +1069,7 @@ XMLCOL::XMLCOL(XMLCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) Rank = col1->Rank; Nx = col1->Nx; Sx = col1->Sx; + N = col1->N; Type = col1->Type; To_Val = col1->To_Val; } // end of XMLCOL copy constructor @@ -1080,8 +1082,8 @@ bool XMLCOL::AllocBuf(PGLOBAL g, bool mode) if (Valbuf) return false; // Already done - Valbuf = (char*)PlugSubAlloc(g, NULL, Long + 1); - Valbuf[Long] = '\0'; +//Valbuf = (char*)PlugSubAlloc(g, NULL, Long + 1); +//Valbuf[Long] = '\0'; return ParseXpath(g, mode); } // end of AllocBuf @@ -1095,7 +1097,7 @@ bool XMLCOL::AllocBuf(PGLOBAL g, bool mode) bool XMLCOL::ParseXpath(PGLOBAL g, bool mode) { char *p, *p2, *pbuf = NULL; - int i, len = strlen(Name); + int i, n = 1, len = strlen(Name); len += ((Tdbp->Colname) ? strlen(Tdbp->Colname) : 0); len += ((Xname) ? strlen(Xname) : 0); @@ -1122,7 +1124,7 @@ bool XMLCOL::ParseXpath(PGLOBAL g, bool mode) // For Update or Insert the Xpath must be analyzed if (mode) { for (i = 0, p = pbuf; (p = strchr(p, '/')); i++, p++) - Nod++; // One path node found + Nod++; // One path node found if (Nod) Nodes = (char**)PlugSubAlloc(g, NULL, Nod * sizeof(char*)); @@ -1136,7 +1138,7 @@ bool XMLCOL::ParseXpath(PGLOBAL g, bool mode) strcpy(g->Message, MSG(CONCAT_SUBNODE)); return true; } else - Inod = i; // Index of multiple node + Inod = i; // Index of multiple node if (mode) { // For Update or Insert the Xpath must be explicit @@ -1171,7 +1173,7 @@ bool XMLCOL::ParseXpath(PGLOBAL g, bool mode) } else if (Type == 2) { // HTML like table, columns are retrieved by position - new(this) XPOSCOL(Value); // Change the class of this column + new(this) XPOSCOL(Value); // Change the class of this column Tdbp->Hasnod = true; return false; } else if (Type == 0 && !mode) { @@ -1185,9 +1187,18 @@ bool XMLCOL::ParseXpath(PGLOBAL g, bool mode) if (Inod >= 0) { Tdbp->Colp = this; // To force expand - new(this) XMULCOL(Value); // Change the class of this column + + if (Tdbp->Xpand) + n = Tdbp->Limit; + + new(this) XMULCOL(Value); // Change the class of this column } // endif Inod + Valbuf = (char*)PlugSubAlloc(g, NULL, n * (Long + 1)); + + for (i = 0; i < n; i++) + Valbuf[Long + (i * (Long + 1))] = '\0'; + if (Type || Nod) Tdbp->Hasnod = true; @@ -1470,60 +1481,72 @@ void XMLCOL::WriteColumn(PGLOBAL g) void XMULCOL::ReadColumn(PGLOBAL g) { char *p; - int i, n, len; + int i, len; + bool b = Tdbp->Xpand; - if (Nx != Tdbp->Irow) // New row + if (Nx != Tdbp->Irow) { // New row Nl = Tdbp->RowNode->SelectNodes(g, Xname, Nl); - else if (Sx == Tdbp->Nsub) - return; // Same row - if ((n = Nl->GetLength())) { - *(p = Valbuf) = '\0'; - len = Long; + if ((N = Nl->GetLength())) { + *(p = Valbuf) = '\0'; + len = Long; - for (i = Tdbp->Nsub; i < n; i++) { - ValNode = Nl->GetItem(g, i, Vxnp); + if (N > Tdbp->Limit) { + N = Tdbp->Limit; + sprintf(g->Message, "Mutiple values limited to %d", Tdbp->Limit); + PushWarning(g, Tdbp); + } // endif N - if (ValNode->GetType() != XML_ELEMENT_NODE && - ValNode->GetType() != XML_ATTRIBUTE_NODE) { - sprintf(g->Message, MSG(BAD_VALNODE), ValNode->GetType(), Name); - longjmp(g->jumper[g->jump_level], TYPE_AM_XML); - } // endif type + for (i = 0; i < N; i++) { + ValNode = Nl->GetItem(g, i, Vxnp); - // Get the Xname value from the XML file - switch (ValNode->GetContent(g, p, len + 1)) { - case RC_OK: - break; - case RC_INFO: - PushWarning(g, Tdbp); - break; - default: + if (ValNode->GetType() != XML_ELEMENT_NODE && + ValNode->GetType() != XML_ATTRIBUTE_NODE) { + sprintf(g->Message, MSG(BAD_VALNODE), ValNode->GetType(), Name); longjmp(g->jumper[g->jump_level], TYPE_AM_XML); - } // endswitch - - if (!Tdbp->Xpand) { - // Concatenate all values - if (n - i > 1) - strncat(Valbuf, ", ", Long + 1); - - len -= strlen(p); - p += strlen(p); - } else - break; - - } // endfor i + } // endif type + + // Get the Xname value from the XML file + switch (ValNode->GetContent(g, p, (b ? Long : len))) { + case RC_OK: + break; + case RC_INFO: + PushWarning(g, Tdbp); + break; + default: + longjmp(g->jumper[g->jump_level], TYPE_AM_XML); + } // endswitch + + if (!b) { + // Concatenate all values + if (N - i > 1) + strncat(Valbuf, ", ", len - strlen(p)); + + if ((len -= strlen(p)) <= 0) + break; + + p += strlen(p); + } else // Xpand + p += (Long + 1); + + } // endfor i + + Value->SetValue_psz(Valbuf); + } else { + if (Nullable) + Value->SetNull(true); - Value->SetValue_psz(Valbuf); - } else { - if (Nullable) - Value->SetNull(true); + Value->Reset(); // Null value + } // endif ValNode - Value->Reset(); // Null value - } // endif ValNode + } else if (Sx == Tdbp->Nsub) + return; // Same row + else // Expanded value + Value->SetValue_psz(Valbuf + (Tdbp->Nsub * (Long + 1))); Nx = Tdbp->Irow; Sx = Tdbp->Nsub; - Tdbp->NextSame = (Tdbp->Xpand && Nl->GetLength() - Sx > 1); + Tdbp->NextSame = (Tdbp->Xpand && N - Sx > 1); } // end of ReadColumn /***********************************************************************/ diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h index 1284cf6197..23c46c03a8 100644 --- a/storage/connect/tabxml.h +++ b/storage/connect/tabxml.h @@ -190,6 +190,7 @@ class XMLCOL : public COLBLK { int Long; // Buffer length int Nx; // The last read row int Sx; // The last read sub-row + int N; // The number of (multiple) values PVAL To_Val; // To value used for Update/Insert }; // end of class XMLCOL -- 2.30.9