浏览代码

Merge branch 'candidate-7.0.x' into candidate-7.2.x

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 6 年之前
父节点
当前提交
2d00bd2dcc

+ 0 - 9
docs/EN_US/ECLLanguageReference/ECLR_mods/Templ-OPTION.xml

@@ -1017,15 +1017,6 @@
 
           <tbody>
             <row>
-              <entry><emphasis>clusterSize</emphasis></entry>
-
-              <entry>Default: none</entry>
-
-              <entry>Override the number of nodes in the cluster (for
-              testing)</entry>
-            </row>
-
-            <row>
               <entry><emphasis>debugNlp</emphasis></entry>
 
               <entry>Default: false</entry>

+ 15 - 14
docs/EN_US/HPCCSpark/SparkHPCC.xml

@@ -707,23 +707,24 @@
       such as Eclipse via the Spark-submit application whereas the examples
       below are dependent on the Spark shell.</para>
 
-      <sect2 id="iris_lr">
-        <title>Iris_LR</title>
+      <para>The following examples assume a Spark Shell. You can use the
+      spark-submit command if you intend to compile and package these
+      examples. To properly connect your spark shell to the Integrated Spark
+      cluster, provide the following parameters when starting the
+      shell:</para>
 
-        <para>The Iris_LR example assumes a Spark Shell. You can use the
-        spark-submit command if you intend to compile and package these
-        examples. If you are already logged onto a node on an integrated
-        Spark-HPCC Cluster run the spark-shell:</para>
+      <programlisting>bin/spark-shell  \
+ --master=&lt;spark://{remotesparkhost-IP}:{sparkport}&gt; --conf="spark.driver.host={localhost-ip}" </programlisting>
 
-        <programlisting> /opt/HPCCSystems/externals \
- /spark-hadoop/bin/spark-shell --master=spark://{sparkhost-IP}:{sparkport}</programlisting>
+      <sect2 id="iris_lr">
+        <title>Iris_LR</title>
 
-        <para>The next step is to establish your HpccFile and your RDD for
-        that file. You need the name of the file, the protocol (http or
-        https), the name or IP of the ESP, the port for the ESP (usually
-        8010), and your user account and password. The <emphasis>sc</emphasis>
-        value is the <emphasis>SparkContext</emphasis> object provided by the
-        shell.</para>
+        <para>This example assumes that you have Spark Shell running. The next
+        step is to establish your HpccFile and your RDD for that file. You
+        need the name of the file, the protocol (http or https), the name or
+        IP of the ESP, the port for the ESP (usually 8010), and your user
+        account and password. The <emphasis>sc</emphasis> value is the
+        <emphasis>SparkContext</emphasis> object provided by the shell.</para>
 
         <programlisting> val espcon = new Connection("http", "myeclwatchhost", "8010");
  espcon.setUserName("myuser");

+ 12 - 9
esp/services/esdl_svc_engine/esdl_binding.cpp

@@ -506,6 +506,7 @@ void EsdlServiceImpl::handleServiceRequest(IEspContext &context,
 {
     const char *mthName = mthdef.queryName();
     context.addTraceSummaryValue(LogMin, "method", mthName);
+    const char* srvName = srvdef.queryName();
 
     if (m_serviceLevelCrtFail)
         throw MakeStringException(-1, "%s::%s disabled due to Custom Transform errors. Review transform template in configuration.", srvdef.queryName(), mthName);
@@ -578,7 +579,7 @@ void EsdlServiceImpl::handleServiceRequest(IEspContext &context,
     else if
     (stricmp(mthName, "ping")==0 || mthdef.hasProp("Ping"))
     {
-        handlePingRequest(mthdef.queryName(),out,context.getResponseFormat());
+        handlePingRequest(srvName, out, flags);
         return;
     }
     else
@@ -1033,12 +1034,12 @@ void EsdlServiceImpl::handleEchoTest(const char *mthName,
     }
 }
 
-void EsdlServiceImpl::handlePingRequest(const char *mthName,StringBuffer &out,ESPSerializationFormat format)
+void EsdlServiceImpl::handlePingRequest(const char *srvName, StringBuffer &out, unsigned int flags)
 {
-    if (format == ESPSerializationJSON)
-        out.appendf("{\"%sPingResponse\": {}}", mthName);
+    if (flags & ESDL_BINDING_RESPONSE_JSON)
+        out.appendf("\"%sPingResponse\": {}", srvName);
     else
-        out.appendf("<%sResponse></%sResponse>", mthName, mthName);
+        out.appendf("<%sPingResponse></%sPingResponse>", srvName, srvName);
 }
 
 void EsdlServiceImpl::generateTargetURL(IEspContext & context,
@@ -2691,12 +2692,14 @@ void EsdlBindingImpl::handleJSONPost(CHttpRequest *request, CHttpResponse *respo
         Owned<IPropertyTree> contentTree = createPTreeFromJSONString(content.str());
         if (contentTree)
         {
-            StringBuffer reqestName(methodName);
-            reqestName.append("Request");
+            StringBuffer requestName;
+            if (stricmp(methodName, "ping") == 0)
+                requestName.append(serviceName);
+            requestName.append(methodName).append("Request");
 
-            Owned<IPropertyTree> reqTree = contentTree->getBranch(reqestName.str());
+            Owned<IPropertyTree> reqTree = contentTree->getBranch(requestName.str());
             if (!reqTree)
-                throw MakeStringException(-1, "EsdlBinding::%s::%s: Could not find \"%s\" section in JSON request", serviceName, methodName, reqestName.str());
+                throw MakeStringException(-1, "EsdlBinding::%s::%s: Could not find \"%s\" section in JSON request", serviceName, methodName, requestName.str());
 
             if (!m_esdl)
             {

+ 1 - 1
esp/services/esdl_svc_engine/esdl_binding.hpp

@@ -172,7 +172,7 @@ public:
     virtual void createServersList(IEspContext &context, IEsdlDefService &srvdef, IEsdlDefMethod &mthdef, StringBuffer &servers) {};
     virtual bool handleResultLogging(IEspContext &espcontext, IPropertyTree * reqcontext, IPropertyTree * request,  const char * rawreq, const char * rawresp, const char * finalresp, const char * logdata);
     void handleEchoTest(const char *mthName, IPropertyTree *req, StringBuffer &soapResp, ESPSerializationFormat format);
-    void handlePingRequest(const char *mthName,StringBuffer &out,ESPSerializationFormat format);
+    void handlePingRequest(const char *srvName, StringBuffer &out, unsigned int flags);
     virtual void handleFinalRequest(IEspContext &context, IEsdlCustomTransform *srvCrt, IEsdlCustomTransform *mthCrt, Owned<IPropertyTree> &tgtcfg, Owned<IPropertyTree> &tgtctx, IEsdlDefService &srvdef, IEsdlDefMethod &mthdef, const char *ns, StringBuffer& req, StringBuffer &out, bool isroxie, bool isproxy, StringBuffer &rawreq);
     void getSoapBody(StringBuffer& out,StringBuffer& soapresp);
     void getSoapError(StringBuffer& out,StringBuffer& soapresp,const char *,const char *);

+ 19 - 1
esp/services/esdl_svc_engine/esdl_svc_custom.cpp

@@ -78,6 +78,18 @@ void CEsdlCustomTransformChoose::processClauses(IPropertyTree *request, IXpathCo
                             request->appendProp(targetField, evaluatedValue.str());
                         }
                     }
+                    catch (IException* e)
+                    {
+                        StringBuffer eMsg;
+                        e->errorMessage(eMsg);
+                        e->Release();
+
+                        VStringBuffer msg("Could not process Custom Transform: '%s' [%s]", cur.queryName(), eMsg.str());
+                        if (!optional)
+                            throw MakeStringException(-1, "%s", msg.str());
+                        else
+                            ERRLOG("%s", msg.str());
+                    }
                     catch (...)
                     {
                         VStringBuffer msg("Could not process Custom Transform: '%s' ", cur.queryName());
@@ -207,9 +219,15 @@ bool CEsdlCustomTransformChoose::evaluate(IXpathContext * xpathContext)
     {
         evalresp = xpathContext->evaluateAsBoolean(m_compiledConditionalXpath.getLink());
     }
+    catch (IException* e)
+    {
+        StringBuffer msg;
+        DBGLOG("%s", e->errorMessage(msg).str());
+        e->Release();
+    }
     catch (...)
     {
-        DBGLOG("CEsdlCustomTransformChoose:evaluate: Could not evaluate xpath '%s'", xpathContext->getXpath());
+        DBGLOG("CEsdlCustomTransformChoose:evaluate: Could not evaluate xpath '%s'", m_compiledConditionalXpath->getXpath());
     }
     return evalresp;
 }

+ 1 - 0
esp/src/eclwatch/FilterDropDownWidget.js

@@ -59,6 +59,7 @@ define([
 
             //  Hitched actions  ---
             _onFilterClear: function (event) {
+                this.emit("clear");
                 this.clear();
             },
 

+ 1 - 1
esp/src/eclwatch/nls/hpcc.js

@@ -375,7 +375,7 @@ define({root:
     Logout: "Log Out",
     Logs: "Logs",
     LogVisualization: "Log Visualization",
-    LogVisualizationUnconfigured: "Log Visualization is not configured, please check your confriguration manager settings.",
+    LogVisualizationUnconfigured: "Log Visualization is not configured, please check your configuration manager settings.",
     log_analysis_1: "log_analysis_1*",
     LostFile: "Lost Files",
     LostFile2: "Lost Files",

+ 1 - 1
esp/src/eclwatch/templates/LockDialogWidget.html

@@ -4,7 +4,7 @@
         <div id="${id}UnlockForm" style="width:${_width}"  onsubmit="return false;" data-dojo-type="dijit.form.Form">
             <div data-dojo-props="cols:2" data-dojo-type="hpcc.TableContainer">
                 <input id="${id}UnlockUserName" title="${i18n.Username}:" name="username" colspan="2" style="width:90%;" data-dojo-props="trim: true, readonly: true" data-dojo-type="dijit.form.TextBox" />
-                <input id="${id}UnlockPassword" type="password" title="${i18n.Password}:" name="password" colspan="2" style="width:90%;" data-dojo-props="trim: true, readonly: false" data-dojo-type="dijit.form.ValidationTextBox" />
+                <input id="${id}UnlockPassword" type="password" title="${i18n.Password}:" name="password" colspan="2" style="width:90%;" data-dojo-props="trim: true, readonly: false" data-dojo-type="dijit.form.ValidationTextBox" autofocus/>
             </div>
             <p id="${id}UnlockStatus" class="unlockStatus"></p>
         </div>

+ 8 - 1
plugins/mysql/mysqlembed.cpp

@@ -675,11 +675,18 @@ public:
     {
         if (stmt && *stmt)
         {
+            // NOTE - we ignore all but the first result from stored procedures
+#if MYSQL_VERSION_ID >= 50500
+            while (mysql_stmt_next_result(*stmt)==0)
+            {
+                // Ignore and discard any additional results for the current row - typically the status result from a stored procedure call
+                mysql_stmt_free_result(*stmt);
+            }
+#endif
             if (inputBindings.numColumns() && mysql_stmt_bind_param(*stmt, inputBindings.queryBindings()))
                 fail(mysql_stmt_error(*stmt));
             if (mysql_stmt_execute(*stmt))
                 fail(mysql_stmt_error(*stmt));
-            // NOTE - we ignore all but the first result from stored procedures
             res.setown(new MySQLResult(mysql_stmt_result_metadata(*stmt)));
             if (*res)
             {

+ 4 - 0
system/jlib/jcomp.cpp

@@ -81,7 +81,11 @@ static const char * LIBFLAG_DEBUG[] = { "/MDd", "" };
 static const char * LIBFLAG_RELEASE[] = { "/MD", "" };
 static const char * COMPILE_ONLY[] = { "/c", "-c" };
 
+#ifdef _DEBUG
 static const char * CC_OPTION_CORE[] = { "", "-fvisibility=hidden -DUSE_VISIBILITY=1 -Werror -Wno-tautological-compare" };
+#else
+static const char * CC_OPTION_CORE[] = { "", "-fvisibility=hidden -DUSE_VISIBILITY=1 -Wno-tautological-compare" };
+#endif
 static const char * LINK_OPTION_CORE[] = { "/DLL /libpath:." , "" };
 static const char * CC_OPTION_DEBUG[] = { "/Zm500 /EHsc /GR /Zi /nologo /bigobj", "-g -fPIC  -O0" };
 static const char * CC_OPTION_RELEASE[] = { "/Zm500 /EHsc /GR /Oi /Ob1 /GF /nologo /bigobj", "-fPIC  -O0" };

+ 2 - 1
system/jlib/jstats.cpp

@@ -1468,7 +1468,8 @@ bool StatsScopeId::setScopeText(const char * text, const char * * _next)
         if (MATCHES_CONST_PREFIX(text, FunctionScopePrefix))
         {
             setFunctionId(text+ strlen(FunctionScopePrefix));
-            *_next = text + strlen(text);
+            if (_next)
+                *_next = text + strlen(text);
             return true;
         }
         break;

+ 81 - 101
system/xmllib/libxml_xpathprocessor.cpp

@@ -34,6 +34,7 @@
 #include <libxml/hash.h>
 
 #include "xpathprocessor.hpp"
+#include "xmlerror.hpp"
 
 class CLibCompiledXpath : public CInterface, public ICompiledXpath
 {
@@ -98,7 +99,7 @@ public:
     virtual bool addVariable(const char * name,  const char * val) override
     {
         WriteLockBlock wblock(m_rwlock);
-        if (m_xpathContext && val && *val)
+        if (m_xpathContext && val)
         {
             return xmlXPathRegisterVariable(m_xpathContext, (xmlChar *)name, xmlXPathNewCString(val)) == 0;
         }
@@ -121,116 +122,31 @@ public:
 
     virtual bool evaluateAsBoolean(const char * xpath)
     {
-        bool bresult = false;
-        if (xpath && *xpath)
-        {
-            xmlXPathObjectPtr evaluatedXpathObj = evaluate(xpath);
-            if (evaluatedXpathObj && evaluatedXpathObj->type == XPATH_BOOLEAN)
-            {
-                bresult = evaluatedXpathObj->boolval;
-            }
-            else
-                throw MakeStringException(-1,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as boolean", m_xpath.str());
-
-            xmlXPathFreeObject(evaluatedXpathObj);
-        }
-        else
-            throw MakeStringException(-1,"XpathProcessor:evaluateAsBoolean: Error: empty xpath provided");
-
-        return bresult;
+        if (!xpath || !*xpath)
+            throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate empty XPATH");
+        return evaluateAsBoolean(evaluate(xpath), xpath);
     }
     virtual bool evaluateAsString(const char * xpath, StringBuffer & evaluated)
     {
-        if (xpath && *xpath)
-        {
-            xmlXPathObjectPtr evaluatedXpathObj = evaluate(xpath);
-            evaluated.clear();
-            if (evaluatedXpathObj && evaluatedXpathObj->type == XPATH_NODESET)
-            {
-                xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
-                for (int i = 0; i < nodes->nodeNr; i++)
-                {
-                    xmlNodePtr nodeTab = nodes->nodeTab[i];
-                    evaluated.append((const char *)xmlNodeGetContent(nodeTab));
-                }
-                xmlXPathFreeObject(evaluatedXpathObj);
-            }
-            else
-                throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH as string");
-        }
-        else
-            throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: empty xpath provided");
-
-        return evaluated.str();
+        if (!xpath || !*xpath)
+            throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate empty XPATH");
+        return evaluateAsString(evaluate(xpath), evaluated, xpath);
     }
 
     virtual bool evaluateAsBoolean(ICompiledXpath * compiledXpath) override
     {
-        bool bresult = false;
         CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
-        xmlXPathObjectPtr evaluatedXpathObj = evaluate(clibCompiledXpath->getCompiledXPathExpression());
-
-        if (evaluatedXpathObj && evaluatedXpathObj->type == XPATH_BOOLEAN)
-        {
-            bresult = evaluatedXpathObj->boolval;
-        }
-        else
-            throw MakeStringException(-1,"XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as boolean", m_xpath.str());
-
-        xmlXPathFreeObject(evaluatedXpathObj);
-        return bresult;
+        if (!clibCompiledXpath)
+            throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsBoolean: Error: Missing compiled XPATH");
+        return evaluateAsBoolean(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), compiledXpath->getXpath());
     }
 
     virtual const char * evaluateAsString(ICompiledXpath * compiledXpath, StringBuffer & evaluated) override
     {
         CLibCompiledXpath * clibCompiledXpath = static_cast<CLibCompiledXpath *>(compiledXpath);
-        xmlXPathObjectPtr evaluatedXpathObj = evaluate(clibCompiledXpath->getCompiledXPathExpression());
-        evaluated.clear();
-        if (evaluatedXpathObj)
-        {
-            switch (evaluatedXpathObj->type)
-            {
-                case XPATH_NODESET:
-                {
-                    xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
-                    for (int i = 0; i < nodes->nodeNr; i++)
-                    {
-                        xmlNodePtr nodeTab = nodes->nodeTab[i];
-                        evaluated.append((const char *)xmlNodeGetContent(nodeTab));
-                    }
-                    break;
-                }
-                case XPATH_BOOLEAN:
-                case XPATH_NUMBER:
-                case XPATH_STRING:
-                case XPATH_POINT:
-                case XPATH_RANGE:
-                case XPATH_LOCATIONSET:
-                case XPATH_USERS:
-                case XPATH_XSLT_TREE:
-                {
-                    evaluatedXpathObj = xmlXPathConvertString (evaluatedXpathObj); //existing object is freed
-                    if (!evaluatedXpathObj)
-                        throw MakeStringException(-1,"XpathProcessor:evaluateAsString: could not convert result to string");
-                    evaluated.append(evaluatedXpathObj->stringval);
-                    break;
-                }
-                default:
-                {
-                    xmlXPathFreeObject(evaluatedXpathObj);
-                    throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: Encountered unsupported XPATH type");
-                    break;
-                }
-             }
-         }
-         else
-         {
-             xmlXPathFreeObject(evaluatedXpathObj);
-             throw MakeStringException(-1,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH as string");
-         }
-
-        xmlXPathFreeObject(evaluatedXpathObj);
-        return evaluated.str();
+        if (!clibCompiledXpath)
+            throw MakeStringException(XPATHERR_MissingInput,"XpathProcessor:evaluateAsString: Error: Missing compiled XPATH");
+        return evaluateAsString(evaluate(clibCompiledXpath->getCompiledXPathExpression(), compiledXpath->getXpath()), evaluated, compiledXpath->getXpath());
     }
 
 private:
@@ -257,7 +173,71 @@ private:
         return false;
     }
 
-    virtual xmlXPathObjectPtr evaluate(xmlXPathCompExprPtr compiledXpath)
+    bool evaluateAsBoolean(xmlXPathObjectPtr evaluatedXpathObj, const char* xpath)
+    {
+        if (!evaluatedXpathObj)
+        {
+            throw MakeStringException(XPATHERR_InvalidInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s'", xpath);
+        }
+        if (XPATH_BOOLEAN != evaluatedXpathObj->type)
+        {
+            xmlXPathFreeObject(evaluatedXpathObj);
+            throw MakeStringException(XPATHERR_UnexpectedInput, "XpathProcessor:evaluateAsBoolean: Error: Could not evaluate XPATH '%s' as Boolean", xpath);
+        }
+
+        bool bresult = evaluatedXpathObj->boolval;
+
+        xmlXPathFreeObject(evaluatedXpathObj);
+        return bresult;
+    }
+
+    const char* evaluateAsString(xmlXPathObjectPtr evaluatedXpathObj, StringBuffer& evaluated, const char* xpath)
+    {
+        if (!evaluatedXpathObj)
+            throw MakeStringException(XPATHERR_InvalidInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'", xpath);
+
+        evaluated.clear();
+        switch (evaluatedXpathObj->type)
+        {
+            case XPATH_NODESET:
+            {
+                xmlNodeSetPtr nodes = evaluatedXpathObj->nodesetval;
+                for (int i = 0; i < nodes->nodeNr; i++)
+                {
+                    xmlNodePtr nodeTab = nodes->nodeTab[i];
+                    auto nodeContent = xmlNodeGetContent(nodeTab);
+                    evaluated.append((const char *)nodeContent);
+                    xmlFree(nodeContent);
+                }
+                break;
+            }
+            case XPATH_BOOLEAN:
+            case XPATH_NUMBER:
+            case XPATH_STRING:
+            case XPATH_POINT:
+            case XPATH_RANGE:
+            case XPATH_LOCATIONSET:
+            case XPATH_USERS:
+            case XPATH_XSLT_TREE:
+            {
+                evaluatedXpathObj = xmlXPathConvertString (evaluatedXpathObj); //existing object is freed
+                if (!evaluatedXpathObj)
+                    throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s'; could not convert result to string", xpath);
+                evaluated.append(evaluatedXpathObj->stringval);
+                break;
+            }
+            default:
+            {
+                xmlXPathFreeObject(evaluatedXpathObj);
+                throw MakeStringException(XPATHERR_UnexpectedInput,"XpathProcessor:evaluateAsString: Error: Could not evaluate XPATH '%s' as string; unexpected type %d", xpath, evaluatedXpathObj->type);
+                break;
+            }
+        }
+        xmlXPathFreeObject(evaluatedXpathObj);
+        return evaluated.str();
+    }
+
+    virtual xmlXPathObjectPtr evaluate(xmlXPathCompExprPtr compiledXpath, const char* xpath)
     {
         xmlXPathObjectPtr evaluatedXpathObj = nullptr;
         if (compiledXpath)
@@ -269,7 +249,7 @@ private:
             }
             else
             {
-                throw MakeStringException(-1,"XpathProcessor:evaluate: Error: Invalid xpathCotext detected. Ensure xmldoc has been set");
+                throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'; ensure xmldoc has been set", xpath);
             }
         }
 
@@ -288,7 +268,7 @@ private:
             }
             else
             {
-                throw MakeStringException(-1,"XpathProcessor:evaluate: Error: Invalid xpathCotext detected. Ensure xmldoc has been set");
+                throw MakeStringException(XPATHERR_InvalidState,"XpathProcessor:evaluate: Error: Could not evaluate XPATH '%s'; ensure xmldoc has been set", xpath);
             }
         }
 

+ 8 - 0
system/xmllib/xmlerror.hpp

@@ -41,4 +41,12 @@
 #define XSLERR_ExternalFunctionIncompatible             5658
 #define XSLERR_ExtenrnalFunctionMultipleURIs            5659
 
+
+
+#define XPATHERR_InvalidState                           5680
+#define XPATHERR_MissingInput                           5681
+#define XPATHERR_InvalidInput                           5682
+#define XPATHERR_UnexpectedInput                        5683
+
+
 #endif

+ 4 - 0
testing/regress/ecl/key/mysqlembed.xml

@@ -72,3 +72,7 @@
 <Dataset name='Result 20'>
  <Row><name>name1</name><bval>16737</bval><value>1</value><boolval>true</boolval><r8>1.2</r8><r4>3.400000095367432</r4><d>6161353561613535</d><ddd>1234567.89</ddd><u1>Straße</u1><u2>Straße  </u2><dt>1963-11-22 12:30:00</dt></Row>
 </Dataset>
+<Dataset name='Result 20'>
+ <Row><name>name1</name><bval>16737</bval><value>1</value><boolval>true</boolval><r8>1.2</r8><r4>3.400000095367432</r4><d>6161353561613535</d><ddd>1234567.89</ddd><u1>Straße</u1><u2>Straße  </u2><dt>1963-11-22 12:30:00</dt></Row>
+ <Row><name>name2</name><bval>66</bval><value>2</value><boolval>false</boolval><r8>5.6</r8><r4>7.800000190734863</r4><d>3030</d><ddd>-1234567.89</ddd><u1>là</u1><u2>là      </u2><dt>2015-12-25 01:23:45</dt></Row>
+</Dataset>

+ 6 - 1
testing/regress/ecl/mysqlembed.ecl

@@ -148,6 +148,10 @@ streamed dataset(childrec) testMySQLStoredProcedure2(STRING lid) := EMBED(mysql
   CALL testSP(?);
 ENDEMBED;
 
+streamed dataset(childrec) testMySQLStoredProcedure3(DATASET(stringrec) lids) := EMBED(mysql : server(myServer),user(myUser),database(myDB))
+  CALL testSP(?);
+ENDEMBED;
+
 dataset(childrec) testMySQLStringParam(string filter) := EMBED(mysql : server(myServer),user(myUser),database(myDB))
   SELECT * from tbl1 where name = ?;
 ENDEMBED;
@@ -220,6 +224,7 @@ sequential (
       OUTPUT(testMySQLDateTime()),
       OUTPUT(testMySQLTransform()),
       OUTPUT(testMySQLStoredProcedure()),
-      OUTPUT(testMySQLStoredProcedure2('name1'))
+      OUTPUT(testMySQLStoredProcedure2('name1')),
+      OUTPUT(testMySQLStoredProcedure3(DATASET([{'name1'},{'name2'}], stringrec)))
   )
 );