You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by bp...@apache.org on 2006/09/10 00:04:55 UTC

svn commit: r441864 - in /db/derby/code/branches/10.2/java: engine/org/apache/derby/iapi/types/ engine/org/apache/derby/loc/ shared/org/apache/derby/shared/common/reference/ testing/org/apache/derbyTesting/functionTests/master/ testing/org/apache/derby...

Author: bpendleton
Date: Sat Sep  9 15:04:54 2006
New Revision: 441864

URL: http://svn.apache.org/viewvc?view=rev&rev=441864
Log:
DERBY-1759: XMLSERIALIZE doesn't follow spec when serializing sequence

This patch was contributed by Army Brown (qozinx@gmail.com)

Merged by svn merge -r 441739:441740 ../trunk/

Modified:
    db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java
    db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XML.java
    db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XMLDataValue.java
    db/derby/code/branches/10.2/java/engine/org/apache/derby/loc/messages_en.properties
    db/derby/code/branches/10.2/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/xml_general.out
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/xml_general.out
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/xml_general.out
    db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/lang/xml_general.sql

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java Sat Sep  9 15:04:54 2006
@@ -32,6 +32,7 @@
 
 // -- JDBC 3.0 JAXP API classes.
 
+import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -297,23 +298,43 @@
         ArrayList aList = new ArrayList();
         aList.add(dBuilder.parse(
             new InputSource(new StringReader(xmlAsText))));
-        return serializeToString(aList);
+
+        /* The second argument in the following call is for
+         * catching cases where we have a top-level (parentless)
+         * attribute node--but since we just created the list
+         * with a single Document node, we already we know we
+         * don't have a top-level attribute node in the list,
+         * so we don't have to worry.  Hence the "null" here.
+         */
+        return serializeToString(aList, null);
     }
 
     /**
      * Take an array list (sequence) of XML nodes and/or string values
      * and serialize that entire list according to SQL/XML serialization
-     * rules.  We do that by going through each item in the array
-     * list and either serializing it (if it's a Node) or else
-     * just echoing the value to the serializer (if it's a Text
-     * node or an atomic value).
+     * rules, which ultimately point to XML serialization rules as
+     * defined by w3c.  As part of that serialization process we have
+     * to first "normalize" the sequence.  We do that by iterating through
+     * the list and performing the steps for "sequence normalization" as
+     * defined here:
+     *
+     * http://www.w3.org/TR/xslt-xquery-serialization/#serdm
+     *
+     * This method primarily focuses on taking the steps for normalization;
+     * for the rest of the serialization work, we just make calls on the
+     * DOMSerializer class provided by Xalan.
      *
      * @param items List of items to serialize
-     * @return Single string holding the concatenation of the serialized
-     *  form of all items in the list
+     * @param xmlVal XMLDataValue into which the serialized string
+     *  returned by this method is ultimately going to be stored.
+     *  This is used for keeping track of XML values that represent
+     *  sequences having top-level (parentless) attribute nodes.
+     * @return Single string holding the serialized version of the
+     *  normalized sequence created from the items in the received
+     *  list.
      */
-    protected String serializeToString(ArrayList items)
-        throws java.io.IOException
+    protected String serializeToString(ArrayList items,
+        XMLDataValue xmlVal) throws java.io.IOException
     {
         if ((items == null) || (items.size() == 0))
         // nothing to do; return empty sequence.
@@ -334,26 +355,121 @@
         int sz = items.size();
         Object obj = null;
 
+        /* Step 1: Empty sequence.  If we have an empty sequence then we
+         * won't ever enter the for loop and the call to sWriter.toString()
+         * at the end of this method will return an empty string, as
+         * required.  Otherwise, for a non-empty sequence our "items"
+         * list already corresponds to "S1".
+         */
+
         // Iterate through the list and serialize each item.
+        boolean lastItemWasString = false;
         for (int i = 0; i < sz; i++)
         {
             obj = items.get(i);
-            if (obj instanceof String)
             // if it's a string, then this corresponds to some atomic
             // value, so just echo the string as it is.
+            if (obj instanceof String)
+            {
+                /* Step 2: Atomic values.  If "obj" is a string then it
+                 * corresponds to some atomic value whose "lexical
+                 * representation" is obj.  So we just take that.
+                 */
+
+                if (lastItemWasString)
+                {
+                    /* Step 3: Adjacent strings.  If we have multiple adjacent
+                     * strings then concatenate them with a single space
+                     * between them.
+                     */
+                    sWriter.write(" ");
+                }
+
+                /* Step 4: Create a Text node from the adjacent strings.
+                 * Since we're just going to serialize the Text node back
+                 * into a string, we short-cut this step by skipping the
+                 * creation of the Text node and just writing the string
+                 * out directly to our serialized stream.
+                 */
                 sWriter.write((String)obj);
+                lastItemWasString = true;
+            }
+            else if (obj instanceof Attr)
+            {
+                /* Step 7a: Attribute nodes.  If there is an Attribute node
+                 * node in the sequence then we have to throw a serialization
+                 * error.  NOTE: The rules say we also have to throw an error
+                 * for Namespace nodes, but JAXP doesn't define a "Namespace"
+                 * object per se; it just defines namespace prefixes and URIs
+                 * on other Nodes.  So we just check for attributes.  If we
+                 * find one then we take note of the fact that the result has
+                 * a parentless attribute node and later, if the user calls
+                 * XMLSERIALIZE on the received XMLDataValue we'll throw the
+                 * error as required.  Note that we currently only get here
+                 * for the XMLQUERY operator, which means we're serializing
+                 * a result sequence returned from Xalan and we're going to
+                 * store the serialized version into a Derby XML value.  In
+                 * that case the serialization is an internal operation--and
+                 * since the user didn't ask for it, we don't want to throw
+                 * the serialization error here.  If we did, then whenever an
+                 * XMLQUERY operation returned a result sequence with a top-
+                 * level attribute in it, the user would see a serialization
+                 * error. That's not correct since it is technically okay for
+                 * the XMLQUERY operation to return a sequence with an attribute
+                 * node; it's just not okay for a user to explicitly try to
+                 * serialize that sequence. So instead of throwing the error
+                 * here, we just take note of the fact that the sequence has
+                 * a top-level attribute.  Then later, IF the user makes an
+                 * explicit call to serialize the sequence, we'll throw the
+                 * appropriate error (see XML.XMLSerialize()).
+                 */
+                if (xmlVal != null)
+                    xmlVal.markAsHavingTopLevelAttr();
+                dSer.serialize((Node)obj);
+                lastItemWasString = false;
+            }
             else
             { // We have a Node, so try to serialize it.
                 Node n = (Node)obj;
                 if (n instanceof Text)
-                // Xalan doesn't allow a "serialize" call on Text nodes,
-                // so we just go ahead and echo the value of the text.
+                {
+                    /* Step 6: Combine adjacent text nodes into a single
+                     * text node.  Since we're just going to serialize the
+                     * Text node back into a string, we short-cut this step
+                     * by skipping the creation of a new Text node and just
+                     * writing the text value out directly to our serialized
+                     * stream.  Step 6 also says that empty text nodes should
+                     * be dropped--but if the text node is empty, the call
+                     * to getNodeValue() will return an empty string and
+                     * thus we've effectively "dropped" the text node from
+                     * the serialized result.  Note: it'd be cleaner to just
+                     * call "serialize()" on the Text node like we do for
+                     * all other Nodes, but Xalan doesn't allow that.  So
+                     * use the getNodeValue() method instead.
+                     */
                     sWriter.write(n.getNodeValue());
+                }
                 else
+                {
+                    /* Steps 5 and 7b: Copy all non-attribute, non-text
+                     * nodes to the "normalized sequence" and then serialize
+                     * that normalized sequence.  We short-cut this by
+                     * just letting Xalan do the serialization for every
+                     * Node in the current list of items that wasn't
+                     * "serialized" as an atomic value, attribute, or
+                     * text node.
+                     */
                     dSer.serialize(n);
+                }
+
+                lastItemWasString = false;
             }
         }
 
+        /* At this point sWriter holds the serialized version of the
+         * normalized sequence that corresponds to the received list
+         * of items.  So that's what we return.
+         */
         sWriter.flush();
         return sWriter.toString();
     }

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XML.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XML.java?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XML.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XML.java Sat Sep  9 15:04:54 2006
@@ -130,6 +130,15 @@
      */
     private static String xmlReqCheck = null;
 
+    /*
+     * Whether or not this XML value corresponds to a sequence
+     * that has one or more top-level ("parentless") attribute
+     * nodes.  If so then we have to throw an error if the user
+     * attempts to serialize this value, per XML serialization
+     * rules.
+     */
+    private boolean containsTopLevelAttr;
+
     /**
      * Default constructor.
      */
@@ -137,32 +146,26 @@
     {
         xmlStringValue = null;
         xType = -1;
+        containsTopLevelAttr = false;
     }
 
     /**
      * Private constructor used for the getClone() method.
-     * Takes a SQLChar and clones it.
-     * @param val A SQLChar instance to clone and use for
-     *  this XML value.
-     */
-    private XML(SQLChar val)
-    {
-        xmlStringValue = (val == null ? null : (SQLChar)val.getClone());
-        xType = -1;
-    }
-
-    /**
-     * Private constructor used for the getClone() method.
-     * Takes a SQLChar and clones it and also takes a
-     * qualified XML type and stores that as this XML
-     * object's qualified type.
+     *
      * @param val A SQLChar instance to clone and use for
      *  this XML value.
+     * @param xmlType Qualified XML type for "val"
+     * @param seqWithAttr Whether or not "val" corresponds to
+     *  sequence with one or more top-level attribute nodes.
+     * @return A new instance of XML whose fields are clones
+     *  of the values received.
      */
-    private XML(SQLChar val, int xmlType)
+    private XML(SQLChar val, int xmlType, boolean seqWithAttr)
     {
         xmlStringValue = (val == null ? null : (SQLChar)val.getClone());
         setXType(xmlType);
+        if (seqWithAttr)
+            markAsHavingTopLevelAttr();
     }
 
     /* ****
@@ -174,7 +177,7 @@
      */
     public DataValueDescriptor getClone()
     {
-        return new XML(xmlStringValue, getXType());
+        return new XML(xmlStringValue, getXType(), hasTopLevelAttr());
     }
 
     /**
@@ -284,7 +287,11 @@
          * brings us to this method).
          */
         if (theValue instanceof XMLDataValue)
+        {
             setXType(((XMLDataValue)theValue).getXType());
+            if (((XMLDataValue)theValue).hasTopLevelAttr())
+                markAsHavingTopLevelAttr();
+        }
     }
 
     /** 
@@ -636,6 +643,22 @@
             return result;
         }
 
+        /* XML serialization rules say that sequence "normalization"
+         * must occur before serialization, and normalization dictates
+         * that a serialization error must be thrown if the XML value
+         * is a sequence with a top-level attribute.  We normalized
+         * (and serialized) this XML value when it was first created,
+         * and at that time we took note of whether or not there is
+         * a top-level attribute.  So throw the error here if needed.
+         * See SqlXmlUtil.serializeToString() for more on sequence
+         * normalization.
+         */
+        if (this.hasTopLevelAttr())
+        {
+            throw StandardException.newException(
+                SQLState.LANG_XQUERY_SERIALIZATION_ERROR);
+        }
+
         // Get the XML value as a string.  For this UTF-8 impl,
         // we already have it as a UTF-8 string, so just use
         // that.
@@ -749,11 +772,10 @@
             ArrayList itemRefs = sqlxUtil.evalXQExpression(
                 this, true, xType);
 
-            String strResult = sqlxUtil.serializeToString(itemRefs);
             if (result == null)
-                result = new XML(new SQLChar(strResult));
-            else
-                result.setValue(new SQLChar(strResult));
+                result = new XML();
+            String strResult = sqlxUtil.serializeToString(itemRefs, result);
+            result.setValue(new SQLChar(strResult));
 
             // Now that we've set the result value, make sure
             // to indicate what kind of XML value we have.
@@ -795,6 +817,17 @@
     public void setXType(int xtype)
     {
         this.xType = xtype;
+
+        /* If the target type is XML_DOC_ANY then this XML value
+         * holds a single well-formed Document.  So we know that
+         * we do NOT have any top-level attribute nodes.  Note: if
+         * xtype is SEQUENCE we don't set "containsTopLevelAttr"
+         * here; assumption is that the caller of this method will
+         * then set the field as appropriate.  Ex. see "setFrom()"
+         * in this class.
+         */
+        if (xtype == XML_DOC_ANY)
+            containsTopLevelAttr = false;
     }
 
     /**
@@ -803,6 +836,24 @@
     public int getXType()
     {
         return xType;
+    }
+
+    /**
+     * Take note of the fact this XML value represents an XML
+     * sequence that has one or more top-level attribute nodes.
+     */
+    public void markAsHavingTopLevelAttr()
+    {
+        this.containsTopLevelAttr = true;
+    }
+
+    /**
+     * Return whether or not this XML value represents a sequence
+     * that has one or more top-level attribute nodes.
+     */
+    public boolean hasTopLevelAttr()
+    {
+        return containsTopLevelAttr;
     }
 
     /**

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XMLDataValue.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XMLDataValue.java?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XMLDataValue.java (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/iapi/types/XMLDataValue.java Sat Sep  9 15:04:54 2006
@@ -115,4 +115,16 @@
      * Retrieve this XML value's qualified type.
      */
     public int getXType();
+
+    /**
+     * Take note of the fact this XML value represents an XML
+     * sequence that has one or more top-level attribute nodes.
+     */
+    public void markAsHavingTopLevelAttr();
+
+    /**
+     * Return whether or not this XML value represents a sequence
+     * that has one or more top-level attribute nodes.
+     */
+    public boolean hasTopLevelAttr();
 }

Modified: db/derby/code/branches/10.2/java/engine/org/apache/derby/loc/messages_en.properties
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/engine/org/apache/derby/loc/messages_en.properties?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/engine/org/apache/derby/loc/messages_en.properties (original)
+++ db/derby/code/branches/10.2/java/engine/org/apache/derby/loc/messages_en.properties Sat Sep  9 15:04:54 2006
@@ -468,6 +468,7 @@
 2200L=Values assigned to XML columns must be well-formed DOCUMENT nodes.
 2200M=Failed to parse XMLPARSE operand; see next exception for details.
 2200V=Invalid context item for {0} operator; context items must be well-formed DOCUMENT nodes.
+2200W=XQuery serialization error: Attempted to serialize one or more top-level Attribute nodes.
 # end SQL/XML errors.
 
 23502=Column ''{0}''  cannot accept a NULL value.

Modified: db/derby/code/branches/10.2/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/shared/org/apache/derby/shared/common/reference/SQLState.java?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/shared/org/apache/derby/shared/common/reference/SQLState.java (original)
+++ db/derby/code/branches/10.2/java/shared/org/apache/derby/shared/common/reference/SQLState.java Sat Sep  9 15:04:54 2006
@@ -706,6 +706,7 @@
 	String LANG_NOT_AN_XML_DOCUMENT                                    = "2200L";
 	String LANG_INVALID_XML_DOCUMENT                                   = "2200M";
 	String LANG_INVALID_XML_CONTEXT_ITEM                               = "2200V";
+	String LANG_XQUERY_SERIALIZATION_ERROR                             = "2200W";
 
 	String LANG_INVALID_TRANSACTION_STATE                              = "25000";
     String CANNOT_CLOSE_ACTIVE_CONNECTION                              = "25001";

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/xml_general.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/xml_general.out?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/xml_general.out (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNet/xml_general.out Sat Sep  9 15:04:54 2006
@@ -479,6 +479,14 @@
 1     
 -----
 1     
+ij> values xmlexists('//lets/@doit' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
+1     
+-----
+1     
+ij> values xmlexists('//lets/@dot' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
+1     
+-----
+0     
 ij> select xmlserialize(x1 as clob) from t5 where xmlexists('//*' passing by ref x1);
 1                                                                                                                               
 -----
@@ -1100,6 +1108,97 @@
 I |2     
 -----
 6 |1     
+ij> -- DERBY-1759: Serialization of attribute nodes.
+----- Add a test row to t1.
+insert into t1 values (10,
+  xmlparse(document
+    '<threeatts first="1" second="two" third="le 3 trois"/>'
+    preserve whitespace
+  ));
+1 row inserted/updated/deleted
+ij> -- Echo t1 rows for reference.
+select i, xmlserialize(x as char(75)) from t1;
+I |2                                                                          
+-----
+1 |<update2> document was inserted as part of an UPDATE </update2>            
+2 |NULL                                                                       
+4 |NULL                                                                       
+3 |NULL                                                                       
+5 |<hmm/>                                                                     
+6 |<half> <masted> bass </masted> boosted. </half>                            
+7 |<umm> decl check </umm>                                                    
+7 |<lets> <try> this out </try> </lets>                                       
+9 |<here><is><my height="4.4">attribute</my></is></here>                      
+10 |<threeatts first="1" second="two" third="le 3 trois"/>                     
+ij> -- This should fail because XML serialization dictates that
+----- we throw an error if an attempt is made to serialize a
+----- sequence that has one or more top-level attributes nodes.
+select
+  xmlserialize(
+    xmlquery(
+      '//@*' passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+ERROR 2200W: XQuery serialization error: Attempted to serialize one or more top-level Attribute nodes.
+ij> -- Demonstrate that Xalan "string" function only returns
+----- string value of first attribute and thus cannot be
+----- used to retrieve a sequence of att values.
+select
+  xmlserialize(
+    xmlquery(
+      'string(//@*)'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+1                                                 
+-----
+4.4                                               
+1                                                 
+ij> -- Xalan doesn't have a function that allows retrieval of a
+----- sequence of attribute values.  One can only retrieve a
+----- sequence of attribute *nodes*, but since those can't be
+----- serialized (because of SQL/XML rules) the user has no
+----- way to get them.  The following is a very (VERY) ugly
+----- two-part workaround that one could use until something
+----- better is available.  First, get the max number of
+----- attributes in the table.
+select
+  max(
+    cast(
+      xmlserialize(
+        xmlquery('count(//@*)' passing by ref x empty on empty)
+      as char(50))
+    as int)
+  )
+from t1;
+1          
+-----
+3          
+WARNING 01003: Null values were eliminated from the argument of a column function. : 
+ij> -- Then use XPath position syntax to retrieve the attributes
+----- and concatenate them.  We need one call to string(//@[i])
+----- for every for every i between 1 and the value found in the
+----- preceding query.  In this case we know the max is three,
+----- so use that.
+select
+  xmlserialize(
+    xmlquery(
+      'concat(string(//@*[1]), " ",
+        string(//@*[2]), " ",
+        string(//@*[3]))'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+1                                                 
+-----
+4.4                                               
+1 two le 3 trois                                  
 ij> -- clean up.
 drop table t0;
 0 rows inserted/updated/deleted

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/xml_general.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/xml_general.out?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/xml_general.out (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/DerbyNetClient/xml_general.out Sat Sep  9 15:04:54 2006
@@ -480,6 +480,14 @@
 1     
 -----
 1     
+ij> values xmlexists('//lets/@doit' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
+1     
+-----
+1     
+ij> values xmlexists('//lets/@dot' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
+1     
+-----
+0     
 ij> select xmlserialize(x1 as clob) from t5 where xmlexists('//*' passing by ref x1);
 1                                                                                                                               
 -----
@@ -1101,6 +1109,97 @@
 I |2     
 -----
 6 |1     
+ij> -- DERBY-1759: Serialization of attribute nodes.
+----- Add a test row to t1.
+insert into t1 values (10,
+  xmlparse(document
+    '<threeatts first="1" second="two" third="le 3 trois"/>'
+    preserve whitespace
+  ));
+1 row inserted/updated/deleted
+ij> -- Echo t1 rows for reference.
+select i, xmlserialize(x as char(75)) from t1;
+I |2                                                                          
+-----
+1 |<update2> document was inserted as part of an UPDATE </update2>            
+2 |NULL                                                                       
+4 |NULL                                                                       
+3 |NULL                                                                       
+5 |<hmm/>                                                                     
+6 |<half> <masted> bass </masted> boosted. </half>                            
+7 |<umm> decl check </umm>                                                    
+7 |<lets> <try> this out </try> </lets>                                       
+9 |<here><is><my height="4.4">attribute</my></is></here>                      
+10 |<threeatts first="1" second="two" third="le 3 trois"/>                     
+ij> -- This should fail because XML serialization dictates that
+----- we throw an error if an attempt is made to serialize a
+----- sequence that has one or more top-level attributes nodes.
+select
+  xmlserialize(
+    xmlquery(
+      '//@*' passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+ERROR 2200W: XQuery serialization error: Attempted to serialize one or more top-level Attribute nodes.
+ij> -- Demonstrate that Xalan "string" function only returns
+----- string value of first attribute and thus cannot be
+----- used to retrieve a sequence of att values.
+select
+  xmlserialize(
+    xmlquery(
+      'string(//@*)'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+1                                                 
+-----
+4.4                                               
+1                                                 
+ij> -- Xalan doesn't have a function that allows retrieval of a
+----- sequence of attribute values.  One can only retrieve a
+----- sequence of attribute *nodes*, but since those can't be
+----- serialized (because of SQL/XML rules) the user has no
+----- way to get them.  The following is a very (VERY) ugly
+----- two-part workaround that one could use until something
+----- better is available.  First, get the max number of
+----- attributes in the table.
+select
+  max(
+    cast(
+      xmlserialize(
+        xmlquery('count(//@*)' passing by ref x empty on empty)
+      as char(50))
+    as int)
+  )
+from t1;
+1          
+-----
+3          
+WARNING 01003: Null values were eliminated from the argument of a column function. : 
+ij> -- Then use XPath position syntax to retrieve the attributes
+----- and concatenate them.  We need one call to string(//@[i])
+----- for every for every i between 1 and the value found in the
+----- preceding query.  In this case we know the max is three,
+----- so use that.
+select
+  xmlserialize(
+    xmlquery(
+      'concat(string(//@*[1]), " ",
+        string(//@*[2]), " ",
+        string(//@*[3]))'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+1                                                 
+-----
+4.4                                               
+1 two le 3 trois                                  
 ij> -- clean up.
 drop table t0;
 0 rows inserted/updated/deleted

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/xml_general.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/xml_general.out?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/xml_general.out (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/master/xml_general.out Sat Sep  9 15:04:54 2006
@@ -498,6 +498,14 @@
 1    
 -----
 true 
+ij> values xmlexists('//lets/@doit' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
+1    
+-----
+true 
+ij> values xmlexists('//lets/@dot' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
+1    
+-----
+false
 ij> select xmlserialize(x1 as clob) from t5 where xmlexists('//*' passing by ref x1);
 1                                                                                                                               
 --------------------------------------------------------------------------------------------------------------------------------
@@ -1126,6 +1134,99 @@
 I          |2    
 -----------------
 6          |true 
+ij> -- DERBY-1759: Serialization of attribute nodes.
+-- Add a test row to t1.
+insert into t1 values (10,
+  xmlparse(document
+    '<threeatts first="1" second="two" third="le 3 trois"/>'
+    preserve whitespace
+  ));
+1 row inserted/updated/deleted
+ij> -- Echo t1 rows for reference.
+select i, xmlserialize(x as char(75)) from t1;
+I          |2                                                                          
+---------------------------------------------------------------------------------------
+1          |<update2> document was inserted as part of an UPDATE </update2>            
+2          |NULL                                                                       
+4          |NULL                                                                       
+3          |NULL                                                                       
+5          |<hmm/>                                                                     
+6          |<half> <masted> bass </masted> boosted. </half>                            
+7          |<umm> decl check </umm>                                                    
+7          |<lets> <try> this out </try> </lets>                                       
+9          |<here><is><my height="4.4">attribute</my></is></here>                      
+10         |<threeatts first="1" second="two" third="le 3 trois"/>                     
+ij> -- This should fail because XML serialization dictates that
+-- we throw an error if an attempt is made to serialize a
+-- sequence that has one or more top-level attributes nodes.
+select
+  xmlserialize(
+    xmlquery(
+      '//@*' passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+1                                                 
+--------------------------------------------------
+ERROR 2200W: XQuery serialization error: Attempted to serialize one or more top-level Attribute nodes.
+ij> -- Demonstrate that Xalan "string" function only returns
+-- string value of first attribute and thus cannot be
+-- used to retrieve a sequence of att values.
+select
+  xmlserialize(
+    xmlquery(
+      'string(//@*)'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+1                                                 
+--------------------------------------------------
+4.4                                               
+1                                                 
+ij> -- Xalan doesn't have a function that allows retrieval of a
+-- sequence of attribute values.  One can only retrieve a
+-- sequence of attribute *nodes*, but since those can't be
+-- serialized (because of SQL/XML rules) the user has no
+-- way to get them.  The following is a very (VERY) ugly
+-- two-part workaround that one could use until something
+-- better is available.  First, get the max number of
+-- attributes in the table.
+select
+  max(
+    cast(
+      xmlserialize(
+        xmlquery('count(//@*)' passing by ref x empty on empty)
+      as char(50))
+    as int)
+  )
+from t1;
+1          
+-----------
+3          
+WARNING 01003: Null values were eliminated from the argument of a column function.
+ij> -- Then use XPath position syntax to retrieve the attributes
+-- and concatenate them.  We need one call to string(//@[i])
+-- for every for every i between 1 and the value found in the
+-- preceding query.  In this case we know the max is three,
+-- so use that.
+select
+  xmlserialize(
+    xmlquery(
+      'concat(string(//@*[1]), " ",
+        string(//@*[2]), " ",
+        string(//@*[3]))'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+1                                                 
+--------------------------------------------------
+4.4                                               
+1 two le 3 trois                                  
 ij> -- clean up.
 drop table t0;
 0 rows inserted/updated/deleted

Modified: db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/lang/xml_general.sql
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/lang/xml_general.sql?view=diff&rev=441864&r1=441863&r2=441864
==============================================================================
--- db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/lang/xml_general.sql (original)
+++ db/derby/code/branches/10.2/java/testing/org/apache/derbyTesting/functionTests/tests/lang/xml_general.sql Sat Sep  9 15:04:54 2006
@@ -210,6 +210,8 @@
 select i, xmlexists('//lets' passing by ref x) from t1;
 values xmlexists('//let' passing by ref xmlparse(document '<lets> try this </lets>' preserve whitespace));
 values xmlexists('//lets' passing by ref xmlparse(document '<lets> try this </lets>' preserve whitespace));
+values xmlexists('//lets/@doit' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
+values xmlexists('//lets/@dot' passing by ref xmlparse(document '<lets doit="true"> try this </lets>' preserve whitespace));
 select xmlserialize(x1 as clob) from t5 where xmlexists('//*' passing by ref x1);
 select xmlserialize(x2 as clob) from t5 where xmlexists('//*' passing by ref x2);
 select xmlserialize(x1 as clob), xmlexists('//*' passing by ref xmlparse(document '<badboy/>' preserve whitespace)) from t5;
@@ -622,6 +624,78 @@
     xmlquery('.' passing by ref x empty on empty)
   )
 from t1 where i = 6;
+
+-- DERBY-1759: Serialization of attribute nodes.
+
+-- Add a test row to t1.
+insert into t1 values (10,
+  xmlparse(document
+    '<threeatts first="1" second="two" third="le 3 trois"/>'
+    preserve whitespace
+  ));
+
+-- Echo t1 rows for reference.
+select i, xmlserialize(x as char(75)) from t1;
+
+-- This should fail because XML serialization dictates that
+-- we throw an error if an attempt is made to serialize a
+-- sequence that has one or more top-level attributes nodes.
+select
+  xmlserialize(
+    xmlquery(
+      '//@*' passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+
+-- Demonstrate that Xalan "string" function only returns
+-- string value of first attribute and thus cannot be
+-- used to retrieve a sequence of att values.
+select
+  xmlserialize(
+    xmlquery(
+      'string(//@*)'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
+
+-- Xalan doesn't have a function that allows retrieval of a
+-- sequence of attribute values.  One can only retrieve a
+-- sequence of attribute *nodes*, but since those can't be
+-- serialized (because of SQL/XML rules) the user has no
+-- way to get them.  The following is a very (VERY) ugly
+-- two-part workaround that one could use until something
+-- better is available.  First, get the max number of
+-- attributes in the table.
+select
+  max(
+    cast(
+      xmlserialize(
+        xmlquery('count(//@*)' passing by ref x empty on empty)
+      as char(50))
+    as int)
+  )
+from t1; 
+
+-- Then use XPath position syntax to retrieve the attributes
+-- and concatenate them.  We need one call to string(//@[i])
+-- for every for every i between 1 and the value found in the
+-- preceding query.  In this case we know the max is three,
+-- so use that.
+select
+  xmlserialize(
+    xmlquery(
+      'concat(string(//@*[1]), " ",
+        string(//@*[2]), " ",
+        string(//@*[3]))'
+      passing by ref x empty on empty
+    )
+  as char(50))
+from t1
+where xmlexists('//@*' passing by ref x);
 
 -- clean up.
 drop table t0;