/*
 *
 * Derby - Class org.apache.derbyTesting.functionTests.tests.lang.XMLTypeAndOpsTest
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); 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.
 */
package org.apache.derbyTesting.functionTests.tests.lang;

import junit.framework.Test;
import junit.framework.TestSuite;

import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.XML;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseJDBCTestSetup;
import org.apache.derbyTesting.junit.TestConfiguration;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Types;

/**
 * XMLTypeAndOpsTest this test is the JUnit equivalent to what used
 * to be the "lang/xml_general.sql" test, which was canon-based.
 * Since the .sql test had different masters for embedded, JCC, and 
 * Derby Client, and since it performed some "sed'ing" to ensure
 * consistent results across JVMs, it was not sufficient to just
 * wrap the test in a JUnit ScriptTestCase (because ScriptTestCase
 * doesn't deal with multiple masters nor with sed'ing).  Hence the
 * creation of this pure JUnit version of the test.
 */
public final class XMLTypeAndOpsTest extends BaseJDBCTestCase {
    
    /* For the test methods in this class, "expRS" refers to a
     * two-dimensional array representing an expected result set when
     * executing queries.  The "rows" and "columns" in this array
     * are compard with those from a SQL ResultSet.  Note that all
     * values are represented as Strings here; we don't actually
     * check the *types* of the columns; just their values.  This
     * is because this test was created from a .sql test, where
     * results are similarly treated (i.e. in an ij test with a
     * .sql file, the test passes if the values "look the same";
     * there's no checking of specific value types for most query
     * results.  So we do the same for this JUnit test).
     */

    /**
     * Public constructor required for running test as standalone JUnit.
     */
    public XMLTypeAndOpsTest(String name)
    {
        super(name);
    }

    /**
     * Return a suite that runs a set of tests which are meant to
     * be the equivalent to the test cases in the old xml_general.sql.
     * But only return such a suite IF the testing classpath has the
     * required XML classes.  Otherwise just return an empty suite.
     */
    public static Test suite()
    {
        TestSuite suite = new TestSuite("XML Type and Operators Suite\n");
        if (!XML.classpathMeetsXMLReqs())
            return suite;

        /* "false" in the next line means that we will *not* clean the
         * database before the embedded and client suites.  This ensures
         * that we do not remove the objects created by XMLTestSetup.
         */
        suite.addTest(
            TestConfiguration.defaultSuite(XMLTypeAndOpsTest.class, false));

        return (new XMLTestSetup(suite));
    }

    /**
     * Test creation of XML columns.
     */
    public void testXMLColCreation() throws Exception
    {
        // If the column's definition doesn't make sense for XML,
        // then we should throw the correct error.

        assertCompileError("42894",
            "create table fail1 (i int, x xml default 'oops')");

        assertCompileError("42894",
            "create table fail2 (i int, x xml default 8)");

        assertCompileError("42818",
            "create table fail3 (i int, x xml check (x != 0))");

        // These should all work.

        Statement st = createStatement();
        st.executeUpdate("create table tc1 (i int, x xml)");
        st.executeUpdate("create table tc2 (i int, x xml not null)");
        st.executeUpdate("create table tc3 (i int, x xml default null)");
        st.executeUpdate("create table tc4 (x2 xml not null)");
        st.executeUpdate("alter table tc4 add column x1 xml");

        // Cleanup.

        st.executeUpdate("drop table tc1");
        st.executeUpdate("drop table tc2");
        st.executeUpdate("drop table tc3");
        st.executeUpdate("drop table tc4");
        st.close();
    }

    /**
     * Check insertion of null values into XML columns.  This
     * test just checks the negative cases--i.e. cases where
     * we expect the insertions to fail.  The positive cases
     * are tested implicitly as part of XMLTestSetup.setUp()
     * when we load the test data.
     */
    public void testIllegalNullInserts() throws Exception
    {
        // These should fail because target column is declared
        // as non-null.

        Statement st = createStatement();
        st.executeUpdate("create table tc2 (i int, x xml not null)");
        assertStatementError("23502", st, "insert into tc2 values (1, null)");
        assertStatementError("23502", st,
            "insert into tc2 values (2, cast (null as xml))");
        st.executeUpdate("drop table tc2");
        st.close();
    }

    /**
     * Test insertion of non-XML values into XML columns.  These
     * should all fail because such an operation is not allowed.
     */
    public void testXMLColsWithNonXMLVals() throws Exception
    {
        assertCompileError("42821", "insert into t1 values (3, 'hmm')");
        assertCompileError("42821", "insert into t1 values (1, 2)");
        assertCompileError("42821", "insert into t1 values (1, 123.456)");

        assertCompileError("42821", "insert into t1 values (1, x'01')");
        assertCompileError("42821", "insert into t1 values (1, x'ab')");
        assertCompileError("42821", "insert into t1 values (1, current date)");

        assertCompileError("42821", "insert into t1 values (1, current time)");

        assertCompileError("42821",
            " insert into t1 values (1, current timestamp)");

        assertCompileError("42821",
            " insert into t1 values (1, ('hmm' || 'andstuff'))");
    }

    /**
     * Test insertion of XML values into non-XML columns.  These
     * should all fail because such an operation is not allowed.
     */
    public void testNonXMLColsWithXMLVals() throws Exception
    {
        Statement st = createStatement();
        st.executeUpdate(
            "create table nonXTable (si smallint, i int, bi bigint, vcb "
            + "varchar (32) for bit data, nu numeric(10,2), f "
            + "float, d double, vc varchar(20), da date, ti time, "
            + "ts timestamp, cl clob, bl blob)");

        assertCompileError("42821",
            "insert into nonXTable (si) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (i) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (bi) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (vcb) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (nu) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (f) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (d) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (vc) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (da) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (ti) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (ts) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (cl) values (cast (null as xml))");
        
        assertCompileError("42821",
            "insert into nonXTable (bl) values (cast (null as xml))");

        // And just to be safe, try to insert a non-null XML
        // value.  This should fail, too.

        assertCompileError("42821",
            "insert into nonXTable (cl) values (xmlparse(document " +
            "'</simp>' preserve whitespace))");

        st.executeUpdate("drop table nonXTable");
        st.close();
    }

    /**
     * Test casting of values to type XML.  These should all
     * fail because such casting is not allowed.
     */
    public void testXMLCasting() throws Exception
    {
        assertCompileError("42846",
            "insert into t1 values (1, cast ('hmm' as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (2 as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (123.456 as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (x'01' as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (x'ab' as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (current date as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (current time as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (current timestamp as xml))");
        
        assertCompileError("42846",
            "insert into t1 values (1, cast (('hmm' || "
            + "'andstuff') as xml))");

        // And try to cast an XML value into something else.
        // These should fail, too.

        Statement st = createStatement();
        st.executeUpdate("create table nonXTable (i int, cl clob)");

        assertCompileError("42846",
            "insert into nonXTable (cl) values (cast ((xmlparse(document " +
            "'</simp>' preserve whitespace)) as clob))");

        assertCompileError("42846",
            "insert into nonXTable (i) values (cast ((xmlparse(document " +
            "'</simp>' preserve whitespace)) as int))");

        st.executeUpdate("drop table nonXTable");
        st.close();
    }

    /**
     * Try to use XML values in non-XML operations.  These
     * should all fail (the only operations allowed with XML
     * are the specified XML operations (xmlparse, xmlserialize,
     * xmlexists, xmlquery)).
     */
    public void testXMLInNonXMLOps() throws Exception
    {
        assertCompileError("42Y95", "select i + x from t1");
        assertCompileError("42Y95", "select i * x from t1");
        assertCompileError("42Y95", "select i / x from t1");
        assertCompileError("42Y95", "select i - x from t1");
        assertCompileError("42X37", "select -x from t1");
        assertCompileError("42846", "select 'hi' || x from t1");
        assertCompileError("42X25", "select substr(x, 0) from t1");
        assertCompileError("42Y22", "select max(x) from t1");
        assertCompileError("42Y22", "select min(x) from t1");
        assertCompileError("42X25", "select length(x) from t1");
        assertCompileError("42884", "select i from t1 where x like 'hmm'");
    }

    /**
     * Test simple comparisons with XML.  These should all fail
     * because no such comparisons are allowed.
     */
    public void testXMLComparisons() throws Exception
    {
        assertCompileError("42818", "select i from t1 where x = 'hmm'");
        assertCompileError("42818", "select i from t1 where x > 0");
        assertCompileError("42818", "select i from t1 where x < x");
        assertCompileError("42818", "select i from t1 where x <> 'some char'");
    }

    /**
     * Test additional restrictions on use of XML values.
     * These should all fail.
     */
    public void testIllegalOps() throws Exception
    {
        // Indexing/ordering on XML cols is not allowed.

        assertCompileError("X0X67", "create index oops_ix on t1(x)");
        assertCompileError("X0X67",
            "select i from t1 where x is null order by x");
        
        // XML cannot be imported or exported (DERBY-1892).

        CallableStatement cSt = prepareCall(
            "CALL SYSCS_UTIL.SYSCS_EXPORT_TABLE ("
            + "  null, 'T1', 'extinout/xmlexport.del', null, null, null)");

        assertStatementError("42Z71", cSt);
        
        cSt = prepareCall(
            " CALL SYSCS_UTIL.SYSCS_EXPORT_QUERY("
            + "  'select x from t1', 'extinout/xmlexport.del', null, null, null)");
        assertStatementError("42Z71", cSt);

        cSt = prepareCall(
            " CALL SYSCS_UTIL.SYSCS_IMPORT_TABLE ("
            + "  null, 'T1', 'extinout/shouldntmatter.del', null, null, null, 0)");
        assertStatementError("XIE0B", cSt);
        
        cSt = prepareCall(
            " CALL SYSCS_UTIL.SYSCS_IMPORT_DATA ("
            + "  NULL, 'T1', null, '2', 'extinout/shouldntmatter.del', "
            + "null, null, null,0)");
        assertStatementError("XIE0B", cSt);

        // Done with cSt.
        cSt.close();
        
        // XML cannot be used with procedures/functions.

        assertCompileError("42962",
            "create procedure hmmproc (in i int, in x xml)"
            + "  parameter style java language java external name "
            + "'hi.there'");
        
        assertCompileError("42962",
            " create function hmmfunc (i int, x xml) returns int"
            + "  parameter style java language java external name "
            + "'hi.there'");
        
        // XML columns cannot be used for global temporary 
        // tables.

        assertCompileError("42962",
            "declare global temporary table SESSION.xglobal (myx XML)"
            + "  not logged on commit preserve rows");
    }

    /**
     * Test use of XML columns in a trigger's "SET" clause.  Should
     * work so long as the target value has type XML.
     */
    public void testTriggerSetXML() throws Exception
    {
        // This should fail.
        assertCompileError("42821",
            "create trigger tr2 after insert on t1 for each row "
            + "mode db2sql update t1 set x = 'hmm'");

        // This should succeed.
        Statement st = createStatement();
        st.executeUpdate("create trigger tr1 after insert on t1 for each row "
            + "mode db2sql update t1 set x = null");

        st.executeUpdate(" drop trigger tr1");
        st.close();
    }

    /**
     * Various tests for the XMLPARSE operator.  Note that this
     * test primarily checks the negative cases--i.e. cases where
     * we expect the XMLPARSE op to fail.  The positive cases
     * were tested implicitly as part of XMLTestSetup.setUp()
     * when we loaded the test data.
     */
    public void testXMLParse() throws Exception
    {
        // These should fail with various parse errors.

        Statement st = createStatement();
        assertCompileError("42Z74",
            "insert into t1 values (1, xmlparse(document "
            + "'<hmm/>' strip whitespace))");
        
        assertCompileError("42Z72",
            " insert into t1 values (1, xmlparse(document '<hmm/>'))");
        
        assertCompileError("42Z72",
            " insert into t1 values (1, xmlparse('<hmm/>' "
            + "preserve whitespace))");
        
        assertCompileError("42Z74",
            " insert into t1 values (1, xmlparse(content "
            + "'<hmm/>' preserve whitespace))");
        
        assertCompileError("42X25",
            " select xmlparse(document xmlparse(document "
            + "'<hein/>' preserve whitespace) preserve whitespace) from t1");
        
        assertCompileError("42X19",
            " select i from t1 where xmlparse(document '<hein/>' "
            + "preserve whitespace)");

        // This should fail because operand does not constitute
        // well-formed XML.
        
        assertStatementError("2200M", st,
            " insert into t1 values (1, xmlparse(document "
            + "'<oops>' preserve whitespace))");

        // This should fail because use of a parameter for the operand
        // requires an explicit CAST to a char type.

        assertCompileError("42Z79",
            "insert into t1(x) values XMLPARSE(document ? "
            + "preserve whitespace)");
        
        // Creation of a table with a default as XMLPARSE should throw
        // an error--use of functions as a default is not allowed
        // by the Derby syntax.

        assertCompileError("42894",
            "create table fail1 (i int, x xml default xmlparse("
            + "document '<my>default col</my>' preserve whitespace))");

        // XMLPARSE is valid operand for "is [not] null" so
        // this should work (and we should see a row for every
        // successful "insert" statement that we executed on T1).

        ResultSet rs = st.executeQuery(
            " select i from t1 where xmlparse(document '<hein/>' "
            + "preserve whitespace) is not null");

        String [] expColNames = new String [] { "I" };
        JDBC.assertColumnNames(rs, expColNames);
        
        String [][] expRS = new String [][]
        {
            {"1"},
            {"2"},
            {"4"},
            {"3"},
            {"5"},
            {"6"},
            {"7"},
            {"8"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Ensure that order by on a table with an XML column in it
        // works correctly.
        
        rs = st.executeQuery(
            " select i from t1 where xmlparse(document '<hein/>' "
            + "preserve whitespace) is not null order by i");

        expColNames = new String [] { "I" };
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"1"},
            {"2"},
            {"3"},
            {"4"},
            {"5"},
            {"6"},
            {"7"},
            {"8"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // Insertions using XMLPARSE with a parameter that is cast
        // to a character type should work.

        st.execute("create table paramInsert(x xml)");
        PreparedStatement pSt = prepareStatement(
            "insert into paramInsert values XMLPARSE(document "
            + "cast (? as CLOB) preserve whitespace)");
        
        pSt.setString(1, "<ay>caramba</ay>");
        assertUpdateCount(pSt, 1);
        pSt.close();

        // Run a select to view everything that was inserted.

        rs = st.executeQuery("select xmlserialize(x as clob) from t1");

        expColNames = new String [] { "1" };
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"<update2> document was inserted as part of an "
                + "UPDATE </update2>"},
            {null},
            {null},
            {null},
            {"<hmm/>"},
            {"<half> <masted> bass </masted> boosted. </half>"},
            {"<umm> decl check </umm>"},
            {"<lets> <try> this out </try> </lets>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select xmlserialize(x as clob) from paramInsert");

        expColNames = new String [] { "1" };
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"<ay>caramba</ay>"}
        };

        // Cleanup.

        st.executeUpdate("drop table paramInsert");
        st.close();
    }

    /**
     * Test use of the "is [not] null" clause with XML values.
     * These should work.
     */
    public void testIsNull() throws Exception
    {
        Statement st = createStatement();
        ResultSet rs = st.executeQuery(
            "select i from t1 where x is not null");
        
        String [] expColNames = new String [] { "I" };
        JDBC.assertColumnNames(rs, expColNames);
        
        String [][] expRS = new String [][]
        {
            {"1"},
            {"5"},
            {"6"},
            {"7"},
            {"8"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(" select i from t1 where x is null");

        expColNames = new String [] { "I" };
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"2"},
            {"4"},
            {"3"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        st.close();
    }

    /**
     * Derby doesn't currently support XML values in a top-level
     * result set.  So make sure that doesn't work.  These should
     * all fail.
     */
    public void testTopLevelSelect() throws Exception
    {
        Statement st = createStatement();
        st.executeUpdate("create table vcTab (vc varchar(100))");

        assertCompileError("42Z71", "select x from t1");
        assertCompileError("42Z71", "select * from t1");
        assertCompileError("42Z71",
            " select xmlparse(document vc preserve whitespace) from vcTab");
        
        assertCompileError("42Z71",
            " values xmlparse(document '<bye/>' preserve whitespace)");
        
        assertCompileError("42Z71",
            " values xmlparse(document '<hel' || 'lo/>' preserve "
            + "whitespace)");

        st.executeUpdate("drop table vcTab");
        st.close();
    }

    /**
     * Various tests for the XMLSERIALIZE operator.
     */
    public void testXMLSerialize() throws Exception
    {
        // Test setup.

        Statement st = createStatement();
        st.executeUpdate("create table vcTab (vc varchar(100))");
        assertUpdateCount(st, 1, "insert into vcTab values ('<hmm/>')");
        assertUpdateCount(st, 1, "insert into vcTab values 'no good'");
        
        // These should fail with various parse errors.

        assertCompileError("42Z72", "select xmlserialize(x) from t1");
        assertCompileError("42X01", "select xmlserialize(x as) from t1");
        assertCompileError("42Z73",
            " select xmlserialize(x as int) from t1");

        assertCompileError("42Z73",
            " select xmlserialize(x as boolean) from t1");
        
        assertCompileError("42Z73",
            " select xmlserialize(x as varchar(20) for bit data) from t1");
        
        assertCompileError("42X04",
            " select xmlserialize(y as char(10)) from t1");
        
        assertCompileError("42X25",
            " select xmlserialize(xmlserialize(x as clob) as "
            + "clob) from t1");
        
        assertCompileError("42X25",
            " values xmlserialize('<okay> dokie </okay>' as clob)");
        
        // These should succeed.
        
        ResultSet rs = st.executeQuery("select xmlserialize(x as clob) from t1");

        String [] expColNames = new String [] { "1" };
        JDBC.assertColumnNames(rs, expColNames);
        
        String [][] expRS = new String [][]
        {
            {"<update2> document was inserted as part of an "
                + "UPDATE </update2>"},
            {null},
            {null},
            {null},
            {"<hmm/>"},
            {"<half> <masted> bass </masted> boosted. </half>"},
            {"<umm> decl check </umm>"},
            {"<lets> <try> this out </try> </lets>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlserialize(x1 as clob), xmlserialize(x2 as "
            + "clob) from t2");
        
        expColNames = new String [] {"1", "2"};
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {null, "<notnull/>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlserialize(x as char(100)) from t1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"<update2> document was inserted as part of an "
                + "UPDATE </update2>"},
            {null},
            {null},
            {null},
            {"<hmm/>"},
            {"<half> <masted> bass </masted> boosted. </half>"},
            {"<umm> decl check </umm>"},
            {"<lets> <try> this out </try> </lets>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlserialize(x as varchar(300)) from t1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"<update2> document was inserted as part of an "
                + "UPDATE </update2>"},
            {null},
            {null},
            {null},
            {"<hmm/>"},
            {"<half> <masted> bass </masted> boosted. </half>"},
            {"<umm> decl check </umm>"},
            {"<lets> <try> this out </try> </lets>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // This should succeed at the XMLPARSE level, but fail 
        // with parse/truncation errors at the XMLSERIALIZE.
        
        assertStatementError("2200M", st,
            "select xmlserialize(xmlparse(document vc preserve "
            + "whitespace) as char(10)) from vcTab");
        
        // These should all fail with truncation errors.

        assertStatementError("22001", st,
            " select xmlserialize(x as char) from t1");
        
        assertStatementError("22001", st,
            " select xmlserialize(x as clob(10)) from t1");
        
        assertStatementError("22001", st,
            " select xmlserialize(x as char(1)) from t1");
        
        assertStatementError("22001", st,
            " select length(xmlserialize(x as char(1))) from t1");
        
        assertStatementError("22001", st,
            " select xmlserialize(x as varchar(1)) from t1");
        
        assertStatementError("22001", st,
            " select length(xmlserialize(x as varchar(1))) from t1");
        
        // These checks verify that the XMLSERIALIZE result is the 
        // correct type.

        rs = st.executeQuery("select xmlserialize(x as char(100)) from t1");
        
        ResultSetMetaData rsmd = rs.getMetaData();
        assertEquals("Incorrect XMLSERIALIZE result type:",
            Types.CHAR, rsmd.getColumnType(1));

        rs = st.executeQuery("select xmlserialize(x as varchar(100)) from t1");
        
        rsmd = rs.getMetaData();
        assertEquals("Incorrect XMLSERIALIZE result type:",
            Types.VARCHAR, rsmd.getColumnType(1));

        rs = st.executeQuery("select xmlserialize(x as long varchar) from t1");
        
        rsmd = rs.getMetaData();
        assertEquals("Incorrect XMLSERIALIZE result type:",
            Types.LONGVARCHAR, rsmd.getColumnType(1));

        rs = st.executeQuery("select xmlserialize(x as clob(100)) from t1");
        
        rsmd = rs.getMetaData();
        assertEquals("Incorrect XMLSERIALIZE result type:",
            Types.CLOB, rsmd.getColumnType(1));

        // Cleanup.

        rs.close();
        st.executeUpdate("drop table vcTab");
        st.close();
    }

    /**
     * Various tests with XMLPARSE and XMLSERIALIZE combinations.
     */
    public void testXMLParseSerializeCombos() throws Exception
    {
        // These should fail at the XMLPARSE level.

        Statement st = createStatement();
        assertStatementError("2200M", st,
            "select xmlserialize(xmlparse(document '<hmm>' "
            + "preserve whitespace) as clob) from t1");
        
        assertCompileError("42X25",
            " select xmlserialize(xmlparse(document x preserve "
            + "whitespace) as char(100)) from t1");
        
        // These should succeed.
        
        ResultSet rs = st.executeQuery(
            "select xmlserialize(xmlparse(document '<hmm/>' "
            + "preserve whitespace) as clob) from t1 where i = 1");
        
        String [] expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        
        String [][] expRS = new String [][]
        {
            {"<hmm/>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlserialize(xmlparse(document "
            + "xmlserialize(x as clob) preserve whitespace) as "
            + "clob) from t1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"<update2> document was inserted as part of an "
                + "UPDATE </update2>"},
            {null},
            {null},
            {null},
            {"<hmm/>"},
            {"<half> <masted> bass </masted> boosted. </half>"},
            {"<umm> decl check </umm>"},
            {"<lets> <try> this out </try> </lets>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "values xmlserialize(xmlparse(document '<okay> dokie "
            + "</okay>' preserve whitespace) as clob)");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"<okay> dokie </okay>"}
        };
        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i from t1 where xmlparse(document "
            + "xmlserialize(x as clob) preserve whitespace) is not "
            + "null order by i");
        
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        
        expRS = new String [][]
        {
            {"1"},
            {"5"},
            {"6"},
            {"7"},
            {"8"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        st.close();
    }

    /**
     * Various tests for the XMLEXISTS operator.
     */
    public void testXMLExists() throws Exception
    {
        // These should fail with various parse errors.

        assertCompileError("42X01",
            "select i from t1 where xmlexists(x)");
        
        assertCompileError("42X01",
            "select i from t1 where xmlexists(i)");
        
        assertCompileError("42X01",
            "select i from t1 where xmlexists('//*')");
        
        assertCompileError("42X01",
            "select i from t1 where xmlexists('//*' x)");
        
        assertCompileError("42X01",
            "select i from t1 where xmlexists('//*' passing x)");
        
        assertCompileError("42Z74",
            "select i from t1 where xmlexists('//*' passing by value x)");
        
        assertCompileError("42Z77",
            "select i from t1 where xmlexists('//*' passing by ref i)");
        
        assertCompileError("42Z75",
            "select i from t1 where xmlexists(i passing by ref x)");
        
        assertCompileError("42Z76",
            "select i from t1 where xmlexists(i passing by ref x, x)");
        
        // These should succeed.
        
        Statement st = createStatement();
        ResultSet rs = st.executeQuery(
            "select i from t1 where xmlexists('//*' passing by ref x)");
        
        String [] expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        
        String [][] expRS = new String [][]
        {
            {"1"},
            {"5"},
            {"6"},
            {"7"},
            {"8"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // This should succeed but return no rows.

        rs = st.executeQuery(
            "select i from t1 where xmlexists('//person' passing "
            + "by ref x)");
        
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        // This should return one row.
        
        rs = st.executeQuery(
            "select i from t1 where xmlexists('//lets' passing by ref x)");
        
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"8"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // XMLEXISTS should return null if the operand is null.

        rs = st.executeQuery(
            "select xmlexists('//lets' passing by ref x) from t1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {null},
            {null},
            {null},
            {"false"},
            {"false"},
            {"false"},
            {"true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//try[text()='' this out '']' "
            + "passing by ref x) from t1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {null},
            {null},
            {null},
            {"false"},
            {"false"},
            {"false"},
            {"true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//let' passing by ref x) from t1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {null},
            {null},
            {null},
            {"false"},
            {"false"},
            {"false"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//try[text()='' this in '']' "
            + "passing by ref x) from t1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {null},
            {null},
            {null},
            {"false"},
            {"false"},
            {"false"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // Make sure selection of other columns along with XMLEXISTS
        // still works.

        rs = st.executeQuery(
            "select i, xmlexists('//let' passing by ref x) from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "false"},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", "false"},
            {"6", "false"},
            {"7", "false"},
            {"8", "false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i, xmlexists('//lets' passing by ref x) from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "false"},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", "false"},
            {"6", "false"},
            {"7", "false"},
            {"8", "true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // XMLEXISTS should work in a VALUES clause, too.
        
        rs = st.executeQuery(
            "values xmlexists('//let' passing by ref "
            + "xmlparse(document '<lets> try this </lets>' "
            + "preserve whitespace))");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "values xmlexists('//lets' passing by ref "
            + "xmlparse(document '<lets> try this </lets>' "
            + "preserve whitespace))");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Simple check for attribute existence.
        
        rs = st.executeQuery(
            "values xmlexists('//lets/@doit' passing by ref "
            + "xmlparse(document '<lets doit=\"true\"> try this "
            + "</lets>' preserve whitespace))");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "values xmlexists('//lets/@dot' passing by ref "
            + "xmlparse(document '<lets doit=\"true\"> try this "
            + "</lets>' preserve whitespace))");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // XMLEXISTS in a WHERE clause.
        
        rs = st.executeQuery(
            "select xmlserialize(x1 as clob) from t2 where "
            + "xmlexists('//*' passing by ref x1)");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);
        
        rs = st.executeQuery(
            "select xmlserialize(x2 as clob) from t2 where "
            + "xmlexists('//*' passing by ref x2)");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"<notnull/>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlserialize(x1 as clob), xmlexists('//*' "
            + "passing by ref xmlparse(document '<badboy/>' "
            + "preserve whitespace)) from t2");
        
        expColNames = new String [] {"1", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {null, "true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlserialize(x1 as clob), "
            + "xmlexists('//goodboy' passing by ref "
            + "xmlparse(document '<badboy/>' preserve whitespace)) from t2");
        
        expColNames = new String [] {"1", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {null, "false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Add some more test tables/data.

        st.executeUpdate(
            "create table xqExists2 (i int, x1 xml, x2 xml not null)");
        
        assertUpdateCount(st, 1,
            " insert into xqExists2 values (1, null, xmlparse(document "
            + "'<ok/>' preserve whitespace))");
        
        rs = st.executeQuery(
            "select i, xmlserialize(x1 as char(10)), "
            + "xmlserialize (x2 as char(10)) from xqExists2");
        
        expColNames = new String [] {"I", "2", "3"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", null, "<ok/>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Now run some XMLEXISTS queries on xqExists2 using boolean
        // operations ('and', 'or') on the XMLEXISTS result.
        
        rs = st.executeQuery(
            "select i from xqExists2 where xmlexists('/ok' passing by "
            + "ref x1) and xmlexists('/ok' passing by ref x2)");
        
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select i from xqExists2 where xmlexists('/ok' passing by "
            + "ref x1) or xmlexists('/ok' passing by ref x2)");
        
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // XMLEXISTS can be used wherever a boolean function is 
        // allowed, for ex, a check constraint...
        
        st.executeUpdate(
            "create table xqExists1 (i int, x xml check "
            + "(xmlexists('//should' passing by ref x)))");
        
        assertUpdateCount(st, 1,
            " insert into xqExists1 values (1, xmlparse(document "
            + "'<should/>' preserve whitespace))");
        
        assertStatementError("23513", st,
            " insert into xqExists1 values (1, xmlparse(document "
            + "'<shouldnt/>' preserve whitespace))");
        
        rs = st.executeQuery(
            "select xmlserialize(x as char(20)) from xqExists1");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"<should/>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // Do some namespace queries/examples.
        
        st.executeUpdate("create table xqExists3 (i int, x xml)");

        assertUpdateCount(st, 1,
            " insert into xqExists3 values (1, xmlparse(document '<a:hi "
            + "xmlns:a=\"http://www.hi.there\"/>' preserve whitespace))");
        
        assertUpdateCount(st, 1,
            " insert into xqExists3 values (2, xmlparse(document '<b:hi "
            + "xmlns:b=\"http://www.hi.there\"/>' preserve whitespace))");
        
        assertUpdateCount(st, 1,
            " insert into xqExists3 values (3, xmlparse(document "
            + "'<a:bye xmlns:a=\"http://www.good.bye\"/>' preserve "
            + "whitespace))");
        
        assertUpdateCount(st, 1,
            " insert into xqExists3 values (4, xmlparse(document "
            + "'<b:bye xmlns:b=\"http://www.hi.there\"/>' preserve "
            + "whitespace))");
        
        assertUpdateCount(st, 1,
            " insert into xqExists3 values (5, xmlparse(document "
            + "'<hi/>' preserve whitespace))");
        
        rs = st.executeQuery(
            "select xmlexists('//child::*[name()=\"none\"]' "
            + "passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {"false"},
            {"false"},
            {"false"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//child::*[name()=''hi'']' "
            + "passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {"false"},
            {"false"},
            {"false"},
            {"true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//child::*[local-name()=''hi'']' "
            + "passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"true"},
            {"true"},
            {"false"},
            {"false"},
            {"true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//child::*[local-name()=''bye'']' "
            + "passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {"false"},
            {"true"},
            {"true"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select "
            + "xmlexists('//*[namespace::*[string()=''http://www.hi"
            + ".there'']]' passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"true"},
            {"true"},
            {"false"},
            {"true"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select "
            + "xmlexists('//*[namespace::*[string()=''http://www.go"
            + "od.bye'']]' passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {"false"},
            {"true"},
            {"false"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//child::*[local-name()=''hi'' "
            + "and "
            + "namespace::*[string()=''http://www.hi.there'']]' "
            + "passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"true"},
            {"true"},
            {"false"},
            {"false"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//child::*[local-name()=''bye'' "
            + "and "
            + "namespace::*[string()=''http://www.good.bye'']]' "
            + "passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {"false"},
            {"true"},
            {"false"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select xmlexists('//child::*[local-name()=''bye'' "
            + "and "
            + "namespace::*[string()=''http://www.hi.there'']]' "
            + "passing by ref x) from xqExists3");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"false"},
            {"false"},
            {"false"},
            {"true"},
            {"false"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Cleanup.

        st.executeUpdate("drop table xqExists1");
        st.executeUpdate("drop table xqExists2");
        st.executeUpdate("drop table xqExists3");
        st.close();
    }

    /**
     * Various tests for the XMLQUERY operator.
     */
    public void testXMLQuery() throws Exception
    {
        // These should fail w/ syntax errors.

        assertCompileError("42X01", "select i, xmlquery('//*') from t1");
        assertCompileError("42X01",
            " select i, xmlquery('//*' passing) from t1");
        
        assertCompileError("42X01",
            " select i, xmlquery('//*' passing by ref x) from t1");
        
        assertCompileError("42X01",
            " select i, xmlquery('//*' passing by ref x "
            + "returning sequence) from t1");
        
        assertCompileError("42X01",
            " select i, xmlquery(passing by ref x empty on empty) from t1");
        
        assertCompileError("42X01",
            " select i, xmlquery(xmlquery('//*' returning "
            + "sequence empty on empty) as char(75)) from t1");
        
        // These should fail with "not supported" errors.
        
        assertCompileError("42Z74",
            "select i, xmlquery('//*' passing by ref x returning "
            + "sequence null on empty) from t1");
        
        assertCompileError("42Z74",
            " select i, xmlquery('//*' passing by ref x "
            + "returning content empty on empty) from t1");
        
        // This should fail because XMLQUERY returns an XML value 
        // which is not allowed in top-level result set.

        assertCompileError("42Z71",
            "select i, xmlquery('//*' passing by ref x empty on "
            + "empty) from t1");
        
        // These should fail because context item must be XML.

        assertCompileError("42Z77",
            "select i, xmlquery('//*' passing by ref i empty on "
            + "empty) from t1");
        
        assertCompileError("42Z77",
            " select i, xmlquery('//*' passing by ref 'hello' "
            + "empty on empty) from t1");
        
        assertCompileError("42Z77",
            " select i, xmlquery('//*' passing by ref cast "
            + "('hello' as clob) empty on empty) from t1");
        
        // This should fail because the function is not recognized 
        // by Xalan. The failure should be an error from Xalan 
        // saying what the problem is; it should *NOT* be a NPE, 
        // which is what we were seeing before DERBY-688 was completed.

        assertCompileError("10000",
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('data(//@*)' passing by ref x "
            + "returning sequence empty on empty)"
            + "  as char(70))"
            + "from t1");

        // This should also fail because the function is not recognized.
        // In addition, we have prefixed the function with an unrecognized
        // namespace. Verify that it fails with an SQLException and that there
        // isn't any NPE in the exception chain.
        try {
            prepareStatement(
                    "select i,"
                    + "  xmlserialize("
                    + "    xmlquery('myns:data(//@*)' passing by ref x "
                    + "returning sequence empty on empty)"
                    + "  as char(70))"
                    + "from t1");
            fail("Compilation should fail because of unrecognized namespace");
        } catch (SQLException sqle) {
            assertSQLState("10000", sqle);
            Throwable t = sqle;
            while ((t = t.getCause()) != null) {
                if (t instanceof NullPointerException) {
                    fail("No NPE, please!", t);
                }
            }
        }

        // These should all succeed.  Since it's Xalan that's 
        // actually doing the query evaluation we don't need to 
        // test very many queries; we just want to make sure we get 
        // the correct results when there is an empty sequence, 
        // when the xml context is null, and when there is a 
        // sequence with one or more nodes/items in it.  So we just 
        // try out some queries and look at the results.  The 
        // selection of queries is random and is not meant to be 
        // exhaustive.
        
        Statement st = createStatement();
        ResultSet rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('2+2' passing by ref x returning "
            + "sequence empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        String [] expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        String [][] expRS = new String [][]
        {
            {"1", "4"},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", "4"},
            {"6", "4"},
            {"7", "4"},
            {"8", "4"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('./notthere' passing by ref x "
            + "returning sequence empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", ""},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", ""},
            {"6", ""},
            {"7", ""},
            {"8", ""}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//*' passing by ref x empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"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><masted> bass </masted>"},
            {"7", "<umm> decl check </umm>"},
            {"8", "<lets> <try> this out </try> </lets><try> this out </try>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//*[text() = \" bass \"]' passing by "
            + "ref x empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", ""},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", ""},
            {"6", "<masted> bass </masted>"},
            {"7", ""},
            {"8", ""}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//lets' passing by ref x empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", ""},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", ""},
            {"6", ""},
            {"7", ""},
            {"8", "<lets> <try> this out </try> </lets>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//text()' passing by ref x empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "document was inserted as part of an UPDATE"},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", ""},
            {"6", "bass  boosted."},
            {"7", "decl check"},
            {"8", "this out"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//try[text()='' this out '']' passing "
            + "by ref x empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", ""},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", ""},
            {"6", ""},
            {"7", ""},
            {"8", "<try> this out </try>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//try[text()='' this in '']' passing "
            + "by ref x empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", ""},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", ""},
            {"6", ""},
            {"7", ""},
            {"8", ""}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('2+.//try' passing by ref x returning "
            + "sequence empty on empty)"
            + "  as char(70))"
            + "from t1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "NaN"},
            {"2", null},
            {"4", null},
            {"3", null},
            {"5", "NaN"},
            {"6", "NaN"},
            {"7", "NaN"},
            {"8", "NaN"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "values ('x', xmlserialize("
            + "  xmlquery('//let' passing by ref"
            + "    xmlparse(document '<lets> try this </lets>' "
            + "preserve whitespace)"
            + "  empty on empty)"
            + "as char(30)))");
        
        expColNames = new String [] {"1", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"x", ""}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "values xmlserialize("
            + "  xmlquery('//lets' passing by ref"
            + "    xmlparse(document '<lets> try this </lets>' "
            + "preserve whitespace)"
            + "  empty on empty)"
            + "as char(30))");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"<lets> try this </lets>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        st.close();
    }

    /* Check insertion of XMLQUERY result into a table.  Should 
     * only allow results that are a sequence of exactly one 
     * Document node.
     */
    public void testXMLQueryInsert() throws Exception
    {
        // Create test table and data specific to this test.

        Statement st = createStatement();
        st.executeUpdate("create table xqInsert1 (i int, x xml not null)");

        assertUpdateCount(st, 1,
            " insert into xqInsert1 values (1, xmlparse(document "
            + "'<should> work as planned </should>' preserve whitespace))");
        
        st.executeUpdate("create table xqInsert2 (i int, x xml default null)");
        
        assertUpdateCount(st, 1,
            "insert into xqInsert2 values ("
            + "  9,"
            + "  xmlparse(document '<here><is><my "
            + "height=\"4.4\">attribute</my></is></here>' preserve "
            + "whitespace)"
            + ")");
        
        assertUpdateCount(st, 1,
            " insert into xqInsert2 values ("
            + "  0,"
            + "  xmlparse(document '<there><goes><my "
            + "weight=\"180\">attribute</my></goes></there>' "
            + "preserve whitespace)"
            + ")");
        
        // Show target tables before insertions.
        
        ResultSet rs = st.executeQuery(
            "select i, xmlserialize(x as char(75)) from xqInsert1");
        
        String [] expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        String [][] expRS = new String [][]
        {
            {"1", "<should> work as planned </should>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as char(75)) from xqInsert2");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"9", "<here><is><my height=\"4.4\">attribute</my></is></here>"},
            {"0", "<there><goes><my "
                + "weight=\"180\">attribute</my></goes></there>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // These should all fail because the result of the XMLQUERY 
        // op is not a valid document (it's either an empty 
        // sequence, a node that is not a Document node, some 
        // undefined value, or a sequence with more than one item 
        // in it).

        assertStatementError("2200L", st,
            "insert into xqInsert1 (i, x) values ("
            + "  20, "
            + "  (select"
            + "    xmlquery('./notthere' passing by ref x "
            + "returning sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        assertStatementError("2200L", st,
            " insert into xqInsert1 (i, x) values ("
            + "  21,"
            + "  (select"
            + "    xmlquery('//@*' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        assertStatementError("2200L", st,
            " insert into xqInsert1 (i, x) values ("
            + "  22,"
            + "  (select"
            + "    xmlquery('. + 2' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        assertStatementError("2200L", st,
            " insert into xqInsert1 (i, x) values ("
            + "  23,"
            + "  (select"
            + "    xmlquery('//*' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        assertStatementError("2200L", st,
            " insert into xqInsert1 (i, x) values ("
            + "  24,"
            + "  (select"
            + "    xmlquery('//*[//@*]' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        assertStatementError("2200L", st,
            " insert into xqInsert1 (i, x) values ("
            + "  25,"
            + "  (select"
            + "    xmlquery('//is' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        assertStatementError("2200L", st,
            " insert into xqInsert1 (i, x) values ("
            + "  26,"
            + "  (select"
            + "    xmlquery('//*[@*]' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        // These should succeed.
        
        assertUpdateCount(st, 1,
            "insert into xqInsert1 (i, x) values ("
            + "  27,"
            + "  (select"
            + "    xmlquery('.' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        assertUpdateCount(st, 1,
            " insert into xqInsert1 (i, x) values ("
            + "  28,"
            + "  (select"
            + "    xmlquery('/here/..' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 9"
            + "  )"
            + ")");
        
        // Verify results.
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as char(75)) from xqInsert1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "<should> work as planned </should>"},
            {"27", "<here><is><my height=\"4.4\">attribute</my></is></here>"},
            {"28", "<here><is><my height=\"4.4\">attribute</my></is></here>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // Next two should _both_ succeed because there's no row 
        // with i = 100 in xqInsert2, thus the SELECT will return null
        // and XMLQUERY operator should never get executed.  x will be 
        // NULL in these cases.
        
        assertUpdateCount(st, 1,
            "insert into xqInsert2 (i, x) values ("
            + "  29,"
            + "  (select"
            + "    xmlquery('2+2' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 100"
            + "  )"
            + ")");
        
        assertUpdateCount(st, 1,
            " insert into xqInsert2 (i, x) values ("
            + "  30,"
            + "  (select"
            + "    xmlquery('.' passing by ref x returning "
            + "sequence empty on empty)"
            + "    from xqInsert2 where i = 100"
            + "  )"
            + ")");
        
        // Verify results.
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as char(75)) from xqInsert2");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"9", "<here><is><my height=\"4.4\">attribute</my></is></here>"},
            {"0", "<there><goes><my "
                + "weight=\"180\">attribute</my></goes></there>"},
            {"29", null},
            {"30", null}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Cleanup.

        st.executeUpdate("drop table xqInsert1");
        st.executeUpdate("drop table xqInsert2");
        st.close();
    }

    /* Check updates using XMLQUERY results.  Should only allow 
     * results that constitute a valid DOCUMENT node (i.e. that 
     * can be parsed by the XMLPARSE operator).
     */
        
    public void testXMLQueryUpdate() throws Exception
    {
        // Create test table and data.

        Statement st = createStatement();
        st.executeUpdate("create table xqUpdate (i int, x xml default null)");
        assertUpdateCount(st, 2, "insert into xqUpdate (i) values 29, 30");
        assertUpdateCount(st, 1,
            "insert into xqUpdate values ("
            + "  9,"
            + "  xmlparse(document '<here><is><my "
            + "height=\"4.4\">attribute</my></is></here>' preserve "
            + "whitespace)"
            + ")");
        
        // These updates should succeed.

        assertUpdateCount(st, 1,
            "update xqUpdate"
            + "  set x = "
            + "    xmlquery('.' passing by ref"
            + "      xmlparse(document '<none><here/></none>' "
            + "preserve whitespace)"
            + "    returning sequence empty on empty)"
            + "where i = 29");
        
        assertUpdateCount(st, 1,
            " update xqUpdate"
            + "  set x = "
            + "    xmlquery('self::node()[//@height]' passing by ref"
            + "      (select"
            + "        xmlquery('.' passing by ref x empty on empty)"
            + "        from xqUpdate"
            + "        where i = 9"
            + "      )"
            + "    empty on empty)"
            + "where i = 30");
        
        // These should fail because result of XMLQUERY isn't a 
        // DOCUMENT.
        
        assertStatementError("2200L", st,
            "update xqUpdate"
            + "  set x = xmlquery('.//*' passing by ref x empty on empty)"
            + "where i = 29");
        
        assertStatementError("2200L", st,
            " update xqUpdate"
            + "  set x = xmlquery('./notthere' passing by ref x "
            + "empty on empty)"
            + "where i = 30");
        
        assertStatementError("2200L", st,
            " update xqUpdate"
            + "  set x ="
            + "    xmlquery('//*[@weight]' passing by ref"
            + "      (select"
            + "        xmlquery('.' passing by ref x empty on empty)"
            + "        from xqUpdate"
            + "        where i = 9"
            + "      )"
            + "    empty on empty)"
            + "where i = 30");
        
        assertStatementError("2200L", st,
            " update xqUpdate"
            + "  set x ="
            + "    xmlquery('//*/@height' passing by ref"
            + "      (select"
            + "        xmlquery('.' passing by ref x empty on empty)"
            + "        from xqUpdate"
            + "        where i = 9"
            + "      )"
            + "    empty on empty)"
            + "where i = 30");
        
        // Next two should succeed because there's no row with i = 
        // 100 in xqUpdate and thus xqUpdate should remain unchanged after 
        // these updates.

        assertUpdateCount(st, 0,
            "update xqUpdate"
            + "  set x = xmlquery('//*' passing by ref x empty on empty)"
            + "where i = 100");
        
        assertUpdateCount(st, 0,
            " update xqUpdate"
            + "  set x = xmlquery('4+4' passing by ref x empty on empty)"
            + "where i = 100");
        
        // Verify results.
        
        ResultSet rs = st.executeQuery(
            "select i, xmlserialize(x as char(75)) from xqUpdate");
        
        String [] expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        String [][] expRS = new String [][]
        {
            {"29", "<none><here/></none>"},
            {"30", "<here><is><my height=\"4.4\">attribute</my></is></here>"},
            {"9", "<here><is><my height=\"4.4\">attribute</my></is></here>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Cleanup.
        st.executeUpdate("drop table xqUpdate");
        st.close();
    }

    /* Pass results of an XMLQUERY op into another XMLQUERY op. 
     * Should work so long as results of the first op constitute
     * a valid document.
     */
    public void testNestedXMLQuery() throws Exception
    {
        // Should fail because result of inner XMLQUERY op
        // isn't a valid document.

        Statement st = createStatement();
        assertStatementError("2200V", st,
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//lets/@*' passing by ref"
            + "      xmlquery('/okay/text()' passing by ref"
            + "        xmlparse(document '<okay><lets "
            + "boki=\"inigo\"/></okay>' preserve whitespace)"
            + "      empty on empty)"
            + "    empty on empty)"
            + "  as char(100))"
            + "from t1 where i > 5");
        
        assertStatementError("2200V", st,
            " select i,"
            + "  xmlserialize("
            + "    xmlquery('.' passing by ref"
            + "      xmlquery('//lets' passing by ref"
            + "        xmlparse(document '<okay><lets "
            + "boki=\"inigo\"/></okay>' preserve whitespace)"
            + "      empty on empty)"
            + "    empty on empty)"
            + "  as char(100))"
            + "from t1 where i > 5");
        
        assertStatementError("2200V", st,
            " select i,"
            + "  xmlexists('.' passing by ref"
            + "    xmlquery('/okay' passing by ref"
            + "      xmlparse(document '<okay><lets "
            + "boki=\"inigo\"/></okay>' preserve whitespace)"
            + "    empty on empty)"
            + "  )"
            + "from t1 where i > 5");
        
        // Should succeed but result is empty sequence.
        
        ResultSet rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('/not' passing by ref"
            + "      xmlquery('.' passing by ref"
            + "        xmlparse(document '<okay><lets "
            + "boki=\"inigo\"/></okay>' preserve whitespace)"
            + "      empty on empty)"
            + "    empty on empty)"
            + "  as char(100))"
            + "from t1 where i > 5");
        
        String [] expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        String [][] expRS = new String [][]
        {
            {"6", ""},
            {"7", ""},
            {"8", ""}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // Should succeed with various results.
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('//lets' passing by ref"
            + "      xmlquery('.' passing by ref"
            + "        xmlparse(document '<okay><lets "
            + "boki=\"inigo\"/></okay>' preserve whitespace)"
            + "      empty on empty)"
            + "    empty on empty)"
            + "  as char(100))"
            + "from t1 where i > 5");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"6", "<lets boki=\"inigo\"/>"},
            {"7", "<lets boki=\"inigo\"/>"},
            {"8", "<lets boki=\"inigo\"/>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('string(//@boki)' passing by ref"
            + "      xmlquery('/okay/..' passing by ref"
            + "        xmlparse(document '<okay><lets "
            + "boki=\"inigo\"/></okay>' preserve whitespace)"
            + "      empty on empty)"
            + "    empty on empty)"
            + "  as char(100))"
            + "from t1 where i > 5");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"6", "inigo"},
            {"7", "inigo"},
            {"8", "inigo"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlserialize("
            + "    xmlquery('/half/masted/text()' passing by ref"
            + "      xmlquery('.' passing by ref x empty on empty)"
            + "    empty on empty)"
            + "  as char(100))"
            + "from t1 where i = 6");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"6", "bass"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i,"
            + "  xmlexists('/half/masted/text()' passing by ref"
            + "    xmlquery('.' passing by ref x empty on empty)"
            + "  )"
            + "from t1 where i = 6");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"6", "true"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        st.close();
    }

    /**
     *  DERBY-1759: Serialization of attribute nodes.
     */
    public void testAttrSerialization() throws Exception
    {
        // Create test table and one row of data.

        Statement st = createStatement();
        st.executeUpdate("create table attserTable (i int, x xml)");
        assertUpdateCount(st, 1, "insert into attserTable values (0, null)");
        assertUpdateCount(st, 1,
            "insert into attserTable values (10,"
            + "  xmlparse(document"
            + "    '<threeatts first=\"1\" second=\"two\" "
            + "third=\"le 3 trois\"/>'"
            + "    preserve whitespace"
            + "  ))");
        
        // Echo attserTable rows for reference.
        
        ResultSet rs = st.executeQuery(
            "select i, xmlserialize(x as char(75)) from attserTable");
        
        String [] expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        String [][] expRS = new String [][]
        {
            {"0", null},
            {"10", "<threeatts first=\"1\" second=\"two\" third=\"le 3 trois\"/>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // 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.

        assertStatementError("2200W", st,
            "select"
            + "  xmlserialize("
            + "    xmlquery("
            + "      '//@*' passing by ref x empty on empty"
            + "    )"
            + "  as char(50))"
            + " from attserTable"
            + " 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.
        
        rs = st.executeQuery(
            "select"
            + "  xmlserialize("
            + "    xmlquery("
            + "      'string(//@*)'"
            + "      passing by ref x empty on empty"
            + "    )"
            + "  as char(50))"
            + " from attserTable"
            + " where xmlexists('//@*' passing by ref x)");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        // 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.
        
        rs = st.executeQuery(
            "select"
            + "  max("
            + "    cast("
            + "      xmlserialize("
            + "        xmlquery('count(//@*)' passing by ref x "
            + "empty on empty)"
            + "      as char(50))"
            + "    as int)"
            + "  )"
            + " from attserTable");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"3"}
        };

        JDBC.assertFullResultSet(rs, expRS, true, false);
        
        // The use of MAX in the previous query throws a warning because
        // the table T1 has null values.  Just for sanity check for that
        // warning if we're in embedded mode (warnings are not returned
        // in client/server mode--see DERBY-159).

        if (usingEmbedded())
        {
            SQLWarning sqlWarn = rs.getWarnings();
            if (sqlWarn == null)
                sqlWarn = st.getWarnings();
            if (sqlWarn == null)
                sqlWarn = getConnection().getWarnings();
            assertTrue("Expected warning but found none.", (sqlWarn != null));
            assertSQLState("01003", sqlWarn);
        }

        rs.close();
        
        // 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.
        
        rs = st.executeQuery(
            "select"
            + "  xmlserialize("
            + "    xmlquery("
            + "      'concat(string(//@*[1]), \" \","
            + "        string(//@*[2]), \" \","
            + "        string(//@*[3]))'"
            + "      passing by ref x empty on empty"
            + "    )"
            + "  as char(50))"
            + " from attserTable"
            + " where xmlexists('//@*' passing by ref x)");
        
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1 two le 3 trois"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);

        // Cleanup.
        st.executeUpdate("drop table attserTable");
        st.close();
    }

    /**
     * DERBY-1718 create trigger fails when SPS contains XML 
     * related op.
     */
    public void testTriggerSPSWithXML() throws Exception
    {
        Statement st = createStatement();
        st.executeUpdate("create table trigSPS1 (i int, x xml)");
        st.executeUpdate("create table trigSPS2 (i int, x xml)");

        assertUpdateCount(st, 1,
            " insert into trigSPS1 values (1, xmlparse(document "
            + "'<name> john </name>' preserve whitespace))");

        st.executeUpdate(
            "create trigger tx after insert on trigSPS1 for each "
            + "statement mode db2sql insert into trigSPS2 values "
            + "(1, xmlparse(document '<name> jane </name>' "
            + "preserve whitespace))");
        
        assertUpdateCount(st, 1,
            " insert into trigSPS1 values (2, xmlparse(document "
            + "'<name> ally </name>' preserve whitespace))");
        
        ResultSet rs = st.executeQuery(
            "select i, xmlserialize(x as varchar(20)) from trigSPS1");
        
        String [] expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        String [][] expRS = new String [][]
        {
            {"1", "<name> john </name>"},
            {"2", "<name> ally </name>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as varchar(20)) from trigSPS2");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "<name> jane </name>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        assertUpdateCount(st, 2, "insert into trigSPS1 select * from trigSPS1");
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as varchar(20)) from trigSPS1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "<name> john </name>"},
            {"2", "<name> ally </name>"},
            {"1", "<name> john </name>"},
            {"2", "<name> ally </name>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as varchar(20)) from trigSPS2");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "<name> jane </name>"},
            {"1", "<name> jane </name>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        st.executeUpdate("drop trigger tx");
        assertUpdateCount(st, 4, "delete from trigSPS1");
        assertUpdateCount(st, 2, "delete from trigSPS2");
        assertUpdateCount(st, 1,
            " insert into trigSPS1 values (1, xmlparse(document "
            + "'<name> john </name>' preserve whitespace))");
        
        st.executeUpdate(
            "create trigger tx after insert on trigSPS1 for each "
            + "statement mode db2sql insert into trigSPS2 values "
            + "(1, (select xmlquery('.' passing by ref x "
            + "returning sequence empty on empty) from trigSPS1 "
            + "where i = 1))");
        
        assertUpdateCount(st, 1,
            " insert into trigSPS1 values (2, xmlparse(document "
            + "'<name> ally </name>' preserve whitespace))");
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as varchar(20)) from trigSPS1");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "<name> john </name>"},
            {"2", "<name> ally </name>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        
        rs = st.executeQuery(
            "select i, xmlserialize(x as varchar(20)) from trigSPS2");
        
        expColNames = new String [] {"I", "2"};
        JDBC.assertColumnNames(rs, expColNames);

        expRS = new String [][]
        {
            {"1", "<name> john </name>"}
        };

        JDBC.assertFullResultSet(rs, expRS, true);
        st.executeUpdate("drop trigger tx");

        st.executeUpdate("drop table trigSPS1");
        st.executeUpdate("drop table trigSPS2");
        st.close();
    }

    /**
     * Test how numeric values returned by XPath queries are formatted.
     */
    public void testNumericReturnValues() throws SQLException {
        // Array of XPath queries and their expected return values
        String[][] queries = {
            // Long.MAX_VALUE. We lose some precision.
            { "9223372036854775807", "9223372036854776000" },
            // We can also have numbers larger than Long.MAX_VALUE, but we
            // don't get higher precision.
            { "9223372036854775807123456789", "9223372036854776000000000000" },
            // Expect plain format, not scientific notation like 1.23E-10
            { "123 div 1000000000000", "0.000000000123" },
            // Trailing zeros after decimal point should be stripped away
            { "1", "1" },
            { "1.0", "1" },
            { "1.000", "1" },
            { "1.00010", "1.0001" },
            // -0 should be normalized to 0
            { "-0", "0" },
            { "-0.0", "0" },
            { "-0.00", "0" },
            // Division by zero yields Not A Number or +/- Infinity
            { "0 div 0", "NaN" },
            { "3.14 div 0", "Infinity" },
            { "-3.14 div 0", "-Infinity" },
        };

        Statement s = createStatement();

        for (int i = 0; i < queries.length; i++) {
            String xpath = queries[i][0];
            String expected = queries[i][1];

            String sql = "select xmlserialize(xmlquery('" + xpath +
                    "' passing by ref x empty on empty) as clob) " +
                    "from t1 where i = 1";

            JDBC.assertSingleValueResultSet(s.executeQuery(sql), expected);
        }
    }

    /**
     * Wrapper for the tests in XMLTypeAndOpsTest.  We have some
     * fixture tables/data that we want to create a single time
     * before the tests run and then which we want to clean up
     * when the tests complete.  This class acts the "wrapper"
     * that does this one-time setup and teardown.  (Actually,
     * we do it two times: once for running in embedded mode
     * and once for running in client/server mode--we create an
     * instance of this class for each mode.)
     */
    private static class XMLTestSetup extends BaseJDBCTestSetup
    {
        public XMLTestSetup(TestSuite tSuite) {
            super(tSuite);
        }

        /**
         * Before running the tests in XMLTypeAndOps we create two
         * base tables and insert the common "fixture" data.
         */
        public void setUp() throws Exception
        {
            Connection c = getConnection();
            Statement s = c.createStatement();

            /* Create test tables as a fixture for this test.  Note
             * that we're implicitly testing the creation of XML columns
             * as part of this setup.  All of the following should
             * succeed; see testXMLColCreation() for some tests where
             * column creation is expected to fail.
             */

            s.executeUpdate("create table t1 (i int, x xml)");
            s.executeUpdate("create table t2 (x2 xml not null)");
            s.executeUpdate("alter table t2 add column x1 xml");

            /* Insert test data.  Here we're implicitly tesing
             * the XMLPARSE operator in situations where it should
             * succeed.  Negative test cases are tesed in the
             * testIllegalNullInserts() and testXMLParse() methods
             * of the XMLTypeAndOps class.
             */

            // Null values.

            assertUpdateCount(s, 1, "insert into t1 values (1, null)");
            assertUpdateCount(s, 1,
                "insert into t1 values (2, cast (null as xml))");

            assertUpdateCount(s, 1, "insert into t1 (i) values (4)");
            assertUpdateCount(s, 1, "insert into t1 values (3, default)");

            // Non-null values.

            assertUpdateCount(s, 1,
                "insert into t1 values (5, xmlparse(document "
                + "'<hmm/>' preserve whitespace))");
            
            assertUpdateCount(s, 1,
                " insert into t1 values (6, xmlparse(document "
                + "'<half> <masted> bass </masted> boosted. </half>' "
                + "preserve whitespace))");
            
            assertUpdateCount(s, 1,
                " insert into t2 (x1, x2) values (null, "
                + "xmlparse(document '<notnull/>' preserve whitespace))");
            
            assertUpdateCount(s, 1,
                " insert into t1 values (7, xmlparse(document '<?xml "
                + "version=\"1.0\" encoding= \"UTF-8\"?><umm> decl "
                + "check </umm>' preserve whitespace))");
            
            assertUpdateCount(s, 1,
                "insert into t1 values (8, xmlparse(document '<lets> "
                + "<try> this out </try> </lets>' preserve whitespace))");

            assertUpdateCount(s, 1,
                " update t1 set x = xmlparse(document '<update> "
                + "document was inserted as part of an UPDATE "
                + "</update>' preserve whitespace) where i = 1");
            
            assertUpdateCount(s, 1,
                " update t1 set x = xmlparse(document '<update2> "
                + "document was inserted as part of an UPDATE "
                + "</update2>' preserve whitespace) where "
                + "xmlexists('/update' passing by ref x)");

            s.close();
            c.close();
            s = null;
            c= null;
        }

        /**
         * For test teardown we just drop the two tables we created in
         * test setup and then clean up local objects if needed.
         */
        public void tearDown() throws Exception
        {
            Connection c = getConnection();
            Statement s = c.createStatement();

            s.executeUpdate("drop table t1");
            s.executeUpdate("drop table t2");
            s.close();
            c.close();

            s = null;
            c = null; 
            super.tearDown();
        }
    }
}
