فهرست منبع

HPCC-8845 Support XMLNS for workunit and roxie xml outputs

Add a new "XMLNS" option to ECL output for declaring the URI for
xml namespace prefixes.

Example:

OUTPUT(ds, NAMED(''), XMLNS('ns1', 'urn:mynamspace:uri'));

Signed-off-by: Anthony Fishbeck <anthony.fishbeck@lexisnexis.com>
Anthony Fishbeck 11 سال پیش
والد
کامیت
381158240e

+ 4 - 4
common/fileview2/fileview.hpp

@@ -189,11 +189,11 @@ extern FILEVIEW_API IResultSetFactory * getSecRemoteResultSetFactory(const char
 extern FILEVIEW_API IResultSetFactory * getRemoteResultSetFactory(const char * remoteServer, const char * username, const char * password);
 extern FILEVIEW_API int findResultSetColumn(const INewResultSet * results, const char * columnName);
 
-extern FILEVIEW_API unsigned getResultCursorXml(IStringVal & ret, IResultSetCursor * cursor, const char * name, unsigned start=0, unsigned count=0, const char * schemaName=NULL);
-extern FILEVIEW_API unsigned getResultXml(IStringVal & ret, INewResultSet * cursor,  const char* name, unsigned start=0, unsigned count=0, const char * schemaName=NULL);
+extern FILEVIEW_API unsigned getResultCursorXml(IStringVal & ret, IResultSetCursor * cursor, const char * name, unsigned start=0, unsigned count=0, const char * schemaName=NULL, IProperties *xmlns=NULL);
+extern FILEVIEW_API unsigned getResultXml(IStringVal & ret, INewResultSet * cursor,  const char* name, unsigned start=0, unsigned count=0, const char * schemaName=NULL, IProperties *xmlns=NULL);
 extern FILEVIEW_API unsigned getResultJSON(IStringVal & ret, INewResultSet * cursor,  const char* name, unsigned start=0, unsigned count=0, const char * schemaName=NULL);
-extern FILEVIEW_API unsigned writeResultCursorXml(IXmlWriter & writer, IResultSetCursor * cursor, const char * name, unsigned start=0, unsigned count=0, const char * schemaName=NULL);
-extern FILEVIEW_API unsigned writeResultXml(IXmlWriter & writer, INewResultSet * cursor,  const char* name, unsigned start=0, unsigned count=0, const char * schemaName=NULL);
+extern FILEVIEW_API unsigned writeResultCursorXml(IXmlWriter & writer, IResultSetCursor * cursor, const char * name, unsigned start=0, unsigned count=0, const char * schemaName=NULL, IProperties *xmlns = NULL);
+extern FILEVIEW_API unsigned writeResultXml(IXmlWriter & writer, INewResultSet * cursor,  const char* name, unsigned start=0, unsigned count=0, const char * schemaName=NULL, IProperties *xmlns = NULL);
 
 extern FILEVIEW_API unsigned getResultCursorBin(MemoryBuffer & ret, IResultSetCursor * cursor, unsigned start=0, unsigned count=0);
 extern FILEVIEW_API unsigned getResultBin(MemoryBuffer & ret, INewResultSet * cursor, unsigned start=0, unsigned count=0);

+ 18 - 9
common/fileview2/fvresultset.cpp

@@ -3168,19 +3168,19 @@ int findResultSetColumn(const INewResultSet * results, const char * columnName)
 }
 
 
-extern FILEVIEW_API unsigned getResultCursorXml(IStringVal & ret, IResultSetCursor * cursor, const char * name, unsigned start, unsigned count, const char * schemaName)
+extern FILEVIEW_API unsigned getResultCursorXml(IStringVal & ret, IResultSetCursor * cursor, const char * name, unsigned start, unsigned count, const char * schemaName, IProperties *xmlns)
 {
     Owned<CommonXmlWriter> writer = CreateCommonXmlWriter(XWFexpandempty);
-    unsigned rc = writeResultCursorXml(*writer, cursor, name, start, count, schemaName);
+    unsigned rc = writeResultCursorXml(*writer, cursor, name, start, count, schemaName, xmlns);
     ret.set(writer->str());
     return rc;
 
 }
 
-extern FILEVIEW_API unsigned getResultXml(IStringVal & ret, INewResultSet * result, const char* name,unsigned start, unsigned count, const char * schemaName)
+extern FILEVIEW_API unsigned getResultXml(IStringVal & ret, INewResultSet * result, const char* name,unsigned start, unsigned count, const char * schemaName, IProperties *xmlns)
 {
     Owned<IResultSetCursor> cursor = result->createCursor();
-    return getResultCursorXml(ret, cursor, name, start, count, schemaName);
+    return getResultCursorXml(ret, cursor, name, start, count, schemaName, xmlns);
 }
 
 extern FILEVIEW_API unsigned getResultJSON(IStringVal & ret, INewResultSet * result, const char* name,unsigned start, unsigned count, const char * schemaName)
@@ -3194,7 +3194,7 @@ extern FILEVIEW_API unsigned getResultJSON(IStringVal & ret, INewResultSet * res
     return rc;
 }
 
-extern FILEVIEW_API unsigned writeResultCursorXml(IXmlWriter & writer, IResultSetCursor * cursor, const char * name, unsigned start, unsigned count, const char * schemaName)
+extern FILEVIEW_API unsigned writeResultCursorXml(IXmlWriter & writer, IResultSetCursor * cursor, const char * name, unsigned start, unsigned count, const char * schemaName, IProperties *xmlns)
 {
     if (schemaName)
     {
@@ -3210,7 +3210,15 @@ extern FILEVIEW_API unsigned writeResultCursorXml(IXmlWriter & writer, IResultSe
     writer.outputBeginDataset(name, true);
     if (schemaName)
         writer.outputCString(schemaName, "@xmlSchema");
-
+    if (xmlns)
+    {
+        Owned<IPropertyIterator> it = xmlns->getIterator();
+        ForEach(*it)
+        {
+            const char *name = it->getPropKey();
+            writer.outputXmlns(name,xmlns->queryProp(name));
+        }
+    }
     cursor->beginWriteXmlRows(writer);
     unsigned c=0;
     for(bool ok=cursor->absolute(start);ok;ok=cursor->next())
@@ -3226,10 +3234,10 @@ extern FILEVIEW_API unsigned writeResultCursorXml(IXmlWriter & writer, IResultSe
     return c;
 }
 
-extern FILEVIEW_API unsigned writeResultXml(IXmlWriter & writer, INewResultSet * result, const char* name,unsigned start, unsigned count, const char * schemaName)
+extern FILEVIEW_API unsigned writeResultXml(IXmlWriter & writer, INewResultSet * result, const char* name,unsigned start, unsigned count, const char * schemaName, IProperties *xmlns)
 {
     Owned<IResultSetCursor> cursor = result->createCursor();
-    return writeResultCursorXml(writer, cursor, name, start, count, schemaName);
+    return writeResultCursorXml(writer, cursor, name, start, count, schemaName, xmlns);
 }
 
 extern FILEVIEW_API unsigned getResultCursorBin(MemoryBuffer & ret, IResultSetCursor * cursor, unsigned start, unsigned count)
@@ -3313,7 +3321,8 @@ extern FILEVIEW_API void writeFullWorkUnitResults(const char *username, const ch
                     SCMStringBuffer name;
                     ds.getResultName(name);
                     Owned<INewResultSet> nr = factory->createNewResultSet(&ds, wuid.str());
-                    writeResultXml(writer, nr.get(), name.str(), 0, 0, (flags & WorkUnitXML_InclSchema) ? name.str() : NULL);
+                    IProperties *xmlns = ds.queryXmlns();
+                    writeResultXml(writer, nr.get(), name.str(), 0, 0, (flags & WorkUnitXML_InclSchema) ? name.str() : NULL, xmlns);
                 }
             }
         }

+ 4 - 0
common/thorhelper/roxiedebug.cpp

@@ -527,6 +527,10 @@ public:
     {
         // nothing for now
     }
+    virtual void outputXmlns(const char *prefix, const char *uri)
+    {
+        // nothing for now
+    }
 };
 
 class ContainsFieldSearcher : public SimpleFieldSearcher

+ 10 - 1
common/thorhelper/roxiehelper.cpp

@@ -996,7 +996,7 @@ void *FlushingStringBuffer::getPayload(size32_t &length)
     return length ? s.detach() : NULL;
 }
 
-void FlushingStringBuffer::startDataset(const char *elementName, const char *resultName, unsigned sequence, bool _extend)
+void FlushingStringBuffer::startDataset(const char *elementName, const char *resultName, unsigned sequence, bool _extend, IProperties *xmlns)
 {
     CriticalBlock b(crit);
     extend = _extend;
@@ -1017,6 +1017,15 @@ void FlushingStringBuffer::startDataset(const char *elementName, const char *res
                         s.appendLower(strlen(resultName), resultName).append('\'');
                     else
                         s.append("result_").append(sequence+1).append('\'');
+                    if (xmlns)
+                    {
+                        Owned<IPropertyIterator> it = xmlns->getIterator();
+                        ForEach(*it)
+                        {
+                            const char *name = it->getPropKey();
+                            s.append(" xmlns:").append(name).append("='").append(xmlns->queryProp(name)).append("'");
+                        }
+                    }
                 }
                 if (resultName && *resultName)
                     s.appendf(" name='%s'",resultName);

+ 1 - 1
common/thorhelper/roxiehelper.hpp

@@ -160,7 +160,7 @@ public:
     virtual void flush(bool closing) ;
     virtual void addPayload(StringBuffer &s, unsigned int reserve=0);
     virtual void *getPayload(size32_t &length);
-    virtual void startDataset(const char *elementName, const char *resultName, unsigned sequence, bool _extend = false);
+    virtual void startDataset(const char *elementName, const char *resultName, unsigned sequence, bool _extend = false, IProperties *xmlns=NULL);
     virtual void startScalar(const char *resultName, unsigned sequence);
     virtual void incrementRowCount();
 };

+ 6 - 0
common/thorhelper/thorxmlwrite.cpp

@@ -241,6 +241,12 @@ void CommonXmlWriter::outputUtf8(unsigned len, const char *field, const char *fi
     }
 }
 
+void CommonXmlWriter::outputXmlns(const char *name, const char *uri)
+{
+    StringBuffer fieldname("xmlns:");
+    outputXmlAttrString(strlen(uri), uri, fieldname.append(name), out);
+}
+
 void CommonXmlWriter::outputBeginDataset(const char *dsname, bool nestChildren)
 {
     outputBeginNested("Dataset", nestChildren, false); //indent row, not dataset for backward compatibility

+ 4 - 0
common/thorhelper/thorxmlwrite.hpp

@@ -71,6 +71,7 @@ public:
     virtual void outputBeginArray(const char *fieldname){}; //repeated elements are inline for xml
     virtual void outputEndArray(const char *fieldname){};
     virtual void outputSetAll();
+    virtual void outputXmlns(const char *name, const char *uri);
 
 protected:
     bool checkForAttribute(const char * fieldname);
@@ -127,6 +128,7 @@ public:
     virtual void outputBeginArray(const char *fieldname);
     virtual void outputEndArray(const char *fieldname);
     virtual void outputSetAll();
+    virtual void outputXmlns(const char *name, const char *uri){}
 
     void outputBeginRoot(){out.append('{');}
     void outputEndRoot(){out.append('}');}
@@ -224,6 +226,8 @@ public:
     virtual void outputEndArray(const char *fieldname){}
     virtual void outputSetAll();
     virtual void outputInlineXml(const char *text){} //for appending raw xml content
+    virtual void outputXmlns(const char *name, const char *uri){}
+
 
 
     void newline();

+ 30 - 0
common/workunit/workunit.cpp

@@ -1570,6 +1570,7 @@ class CLocalWUResult : public CInterface, implements IWUResult
     friend class CLocalWorkUnit;
 
     Owned<IPropertyTree> p;
+    Owned<IProperties> xmlns;
     void getSchema(TypeInfoArray &types, StringAttrArray &names, IStringVal * ecl=NULL) const;
 
 public:
@@ -1607,6 +1608,7 @@ public:
     virtual WUResultFormat getResultFormat() const;
     virtual unsigned    getResultHash() const;
     virtual bool        getResultIsAll() const;
+    virtual IProperties *queryXmlns();
 
     // interface IWUResult
     virtual void        setResultStatus(WUResultStatus status);
@@ -1639,6 +1641,7 @@ public:
     virtual void        setResultFormat(WUResultFormat format);
     virtual void        setResultXML(const char *val);
     virtual void        setResultRow(unsigned len, const void * data);
+    virtual void        setXmlns(const char *prefix, const char *uri);
 };
 
 class CLocalWUPlugin : public CInterface, implements IWUPlugin
@@ -3501,6 +3504,7 @@ void CLocalWorkUnit::loadXML(const char *xml)
 {
     CriticalBlock block(crit);
     init();
+    DBGLOG("%s",xml);
     assertex(xml);
     p.setown(createPTreeFromXMLString(xml));
 }
@@ -6034,6 +6038,7 @@ void CLocalWorkUnit::loadResults() const
         for (r->first(); r->isValid(); r->next())
         {
             IPropertyTree *rp = &r->query();
+            const char *xmlns = rp->queryProp("@xmlns:yyy");
 
             rp->Link();
             results.append(*new CLocalWUResult(rp));
@@ -7802,6 +7807,24 @@ IStringVal& CLocalWUResult::getResultXml(IStringVal &str) const
     return str;
 }
 
+IProperties *CLocalWUResult::queryXmlns()
+{
+    if (xmlns)
+        return xmlns;
+    xmlns.setown(createProperties());
+    Owned<IAttributeIterator> it = p->getAttributes();
+    unsigned prefixLen = strlen("@xmlns:");
+    ForEach(*it)
+    {
+        const char *name = it->queryName();
+        if (!strncmp("@xmlns:", name, prefixLen))
+        {
+            xmlns->setProp(name+prefixLen, it->queryValue());
+        }
+    }
+    return xmlns;
+}
+
 unsigned CLocalWUResult::getResultFetchSize() const
 {
     return p->getPropInt("fetchSize", 100);
@@ -7890,6 +7913,13 @@ void CLocalWUResult::setResultSchemaRaw(unsigned size, const void *schema)
 {
     p->setPropBin("SchemaRaw", size, schema);
 }
+void CLocalWUResult::setXmlns(const char *prefix, const char *uri)
+{
+    StringBuffer xpath("@xmlns");
+    if (prefix && *prefix)
+        xpath.append(':').append(prefix);
+    p->setProp(xpath, uri);
+}
 void CLocalWUResult::setResultScalar(bool isScalar)
 {
     p->setPropInt("@isScalar", (int) isScalar);

+ 3 - 0
common/workunit/workunit.hpp

@@ -37,6 +37,7 @@
 #include "jtime.hpp"
 #include "jsocket.hpp"
 #include "jstats.h"
+#include "jprop.hpp"
 
 #define CHEAP_UCHAR_DEF
 #ifdef _WIN32
@@ -300,6 +301,7 @@ interface IConstWUResult : extends IInterface
     virtual unsigned getResultHash() const = 0;
     virtual void getResultDecimal(void * val, unsigned length, unsigned precision, bool isSigned) const = 0;
     virtual bool getResultIsAll() const = 0;
+    virtual IProperties *queryXmlns() = 0;
 };
 
 
@@ -335,6 +337,7 @@ interface IWUResult : extends IConstWUResult
     virtual void setResultFormat(WUResultFormat format) = 0;
     virtual void setResultXML(const char * xml) = 0;
     virtual void setResultRow(unsigned len, const void * data) = 0;
+    virtual void setXmlns(const char *prefix, const char *uri) = 0;
 };
 
 

+ 2 - 0
ecl/hql/hqlatoms.cpp

@@ -421,6 +421,7 @@ IAtom * workunitAtom;
 IAtom * wuidAtom;
 IAtom * xmlAtom;
 IAtom * xmlDefaultAtom;
+IAtom * xmlnsAtom;
 IAtom * _xmlParse_Atom;
 IAtom * xpathAtom;
 
@@ -835,6 +836,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(wuid);
     MAKEATOM(xml);
     MAKEATOM(xmlDefault);
+    MAKEATOM(xmlns);
     MAKESYSATOM(xmlParse);
     MAKEATOM(xpath);
 

+ 1 - 0
ecl/hql/hqlatoms.hpp

@@ -425,6 +425,7 @@ extern HQL_API IAtom * workunitAtom;
 extern HQL_API IAtom * wuidAtom;
 extern HQL_API IAtom * xmlAtom;
 extern HQL_API IAtom * xmlDefaultAtom;
+extern HQL_API IAtom * xmlnsAtom;
 extern HQL_API IAtom * _xmlParse_Atom;
 extern HQL_API IAtom * xpathAtom;
 

+ 6 - 20
ecl/hql/hqlexpr.cpp

@@ -589,31 +589,17 @@ extern HQL_API void gatherMetaAttributes(HqlExprArray & matches, IAtom * search,
     }
 }
 
-extern HQL_API void gatherMetaAttributes(HqlExprCopyArray & matches, IAtom * search, IHqlExpression * expr)
+extern HQL_API void gatherAttributes(HqlExprArray & matches, IAtom * search, IHqlExpression * expr)
 {
-    loop
+    unsigned kids = expr->numChildren();
+    for (unsigned i = 0; i < kids; i++)
     {
-        annotate_kind kind = expr->getAnnotationKind();
-        if (kind == annotate_none)
-            return;
-        if (kind == annotate_meta)
-        {
-            unsigned i=0;
-            IHqlExpression * cur;
-            while ((cur = expr->queryAnnotationParameter(i++)) != NULL)
-            {
-                //It's possible we may want to implement this whole function as a member function and allow
-                //information to be stored in a non expression format, and only create expressions when requested.
-                //may end up less efficient in the end.
-                if (cur->queryName() == search && cur->isAttribute())
-                    matches.append(*cur);
-            }
-        }
-        expr = expr->queryBody(true);
+        IHqlExpression *kid = expr->queryChild(i);
+        if (kid->isAttribute() && kid->queryName()==search)
+            matches.append(*LINK(kid));
     }
 }
 
-
 extern HQL_API IHqlExpression * queryLocation(IHqlExpression * expr)
 {
     IHqlExpression * best = NULL;

+ 1 - 0
ecl/hql/hqlexpr.hpp

@@ -1451,6 +1451,7 @@ extern HQL_API IHqlExpression * queryAttributeInList(IAtom * search, IHqlExpress
 extern HQL_API IHqlExpression * queryAttribute(IAtom * search, const HqlExprArray & exprs);
 extern HQL_API IHqlExpression * queryAnnotation(IHqlExpression * expr, annotate_kind search);       // return first match
 extern HQL_API IHqlNamedAnnotation * queryNameAnnotation(IHqlExpression * expr);
+extern HQL_API void gatherAttributes(HqlExprArray & matches, IAtom * search, IHqlExpression * expr);
 
 inline bool hasAnnotation(IHqlExpression * expr, annotate_kind search){ return queryAnnotation(expr, search) != NULL; }
 inline IHqlExpression * queryNamedSymbol(IHqlExpression * expr) { return queryAnnotation(expr, annotate_symbol); }

+ 9 - 1
ecl/hql/hqlgram.y

@@ -461,6 +461,7 @@ static void eclsyntaxerror(HqlGram * parser, const char * s, short yystate, int
   XMLDECODE
   XMLDEFAULT
   XMLENCODE
+  XMLNS
   XMLPROJECT
   XMLTEXT
   XMLUNICODE
@@ -3467,12 +3468,19 @@ outputWuFlag
                             $$.setExpr(createAttribute(allAtom));
                             $$.setPosition($1);
                         }
+    | XMLNS '(' expression ',' expression ')'
+                        {
+                            parser->normalizeExpression($3, type_string, true);
+                            parser->normalizeExpression($5, type_string, true);
+                            $$.setExpr(createAttribute(xmlnsAtom, $3.getExpr(), $5.getExpr()));
+                            $$.setPosition($1);
+                        }
     | FIRST '(' constExpression ')'
                         {
                             parser->normalizeExpression($3, type_int, true);
                             $$.setExpr(createAttribute(firstAtom, $3.getExpr()));
                             $$.setPosition($1);
-                        }
+                2        }
     | THOR              {
                             $$.setExpr(createAttribute(diskAtom));
                             $$.setPosition($1);

+ 1 - 0
ecl/hql/hqlgram2.cpp

@@ -10609,6 +10609,7 @@ static void getTokenText(StringBuffer & msg, int token)
     case XMLDECODE: msg.append("XMLDECODE"); break;
     case XMLDEFAULT: msg.append("XMLDEFAULT"); break;
     case XMLENCODE: msg.append("XMLENCODE"); break;
+    case XMLNS: msg.append("XMLNS"); break;
     case XMLPROJECT: msg.append("XMLPROJECT"); break;
     case XMLTEXT: msg.append("XMLTEXT"); break;
     case XMLUNICODE: msg.append("XMLUNICODE"); break;

+ 1 - 0
ecl/hql/hqllex.l

@@ -944,6 +944,7 @@ XML                 { RETURNSYM(XML_TOKEN); }
 XMLDECODE           { RETURNSYM(XMLDECODE); }
 XMLDEFAULT          { RETURNSYM(XMLDEFAULT); }
 XMLENCODE           { RETURNSYM(XMLENCODE); }
+XMLNS               { RETURNSYM(XMLNS); }
 XMLPROJECT          { RETURNSYM(XMLPROJECT); }
 XMLTEXT             { RETURNSYM(XMLTEXT); }
 XMLUNICODE          { RETURNSYM(XMLUNICODE); }

+ 1 - 1
ecl/hqlcpp/hqlcpp.ipp

@@ -1803,7 +1803,7 @@ protected:
     void addSchemaResource(int seq, const char * name, IHqlExpression * record);
     void addSchemaResource(int seq, const char * name, unsigned len, const char * schemaXml);
     void doAddSchemaFields(IHqlExpression * record, MemoryBuffer &schema, IHqlExpression *selector);
-    IWUResult * createDatasetResultSchema(IHqlExpression * sequenceExpr, IHqlExpression * name, IHqlExpression * record, bool createTransformer, bool isFile);
+    IWUResult * createDatasetResultSchema(IHqlExpression * sequenceExpr, IHqlExpression * name, IHqlExpression * record, HqlExprArray &xmlnsAttrs, bool createTransformer, bool isFile);
 
     void buildReturnCsvValue(BuildCtx & ctx, IHqlExpression * _expr);
     void buildCsvListFunc(BuildCtx & classctx, const char * func, IHqlExpression * value, const char * defaultValue);

+ 115 - 8
ecl/hqlcpp/hqlhtcpp.cpp

@@ -1683,6 +1683,7 @@ ActivityInstance::ActivityInstance(HqlCppTranslator & _translator, BuildCtx & ct
         outputDataset = dataset->queryChild(0);
 
     bool removeXpath = dataset->hasAttribute(noXpathAtom) || (op == no_output && translator.queryOptions().removeXpathFromOutput);
+
     LinkedHqlExpr record = queryRecord(outputDataset);
     if (removeXpath)
         record.setown(removeAttributeFromFields(record, xpathAtom));
@@ -5027,6 +5028,94 @@ IWUResult * HqlCppTranslator::createWorkunitResult(int sequence, IHqlExpression
     return result.getClear();
 }
 
+void getXpathAttrPrefixes(StringArray &prefixes, IHqlExpression *xpathAttr)
+{
+    if (!xpathAttr)
+        return;
+    StringBuffer xpathtext;
+    if (xpathAttr->queryChild(0)->queryValue())
+        xpathAttr->queryChild(0)->queryValue()->getStringValue(xpathtext);
+    if (!xpathtext.length())
+        return;
+    const char *xpath=xpathtext.str();
+    const char *colon = strchr(xpath, ':');
+    while (colon)
+    {
+        const char *finger=colon-1;
+        while (finger>xpath && !strchr("/[ ", *(finger-1)))
+            finger--;
+        StringAttr prefix(finger, colon - finger);
+        if (prefix.length() && prefixes.find(prefix.get())==NotFound)
+            prefixes.append(prefix);
+        colon = strchr(colon+1, ':');
+    }
+}
+
+void gatherXpathPrefixes(StringArray &prefixes, IHqlExpression * record)
+{
+    ForEachChild(i, record)
+    {
+        IHqlExpression * cur = record->queryChild(i);
+        switch (cur->getOperator())
+        {
+        case no_field:
+        {
+            getXpathAttrPrefixes(prefixes, cur->queryAttribute(xpathAtom));
+
+            ITypeInfo * type = cur->queryType();
+            switch (type->getTypeCode())
+            {
+            case type_row:
+            case type_dictionary:
+            case type_table:
+            case type_groupedtable:
+                gatherXpathPrefixes(prefixes, cur->queryRecord());
+                break;
+            }
+            break;
+        }
+        case no_ifblock:
+        case no_record:
+            gatherXpathPrefixes(prefixes, cur->queryChild(1));
+            break;
+        }
+    }
+}
+
+void addDatasetResultXmlNamespaces(IWUResult &result, HqlExprArray &xmlnsAttrs, IHqlExpression *record)
+{
+    StringArray declaredPrefixes;
+    ForEachItemIn(idx, xmlnsAttrs)
+    {
+        IHqlExpression & xmlns = xmlnsAttrs.item(idx);
+        StringBuffer xmlnsPrefix;
+        StringBuffer xmlnsURI;
+        if (xmlns.queryChild(0)->queryValue())
+            xmlns.queryChild(0)->queryValue()->getStringValue(xmlnsPrefix);
+        if (xmlns.queryChild(1)->queryValue())
+            xmlns.queryChild(1)->queryValue()->getStringValue(xmlnsURI);
+        if (xmlnsURI.length())
+        {
+            result.setXmlns(xmlnsPrefix, xmlnsURI);
+            if (xmlnsPrefix.length() && declaredPrefixes.find(xmlnsPrefix)==NotFound)
+                declaredPrefixes.append(xmlnsPrefix);
+        }
+    }
+    StringArray usedPrefixes;
+    if (record)
+        gatherXpathPrefixes(usedPrefixes, record);
+    ForEachItemIn(p, usedPrefixes)
+    {
+        const char *prefix = usedPrefixes.item(p);
+        if (declaredPrefixes.find(prefix)==NotFound)
+        {
+            StringBuffer uri("urn:hpccsystems:ecl:unknown:");
+            uri.append(prefix);
+            result.setXmlns(prefix, uri);
+        }
+    }
+}
+
 void HqlCppTranslator::buildSetResultInfo(BuildCtx & ctx, IHqlExpression * originalExpr, IHqlExpression * value, ITypeInfo * type, bool isPersist, bool associateResult)
 {
     IHqlExpression * seq = queryAttributeChild(originalExpr, sequenceAtom, 0);
@@ -5185,9 +5274,11 @@ void HqlCppTranslator::buildSetResultInfo(BuildCtx & ctx, IHqlExpression * origi
 
     if(wu())
     {
+        HqlExprArray xmlnsAttrs;
+        gatherAttributes(xmlnsAttrs, xmlnsAtom, originalExpr);
         if (retType == type_row)
         {
-            Owned<IWUResult> result = createDatasetResultSchema(seq, name, ::queryRecord(schemaType), false, false);
+            Owned<IWUResult> result = createDatasetResultSchema(seq, name, ::queryRecord(schemaType), xmlnsAttrs, false, false);
             if (result)
                 result->setResultTotalRowCount(1);
         }
@@ -5211,6 +5302,8 @@ void HqlCppTranslator::buildSetResultInfo(BuildCtx & ctx, IHqlExpression * origi
                         fieldName.append('_');
                 }
 
+                addDatasetResultXmlNamespaces(*result, xmlnsAttrs, NULL);
+
                 MemoryBuffer schema;
                 schema.append(fieldName.str());
                 schemaType->serialize(schema);
@@ -7559,7 +7652,9 @@ void HqlCppTranslator::doBuildStmtSetResult(BuildCtx & ctx, IHqlExpression * exp
                 args.append(*createValue(no_translated, makeSetType(NULL), createValue(no_nullptr, makeSetType(NULL)), getSizetConstant(0)));
                 args.append(*createTranslatedOwned(createValue(no_nullptr, makeBoolType())));
                 buildFunctionCall(subctx, setResultSetId, args);
-                Owned<IWUResult> result = createDatasetResultSchema(seq, name, value->queryRecord(), true, false);
+                HqlExprArray xmlnsAttrs;
+                gatherAttributes(xmlnsAttrs, xmlnsAtom, expr);
+                Owned<IWUResult> result = createDatasetResultSchema(seq, name, value->queryRecord(), xmlnsAttrs, true, false);
                 break;
             }
         default:
@@ -10104,7 +10199,9 @@ ABoundActivity * HqlCppTranslator::doBuildActivityOutputIndex(BuildCtx & ctx, IH
     buildRecordEcl(instance->createctx, dataset, "queryRecordECL");
 
     doBuildSequenceFunc(instance->classctx, querySequence(expr), false);
-    Owned<IWUResult> result = createDatasetResultSchema(querySequence(expr), queryResultName(expr), dataset->queryRecord(), false, true);
+    HqlExprArray xmlnsAttrs;
+    gatherAttributes(xmlnsAttrs, xmlnsAtom, expr);
+    Owned<IWUResult> result = createDatasetResultSchema(querySequence(expr), queryResultName(expr), dataset->queryRecord(), xmlnsAttrs, false, true);
 
     if (expr->hasAttribute(setAtom))
     {
@@ -10419,7 +10516,9 @@ ABoundActivity * HqlCppTranslator::doBuildActivityOutput(BuildCtx & ctx, IHqlExp
 
         IHqlExpression * outputRecord = instance->meta.queryRecord();
         OwnedHqlExpr outputDs = createDataset(no_null, LINK(outputRecord));
-        Owned<IWUResult> result = createDatasetResultSchema(seq, queryResultName(expr), outputRecord, (kind != TAKcsvwrite) && (kind != TAKxmlwrite), true);
+        HqlExprArray xmlnsAttrs;
+        gatherAttributes(xmlnsAttrs, xmlnsAtom, expr);
+        Owned<IWUResult> result = createDatasetResultSchema(seq, queryResultName(expr), outputRecord, xmlnsAttrs, (kind != TAKcsvwrite) && (kind != TAKxmlwrite), true);
         if (expr->hasAttribute(resultAtom))
             result->setResultRowLimit(-1);
 
@@ -10586,8 +10685,7 @@ void HqlCppTranslator::finalizeResources()
 {
 }
 
-
-IWUResult * HqlCppTranslator::createDatasetResultSchema(IHqlExpression * sequenceExpr, IHqlExpression * name, IHqlExpression * record, bool createTransformer, bool isFile)
+IWUResult * HqlCppTranslator::createDatasetResultSchema(IHqlExpression * sequenceExpr, IHqlExpression * name, IHqlExpression * record, HqlExprArray &xmlnsAttrs, bool createTransformer, bool isFile)
 {
     //Some spills have no sequence attached
     if (!sequenceExpr)
@@ -10598,6 +10696,8 @@ IWUResult * HqlCppTranslator::createDatasetResultSchema(IHqlExpression * sequenc
     if (!result)
         return NULL;
 
+    addDatasetResultXmlNamespaces(*result, xmlnsAttrs, record);
+
     MemoryBuffer schema;
     OwnedHqlExpr self = getSelf(record);
     addSchemaFields(record, schema, self);
@@ -11027,10 +11127,14 @@ ABoundActivity * HqlCppTranslator::doBuildActivityOutputWorkunit(BuildCtx & ctx,
         if (maxsize)
             doBuildUnsignedFunction(instance->createctx, "getMaxSize", maxsize->queryChild(0));
 
+        HqlExprArray xmlnsAttrs;
+        gatherAttributes(xmlnsAttrs, xmlnsAtom, expr);
         IHqlExpression * outputRecord = instance->meta.queryRecord();
-        Owned<IWUResult> result = createDatasetResultSchema(seq, name, outputRecord, true, false);
+        Owned<IWUResult> result = createDatasetResultSchema(seq, name, outputRecord, xmlnsAttrs, true, false);
         if (result)
         {
+            SCMStringBuffer debugName;
+            result->getResultName((IStringVal &)debugName);
             result->setResultRowLimit(-1);
 
             if (sequence >= 0)
@@ -11042,6 +11146,7 @@ ABoundActivity * HqlCppTranslator::doBuildActivityOutputWorkunit(BuildCtx & ctx,
 
         if (flags.length())
             doBuildUnsignedFunction(instance->classctx, "getFlags", flags.str()+1);
+
     }
     else
     {
@@ -11083,7 +11188,9 @@ void HqlCppTranslator::doBuildStmtOutput(BuildCtx & ctx, IHqlExpression * expr)
     if (!name)
         name.setown(createQuoted("NULL", LINK(constUnknownVarStringType)));
 
-    Owned<IWUResult> result = createDatasetResultSchema(seq, name, dataset->queryRecord(), true, false);
+    HqlExprArray xmlnsAttrs;
+    gatherAttributes(xmlnsAttrs, xmlnsAtom, expr);
+    Owned<IWUResult> result = createDatasetResultSchema(seq, name, dataset->queryRecord(), xmlnsAttrs, true, false);
 
     CHqlBoundExpr bound;
     buildDataset(ctx, dataset, bound, FormatNatural);

+ 1 - 0
ecl/hqlcpp/hqlttcpp.cpp

@@ -1422,6 +1422,7 @@ IHqlExpression * SequenceNumberAllocator::attachSequenceNumber(IHqlExpression *
                 args.append(*createAttribute(namedAtom, LINK(name)));
             args.append(*createAttribute(outputAtom));
             args.append(*createUniqueId());
+            gatherAttributes(args, xmlnsAtom, expr);
             return createSetResult(args);
         }
     default:

+ 8 - 4
esp/services/ws_workunits/ws_workunitsService.cpp

@@ -2189,7 +2189,7 @@ bool CWsWorkunitsEx::onWUQuery(IEspContext &context, IEspWUQueryRequest & req, I
     return true;
 }
 
-void appendResultSet(MemoryBuffer& mb, INewResultSet* result, const char *name, __int64 start, unsigned& count, __int64& total, bool bin, bool xsd, ESPSerializationFormat fmt)
+void appendResultSet(MemoryBuffer& mb, INewResultSet* result, const char *name, __int64 start, unsigned& count, __int64& total, bool bin, bool xsd, ESPSerializationFormat fmt, IProperties *xmlns)
 {
     if (!result)
         return;
@@ -2217,7 +2217,7 @@ void appendResultSet(MemoryBuffer& mb, INewResultSet* result, const char *name,
         if (fmt==ESPSerializationJSON)
             count = getResultJSON(adaptor, result, name, (unsigned) start, count, (xsd) ? "myschema" : NULL);
         else
-            count = getResultXml(adaptor, result, name, (unsigned) start, count, (xsd) ? "myschema" : NULL);
+            count = getResultXml(adaptor, result, name, (unsigned) start, count, (xsd) ? "myschema" : NULL, xmlns);
     }
 }
 
@@ -2296,11 +2296,11 @@ void getWsWuResult(IEspContext &context, const char* wuid, const char *name, con
     else
         rs.setown(resultSetFactory->createNewResultSet(result, wuid));
     if (!filterBy || !filterBy->length())
-        appendResultSet(mb, rs, name, start, count, total, bin, xsd, context.getResponseFormat());
+        appendResultSet(mb, rs, name, start, count, total, bin, xsd, context.getResponseFormat(), result->queryXmlns());
     else
     {
         Owned<INewResultSet> filteredResult = createFilteredResultSet(rs, filterBy);
-        appendResultSet(mb, filteredResult, name, start, count, total, bin, xsd, context.getResponseFormat());
+        appendResultSet(mb, filteredResult, name, start, count, total, bin, xsd, context.getResponseFormat(), result->queryXmlns());
     }
 }
 
@@ -2672,6 +2672,7 @@ void getFileResults(IEspContext &context, const char* logicalName, const char* c
 {
     Owned<IResultSetFactory> resultSetFactory = getSecResultSetFactory(context.querySecManager(), context.queryUser(), context.queryUserId(), context.queryPassword());
     Owned<INewResultSet> result(resultSetFactory->createNewFileResultSet(logicalName, cluster));
+<<<<<<< HEAD
     if (!filterBy || !filterBy->length())
         appendResultSet(buf, result, resname.str(), start, count, total, bin, xsd, context.getResponseFormat());
     else
@@ -2679,6 +2680,9 @@ void getFileResults(IEspContext &context, const char* logicalName, const char* c
         Owned<INewResultSet> filteredResult = createFilteredResultSet(result, filterBy);
         appendResultSet(buf, filteredResult, resname.str(), start, count, total, bin, xsd, context.getResponseFormat());
     }
+=======
+    appendResultSet(buf, result, resname.str(), start, count, total, bin, xsd, context.getResponseFormat(), NULL);
+>>>>>>> HPCC-8845 Support XMLNS for workunit and roxie xml outputs
 }
 
 void getWorkunitCluster(IEspContext &context, const char* wuid, SCMStringBuffer& cluster, bool checkArchiveWUs)

+ 11 - 0
roxie/ccd/ccdcontext.cpp

@@ -2825,6 +2825,17 @@ public:
         return trim ? XWFtrim|XWFopt : XWFexpandempty;
     }
 
+    virtual IProperties *queryXmlns(unsigned seqNo)
+    {
+        IConstWorkUnit *cw = serverQueryFactory->queryWorkUnit();
+        if (cw)
+        {
+            Owned<IConstWUResult> result = cw->getResultBySequence(seqNo);
+            if (result)
+                return result->queryXmlns();
+        }
+        return NULL;
+    }
     virtual unsigned getMemoryUsage()
     {
         return rowManager->getMemoryUsage();

+ 1 - 0
roxie/ccd/ccdcontext.hpp

@@ -97,6 +97,7 @@ interface IRoxieServerContext : extends IInterface
     virtual bool outputResultsToSocket() const = 0;
 
     virtual IRoxieDaliHelper *checkDaliConnection() = 0;
+    virtual IProperties *queryXmlns(unsigned seqNo) = 0;
 };
 
 interface IDeserializedResultStore : public IInterface

+ 2 - 1
roxie/ccd/ccdserver.cpp

@@ -19746,7 +19746,8 @@ public:
             response = serverContext->queryResult(sequence);
             if (response)
             {
-                response->startDataset("Dataset", helper.queryName(), sequence, (helper.getFlags() & POFextend) != 0);
+                Owned<IProperties> xmlns = serverContext->queryXmlns(sequence);
+                response->startDataset("Dataset", helper.queryName(), sequence, (helper.getFlags() & POFextend) != 0, xmlns);
                 if (response->mlFmt==MarkupFmt_XML || response->mlFmt==MarkupFmt_JSON)
                 {
                     unsigned int writeFlags = serverContext->getXmlFlags();

+ 1 - 0
rtl/include/eclhelper.hpp

@@ -177,6 +177,7 @@ public:
     virtual void outputBeginArray(const char *fieldname) = 0;
     virtual void outputEndArray(const char *fieldname) = 0;
     virtual void outputInlineXml(const char *text) = 0; //for appending raw xml content
+    virtual void outputXmlns(const char *name, const char *uri) = 0;
     inline void outputCString(const char *field, const char *fieldname) { outputString((size32_t)strlen(field), field, fieldname); }
 };
 

+ 3 - 0
testing/regress/ecl/key/xmlns.xml

@@ -0,0 +1,3 @@
+<Dataset name='Result 1' xmlns:star="urn:hpccsystems:ecl:unknown:star" xmlns:bk="urn:booklist:book" xmlns:bks="urn:booklist:books">
+ <Row><bks:books><bk:book><bk:title>The Emperors New Mind</bk:title><bk:author>Sir Roger Penrose</bk:author><star:rating>*****</star:rating></bk:book><bk:book><bk:title>The Elegant Universe</bk:title><bk:author>Brian Greene</bk:author><star:rating>*****</star:rating></bk:book><bk:book><bk:title>A Brief History of Time</bk:title><bk:author>Stephen Hawking</bk:author><star:rating>*****</star:rating></bk:book></bks:books></Row>
+</Dataset>

+ 33 - 0
testing/regress/ecl/xmlns.ecl

@@ -0,0 +1,33 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2014 HPCC Systems.
+
+    This program is free software: you can redistribute it and/or modify
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+bookRec := RECORD
+string     title {xpath('bk:title')};
+string     author {xpath('bk:author')};
+string     rating {xpath('star:rating')};
+           END;
+
+collectionRec := RECORD
+  dataset(bookRec) books {xpath('bks:books/bk:book')};
+end;
+
+xmlinfo := u8'<Row><bks:books><bk:book><bk:author>Sir Roger Penrose</bk:author><bk:title>The Emperors New Mind</bk:title><star:rating>*****</star:rating></bk:book><bk:book><bk:author>Brian Greene</bk:author><bk:title>The Elegant Universe</bk:title><star:rating>*****</star:rating></bk:book><bk:book><bk:author>Stephen Hawking</bk:author><bk:title>A Brief History of Time</bk:title><star:rating>*****</star:rating></bk:book></bks:books></Row>';
+
+books := FROMXML(collectionRec, xmlinfo, TRIM);
+
+output(books, XMLNS('bks','urn:booklist:books'), XMLNS('bk','urn:booklist:book'));
+