/*
 *
 * Derby - Class org.apache.derbyTesting.functionTests.tests.lang.SubqueryTest
 *
 * 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 java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.RuntimeStatisticsParser;
import org.apache.derbyTesting.junit.SystemPropertyTestSetup;

/**
 * Test case for subquery.sql.
 */
public class SubqueryTest extends BaseJDBCTestCase {

    public SubqueryTest(String name) {
        super(name);
    }

    public static Test suite() {

        Properties props = new Properties();

        props.setProperty("derby.language.statementCacheSize", "0");
        return new DatabasePropertyTestSetup(
            new SystemPropertyTestSetup(new CleanDatabaseTestSetup(
               new BaseTestSuite(SubqueryTest.class, "SubqueryTest")) {

                    /**
                     * @see org.apache.derbyTesting.junit.CleanDatabaseTestSetup#decorateSQL(java.sql.Statement)
                     */
                    protected void decorateSQL(Statement s) throws SQLException {
                        s.execute(
                            "CREATE FUNCTION ConsistencyChecker() " +
                            "RETURNS VARCHAR(128) " +
                            "EXTERNAL NAME " +
                            "'org.apache.derbyTesting.functionTests." +
                            "util.T_ConsistencyChecker.runConsistencyChecker' " +
                            "LANGUAGE JAVA PARAMETER STYLE JAVA");
                        s.execute("create table s " +
                            "(i int, s smallint, c char(30), " +
                                "vc char(30), b bigint)");
                        s.execute("create table t " +
                            "(i int, s smallint, c char(30), " +
                                "vc char(30), b bigint)");
                        s.execute("create table tt " +
                            "(ii int, ss smallint, cc char(30), " +
                                "vcvc char(30), b bigint)");
                        s.execute("create table ttt " +
                            "(iii int, sss smallint, ccc char(30), " +
                                "vcvcvc char(30))");
                        // populate the tables
                        s.execute("insert into s values " +
                            "(null, null, null, null, null)");
                        s.execute("insert into s values (0, 0, '0', '0', 0)");
                        s.execute("insert into s values (1, 1, '1', '1', 1)");
                        s.execute("insert into t values " +
                            "(null, null, null, null, null)");
                        s.execute("insert into t values (0, 0, '0', '0', 0)");
                        s.execute("insert into t values (1, 1, '1', '1', 1)");
                        s.execute("insert into t values (1, 1, '1', '1', 1)");
                        s.execute("insert into t values (2, 2, '2', '2', 1)");
                        s.execute("insert into tt values " +
                            "(null, null, null, null, null)");
                        s.execute("insert into tt values (0, 0, '0', '0', 0)");
                        s.execute("insert into tt values (1, 1, '1', '1', 1)");
                        s.execute("insert into tt values (1, 1, '1', '1', 1)");
                        s.execute("insert into tt values (2, 2, '2', '2', 1)");
                        s.execute("insert into ttt values (null, null, null, null)");
                        s.execute("insert into ttt values (11, 11, '11', '11')");
                        s.execute("insert into ttt values (11, 11, '11', '11')");
                        s.execute("insert into ttt values (22, 22, '22', '22')");
                    }

                }, props), props, true);
    }

    /**
     * exists non-correlated negative tests "mis"qualified all
     * 
     * @throws Exception
     */
    public void testExistsNonCorrelated() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;
        assertStatementError("42X10", st,
        "select * from s where exists (select tt.* from t)");

        assertStatementError("42X10", st,
            "select * from s where exists (select t.* from t tt)");

        // invalid column reference in select list
        assertStatementError("42X04", st,
            "select * from s where exists (select nosuchcolumn from t)");

        // multiple matches at subquery level
        assertStatementError("42X03", st,
            "select * from s where exists (select i from s, t)");

        // ? parameter in select list of exists subquery
        assertStatementError("42X34", st,
            "select * from s where exists (select ? from s)");

        // positive tests
        // qualified *
        rs = st.executeQuery(
            "select * from s where exists (select s.* from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s t where exists (select t.* from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s u where exists (select u.* from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        // column reference in select list
        rs = st.executeQuery("select * from s where exists (select i from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where exists (select t.i from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where exists (select i, s from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        // subquery returns empty result set
        rs = st.executeQuery(
            "select * from s where exists (select * from t where i = -1)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        // test semantics of AnyResultSet
        rs = st.executeQuery(
            "select * from s where exists (select t.* from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select * from s where exists (select 0 from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        // subquery in derived table
        rs = st.executeQuery(
            "select * from (select * from s where exists " +
                "(select * from t) and i = 0) a");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { "0", "0", "0", "0", "0" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        // exists under an OR
        rs = st.executeQuery(
            "select * from s where 0=1 or exists (select * from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where 1=1 or exists " +
                "(select * from t where 0=1)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select * from s where exists (select * from t "
                + "where 0=1) or exists (select * from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(" select * from s where exists (select * from t "
                + "where exists (select * from t where 0=1) or exists "
                + "(select * from t))");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        // (exists empty set) is null
        rs = st.executeQuery("select * from s where (exists (select * from t "
                + "where 0=1)) is null");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        // Not exists
        rs = st.executeQuery(
            "select * from s where not exists (select * from t)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select * from s where not exists " +
                "(select * from t where i = -1)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs.close();
        st.close();
    }

    /**
     * expression subqueries non-correlated negative tests all node
     * 
     * @throws Exception
     */
    public void testExpressionNonCorrelated() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        assertStatementError("42X38", st,
            "select * from s where i = (select * from t)");

        // too many columns in select list
        assertStatementError("42X39", st,
            "select * from s where i = (select i, s from t)");

        // no conversions
        assertStatementError("21000", st,
            "select * from s where i = (select 1 from t)");

        assertStatementError("21000", st,
            "select * from s where i = (select b from t)");

        // ? parameter in select list of expression subquery
        assertStatementError("42X34", st,
            "select * from s where i = (select ? from t)");

        // do consistency check on scans, etc.
        rs = st.executeQuery("values ConsistencyChecker()");

        expColNames = new String[] { "1" };
        JDBC.assertColumnNames(rs, expColNames);

        // cardinality violation
        assertStatementError("21000", st,
            "select * from s where i = (select i from t)");

        // do consistency check on scans, etc.
        rs = st.executeQuery("values ConsistencyChecker()");
        expColNames = new String[] { "1" };
        JDBC.assertColumnNames(rs, expColNames);

        assertStatementError("21000", st,
            "select * from s where s = (select s from t where s = 1)");

        // do consistency check on scans, etc.
        rs = st.executeQuery("values ConsistencyChecker()");
        expColNames = new String[] { "1" };
        JDBC.assertColumnNames(rs, expColNames);
        if (usingEmbedded()) {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        } else {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        }

        assertStatementError("21000", st,
            "update s set b = (select max(b) from t) where vc " +
                "<> (select vc from t where vc = '1')");

        // do consistency check on scans, etc.
        rs = st.executeQuery("values ConsistencyChecker()");
        expColNames = new String[] { "1" };
        JDBC.assertColumnNames(rs, expColNames);
        if (usingEmbedded()) {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        } else {
            expRS = new String[][] 
                {  { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        }

        assertStatementError("21000", st,
            "delete from s where c = (select c from t where c = '1')");

        // do consistency check on scans, etc.
        rs = st.executeQuery("values ConsistencyChecker()");
        expColNames = new String[] { "1" };
        JDBC.assertColumnNames(rs, expColNames);
        if (usingEmbedded()) {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        } else {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        }

        // positive tests

        rs = st.executeQuery("select * from s");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(" select * from t");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { null, null, null, null, null },
                { "0", "0", "0", "0", "0" }, { "1", "1", "1", "1", "1" },
                { "1", "1", "1", "1", "1" }, { "2", "2", "2", "2", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * Testing simple subquery for each data type
     * 
     * @throws Exception
     */
    public void testSimpleSubquery() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select * from s where i = (select i from t where i = 0)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { "0", "0", "0", "0", "0" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where s = (select s from t where s = 0)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { "0", "0", "0", "0", "0" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where c = (select c from t where c = '0')");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { "0", "0", "0", "0", "0" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where vc = " +
                "(select vc from t where vc = '0')");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { "0", "0", "0", "0", "0" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where b = " +
                "(select max(b) from t where b = 0)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { "0", "0", "0", "0", "0" } };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where b = " +
                "(select max(b) from t where i = 2)");
        expColNames = new String[] { "I", "S", "C", "VC", "B" };
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String[][] { { "1", "1", "1", "1", "1" } };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * ? parameter on left hand side of expression subquery
     * @throws Exception
     */
    public void testParameterOnLeft()throws Exception {
        Statement st = createStatement();
        PreparedStatement pSt;
        ResultSetMetaData rsmd;
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        pSt = prepareStatement(
            "select * from s where ? = (select i from t where i = 0)");

        rs = st.executeQuery("values (0)");
        rs.next();
        rsmd = rs.getMetaData();
        for (int i = 1; i <= rsmd.getColumnCount(); i++)
            pSt.setObject(i, rs.getObject(i));

        rs = pSt.executeQuery();
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
        };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * Testing conversions
     * @throws Exception
     */
    public void testConversions()throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select * from s where i = (select s from t where s = 0)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where s = (select i from t where i = 0)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where c = (select vc from t where vc = '0')");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where vc = (select c from t where c = '0')");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * (select nullable_column ...) is null On of each data 
     * type to test clone()
     * @throws Exception
     */
    public void testClone() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
                "select * from s where (select s from s where i is "
                + "null) is null");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                                };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select * from s where " +
                "(select i from s where i is null) is null");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                                };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select * from s where " +
            "(select c from s where i is null) is null");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select * from s where " +
            "(select vc from s where i is null) is null");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select * from s where " +
            "(select b from s where i is null) is null");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select * from s where " +
                "(select 1 from t where exists " +
                    "(select * from t where 1 = 0) and s = -1) is null");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * Test subquery in subqueries
     * @throws Exception
     */
    public void testSubqueryInSubquery() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;
        
        rs = st.executeQuery("select * from s where " +
            "(select i from t where i = 0) = (select s from t where s = 0)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // multiple subqueries at the same level
        rs = st.executeQuery("select * from s where i = " +
            "(select s from t where s = 0) " +
            "and s = (select i from t where i = 2)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery("select * from s where i = " +
            "(select s from t where s = 0) " +
            "and s = (select i from t where i = 0)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        // nested subqueries

        rs = st.executeQuery(
            "select * from s where i = " +
                "(select i from t where s = " +
                "(select i from t where s = 2))");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select * from s where i = " +
                "(select i - 1 from t where s = " +
                "(select i from t where s = 2))");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1", "1", "1", "1", "1"} };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * Test expression subqueries in select list
     * @throws Exception
     */
    public void testSubqueriesInSelect() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;
        
        rs = st.executeQuery("select (select i from t where 0=1) from s");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {null}, {null}, {null} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select " +
            "(select i from t where i = 2) * " +
            "(select s from t where i = 2) from s " +
            "where i > " +
                "(select i from t where i = 0) - " +
                "(select i from t where i = 0)");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"4"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        // in subqueries negative tests select * subquery

        assertStatementError("42X38", st,
            "select * from s where s in (select * from s)");
        
        // incompatable types
        rs = st.executeQuery(
            "select * from s where s in (select b from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * Test constants in left, right and both sides of the subquery
     * @throws Exception
     */
    public void testConstants() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        // positive tests constants on left side of subquery

        rs = st.executeQuery(
            "select * from s where 1 in (select s from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where -1 in (select i from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select * from s where '1' in (select vc from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where 0 in (select b from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // constants in subquery select list
        rs = st.executeQuery(
            "select * from s where i in (select 1 from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where i in (select -1 from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select * from s where c in (select '1' from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1", "1", "1", "1", "1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where b in (select 0 from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        // constants on both sides
        rs = st.executeQuery(
            "select * from s where 0 in (select 0 from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // compatable types

        rs = st.executeQuery(
            "select * from s where c in (select vc from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where vc in (select c from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where i in (select s from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where s in (select i from t)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * empty subquery result set
     * @throws Exception
     */
    public void testEmptyResultSet() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[] expColNames;

        rs = st.executeQuery(
            "select * from s where i in (select i from t where 1 = 0)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select * from s where (i in " +
                "(select i from t where i = 0)) is null");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);
    }

    /**
     * Test subqueries in select list
     */
    public void testSubqueriesInSelectList() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select ( i in (select i from t) ) a from s order by a");            
        expColNames = new String [] {"A"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] {{"false"}, {"true"}, {"true"}};
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select " +
            "( i in (select i from t where 1 = 0) ) a from s order by a");            
        expColNames = new String [] {"A"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] {{"false"}, {"false"}, {"false"}};
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select " +
            "( (i in " +
                "(select i from t where 1 = 0)) is null ) a " +
                "from s order by a");
        expColNames = new String [] {"A"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] {{"false"}, {"false"}, {"false"}};
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * subquery under an or
     * @throws Exception
     */
    public void testSubqueryUnderOR() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;
        
        rs = st.executeQuery(
            "select i from s where i = -1 or i in (select i from t)");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0"}, {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select i from s where i = 0 or i in " +
                "(select i from t where i = -1)");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select i from s where i = -1 or i in " +
                "(select i from t where i = -1 or i = 1)");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * distinct elimination
     * @throws Exception
     */
    public void testDistinct() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;
        
        rs = st.executeQuery(
            "select i from s where i in (select i from s)");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0"}, {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select i from s where i in (select distinct i from s)");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0"}, {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select i from s ss where i in " +
                "(select i from s where s.i = ss.i)");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0"}, {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select i from s ss where i in " +
                "(select distinct i from s where s.i = ss.i)");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0"}, {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        // do consistency check on scans, etc.
        rs = st.executeQuery("values ConsistencyChecker()");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        if (usingEmbedded()) {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        } else {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        }
    }

    /**
     * Test Matches
     * 
     */
    public void testMatches() throws Exception {
        Statement st = createStatement();
        // correlated subqueries negative tests multiple matches 
        // at parent level

        assertStatementError("42X03", st,
        "select * from s, t where exists (select i from tt)");

        // match is against base table, but not derived column list

        assertStatementError("42X04", st,
                "select * from s ss (c1, c2, c3, c4, c5) where "
                + "exists (select i from tt)");

        assertStatementError("42X04", st,
                " select * from s ss (c1, c2, c3, c4, c5) where "
                + "exists (select ss.i from tt)");

        // correlation name exists at both levels, but only column 
        // match is at parent level

        assertStatementError("42X04", st,
        "select * from s where exists (select s.i from tt s)");

        // only match is at peer level

        assertStatementError("42X04", st,
                "select * from s where exists (select * from tt) and "
                + "exists (select ii from t)");

        assertStatementError("42X04", st,
                " select * from s where exists (select * from tt) "
                + "and exists (select tt.ii from t)");

        // correlated column in a derived table

        assertStatementError("42X04", st,
        "select * from s, (select * from tt where i = ii) a");

        assertStatementError("42X04", st,
        " select * from s, (select * from tt where s.i = ii) a");
    }

    /**
     * Test Simple correlated subqueries
     */
    public void testSimpleCorrelated() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;
        
        // positive tests simple correlated subqueries

        rs = st.executeQuery(
            "select (select i from tt where ii = i and ii <> 1) from s");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {null}, {"0"}, {null} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("select " +
            "(select s.i from tt where ii = s.i and ii <> 1) from s");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {null}, {"0"}, {null} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select (select s.i from ttt where iii = i) from s");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {null}, {null}, {null} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where exists " +
                "(select * from tt where i = ii and ii <> 1)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where exists " +
                "(select * from tt where s.i = ii and ii <> 1)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where exists " +
                "(select * from ttt where i = iii)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        // 1 case where we get a cardinality violation after a few 
        // rows
        try{
            rs = st.executeQuery(
            "select (select i from tt where ii = i) from s");
        }catch(SQLException sqle){
            BaseJDBCTestCase.assertSQLState(
                "Scalar subquery is only allowed to return a single row.","21000",sqle);
        }

        // skip levels to find match
        rs = st.executeQuery(
            "select * from s where exists (select * from ttt "
                + "where iii = (select 11 from tt where ii = i and ii <> 1))");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * join in subquery
     * @throws Exception
     */
    public void testJoinInSubqueries() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select * from s where i in " +
                "(select i from t, tt where s.i <> i and i = ii)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select * from s where i in " +
                "(select i from t, ttt where s.i < iii and s.i = t.i)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // join in outer query block
        rs = st.executeQuery(
            "select s.i, t.i from s, t where exists " +
                "(select * from ttt where iii = 1)");
        expColNames = new String [] {"I", "I"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select s.i, t.i from s, t where exists " +
                "(select * from ttt where iii = 11)");
        expColNames = new String [] {"I", "I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null},
                {null, "0"},
                {null, "1"},
                {null, "1"},
                {null, "2"},
                {"0", null},
                {"0", "0"},
                {"0", "1"},
                {"0", "1"},
                {"0", "2"},
                {"1", null},
                {"1", "0"},
                {"1", "1"},
                {"1", "1"},
                {"1", "2"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // joins in both query blocks
        rs = st.executeQuery(
            "select s.i, t.i from s, t where t.i = " +
                "(select iii from ttt, tt where iii = t.i)");
        expColNames = new String [] {"I", "I"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        rs = st.executeQuery(
            "select s.i, t.i from s, t " +
                "where t.i = (select ii from ttt, tt " +
                    "where s.i = t.i and t.i = tt.ii " +
                    "and iii = 22 and ii <> 1)");
        expColNames = new String [] {"I", "I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * Test proper caching of subqueries in prepared statements
     * This section (old Cloudscape reference 'Beetle 5382') tests the fix for
     * a problem where sub-queries were executed not once per execution of the
     * statement, but only once, when the statement was first executed. 
     * If the parameter changed between executions or if the data changed 
     * between executions then the top level select returned the wrong results.

     * @throws Exception
     */
    public void testSubqueriesInPS() throws Exception {
        Statement st = createStatement();
        PreparedStatement pSt;
        ResultSetMetaData rsmd;
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        pSt = prepareStatement(
            "select s.i from s where s.i in " +
                "(select s.i from s, t where s.i = t.i and t.s = ?)");
        rs = st.executeQuery("values(0)");
        rs.next();
        rsmd = rs.getMetaData();
        for (int i = 1; i <= rsmd.getColumnCount(); i++)
            pSt.setObject(i, rs.getObject(i));

        rs = pSt.executeQuery();
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("values(1)");
        rs.next();
        rsmd = rs.getMetaData();
        for (int i = 1; i <= rsmd.getColumnCount(); i++)
            pSt.setObject(i, rs.getObject(i));

        rs = pSt.executeQuery();
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        setAutoCommit(false);
        
        pSt = prepareStatement(
            "select s.i from s where s.i in " +
                "(select s.i from s, t where s.i = t.i and t.s = 3)");

        rs = pSt.executeQuery();
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        setAutoCommit(false);
        
        st.executeUpdate("insert into t(i,s) values(1,3)");

        rs = pSt.executeQuery();
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rollback();
    }

    /**
     * correlated subquery in select list of a derived table
     * @throws Exception
     */
    public void testSubuqeryInSelectListOfDerivedTable() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select * from " +
                "(select (select iii from ttt " +
                    "where  sss > i and " +
                    "sss = iii and iii <> 11) " +
                "from s) a");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {null}, {"22"}, {"22"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        // bigint and subqueries

        st.executeUpdate("create table li(i int, s smallint, l bigint)");
        st.executeUpdate("insert into li values (null, null, null)");
        st.executeUpdate("insert into li values (1, 1, 1)");
        st.executeUpdate("insert into li values (2, 2, 2)");

        rs = st.executeQuery(
            "select l from li o where l = " +
                "(select i from li i where o.l = i.i)");
        expColNames = new String [] {"L"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select l from li o where l = " +
                "(select s from li i where o.l = i.s)");
        expColNames = new String [] {"L"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select l from li o where l = " +
                "(select l from li i where o.l = i.l)");
        expColNames = new String [] {"L"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select l from li where l in (select i from li)");
        expColNames = new String [] {"L"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select l from li where l in (select s from li)");
        expColNames = new String [] {"L"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select l from li where l in (select l from li)");
        expColNames = new String [] {"L"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * Some extra tests for subquery flattening on table expressions 
     * (remapColumnReferencesToExpressions() binary list node
     * @throws Exception
     */
    public void testSubqueryFlattening() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select i in (1,2) from (select i from s) as tmp(i)");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] {{null}, {"false"}, {"true"}};
        JDBC.assertFullResultSet(rs, expRS, true);

        // conditional expression
        assertStatementError("42X01", st,
            "select i = 1 ? 1 : i from (select i from s) as tmp(i)");

        // more tests for correlated column resolution

        rs = st.executeQuery(
            "select * from s where i = (values i)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select t.* from s, t where t.i = (values s.i)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select * from s where i in (values i)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery(
            "select t.* from s, t where t.i in (values s.i)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * tests for not needing to do cardinality check
     * @throws Exception
     */
    public void testNoNeedForCardinalityCheck() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select * from s where i = " +
                "(select min(i) from s where i is not null)");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"0", "0", "0", "0", "0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        assertStatementError("21000", st,
            "select * from s where i = (select min(i) from s group by i)");

        // tests for distinct expression subquery

        st.executeUpdate("create table dist1 (c1 int)");
        st.executeUpdate("create table dist2 (c1 int)");
        st.executeUpdate("insert into dist1 values null, 1, 2");
        st.executeUpdate("insert into dist2 values null, null");

        // no match, no violation
        rs = st.executeQuery(
            "select * from dist1 where c1 = " +
                "(select distinct c1 from dist2)");
        expColNames = new String [] {"C1"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        // violation

        st.executeUpdate("insert into dist2 values 1");

        assertStatementError("21000", st,
            "select * from dist1 where c1 = " +
                "(select distinct c1 from dist2)");

        // match, no violation

        assertUpdateCount(st, 3, "update dist2 set c1 = 2");

        rs = st.executeQuery(
            "select * from dist1 where c1 = " +
                "(select distinct c1 from dist2)");
        expColNames = new String [] {"C1"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        st.executeUpdate("drop table dist1");
        st.executeUpdate("drop table dist2");

        // update

        st.executeUpdate("create table u " +
            "(i int, s smallint, c char(30), vc char(30), b bigint)");
        st.executeUpdate("insert into u select * from s");

        rs = st.executeQuery("select * from u");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        assertStatementError("42821", st,
            "update u set b = exists " +
                "(select b from t) where " +
                    "vc <> (select vc from s where vc = '1')");

        rs = st.executeQuery("select * from u");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        assertUpdateCount(st, 3,"delete from u");

        st.executeUpdate("insert into u select * from s");

        // delete

        assertUpdateCount(st, 2,
            "delete from u where c < (select c from t where c = '2')");

        rs = st.executeQuery("select * from u");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // restore u

        assertUpdateCount(st, 1, "delete from u");

        st.executeUpdate("insert into u select * from s");
    }

    /**
     * check clean up when errors occur in subqueries insert
     * @throws Exception
     */
    public void testErrorsInNestedSubqueries() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        assertStatementError("22012", st,
            "insert into u select * from s s_outer where i = " +
                "(select s_inner.i/(s_inner.i-1) from s s_inner " +
                    "where s_outer.i = s_inner.i)");

        rs = st.executeQuery("select * from u");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // delete

        assertStatementError("22012", st,
            "delete from u " +
                "where i = (select i/(i-1) from s where u.i = s.i)");
        
        rs = st.executeQuery("select * from u");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // update

        assertStatementError("22012", st,
            "update u  set i = (select i from s where u.i = s.i) " +
                "where i = (select i/(i-1) from s where u.i = s.i)");

        assertStatementError("22012", st,
            "update u  set i = (select i/i-1 from s where u.i = s.i) " +
                "where i = (select i from s where u.i = s.i)");

        rs = st.executeQuery("select * from u");
        expColNames = new String [] {"I", "S", "C", "VC", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {null, null, null, null, null},
                {"0", "0", "0", "0", "0"},
                {"1", "1", "1", "1", "1"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // error in nested subquery
        assertStatementError("21000", st,
            "select (select (select (select i from s) from s) from s) from s");

        // do consistency check on scans, etc.
        rs = st.executeQuery("values ConsistencyChecker()");
        expColNames = new String [] {"1"};
        JDBC.assertColumnNames(rs, expColNames);
        if (usingEmbedded()) {
            expRS = new String[][] 
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        } else {
            expRS = new String[][]
                { { "No open scans, etc.\n16 dependencies found" } };
            JDBC.assertFullResultSet(rs, expRS, true);
        }

        // reset autocommit
        setAutoCommit(true);
    }

    /**
     * subquery with groupby and having clause
     */
    public void testSubqueryWithClause() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        rs = st.executeQuery(
            "select distinct vc, i from t as myt1 " +
                "where s <= (select max(myt1.s) from t as myt2 " +
                    "where myt1.vc = myt2.vc " +
                    "and myt1.s <= myt2.s group by s " +
                    "having count(distinct s) <= 3)");
        expColNames = new String [] {"VC", "I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0"},
                {"1", "1"},
                {"2", "2"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        // subquery with having clause but no groupby
        rs = st.executeQuery(
            "select distinct vc, i from t as myt1 " +
                "where s <= (select max(myt1.s) from t as myt2 " +
                    "where myt1.vc = myt2.vc and myt1.s <= myt2.s " +
                    "having count(distinct s) <= 3)");
        expColNames = new String [] {"VC", "I"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"0", "0"},
                {"1", "1"},
                {"2", "2"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);
    }

    /**
     * DERBY-1007: Optimizer for subqueries can return 
     * incorrect cost estimates leading to sub-optimal join 
     * orders for the outer query.  Before the patch for that 
     * issue, the following query plan will show T3 first and 
     * then T1-- but that's determined by the optimizer to be 
     * the "bad" join order.  After the fix, the join order 
     * will show T1 first, then T3, which is correct (based on 
     * the optimizer's estimates).
     * @throws Exception
     */
    public void testDERBY1007() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        st.executeUpdate("create table t_1 (i int, j int)");
        st.executeUpdate
            ("insert into T_1 values (1,1), (2,2), (3,3), (4,4), (5,5)");
        st.executeUpdate("create table t_3 (a int, b int)");
        st.executeUpdate(
            "insert into T_3 values (1,1), (2,2), (3,3), (4,4)");
        st.executeUpdate("insert into t_3 values " +
            "(6, 24), (7, 28), (8, 32), (9, 36), (10, 40)");
        st.execute("CALL SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");

        rs = st.executeQuery(
            "select x1.j, x2.b from (select distinct i,j from t_1) x1, " +
                "(select distinct a,b from t_3) x2 " +
                "where x1.i = x2.a order by x1.j, x2.b");
        expColNames = new String [] {"J", "B"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][]
                              {
                {"1", "1"},
                {"2", "2"},
                {"3", "3"},
                {"4", "4"}
                              };
        JDBC.assertFullResultSet(rs, expRS, true);

        rs = st.executeQuery("values SYSCS_UTIL.SYSCS_GET_RUNTIMESTATISTICS()");
        rs.next();
        String rts = rs.getString(1);

        // Now verify the correct runtimeStatistics output
        RuntimeStatisticsParser rtsp = new RuntimeStatisticsParser(rts);
        // print out the full stats if derby.tests.debug is true
        println("full stats: \n" + rtsp.toString());
        // Checking only on the sequence of T3 and T1 scans.
        // If further checking is needed, uncomment more lines.
        rtsp.assertSequence( 
             new String[] {
                        "Source result set:",
                        "_Project-Restrict ResultSet (5):",
                        "_Source result set:",
                        "__Hash Join ResultSet:",
                        "__Left result set:",
                        "___Distinct Scan ResultSet for T_1 at read committed isolation level using instantaneous share row locking:",
                        "____Bit set of columns fetched=All",
                        "____Scan type=heap",
                        "__Right result set:",
                        "___Hash Table ResultSet (4):",
                        "___Source result set:",
                        "____Distinct Scan ResultSet for T_3 at read committed isolation level using instantaneous share row locking:",
                        "_____Bit set of columns fetched=All",
                        "_____Scan type=heap"
                              });

        st.executeUpdate("drop table t_1");
        st.executeUpdate("drop table t_3");
    }

    /**
     * DERBY-781: Materialize subqueries where possible to avoid creating 
     * invariant result sets many times.  This test case executes a query that
     * that has subqueries twice: the first time the tables have only a few
     * rows in them; the second time they have hundreds of rows in them.
     */
    public void testDERBY781() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        st.executeUpdate("create table t1 (i int, j int)");
        st.executeUpdate("create table t2 (i int, j int)");
        st.executeUpdate
            ("insert into t1 values (1,1), (2,2), (3,3), (4,4), (5,5)");
        st.executeUpdate
            ("insert into t2 values (1,1), (2,2), (3,3), (4,4), (5,5)");
        st.executeUpdate("create table t3 (a int, b int)");
        st.executeUpdate("create table t4 (a int, b int)");
        st.executeUpdate
            ("insert into t3 values (2,2), (4,4), (5,5)");
        st.executeUpdate
            ("insert into t4 values (2,2), (4,4), (5,5)");
    
        // Use of the term "DISTINCT" makes it so that we don't flatten 
        // the subqueries.
        st.executeUpdate("create view V1 as " +
            "select distinct T1.i, T2.j from T1, T2 where T1.i = T2.i");
        st.executeUpdate("create view V2 as " +
            "select distinct T3.a, T4.b from T3, T4 where T3.a = T4.a");
        st.execute("CALL SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");


        /* Run the test query the first time, with only a small number
         * of rows in each table. Before the patch for DERBY-781
         * the optimizer would have chosen a nested loop join, which 
         * means that we would generate the result set for the inner 
         * view multiple times.  After DERBY-781 the optimizer will 
         * choose to do a hash join and thereby materialize the inner 
         * result set, thus improving performance.  Should see a Hash join 
         * as the top-level join with a HashTableResult as the right child 
         * of the outermost join.
         */  
        rs = st.executeQuery(
            "select * from V1, V2 where V1.j = V2.b and V1.i in (1,2,3,4,5)");
            expColNames = new String [] {"I", "J", "A", "B"};
            JDBC.assertColumnNames(rs, expColNames);
            expRS = new String [][]
                                  {
                    {"2", "2", "2", "2"},
                    {"4", "4", "4", "4"},
                    {"5", "5", "5", "5"}
                                  };
            JDBC.assertFullResultSet(rs, expRS, true);

            rs = st.executeQuery(
                "values SYSCS_UTIL.SYSCS_GET_RUNTIMESTATISTICS()");
            rs.next();
            String rts = rs.getString(1);

            // Now verify the correct runtimeStatistics output
            RuntimeStatisticsParser rtsp = new RuntimeStatisticsParser(rts);
            // print out the full stats if derby.tests.debug is true
            println("full stats: \n" + rtsp.toString());
            // the essentials are getting checked as per the comments
            // above. If further checking is needed, uncomment more lines.
            rtsp.assertSequence( 
                    new String[] {
                        "Hash Join ResultSet:",
                        //"Left result set:",
                        //"_Sort ResultSet:",
                        //"_Source result set:",
                        //"__Project-Restrict ResultSet (7):",
                        //"__Source result set:",
                        //"___Nested Loop Join ResultSet:",
                        //"___Left result set:",
                        //"____Project-Restrict ResultSet (5):",
                        //"____Source result set:",
                        //"_____Table Scan ResultSet for T1 at read committed " +
                        //    "isolation level using share row locking chosen " +
                        //    "by the optimizer",
                        //"______Bit set of columns fetched={0}",
                        //"______Scan type=heap",
                        //"___Right result set:",
                        //"____Table Scan ResultSet for T2 at read committed " +
                        //    "isolation level using share row locking chosen " +
                        //    "by the optimizer",
                        //"_____Bit set of columns fetched=All",
                        //"_____Scan type=heap",
                        //"______Operator: =",                        
                        "Right result set:",
                        "_Hash Table ResultSet (13):"
                        //"_Source result set:",
                        //"__Sort ResultSet:",
                        //"__Source result set:",
                        //"___Project-Restrict ResultSet (12):",
                        //"___Source result set:",
                        //"____Hash Join ResultSet:",
                        //"____Left result set:",
                        //"_____Table Scan ResultSet for T3 at read committed " +
                        //    "isolation level using share row locking chosen " +
                        //    "by the optimizer",
                        //"______Bit set of columns fetched=All",
                        //"______Scan type=heap"
                        //,
           // after this, there's something peculiar with the 
           // 'Right result set' line output, and this RuntimeStatisticsParser
           // method cannot find any further matches...
                        //"___Right result set:",
                        //"_____Hash Scan ResultSet for T4 at read committed " +
                        //    "isolation level using instantaneous share row" + 
                        //    "locking: ",
                        //"______Bit set of columns fetched=All",
                        //"______Scan type=heap",
                        //"_______Operator: ="                        
                            });
            // ...so checking on the remaining output another way. 
            assertTrue(rtsp.findString("Right result set:",3));        
            assertTrue(rtsp.findString("Hash Scan ResultSet for T4 at read " +
                "committed isolation level using instantaneous share row " +
                "locking: ",1));        
            //assertTrue(rtsp.findString("Bit set of columns fetched=All",2));        
            //assertTrue(rtsp.findString("Scan type=heap",4));        
            
            // Now add more data to the tables.
            st.executeUpdate("insert into t1 select * from t2");
            st.executeUpdate("insert into t2 select * from t1");
            st.executeUpdate("insert into t2 select * from t1");
            st.executeUpdate("insert into t1 select * from t2");
            st.executeUpdate("insert into t2 select * from t1");
            st.executeUpdate("insert into t1 select * from t2");
            st.executeUpdate("insert into t2 select * from t1");
            st.executeUpdate("insert into t1 select * from t2");
            st.executeUpdate("insert into t2 select * from t1");
            st.executeUpdate("insert into t1 select * from t2");
            st.executeUpdate("insert into t3 select * from t4");
            st.executeUpdate("insert into t4 select * from t3");
            st.executeUpdate("insert into t3 select * from t4");
            st.executeUpdate("insert into t4 select * from t3");
            st.executeUpdate("insert into t3 select * from t4");
            st.executeUpdate("insert into t4 select * from t3");
            st.executeUpdate("insert into t3 select * from t4");
            st.executeUpdate("insert into t4 select * from t3");
            st.executeUpdate("insert into t3 select * from t4");
            st.executeUpdate("insert into t4 select * from t3");
            st.executeUpdate("insert into t3 select * from t4");

            /* Drop the views and recreate them with slightly different
             * names.  The reason we use different names is to ensure that
             * the query will be "different" from the last time and thus we'll 
             * we'll go through optimization again (instead of just using 
             * the cached plan from last time). 
             */

            st.executeUpdate("drop view v1");
            st.executeUpdate("drop view v2");

            // Use of the term "DISTINCT" makes it so that we don't flatten
            // the subqueries.
            st.executeUpdate("create view VV1 as " +
                "select distinct T1.i, T2.j from T1, T2 where T1.i = T2.i");
            st.executeUpdate("create view VV2 as " +
                "select distinct T3.a, T4.b from T3, T4 where T3.a = T4.a");
            // Now execute the query again using the larger tables.
            rs = st.executeQuery(
                "select * from VV1, VV2 " +
                "where VV1.j = VV2.b and VV1.i in (1,2,3,4,5)");
            expColNames = new String [] {"I", "J", "A", "B"};
            JDBC.assertColumnNames(rs, expColNames);
            expRS = new String [][]
                                  {
                    {"2", "2", "2", "2"},
                    {"4", "4", "4", "4"},
                    {"5", "5", "5", "5"}
                                  };
            JDBC.assertFullResultSet(rs, expRS, true);

            rs = st.executeQuery(
                "values SYSCS_UTIL.SYSCS_GET_RUNTIMESTATISTICS()");
            rs.next();
            rts = rs.getString(1);

            // Now verify the correct runtimeStatistics output
            rtsp = new RuntimeStatisticsParser(rts);
            // print out the full stats if derby.tests.debug is true
            println("full stats: \n" + rtsp.toString());
            // the essentials are getting checked as per the comments
            // above. If more detailed checking is needed, uncomment lines.
            rtsp.assertSequence( 
                    new String[] {
                        "Hash Join ResultSet:",
                        //"Left result set:",
                        //"_Sort ResultSet:",
                        "_Rows input = 53055",
                        //"_Source result set:",
                        //"__Project-Restrict ResultSet (7):",
                        //"__Source result set:",
                        //"___Hash Join ResultSet:",
                        //"___Left result set:",
                        //"____Project-Restrict ResultSet (5):",
                        //"____Source result set:",
                        //"_____Table Scan ResultSet for T1 at read committed " +
                        //    "isolation level using share row locking chosen " +
                        //    "by the optimizer",
                        //"______Bit set of columns fetched={0}",
                        //"______Scan type=heap",
                        //"___Right result set:",
                        // with fewer roles, the optimizer chose a Table Scan 
                        //"____Hash Scan ResultSet for T2 at read committed " +
                        //    "isolation level using instantaneous share row " +
                        //    "locking: ",
                        //"_____Bit set of columns fetched=All",
                        //"_____Scan type=heap",
                        //"______Operator: =",                        
                        //"Right result set:",
                        //"_Hash Table ResultSet (13):",
                        //"_Source result set:",
                        //"__Sort ResultSet:",
                        //"__Source result set:",
                        //"___Project-Restrict ResultSet (12):",
                        //"___Source result set:",
                        //"____Hash Join ResultSet:",
                        //"____Left result set:",
                        // with 4 rows, the optimizer used a Table Scan on T3 
                        // for left node and a Hash Scan on T4 for the right.
                        //"_____Table Scan ResultSet for T4 at read committed " +
                        //    "isolation level using share row locking chosen " +
                        //    "by the optimizer",
                        //"______Bit set of columns fetched=All",
                        //"______Scan type=heap",
                        "____Right result set:",
                        "_____Hash Scan ResultSet for T3 at read committed " +
                            "isolation level using instantaneous share row " +
                            "locking: "
                            //,
                        //"______Bit set of columns fetched={0}",
                        //"______Scan type=heap"
                            });

        // clean up.
        st.execute("CALL SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(0)");

        st.executeUpdate("drop view vv1");
        st.executeUpdate("drop view vv2");
        st.executeUpdate("drop table t1");
        st.executeUpdate("drop table t2");
        st.executeUpdate("drop table t3");
        st.executeUpdate("drop table t4");
    }

    /**
     * DERBY-1574: Subquery in COALESCE gives NPE due to 
     * preprocess not implemented for that node type
     */
    public void testSubqueryInCOALESCE() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        st.executeUpdate("create table t1 (id int)");
        st.executeUpdate("create table t2 (i integer primary key, j int)");
        st.executeUpdate("insert into t1 values 1,2,3,4,5");
        st.executeUpdate("insert into t2 values (1,1),(2,4),(3,9),(4,16)");

        assertUpdateCount(st, 5,
            "update t1 set id = coalesce((select j from t2 " +
                "where t2.i=t1.id), 0)");

        rs = st.executeQuery("select * from t1");
        expColNames = new String [] {"ID"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"4"}, {"9"}, {"16"}, {"0"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        st.executeUpdate("drop table t1");
        st.executeUpdate("drop table t2");
    }

    /**
     * Test the fix for DERBY-2218
     * @throws Exception
     */
    public void testDERBY_2218() throws Exception {
        Statement st = createStatement();
        ResultSet rs = null;
        String[] expColNames;
        
        st.executeUpdate("create table t1 (i int)");

        rs = st.executeQuery(
            "select * from t1 " +
                "where i in (1, 2, (values cast(null as integer)))");
        expColNames = new String [] {"I"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        // expect error, this used to throw NPE
        assertStatementError("42X07", st,
            "select * from t1 where i in (1, 2, (values null))");

        assertStatementError("42X07", st,
            "select * from t1 where i in " +
                "(select i from t1 where i in (1, 2, (values null)))");

        // expect error
        assertStatementError("42X07", st,
            "select * from t1 where exists (values null)");

        assertStatementError("42X07", st,
            "select * from t1 where exists " +
                "(select * from t1 where exists(values null))");

        assertStatementError("42X07", st,
            "select i from t1 where exists " +
                "(select i from t1 where exists(values null))");

        assertStatementError("42X07", st,
            "select * from (values null) as t2");

        assertStatementError("42X07", st,
            "select * from t1 where exists " +
                "(select 1 from (values null) as t2)");

        assertStatementError("42X07", st,
            "select * from t1 where exists " +
                "(select * from (values null) as t2)");

        st.executeUpdate("drop table t1");
        st.close();
    }
    
    /**
     * DERBY-4549: NPE in JBitSet
     */
    public void testDERBY_4549() throws Exception {
        Statement st = createStatement();
        PreparedStatement pSt;
        ResultSet rs = null;
        String[][] expRS;
        String[] expColNames;

        st.executeUpdate("create table ABC (ID int)");
        st.executeUpdate("create table DEF (ID int)");

        //compilation of the statement used to fail with NPE
        pSt = prepareStatement(
            "select * from ABC t1 " +
                "where (select distinct t2.ID from DEF t2) in " +
                    "(select t3.ID from DEF t3)");
        
        // empty tables, should give empty result
        rs = pSt.executeQuery();
        expColNames = new String [] {"ID"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        // now, test with data in the tables
        st.executeUpdate("insert into ABC values 1, 2");
        rs = pSt.executeQuery();
        expColNames = new String [] {"ID"};
        JDBC.assertColumnNames(rs, expColNames);
        JDBC.assertDrainResults(rs, 0);

        st.executeUpdate("insert into DEF values 2");
        rs = pSt.executeQuery();
        expColNames = new String [] {"ID"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        st.executeUpdate("insert into DEF values 2");
        rs = pSt.executeQuery();
        expColNames = new String [] {"ID"};
        JDBC.assertColumnNames(rs, expColNames);
        expRS = new String [][] { {"1"}, {"2"} };
        JDBC.assertFullResultSet(rs, expRS, true);

        st.executeUpdate("insert into DEF values 3");
        // will fail because left operand of IN is no longer scalar
        // expect ERROR 21000: 
        //     Scalar subquery is only allowed to return a single row
        assertStatementError("21000", pSt);

        st.executeUpdate("drop table ABC");
        st.executeUpdate("drop table DEF");
    }
}
