Selaa lähdekoodia

gh-300 CResultSetCursor::getXmlText not respecting xpath

Getting XML from workunit did not conform to format specified by
XPATH specifiers in ECL.  Updated fileview2 to make use of the
XPATH information when building xml from results.

Fixes gh-300

Signed-off-by: Anthony Fishbeck <Anthony.Fishbeck@lexisnexis.com>
Anthony Fishbeck 13 vuotta sitten
vanhempi
commit
4acf16b2e6

+ 1 - 0
common/fileview2/fileview.hpp

@@ -157,6 +157,7 @@ interface IResultSetCursor : extends IInterface
     virtual IStringVal & getDisplayText(IStringVal & ret, int columnIndex) = 0;
     virtual IStringVal & getDisplayText(IStringVal & ret, int columnIndex) = 0;
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex) = 0;
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex) = 0;
     virtual IStringVal & getXmlRow(IStringVal & ret) = 0;
     virtual IStringVal & getXmlRow(IStringVal & ret) = 0;
+    virtual IStringVal & getXmlItem(IStringVal & ret) = 0;
     virtual __int64 getNumRows() const = 0;
     virtual __int64 getNumRows() const = 0;
 };
 };
 
 

+ 3 - 0
common/fileview2/fvdatasource.hpp

@@ -49,6 +49,9 @@ interface IFvDataSourceMetaData : extends IInterface
     virtual unsigned numKeyedColumns() const = 0;
     virtual unsigned numKeyedColumns() const = 0;
     
     
     inline bool isVirtual(unsigned column) const { return queryFieldFlags(column) == FVFFvirtual; }
     inline bool isVirtual(unsigned column) const { return queryFieldFlags(column) == FVFFvirtual; }
+    virtual const char *queryXmlTag(unsigned column) const = 0;
+    virtual const char *queryXmlTag() const = 0;
+    virtual const IntArray &queryAttrList() = 0;
 };
 };
 
 
 IFvDataSourceMetaData * deserializeDataSourceMeta(MemoryBuffer & in);
 IFvDataSourceMetaData * deserializeDataSourceMeta(MemoryBuffer & in);

+ 156 - 13
common/fileview2/fvresultset.cpp

@@ -1508,12 +1508,12 @@ IStringVal & CResultSetCursor::getDisplayText(IStringVal &ret, int columnIndex)
 }
 }
 
 
 
 
-void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
+void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex, const char *tag)
 {
 {
     if (!isValid())
     if (!isValid())
         return;
         return;
 
 
-    const char * name = meta.meta->queryName(columnIndex);
+    const char * name = (tag) ? tag : meta.meta->queryXmlTag(columnIndex);
     CResultSetColumnInfo & column = meta.columns.item(columnIndex);
     CResultSetColumnInfo & column = meta.columns.item(columnIndex);
     unsigned flags = column.flag;
     unsigned flags = column.flag;
     switch (flags)
     switch (flags)
@@ -1522,10 +1522,12 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
     case FVFFendif:
     case FVFFendif:
         return;
         return;
     case FVFFbeginrecord:
     case FVFFbeginrecord:
-        out.append("<").append(name).append(">");
+        if (name && *name)
+            out.append("<").append(name).append(">");
         return;
         return;
     case FVFFendrecord:
     case FVFFendrecord:
-        out.append("</").append(name).append(">");
+        if (name && *name)
+            out.append("</").append(name).append(">");
         return;
         return;
     }
     }
 
 
@@ -1621,7 +1623,8 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
     case type_table:
     case type_table:
     case type_groupedtable:
     case type_groupedtable:
         {
         {
-            out.append("<").append(name).append(">");
+            if (name && *name)
+                out.append("<").append(name).append(">");
             Owned<IResultSetCursor> childCursor = getChildren(columnIndex);
             Owned<IResultSetCursor> childCursor = getChildren(columnIndex);
             
             
             ForEach(*childCursor)
             ForEach(*childCursor)
@@ -1630,12 +1633,14 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
                 childCursor->getXmlRow(adaptor);
                 childCursor->getXmlRow(adaptor);
             }
             }
 
 
-            out.append("</").append(name).append(">");
+            if (name && *name)
+                out.append("</").append(name).append(">");
             break;
             break;
         }
         }
     case type_set:
     case type_set:
         {
         {
-            out.append("<").append(name).append(">");
+            if (name && *name)
+                out.append("<").append(name).append(">");
             if (getIsAll(columnIndex))
             if (getIsAll(columnIndex))
                 outputXmlSetAll(out);
                 outputXmlSetAll(out);
             else
             else
@@ -1644,16 +1649,132 @@ void CResultSetCursor::getXmlText(StringBuffer & out, int columnIndex)
                 
                 
                 ForEach(*childCursor)
                 ForEach(*childCursor)
                 {
                 {
-                    out.append("<Item>");
                     StringBufferAdaptor adaptor(out);
                     StringBufferAdaptor adaptor(out);
-                    childCursor->getDisplayText(adaptor, 0);
-                    out.append("</Item>");
+                    childCursor->getXmlItem(adaptor);
                 }
                 }
             }
             }
+            if (name && *name)
+                out.append("</").append(name).append(">");
+            break;
+        }
+    default:
+        UNIMPLEMENTED;
+    }
+    rtlFree(resultStr);
+}
 
 
-            out.append("</").append(name).append(">");
+void CResultSetCursor::getXmlAttrText(StringBuffer & out, int columnIndex, const char *tag)
+{
+    if (!isValid())
+        return;
+
+    const char * name = (tag) ? tag : meta.meta->queryXmlTag(columnIndex);
+    if (name && *name=='@')
+        name++;
+    CResultSetColumnInfo & column = meta.columns.item(columnIndex);
+    unsigned flags = column.flag;
+    switch (flags)
+    {
+    case FVFFbeginif:
+    case FVFFendif:
+    case FVFFbeginrecord:
+    case FVFFendrecord:
+        return;
+    }
+
+    const byte * cur = getColumn(columnIndex);
+    unsigned resultLen;
+    char * resultStr = NULL;
+
+    ITypeInfo & type = *column.type;
+    unsigned size = type.getSize();
+    unsigned len = UNKNOWN_LENGTH;
+    switch (type.getTypeCode())
+    {
+    case type_boolean:
+        outputXmlAttrBool((*((byte *)cur)) != 0, name, out);
+        break;
+    case type_int:
+        {
+            __int64 value = getIntFromInt(type, cur, isMappedIndexField(columnIndex));
+            if (type.isSigned())
+                outputXmlAttrInt((__int64) value, name, out);
+            else
+                outputXmlAttrUInt((unsigned __int64) value, name, out);
             break;
             break;
         }
         }
+    case type_swapint:
+        {
+            __int64 value = getIntFromSwapInt(type, cur, isMappedIndexField(columnIndex));
+            if (type.isSigned())
+                outputXmlAttrInt((__int64) value, name, out);
+            else
+                outputXmlAttrUInt((unsigned __int64) value, name, out);
+            break;
+        }
+    case type_packedint:
+        {
+            if (type.isSigned())
+                outputXmlAttrInt(rtlGetPackedSigned(cur), name, out);
+            else
+                outputXmlAttrUInt(rtlGetPackedUnsigned(cur), name, out);
+            break;
+        }
+    case type_decimal:
+        if (type.isSigned())
+            outputXmlAttrDecimal(cur, size, type.getPrecision(), name, out);
+        else
+            outputXmlAttrUDecimal(cur, size, type.getPrecision(), name, out);
+        break;
+    case type_real:
+        if (size == 4)
+            outputXmlAttrReal(*(float *)cur, name, out);
+        else
+            outputXmlAttrReal(*(double *)cur, name, out);
+        break;
+    case type_qstring:
+        len = getLength(type, cur);
+        rtlQStrToStrX(resultLen, resultStr, len, (const char *)cur);
+        outputXmlAttrString(resultLen, resultStr, name, out);
+        break;
+    case type_data:
+        len = getLength(type, cur);
+        outputXmlAttrData(len, cur, name, out);
+        break;
+    case type_string:
+        len = getLength(type, cur);
+        if (meta.isEBCDIC(columnIndex))
+        {
+            rtlStrToEStrX(resultLen, resultStr, len, (const char *)cur);
+            outputXmlAttrString(resultLen, resultStr, name, out);
+        }
+        else
+            outputXmlAttrString(len, (const char *)cur, name, out);
+        break;
+    case type_unicode:
+        len = getLength(type, cur);
+        outputXmlAttrUnicode(len, (UChar const *)cur, name, out);
+        break;
+    case type_varstring:
+        if (meta.isEBCDIC(columnIndex))
+        {
+            rtlStrToEStrX(resultLen, resultStr, strlen((const char *)cur), (const char *)cur);
+            outputXmlAttrString(resultLen, resultStr, name, out);
+        }
+        else
+            outputXmlAttrString(strlen((const char *)cur), (const char *)cur, name, out);
+        break;
+    case type_varunicode:
+        outputXmlAttrUnicode(rtlUnicodeStrlen((UChar const *)cur), (UChar const *)cur, name, out);
+        break;
+    case type_utf8:
+        len = getLength(type, cur);
+        outputXmlAttrUtf8(len, (const char *)cur, name, out);
+        break;
+    case type_table:
+    case type_groupedtable:
+    case type_set:
+        break;
     default:
     default:
         UNIMPLEMENTED;
         UNIMPLEMENTED;
     }
     }
@@ -1668,16 +1789,33 @@ IStringVal & CResultSetCursor::getXml(IStringVal &ret, int columnIndex)
     return ret;
     return ret;
 }
 }
 
 
+IStringVal & CResultSetCursor::getXmlItem(IStringVal & ret)
+{
+    StringBuffer temp;
+    getXmlText(temp, 0, meta.meta->queryXmlTag());
+    ret.set(temp.str());
+    return ret;
+}
+
 //More efficient than above...
 //More efficient than above...
 IStringVal & CResultSetCursor::getXmlRow(IStringVal &ret)
 IStringVal & CResultSetCursor::getXmlRow(IStringVal &ret)
 {
 {
     StringBuffer temp;
     StringBuffer temp;
-    temp.append("<Row>");
+    const char *rowtag = meta.meta->queryXmlTag();
+    if (rowtag && *rowtag)
+        temp.append('<').append(rowtag);
+    const IntArray &attributes = meta.meta->queryAttrList();
+    ForEachItemIn(ac, attributes)
+        getXmlAttrText(temp, attributes.item(ac));
+    temp.append('>');
     unsigned numColumns = meta.getColumnCount();
     unsigned numColumns = meta.getColumnCount();
     unsigned ignoreNesting = 0;
     unsigned ignoreNesting = 0;
     for (unsigned col = 0; col < numColumns; col++)
     for (unsigned col = 0; col < numColumns; col++)
     {
     {
         unsigned flags = meta.columns.item(col).flag;
         unsigned flags = meta.columns.item(col).flag;
+        const char *tag = meta.meta->queryXmlTag(col);
+        if (tag && *tag=='@')
+            continue;
         switch (flags)
         switch (flags)
         {
         {
         case FVFFbeginif:
         case FVFFbeginif:
@@ -1710,7 +1848,8 @@ IStringVal & CResultSetCursor::getXmlRow(IStringVal &ret)
         }
         }
     }
     }
     assertex(ignoreNesting == 0);
     assertex(ignoreNesting == 0);
-    temp.append("</Row>");
+    if (rowtag && *rowtag)
+        temp.append("</").append(rowtag).append('>');
     ret.set(temp.str());
     ret.set(temp.str());
     return ret;
     return ret;
 }
 }
@@ -1957,6 +2096,10 @@ IStringVal & IndirectResultSetCursor::getXmlRow(IStringVal &ret)
 {
 {
     return queryBase()->getXmlRow(ret);
     return queryBase()->getXmlRow(ret);
 }
 }
+IStringVal & IndirectResultSetCursor::getXmlItem(IStringVal &ret)
+{
+    return queryBase()->getXmlItem(ret);
+}
 void IndirectResultSetCursor::noteRelatedFileChanged()
 void IndirectResultSetCursor::noteRelatedFileChanged()
 {
 {
     queryBase()->noteRelatedFileChanged();
     queryBase()->noteRelatedFileChanged();

+ 6 - 1
common/fileview2/fvresultset.ipp

@@ -368,6 +368,9 @@ public:
     virtual IStringVal & getDisplayText(IStringVal &ret, int columnIndex);
     virtual IStringVal & getDisplayText(IStringVal &ret, int columnIndex);
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex);
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex);
     virtual IStringVal & getXmlRow(IStringVal &ret);
     virtual IStringVal & getXmlRow(IStringVal &ret);
+    virtual IStringVal & getXmlItem(IStringVal & ret);
+
+
 
 
 //IResultSetCursor
 //IResultSetCursor
     virtual void beginAccess();
     virtual void beginAccess();
@@ -381,7 +384,8 @@ protected:
     void init(IExtendedNewResultSet * _resultSet);
     void init(IExtendedNewResultSet * _resultSet);
     bool isMappedIndexField(unsigned columnIndex) { return resultSet->isMappedIndexField(columnIndex); }
     bool isMappedIndexField(unsigned columnIndex) { return resultSet->isMappedIndexField(columnIndex); }
     const byte * getColumn(unsigned idx) const      { return (const byte *)curRowData.toByteArray() + offsets[idx]; }
     const byte * getColumn(unsigned idx) const      { return (const byte *)curRowData.toByteArray() + offsets[idx]; }
-    void getXmlText(StringBuffer & out, int columnIndex);
+    void getXmlText(StringBuffer & out, int columnIndex, const char *tag=NULL);
+    void getXmlAttrText(StringBuffer & out, int columnIndex, const char *tag=NULL);
 
 
     virtual __int64 getCurRow() const;
     virtual __int64 getCurRow() const;
     virtual __int64 translateRow(__int64 row) const;
     virtual __int64 translateRow(__int64 row) const;
@@ -436,6 +440,7 @@ public:
     virtual IStringVal & getDisplayText(IStringVal &ret, int columnIndex);
     virtual IStringVal & getDisplayText(IStringVal &ret, int columnIndex);
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex);
     virtual IStringVal & getXml(IStringVal & ret, int columnIndex);
     virtual IStringVal & getXmlRow(IStringVal &ret);
     virtual IStringVal & getXmlRow(IStringVal &ret);
+    virtual IStringVal & getXmlItem(IStringVal &ret);
     virtual void noteRelatedFileChanged();
     virtual void noteRelatedFileChanged();
 
 
 protected:
 protected:

+ 83 - 1
common/fileview2/fvsource.cpp

@@ -34,12 +34,48 @@
 
 
 #define MAX_RECORD_SIZE         4096
 #define MAX_RECORD_SIZE         4096
 
 
+inline void appendNextXpathName(StringBuffer &s, const char *xpath, const char *&next)
+{
+    while (*xpath && !strchr("*[/", *xpath))
+        s.append(*xpath++);
+    next = strchr(xpath, '/');
+}
+
+void splitXmlTagNamesFromXPath(const char *xpath, StringBuffer &inner, StringBuffer *outer=NULL)
+{
+    if (!xpath || !xpath)
+        return;
+
+    StringBuffer s1;
+    StringBuffer s2;
+
+    const char *next=xpath;
+    appendNextXpathName(s1, xpath, next);
+    if (outer && next)
+        appendNextXpathName(s2, ++next, next);
+    if (next) //xpath too deep
+        return;
+
+    if (!s2.length())
+        inner.swapWith(s1);
+    else
+    {
+        inner.swapWith(s2);
+        outer->swapWith(s1);
+    }
+    if (!inner.length())
+        inner.set("/");
+    if (outer && !outer->length())
+        outer->set("/");
+}
+
 DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, const char * _name, const char * _xpath, ITypeInfo * _type)
 DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, const char * _name, const char * _xpath, ITypeInfo * _type)
 {
 {
     flags = _flags;
     flags = _flags;
     name.set(_name);
     name.set(_name);
     type.set(_type);
     type.set(_type);
     xpath.set(_xpath);
     xpath.set(_xpath);
+    splitXmlTagNamesFromXPath(_xpath, tagname);
 }
 }
 
 
 DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, MemoryBuffer & in)
 DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, MemoryBuffer & in)
@@ -48,6 +84,7 @@ DataSourceMetaItem::DataSourceMetaItem(unsigned _flags, MemoryBuffer & in)
     in.read(name);
     in.read(name);
     in.read(xpath);
     in.read(xpath);
     type.setown(deserializeType(in));
     type.setown(deserializeType(in));
+    splitXmlTagNamesFromXPath(xpath.get(), tagname);
 }
 }
 
 
 
 
@@ -66,6 +103,9 @@ DataSourceDatasetItem::DataSourceDatasetItem(const char * _name, const char * _x
     type.setown(makeTableType(NULL, NULL, NULL, NULL));
     type.setown(makeTableType(NULL, NULL, NULL, NULL));
     name.set(_name);
     name.set(_name);
     xpath.set(_xpath);
     xpath.set(_xpath);
+    splitXmlTagNamesFromXPath(_xpath, record.tagname, &tagname);
+    if (!record.tagname.length())
+        record.tagname.set("Row");
 }
 }
 
 
 DataSourceDatasetItem::DataSourceDatasetItem(unsigned flags, MemoryBuffer & in) : DataSourceMetaItem(FVFFdataset, NULL, NULL, NULL), record(in)
 DataSourceDatasetItem::DataSourceDatasetItem(unsigned flags, MemoryBuffer & in) : DataSourceMetaItem(FVFFdataset, NULL, NULL, NULL), record(in)
@@ -83,14 +123,23 @@ void DataSourceDatasetItem::serialize(MemoryBuffer & out) const
 
 
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 
 
-DataSourceSetItem::DataSourceSetItem(const char * _name, const char * _xpath, ITypeInfo * _type) : DataSourceMetaItem(FVFFset, _name, _xpath, _type)
+DataSourceSetItem::DataSourceSetItem(const char * _name, const char * _xpath, ITypeInfo * _type) : DataSourceMetaItem(FVFFset, _name, NULL, _type)
 {
 {
     createChild();
     createChild();
+    xpath.set(_xpath);
+    StringBuffer attr;
+    splitXmlTagNamesFromXPath(_xpath, record.tagname, &tagname);
+    if (!record.tagname.length())
+        record.tagname.set("Item");
 }
 }
 
 
 DataSourceSetItem::DataSourceSetItem(unsigned flags, MemoryBuffer & in) : DataSourceMetaItem(flags, in)
 DataSourceSetItem::DataSourceSetItem(unsigned flags, MemoryBuffer & in) : DataSourceMetaItem(flags, in)
 {
 {
     createChild();
     createChild();
+    StringBuffer attr;
+    splitXmlTagNamesFromXPath(xpath.get(), record.tagname, &tagname.clear());
+    if (!record.tagname.length())
+        record.tagname.set("Item");
 }
 }
 
 
 void DataSourceSetItem::createChild()
 void DataSourceSetItem::createChild()
@@ -160,10 +209,12 @@ void DataSourceMetaData::init()
     isStoredFixedWidth = false;
     isStoredFixedWidth = false;
     randomIsOk = false;
     randomIsOk = false;
     numFieldsToIgnore = 0;
     numFieldsToIgnore = 0;
+    attrset = false;
 }
 }
 
 
 DataSourceMetaData::DataSourceMetaData(MemoryBuffer & buffer)
 DataSourceMetaData::DataSourceMetaData(MemoryBuffer & buffer)
 {
 {
+    attrset = false;
     numVirtualFields = 0;
     numVirtualFields = 0;
     buffer.read(numFieldsToIgnore);
     buffer.read(numFieldsToIgnore);
     buffer.read(randomIsOk);
     buffer.read(randomIsOk);
@@ -391,6 +442,37 @@ const char * DataSourceMetaData::queryXPath(unsigned column) const
     return fields.item(column).xpath;
     return fields.item(column).xpath;
 }
 }
 
 
+const char * DataSourceMetaData::queryXmlTag(unsigned column) const
+{
+    DataSourceMetaItem &item = fields.item(column);
+    if (item.tagname.length())
+        return (*item.tagname.str()!='/') ? item.tagname.str() : NULL;
+    return item.name.get();
+}
+
+const char *DataSourceMetaData::queryXmlTag() const
+{
+    if (tagname.length())
+        return (*tagname.str()!='/') ? tagname.str() : NULL;
+    return "Row";
+}
+
+const IntArray &DataSourceMetaData::queryAttrList()
+{
+    if (!attrset)
+    {
+        ForEachItemIn(idx, fields)
+        {
+            DataSourceMetaItem &item = fields.item(idx);
+            if (*item.tagname.str()=='@')
+                attributes.append(idx);
+        }
+        attrset=true;
+    }
+    return attributes;
+}
+
+
 IFvDataSourceMetaData * DataSourceMetaData::queryChildMeta(unsigned column) const
 IFvDataSourceMetaData * DataSourceMetaData::queryChildMeta(unsigned column) const
 {
 {
     return fields.item(column).queryChildMeta();
     return fields.item(column).queryChildMeta();

+ 10 - 0
common/fileview2/fvsource.ipp

@@ -79,6 +79,7 @@ public:
 public:
 public:
     StringAttr  name;
     StringAttr  name;
     StringAttr  xpath;
     StringAttr  xpath;
+    StringBuffer tagname;
     OwnedITypeInfo type;
     OwnedITypeInfo type;
     byte           flags;
     byte           flags;
 };
 };
@@ -100,6 +101,10 @@ public:
     virtual bool supportsRandomSeek() const;
     virtual bool supportsRandomSeek() const;
     virtual void serialize(MemoryBuffer & out) const;
     virtual void serialize(MemoryBuffer & out) const;
     virtual unsigned queryFieldFlags(unsigned column) const;
     virtual unsigned queryFieldFlags(unsigned column) const;
+    virtual const char *queryXmlTag(unsigned column) const;
+    virtual const char *queryXmlTag() const;
+    virtual const IntArray &queryAttrList();
+
     virtual IFvDataSourceMetaData * queryChildMeta(unsigned column) const;
     virtual IFvDataSourceMetaData * queryChildMeta(unsigned column) const;
     virtual IFvDataSource * createChildDataSource(unsigned column, unsigned len, const void * data);
     virtual IFvDataSource * createChildDataSource(unsigned column, unsigned len, const void * data);
     virtual unsigned numKeyedColumns() const;
     virtual unsigned numKeyedColumns() const;
@@ -139,6 +144,11 @@ protected:
     bool isStoredFixedWidth;
     bool isStoredFixedWidth;
     bool randomIsOk;
     bool randomIsOk;
     byte numFieldsToIgnore;
     byte numFieldsToIgnore;
+    IntArray attributes;
+    bool attrset;
+
+public:
+    StringBuffer tagname;
 };
 };
 
 
 
 

+ 1 - 1
esp/services/ws_ecl/ws_ecl_wuinfo.cpp

@@ -181,7 +181,7 @@ void WsEclWuInfo::getSchemaFromResult(StringBuffer &schema, IConstWUResult &res)
         
         
         Owned<IResultSetFactory> resultSetFactory(getResultSetFactory(username, password));
         Owned<IResultSetFactory> resultSetFactory(getResultSetFactory(username, password));
         Owned<IResultSetMetaData> meta = resultSetFactory->createResultSetMeta(&res);
         Owned<IResultSetMetaData> meta = resultSetFactory->createResultSetMeta(&res);
-        meta->getXmlSchema(s, true);
+        meta->getXmlXPathSchema(s, true);
         const char *finger=schema.str();
         const char *finger=schema.str();
         while (finger && strncmp(finger, "<xs:schema", 10))
         while (finger && strncmp(finger, "<xs:schema", 10))
             finger++;
             finger++;