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