/*

 Derby - Class org.apache.derbyTesting.functionTests.tests.lang.ConstraintCharacteristicsTest

 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.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import junit.framework.Test;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.impl.jdbc.EmbedConnection;
import org.apache.derby.impl.sql.GenericPreparedStatement;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.J2EEDataSource;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.SupportFilesSetup;
import org.apache.derbyTesting.junit.SystemPropertyTestSetup;
import org.apache.derbyTesting.junit.TestConfiguration;
import org.apache.derbyTesting.junit.XATestUtil;
import org.apache.derby.shared.common.error.DerbySQLIntegrityConstraintViolationException;

public class ConstraintCharacteristicsTest extends BaseJDBCTestCase
{
    private static final String LANG_DUPLICATE_KEY_CONSTRAINT = "23505";
    private static final String LANG_DEFERRED_DUP_VIOLATION_T = "23506";
    private static final String LANG_DEFERRED_DUP_VIOLATION_S = "23507";
    private static final String LANG_CHECK_CONSTRAINT_VIOLATED = "23513";
    private static final String LANG_DEFERRED_CHECK_VIOLATION_T = "23514";
    private static final String LANG_DEFERRED_CHECK_VIOLATION_S = "23515";
    private static final String LANG_DEFERRED_FK_VIOLATION_T = "23516";
    private static final String LANG_DEFERRED_FK_VIOLATION_S = "23517";
    private static final String LOCK_TIMEOUT = "40XL1";
    private static final String LANG_INCONSISTENT_C_CHARACTERISTICS = "42X97";
    private static final String LANG_DROP_OR_ALTER_NON_EXISTING_C = "42X86";
    private static final String LANG_SYNTAX_ERROR = "42X01";
    private static final String NOT_IMPLEMENTED = "0A000";
    private static final String LANG_NOT_NULL_CHARACTERISTICS = "42XAN";
    private static final String LANG_OBJECT_NOT_FOUND = "42X94";
    private static final String LANG_DB2_DUPLICATE_NAMES = "42734";
    private static final String LANG_ADD_CHECK_CONSTRAINT_FAILED = "X0Y59";

    private static String expImpDataFile;  // file used to perform
                                           // import/export
    private static String expImpDataWithNullsFile; // file used to perform
                                                   // import/export
    private static boolean exportFilesCreatedEmbedded = false;
    private static boolean exportFilesCreatedClient = false;

    private static final int WAIT_TIMEOUT_DURATION = 1;

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


    public static Test suite() {
        final String nameRoot = ConstraintCharacteristicsTest.class.getName();
        final BaseTestSuite suite = new BaseTestSuite(nameRoot);

        suite.addTest(baseSuite1(nameRoot + ":embedded 1"));
        suite.addTest(TestConfiguration.clientServerDecorator(
                baseSuite1(nameRoot + ":client 1")));

        suite.addTest(baseSuite2(nameRoot + ":embedded 2"));
        suite.addTest(TestConfiguration.clientServerDecorator(
                baseSuite2(nameRoot + ":client 2")));

        suite.addTest(baseSuite3(nameRoot + ":embedded 3"));
        suite.addTest(TestConfiguration.clientServerDecorator(
                baseSuite3(nameRoot + ":client 3")));

        return suite;
    }

    // this suite holds tests that require a more optimal 
    // locks.waitTimeout setting.
    private static Test baseSuite3(final String name) {

        final BaseTestSuite suite = new BaseTestSuite(name);

        suite.addTest(new ConstraintCharacteristicsTest(
                "testDeferredRowsInvalidation"));
        suite.addTest(new ConstraintCharacteristicsTest(
                "testLockingForUniquePK"));

        final Properties systemProperties = new Properties();
        systemProperties.setProperty(
            "derby.locks.waitTimeout", Integer.toString(WAIT_TIMEOUT_DURATION));

        return new SupportFilesSetup(
                new SystemPropertyTestSetup(suite, systemProperties, true));
    }
    
    private static Test baseSuite2(final String name) {

        final BaseTestSuite suite = new BaseTestSuite(name);
        final Properties systemProperties = new Properties();
        systemProperties.setProperty("derby.language.logQueryPlan", "true");
        suite.addTest(new SupportFilesSetup(
                new SystemPropertyTestSetup(
                          new ConstraintCharacteristicsTest(
                              "testDerby6666"), systemProperties, true)));

        return suite;
    }

    private static Test baseSuite1(final String name) {
        final BaseTestSuite suite = new BaseTestSuite(name);

        suite.addTest(new ConstraintCharacteristicsTest(
                      "testSyntaxAndBinding"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testDropNotNullOnUniqueColumn"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testCompressTableOKUnique"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testLockingForUniquePKWithCommit"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testLockingForUniquePKWithRollback"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testDatabaseMetaData"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testCreateConstraintDictionaryEncodings"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testAlterConstraintDictionaryEncodings"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testAlterConstraintInvalidation"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testBasicDeferral"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testRoutines"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testImport"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testDerby6374"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testXA"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testAlmostRemovedAllDups"));
        suite.addTest(new ConstraintCharacteristicsTest(
                      "testCheckConstraintsWithDeferredRows"));
        suite.addTest(new ConstraintCharacteristicsTest(
                     "testSeveralCheckConstraints"));
        suite.addTest(new ConstraintCharacteristicsTest(
                     "testDerby6670_a"));
        suite.addTest(new ConstraintCharacteristicsTest(
                     "testDerby6670_b"));
        suite.addTest(new ConstraintCharacteristicsTest(
                     "testManySimilarDuplicates"));
        suite.addTest(new ConstraintCharacteristicsTest(
                     "testDerby6773"));

        final Properties systemProperties = new Properties();
        systemProperties.setProperty(
            "derby.locks.waitTimeout", Integer.toString(500));

        return new SupportFilesSetup(
                new SystemPropertyTestSetup(suite, systemProperties, true));
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        final Statement s = createStatement();
        s.executeUpdate("create table referenced(" +
                        "    i int primary key, j int default 0)");

        if ((usingEmbedded() && !exportFilesCreatedEmbedded) ||
            (usingDerbyNetClient() && !exportFilesCreatedClient)) {

            // We have to do this once for embedded and once for client/server
            if (usingEmbedded()) {
                exportFilesCreatedEmbedded = true;
            } else {
                exportFilesCreatedClient = true;
            }

            // Create a file for import that contains duplicate rows,
            // see testImport and testDerby6374.
            //
            expImpDataFile =
                SupportFilesSetup.getReadWrite("t.data").getPath();
            expImpDataWithNullsFile =
                SupportFilesSetup.getReadWrite("t_with_nulls.data").getPath();
            s.executeUpdate("create table t(i int)");
            s.executeUpdate("insert into t values 1,-2,-2, 3");
            s.executeUpdate("create table t_with_nulls(i int)");
            s.executeUpdate("insert into t_with_nulls values 1,null, null, 3");
            s.executeUpdate(
                "call SYSCS_UTIL.SYSCS_EXPORT_TABLE (" +
                "    'APP' , 'T' , '" + expImpDataFile + "'," +
                "    null, null , null)");
            s.executeUpdate(
                "call SYSCS_UTIL.SYSCS_EXPORT_TABLE (" +
                "    'APP' , 'T_WITH_NULLS' , '" + expImpDataWithNullsFile +
                "', null, null , null)");
            s.executeUpdate("drop table t");
            s.executeUpdate("drop table t_with_nulls");
        }

        s.close();
        setAutoCommit(false);
    }

    @Override
    protected void tearDown() throws Exception {
        rollback();
        setAutoCommit(true);
        getConnection().createStatement().
                executeUpdate("drop table referenced");
        super.tearDown();
    }

    public void testSyntaxAndBinding() throws SQLException {
        final Connection c = getConnection();
        final Statement s = c.createStatement();

        //
        //   T A B L E    L E V E L    C O N S T R A I N T S
        //

        assertTableLevelDefaultBehaviorAccepted(c, s);
        assertTableLevelNonDefaultAccepted(s);

        //
        //   A L T E R    C O N S T R A I N T    C H A R A C T E R I S T I C S
        //
        s.executeUpdate(
            "create table t(i int, constraint app.c primary key(i))");

        // default, so allow
        s.executeUpdate("alter table t alter constraint c enforced");

        // not default behavior, expect error until feature implemented
        assertStatementError(
            NOT_IMPLEMENTED, s,
            "alter table t alter constraint c not enforced");

        for (String ch : illegalAlterCharacteristics) {
            // Anything beyond enforcement is illegal in ALTER context
            assertStatementError(
                LANG_SYNTAX_ERROR, s, "alter table t alter constraint c " + ch);
        }

        // Unknown constraint name
        assertStatementError(
            LANG_DROP_OR_ALTER_NON_EXISTING_C, s,
            "alter table t alter constraint cuckoo not enforced");

        //
        //   S E T   C O N S T R A I N T
        //
        s.executeUpdate("alter table t drop constraint c");
        s.executeUpdate("alter table t add constraint c " +
                        "    primary key(i) deferrable");
        s.executeUpdate("set constraints c deferred");
        s.executeUpdate("set constraints all deferred");

        // Unknown constraint name
        assertStatementError(LANG_OBJECT_NOT_FOUND, s,
                             "set constraints cuckoo deferred");
        assertStatementError(LANG_DB2_DUPLICATE_NAMES , s,
                "set constraints c,c deferred");
        c.rollback();

        //
        //   C O L U M N    L E V E L    C O N S T R A I N T S
        //

        assertColumnLevelDefaultBehaviorAccepted(c, s);
        assertColumnLevelNonDefaultAccepted(s);

        // Characteristics are not currently allowed for NOT NULL,
        // since Derby does not represent NOT NULL as a constraint,
        // but rather as an aspect of the column's data type. It is
        // possible to alter the column nullable and vice versa,
        // though.
        assertStatementError(LANG_NOT_NULL_CHARACTERISTICS, s,
                    "create table t(i int " +
                    "not null deferrable initially immediate)");
    }

    /**
     * Check that constraint characteristics are correctly encoded
     * into the STATE column in SYS.SYSCONSTRAINTS.
     * Cf. specification attached to DERBY-532.
     *
     * FIXME: Note that this test runs with property derby.constraintsTesting
     * to bypass NOT IMPLEMENTED checks.  Remove this property usage when
     * DERBY-532 is done.
     * @throws SQLException
     */
    public void testCreateConstraintDictionaryEncodings() throws SQLException {
        final Statement s = getConnection().createStatement();

        for (String[] ch : defaultCharacteristics) {
            assertDictState(s, ch[0], ch[1]);
        }

        for (String[] ch : nonDefaultCharacteristics) {
            assertDictState(s, ch[0], ch[1]);
        }

        for (String ch : illegalCharacteristics) {
            assertCreateInconsistentCharacteristics(s, ch);
        }

        rollback();
    }

    /**
     * Check that constraint characteristics are correctly encoded
     * into the STATE column in SYS.SYSCONSTRAINTS.
     * Cf. specification attached to DERBY-532.
     *
     * FIXME: Note that this test runs with property derby.constraintsTesting
     * to bypass NOT IMPLEMENTED checks.  Remove this property usage when
     * DERBY-532 is done.
     * @throws SQLException
     */
    public void testAlterConstraintDictionaryEncodings() throws SQLException {
        final Statement s = getConnection().createStatement();

        for (String[] ch : defaultCharacteristics) {
            s.executeUpdate(
                    "create table t(i int, constraint c primary key(i) " +
                    ch[0] + ")");

            assertAlterDictState(s, "enforced");
            assertAlterDictState(s, "not enforced");
            rollback();
        }

        for (String[] ch : nonDefaultCharacteristics) {
            if (ch[0].contains("not enforced")) {

                assertStatementError(NOT_IMPLEMENTED,
                        s,
                        "create table t(i int, constraint c primary key(i) " +
                                ch[0] + ")");
            } else {
                s.executeUpdate(
                        "create table t(i int, constraint c primary key(i) " +
                                ch[0] + ")");

                assertAlterDictState(s, "enforced");
                assertAlterDictState(s, "not enforced");
                rollback();
            }
        }

        for (String ch : illegalAlterCharacteristics) {
            assertAlterInconsistentCharacteristics(s, ch);
        }
    }

    /**
     * Check that altering constraint characteristics invalidates prepared
     * statements.
     * @throws SQLException
     */
    public void testAlterConstraintInvalidation() throws SQLException {
        if (usingDerbyNetClient()) {
            // Skip, since we need to see inside an embedded connection here
            return;
        }

        final Connection c = getConnection();
        final Statement s = createStatement();

        s.executeUpdate("create table t(i int, constraint c primary key(i))");
        final PreparedStatement ps =
            c.prepareStatement("insert into t values 3");
        ps.execute();

        s.executeUpdate("alter table t alter constraint c enforced ");

        final LanguageConnectionContext lcc = getLCC( c );
        final GenericPreparedStatement derbyPs =
                (GenericPreparedStatement)lcc.getLastActivation().
                getPreparedStatement();

        assertFalse(derbyPs.isValid());

        rollback();
    }


    static final String[] uniqueForms = {
            "create table t(i int, j int, constraint c primary key(i)",
            "create table t(i int, j int, constraint c unique(i)",
            "create table t(i int not null, j int, constraint c unique(i)"};

    static final String[] uniqueSpec = { // corresponding to above forms
            "primary key(i)",
            "unique(i)",
            "unique(i)"};

    static final String[] checkForms = {
            "create table t(i int, j int, constraint c check (i > 0)"};

    static final String[] fkForms = {
            "create table t(i int, j int, " +
            "    constraint c foreign key(i) references referenced(i)"};

    static final String[] checkSpec = { // corresponding to above forms
            "check (i > 0)"};

    static final String[][] initialContents = new String[][] {
            {"1", "10"},
            {"2", "20"},
            {"3", "30"}};

    static final String[][] negatedInitialContents = new String[][] {
            {"-1", "10"},
            {"-2", "20"},
            {"-3", "30"}};


    static final String[] setConstraintsForms = {
            "set constraints all",
            "set constraints c"};


    public void testDatabaseMetaData() throws SQLException {

        // Test that our constraint backing index is still reported as unique
        // even if we implement it as physically non-unique when deferrable:
        // logically it is still a unique index.
        final Statement s = createStatement();
        s.executeUpdate(
            "create table t(i int not null " +
            "    constraint c primary key deferrable initially immediate)");
        final DatabaseMetaData dbmd = s.getConnection().getMetaData();
        ResultSet rs = dbmd.getIndexInfo(null, null, "T", false, false);
        rs.next();
        assertEquals("false", rs.getString("NON_UNIQUE"));

        // Test that we get the right values for DEFERRABILITY in
        // getImportedKeys, getExportedKeys and getCrossReference

        String[] cchars = new String[]{
            "deferrable initially immediate",
            "deferrable initially deferred",
            "not deferrable"
        };

        int[] dbmdState = new int[]{
            DatabaseMetaData.importedKeyInitiallyImmediate,
            DatabaseMetaData.importedKeyInitiallyDeferred,
            DatabaseMetaData.importedKeyNotDeferrable,
        };

        for (int i = 0; i < cchars.length; i++) {
            s.executeUpdate(
                    "create table child(i int, constraint c2 foreign key(i) " +
                    "    references t(i) " + cchars[i] + ")");
            rs = dbmd.getImportedKeys(null, null, "CHILD");
            rs.next();
            assertEquals(
                    Integer.toString(dbmdState[i]),
                    rs.getString("DEFERRABILITY"));
            rs.close();

            rs = dbmd.getExportedKeys(null, null, "T");
            rs.next();
            assertEquals(
                    Integer.toString(dbmdState[i]),
                    rs.getString("DEFERRABILITY"));
            rs.close();

            rs = dbmd.getCrossReference(null, null, "T", null, null, "CHILD");
            rs.next();
            assertEquals(
                    Integer.toString(dbmdState[i]),
                    rs.getString("DEFERRABILITY"));
            rs.close();
            s.executeUpdate("drop table child");
        }
    }



    public void testLockingForUniquePK() throws SQLException {
        final Statement s = createStatement();
        s.executeUpdate(
            "create table t1(i int, " +
                "constraint c1 primary key(i) not deferrable)");
        s.executeUpdate(
            "create table t2(i int, " +
                "constraint c2 primary key(i) deferrable initially deferred)");
        s.executeUpdate("insert into t1 values 1,2,3");
        s.executeUpdate("insert into t2 values 1,2,3");
        commit();

        //
        // Locks for PK insert, not deferrable
        //
        // There is an X row lock on the inserted row.
        //
        s.executeUpdate("insert into t1 values 4");
        ResultSet rs = s.executeQuery(
                LockTableTest.getSelectLocksString());

        JDBC.assertFullResultSet(rs, new String[][]{
            {"APP", "UserTransaction", "TABLE", "2",
                "IX", "T1", "Tablelock", "GRANT", "ACTIVE"},
            {"APP", "UserTransaction", "ROW", "1",
                "X", "T1", "(1,10)", "GRANT", "ACTIVE"}
        });

        Connection c2 = null;

        try {
            // Verify that another transaction has to wait
            c2 = openDefaultConnection();
            c2.setAutoCommit(false);

            final Statement s2 = c2.createStatement();
            assertStatementError(LOCK_TIMEOUT, s2, "insert into t1 values 4");
        } finally {
            if (c2 != null) {
                c2.rollback();
                c2.close();
            }
        }

        commit();

        //
        // Locks for PK insert, deferrable, not a duplicate.
        //
        s.executeUpdate("insert into t2 values 4");
        rs = s.executeQuery(
                LockTableTest.getSelectLocksString());

        JDBC.assertFullResultSet(rs, new String[][]{
            {"APP", "UserTransaction", "TABLE", "1",
                "IS", "T2", "Tablelock", "GRANT", "ACTIVE"},
            {"APP", "UserTransaction", "TABLE", "2",
                "IX", "T2", "Tablelock", "GRANT", "ACTIVE"},
            {"APP", "UserTransaction", "ROW", "1",
                "X", "T2", "(1,10)", "GRANT", "ACTIVE"}});
        commit();

        //
        // Locks for PK insert, deferrable and a duplicate
        //
        s.executeUpdate("insert into t2 values 4");
        rs = s.executeQuery(
                LockTableTest.getSelectLocksString());
        JDBC.assertFullResultSet(rs, new String[][]{
            {"APP", "UserTransaction", "TABLE", "1",
                "IS", "T2", "Tablelock", "GRANT", "ACTIVE"},
            {"APP", "UserTransaction", "TABLE", "2",
                "IX", "T2", "Tablelock", "GRANT", "ACTIVE"},
            {"APP", "UserTransaction", "ROW", "1",
                "X", "T2", "(1,11)", "GRANT", "ACTIVE"}});

        try {
            // Verify that another transaction doesn't have to wait on insert
            // It will see a timeout and assume a duplicate for checking on
            // commit, which in this case will see the timeout instead.
            c2 = openDefaultConnection();
            c2.setAutoCommit(false);

            final Statement s2 = c2.createStatement();
            s2.executeUpdate("insert into t2 values 4");
            assertCommitError(LOCK_TIMEOUT, c2);
        } finally {
            try {
                if (c2 != null) {
                    c2.rollback();
                    c2.close();
                }
            } catch (SQLException e) {
            }
        }
        rollback();

        // Thread 1: insert a row (not a duplicate)
        s.executeUpdate("insert into t2 values 5");

        // Thread 2: insert same row (duplicate)
        c2 = openDefaultConnection();
        c2.setAutoCommit(false);
        final Statement s2 = c2.createStatement();
        s2.executeUpdate("insert into t2 values 5");

        // Thread 1: try to commit: should not time out we are not doing
        // a checking scan here
        commit();
        c2.rollback();

        //
        // Let a thread 2 insert a key before and after a key inserted by t1.
        // t1 should be able to commit without waiting because the checking
        // scan should not see the rows locked before and after t1's key
        // (read committed mode).
        //
        s2.executeUpdate("insert into t2 values 10,12");

        // Insert a duplicate,
        s.executeUpdate("insert into t2 values 11,11");

        // next delete one of the duplicates,
        final Statement us = createStatement(
                ResultSet.TYPE_FORWARD_ONLY,
                ResultSet.CONCUR_UPDATABLE);
        rs = us.executeQuery("select * from t2 where i=11");
        rs.next();
        rs.deleteRow();
        rs.close();

        // then try to commit.
        commit();

        // clean up
        c2.rollback();
        c2.close();
        s.executeUpdate("drop table t1");
        s.executeUpdate("drop table t2");
        commit();
    }

    public void testBasicDeferral() throws SQLException {
        final Statement s = createStatement();

        for (String sCF : setConstraintsForms) {
            int idx = 0;

            //
            //   P R I M A R Y   K E Y,   U N I Q U E   C O N S T R A I N T S
            //
            for (String ct : uniqueForms) {
                try {
                    s.executeUpdate(
                        ct + " deferrable initially immediate)");
                    s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                    commit();

                    //
                    //   I N S E R T   O F   D U P L I C A T E S
                    //

                    // Normal duplicate insert should fail, still
                    // immediate mode
                    assertStatementError(LANG_DUPLICATE_KEY_CONSTRAINT,
                                         s,
                                         "insert into t values (2,30)");

                    // Now set deferred mode in one of two ways: by specifying
                    // ALL or by naming our index explicitly.
                    s.executeUpdate(sCF + " deferred");

                    // Duplicate insert should now work
                    s.executeUpdate(
                        "insert into t values (2,19),(2,21),(3,31)");

                    // Check contents
                    JDBC.assertFullResultSet(
                        s.executeQuery("select * from t"),
                        new String[][] {
                            {"1", "10"},
                            {"2", "20"},
                            {"3", "30"},
                            {"2", "19"},
                            {"2", "21"},
                            {"3", "31"}});
                    // Check contents: specify ORDER BY and force use of index
                    // use the index.
                    JDBC.assertFullResultSet(
                        s.executeQuery(
                          "select * from t --DERBY-PROPERTIES constraint=c\n" +
                          "     order by i"),
                        new String[][] {
                            {"1", "10"},
                            {"2", "20"},
                            {"2", "19"},
                            {"2", "21"},
                            {"3", "30"},
                            {"3", "31"}});

                    // Try to set immediate mode, and detect violation
                    assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S,
                                         s,
                                         sCF + " immediate");
                    // Once more, error above should not roll back
                    assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S,
                                         s,
                                         sCF + " immediate");

                    // Now try to commit, which should lead to rollback
                    assertCommitError(LANG_DEFERRED_DUP_VIOLATION_T,
                                      getConnection());

                    // Verify that contents are the same as before we did the
                    // duplicate inserts
                    JDBC.assertFullResultSet(
                        s.executeQuery("select * from t"), initialContents);

                    // Setting immediate now should work again:
                    s.executeUpdate(sCF + " immediate");
                    assertStatementError(LANG_DUPLICATE_KEY_CONSTRAINT,
                                         s,
                                         "insert into t values (2,30)");

                    // setting deferred again:
                    s.executeUpdate(sCF + " deferred");

                    // Duplicate insert should now work
                    s.executeUpdate(
                        "insert into t values (2,19),(2,21),(3,31)");
                    assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S,
                                         s,
                                         sCF + " immediate");
                    rollback();

                    //
                    //   U P D A T E   G I V I N G   D U P L I C A T E S
                    //

                    // Now set deferred mode in one of two ways: by specifying
                    // ALL or by naming our constraint explicitly.
                    s.executeUpdate(sCF + " deferred");

                    // Now test the same, but using UPDATE instead of INSERT
                    s.executeUpdate(
                            "insert into t values (20,19),(200,21),(30,31)");
                    s.executeUpdate("update t set i=2 where i=20");
                    s.executeUpdate("update t set i=2 where i=200");
                    s.executeUpdate("update t set i=3 where i=30");

                    // Check result: specify ORDER BY and force use of index
                    // use the index
                    JDBC.assertFullResultSet(
                        s.executeQuery(
                          "select * from t --DERBY-PROPERTIES constraint=c\n" +
                          "     order by i"),
                        new String[][] {
                            {"1", "10"},
                            {"2", "20"},
                            {"2", "19"},
                            {"2", "21"},
                            {"3", "30"},
                            {"3", "31"}});

                    // Now try to commit, which should lead to rollback
                    assertCommitError(LANG_DEFERRED_DUP_VIOLATION_T,
                                      getConnection());

                    // Verify that contents are the same as before we did the
                    // duplicate updates
                    JDBC.assertFullResultSet(
                        s.executeQuery("select * from t"), initialContents);

                    // Specify ORDER BY and force use of index use the index
                    JDBC.assertFullResultSet(s.executeQuery(
                          "select * from t --DERBY-PROPERTIES constraint=c\n" +
                          "     order by i"),
                          initialContents);

                    checkConsistencyOfBaseTableAndIndex(s);

                    // Test add of a deferred constraint to an existing table
                    s.execute("alter table t drop constraint c");

                    // Insert duplicates: no constraint now
                    s.executeUpdate(
                        "insert into t values (2,19),(2,21),(3,31)");
                    commit();

                    // We can't add a constraint with immediate checking
                    // because of the existing duplicates.
                    assertStatementError(
                        LANG_DUPLICATE_KEY_CONSTRAINT,
                        s,
                        "alter table t add constraint c " + uniqueSpec[idx]);

                    // But we can add a deferred constraint:
                    s.executeUpdate(
                        "alter table t add constraint c " +
                        uniqueSpec[idx] + " deferrable initially deferred");

                    // Specify ORDER BY and force use of index use the index
                    JDBC.assertFullResultSet(
                        s.executeQuery(
                          "select * from t --DERBY-PROPERTIES constraint=c\n" +
                          "     order by i"),
                        new String[][] {
                            {"1", "10"},
                            {"2", "20"},
                            {"2", "19"},
                            {"2", "21"},
                            {"3", "30"},
                            {"3", "31"}});

                    // But since we still have duplicates, the commit will fail
                    assertCommitError(LANG_DEFERRED_DUP_VIOLATION_T,
                                      getConnection());

                    checkConsistencyOfBaseTableAndIndex(s);


                } finally {
                    idx++;
                    dropTable("t");
                    commit();
                }
            }


            //
            //   C H E C K   C O N S T R A I N T S
            //

            idx = 0;

            for (String ct : checkForms) {
                try {
                    s.executeUpdate(
                        ct + " deferrable initially immediate)");
                    s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                    commit();

                    //
                    //   I N S E R T   O F   V I O L A T I N G   R O W S
                    //

                    // Normal duplicate insert should fail, still
                    // immediate mode
                    assertStatementError(LANG_CHECK_CONSTRAINT_VIOLATED,
                                         s,
                                         "insert into t values (-2,30)");
	            // Test the DERBY-6773 support:
                    try {
                        s.execute( "insert into t values (-2,30)" );
                        fail();
                    }
                    catch ( DerbySQLIntegrityConstraintViolationException dsicve ) {
                        assertSQLState(LANG_CHECK_CONSTRAINT_VIOLATED, dsicve);
                        assertEquals( "\"APP\".\"T\"", dsicve.getTableName() );
                        assertEquals( "C", dsicve.getConstraintName() );
                    }

                    // Now set deferred mode in one of two ways: by specifying
                    // ALL or by naming our index explicitly.
                    s.executeUpdate(sCF + " deferred");

                    // Rows violating CHECK constraint should now work
                    s.executeUpdate(
                        "insert into t values (-2,30),(1,31),(-3,32)");

                    // Check contents
                    JDBC.assertFullResultSet(
                        s.executeQuery("select * from t"),
                        new String[][] {
                            {"1", "10"},
                            {"2", "20"},
                            {"3", "30"},
                            {"-2", "30"},
                            {"1", "31"},
                            {"-3", "32"}});

                    // Try to set immediate mode, and detect violation
                    assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_S,
                                         s,
                                         sCF + " immediate");
                    // Once more, error above should not roll back
                    assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_S,
                                         s,
                                         sCF + " immediate");
	            // Test the DERBY-6773 support:
                    try {
                        s.execute( sCF + " immediate" );
                        fail();
                    }
                    catch ( DerbySQLIntegrityConstraintViolationException dsicve ) {
                        assertSQLState(LANG_DEFERRED_CHECK_VIOLATION_S, dsicve);
                        assertEquals( "\"APP\".\"T\"", dsicve.getTableName() );
                        assertEquals( "C", dsicve.getConstraintName() );
                    }

                    // Now try to commit, which should lead to rollback
                    //assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T,
                    //                  getConnection());
	            // Test the DERBY-6773 support:
                    try {
                        getConnection().commit();
                        fail();
                    }
                    catch ( DerbySQLIntegrityConstraintViolationException dsicve ) {
                        assertSQLState(LANG_DEFERRED_CHECK_VIOLATION_T, dsicve);
                        assertEquals( "\"APP\".\"T\"", dsicve.getTableName() );
                        assertEquals( "C", dsicve.getConstraintName() );
                    }

                    // Verify that contents are the same as before we did the
                    // duplicate inserts
                    JDBC.assertFullResultSet(
                        s.executeQuery("select * from t"), initialContents);

                    // Setting immediate now should work again:
                    s.executeUpdate(sCF + " immediate");
                    assertStatementError(LANG_CHECK_CONSTRAINT_VIOLATED,
                                         s,
                                         "insert into t values (-2,30)");

                    // setting deferred again:
                    s.executeUpdate(sCF + " deferred");

                    // Insert with check violations should now work
                    s.executeUpdate(
                        "insert into t values (-2,19),(2,21),(-3,31)");
                    assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_S,
                                         s,
                                         sCF + " immediate");
                    rollback();

                    //
                    //  U P D A T E   G I V I N G   V I O L A T I N G   R O W S
                    //

                    // Now set deferred mode in one of two ways: by specifying
                    // ALL or by naming our constraint explicitly.
                    s.executeUpdate(sCF + " deferred");

                    // Now test the same, but using UPDATE instead of INSERT
                    s.executeUpdate(
                            "insert into t values (20,19),(200,21),(30,31)");
                    s.executeUpdate("update t set i=-2 where i=20");
                    s.executeUpdate("update t set i=-3 where i=200");
                    s.executeUpdate("update t set i=-4 where i=30");

                    // Check result
                    JDBC.assertFullResultSet(
                        s.executeQuery(
                          "select * from t order by j"),
                        new String[][] {
                            {"1", "10"},
                            {"-2", "19"},
                            {"2", "20"},
                            {"-3", "21"},
                            {"3", "30"},
                            {"-4", "31"}});

                    // Now try to commit, which should lead to rollback
                    assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T,
                                      getConnection());

                    // Verify that contents are the same as before we did the
                    // duplicate inserts
                    JDBC.assertFullResultSet(
                        s.executeQuery("select * from t"), initialContents);

                    JDBC.assertFullResultSet(s.executeQuery(
                          "select * from t order by i"),
                          initialContents);

                    checkConsistencyOfBaseTableAndIndex(s);

                    // Test add of a deferred constraint to an existing table
                    s.execute("alter table t drop constraint c");

                    // Insert "violating" rows: no constraint now
                    s.executeUpdate(
                        "insert into t values (-2,19),(2,21),(-3,31)");
                    commit();

                    // We can't add a constraint with immediate checking
                    // because of the existing violations..
                    assertStatementError(
                        LANG_ADD_CHECK_CONSTRAINT_FAILED,
                        s,
                        "alter table t add constraint c " + checkSpec[idx]);

                    // But we can add a deferred constraint:
                    s.executeUpdate(
                        "alter table t add constraint c " +
                        checkSpec[idx] + " deferrable initially deferred");

                    JDBC.assertFullResultSet(
                        s.executeQuery(
                          "select * from t order by i,j"),
                        new String[][] {
                            {"-3", "31"},
                            {"-2", "19"},
                            {"1", "10"},
                            {"2", "20"},
                            {"2", "21"},
                            {"3", "30"}});

                    // But since we still have violations, the commit will fail
                    assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T,
                                      getConnection());

                    checkConsistencyOfBaseTableAndIndex(s);


                } finally {
                    idx++;
                    dropTable("t");
                    commit();
                }
            }
        }
    }

    /**
     * Test that if the constraint mode is immediate and a routine has changed
     * this to introduce duplicates, we raise an error and roll back on exit
     * from the routine.
     * @throws SQLException
     */
    public void testRoutines() throws SQLException {
        final Statement s = createStatement();

        //
        //   P R I M A R Y   K E Y,   U N I Q U E   C O N S T R A I N T S
        //

        // Caller has not explicitly done any "SET CONSTRAINTS", but
        // constraint is initially immediate
        for (String ct : uniqueForms) {
            try {
                s.executeUpdate(
                        ct + " deferrable initially immediate)");
                s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                commit();

                declareCalledNested(s);
                assertStatementError(
                        LANG_DEFERRED_DUP_VIOLATION_T,
                        s,
                        "call calledNested(false)");
            } finally {
                dropTable("t");
            }
        }

        // Constraint is initially deferred, but mode then set to immediate
        // before the call
        for (String setConstraintForm : setConstraintsForms) {
            for (String ct : uniqueForms) {
                try {
                    s.executeUpdate(
                        ct + " deferrable initially deferred)");
                    s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                    commit();

                    s.executeUpdate(setConstraintForm + " immediate");
                    declareCalledNested(s);
                    assertStatementError(LANG_DEFERRED_DUP_VIOLATION_T,
                            s,
                            "call calledNested(false)");
                } finally {
                    dropTable("t");
                    commit();
                }
            }
        }

        // Check that we don't bark if we actually introduced the duplicates
        // in the caller session context
        for (String ct : uniqueForms) {
            try {
                s.executeUpdate(
                        ct + " deferrable initially deferred)");
                s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));

                declareCalledNested(s);
                s.executeUpdate("call calledNested(false)");
            } finally {
                rollback();
            }
        }


        //
        //    C H E C K   C O N S T R A I N T S
        //
        for (String ct : checkForms) {
            try {
                s.executeUpdate(
                        ct + " deferrable initially immediate)");
                s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                commit();

                declareCalledNested(s);
                assertStatementError(
                        LANG_DEFERRED_CHECK_VIOLATION_T,
                        s,
                        "call calledNested(true)");
            } finally {
                dropTable("t");
                commit();
            }
        }

        // Constraint is initially deferred, but mode then set to immediate
        // before the call
        for (String setConstraintForm : setConstraintsForms) {
            for (String ct : checkForms) {
                try {
                    s.executeUpdate(
                        ct + " deferrable initially deferred)");
                    s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                    commit();

                    s.executeUpdate(setConstraintForm + " immediate");
                    declareCalledNested(s);
                    assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_T,
                            s,
                            "call calledNested(true)");
                } finally {
                    dropTable("t");
                    commit();
                }
            }
        }

        // Check that we don't bark if we actually introduced the violations
        // in the caller session context
        for (String ct : checkForms) {
            try {
                s.executeUpdate(ct + " deferrable initially deferred)");
                s.executeUpdate("insert into t values " +
                                rs2Values(negatedInitialContents));
                declareCalledNested(s);
                s.executeUpdate("call calledNested(true)");
            } finally {
                rollback();
            }
        }
        
        // Check what happens if routine set mode to immediate with
        // deferred rows inserted by caller
        for (String ct : checkForms) {
            try {
                s.executeUpdate(
                   ct + " deferrable initially deferred)");
                s.executeUpdate(
                   "insert into t values " + rs2Values(negatedInitialContents));
                declareCalledNestedSetImmediate(s);
                assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_S,
                                     s, "call calledNestedSetImmediate()");
            } finally {
                rollback();
            }
        }

        //
        //    F O R E I G N   K E Y   C O N S T R A I N T S
        //
        for (String ct : fkForms) {
            try {
                s.executeUpdate(
                        ct + " deferrable initially immediate)");
                s.executeUpdate("insert into referenced values " +
                        rs2Values(initialContents));
                s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                commit();

                declareCalledNestedFk(s);
                assertStatementError(
                        LANG_DEFERRED_FK_VIOLATION_T,
                        s,
                        "call calledNestedFk()");
            } finally {
                dropTable("t");
                dontThrow(s, "delete from referenced");
                commit();
            }
        }

        // Constraint is initially deferred, but mode then set to immediate
        // before the call
        for (String setConstraintForm : setConstraintsForms) {
            for (String ct : fkForms) {
                try {
                    s.executeUpdate(
                        ct + " deferrable initially deferred)");
                    s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                    s.executeUpdate(
                        "insert into referenced(i) select i from t");
                    commit();

                    s.executeUpdate(setConstraintForm + " immediate");
                    declareCalledNestedFk(s);
                    assertStatementError(LANG_DEFERRED_FK_VIOLATION_T,
                            s,
                            "call calledNestedFk()");
                } finally {
                    dropTable("t");
                    dontThrow(s, "delete from referenced");
                    commit();
                }
            }
        }

        // Check that we don't bark if we actually introduced the violations
        // in the caller session context
        for (String ct : fkForms) {
            try {
                s.executeUpdate(
                        ct + " deferrable initially deferred)");
                s.executeUpdate(
                        "insert into t values " + rs2Values(initialContents));
                declareCalledNestedFk(s);
                s.executeUpdate("call calledNestedFk()");
                assertCommitError(LANG_DEFERRED_FK_VIOLATION_T,
                                  getConnection());
            } finally {
                rollback();
            }
        }

        // Check what happens if routine set mode to immediate with
        // deferred rows inserted by caller
        for (String ct : fkForms) {
            try {
                s.executeUpdate(
                   ct + " deferrable initially deferred)");
                s.executeUpdate(
                   "insert into t values " + rs2Values(initialContents));
                declareCalledNestedSetImmediate(s);
                assertStatementError(LANG_DEFERRED_FK_VIOLATION_S, s,
                                     "call calledNestedSetImmediate()");
            } finally {
                rollback();
            }
        }


    }

    public void testDeferredRowsInvalidation() throws SQLException {
        final Statement s = createStatement();
        //
        //   U N I Q U E,   P R I M A R Y   K E Y   C O N S T R A I N T
        //

        //   D r o p   t h e   c o n s t r a i n t
        s.executeUpdate("create table t(i int, " +
                        "  constraint c primary key (i) initially deferred)");
        s.executeUpdate("insert into t values 1,2,2,3");
        s.executeUpdate("alter table t drop constraint c");

        // Commit (below) normally forces checking of the deferred constraint
        // "c".  The dropping of the constraint should make sure we don't see
        // any issue with the recorded information lcc#deferredHashTables
        // i.e. {index conglomerate -> duplicate rows} .
        //
        // See LanguageConnectionContext#forgetDeferredConstraintsData.
        commit();
        s.executeUpdate("drop table t");
        commit();

        //   D r o p   t h e   t a b l e
        s.executeUpdate("create table t(i int, " +
                        "  constraint c primary key (i) initially deferred)");
        s.executeUpdate("insert into t values 1,2,2,3");
        assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("drop table t");
        commit();

        //   T r u n c a t e   t h e   t a b l e
        s.executeUpdate("create table t(i int, " +
                        "  constraint c primary key (i) initially deferred)");
        s.executeUpdate("insert into t values 1,2,2,3");
        assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("truncate table t");
        s.executeUpdate("set constraints c immediate");
        s.executeUpdate("set constraints c deferred");
        s.executeUpdate("insert into t values 1,2,2,3");
        assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("drop table t");
        commit();

        //   C o m p r e s s   t h e   t a b l e
        s.executeUpdate("create table t(i int, " +
                "  constraint c primary key (i) initially deferred)");
        s.executeUpdate("insert into t values 1,2,2,3");
        assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("delete from t where i=1");
        s.executeUpdate("call syscs_util.syscs_compress_table('APP', 'T', 0)");
        assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S, s,
                             "set constraints c immediate");
        assertCommitError(LANG_DEFERRED_DUP_VIOLATION_T, getConnection());

        s.executeUpdate("create table t(i int, " +
                "  constraint c primary key (i) initially deferred)");
        commit();
        s.executeUpdate("insert into t values 1,2,3");
        s.executeUpdate("delete from t where i=1");

        // Inline compress times out if we add a PK (even without deferred
        // constraints)
        assertStatementError(LOCK_TIMEOUT, s,
                "call syscs_util.syscs_inplace_compress_table(" +
                "'APP', 'T', 1, 1, 1)");
        // assertStatementError(LANG_DEFERRED_DUP_VIOLATION_S, s,
        //                      "set constraints c immediate");
        // assertCommitError(LANG_DEFERRED_DUP_VIOLATION_T, getConnection());
        s.executeUpdate("drop table t");
        commit();

        //
        //   C H E C K   C O N S T R A I N T
        //

        //   D r o p   t h e   c o n s t r a i n t
        s.executeUpdate("create table t(i int, " +
                "  constraint c check (i > 0) initially deferred)");
        s.executeUpdate("insert into t values -1,-2, -2, -3");
        assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_S, s,
                             "set constraints c immediate");

        s.executeUpdate("alter table t drop constraint c");
        commit();
        s.executeUpdate("drop table t");
        commit();

        //   D r o p   t h e   t a b l e
        s.executeUpdate("create table t(i int, " +
                "  constraint c check (i > 0) initially deferred)");
        s.executeUpdate("insert into t values -1, -2, -2, -3");
        assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("drop table t");
        commit();

        //   T r u n c a t e   t h e   t a b l e
        s.executeUpdate("create table t(i int, " +
                "  constraint c check (i > 0) initially deferred)");
        s.executeUpdate("insert into t values -1, -2, -2, -3");
        assertStatementError(LANG_DEFERRED_CHECK_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("truncate table t");
        commit();
        s.executeUpdate("drop table t");
        commit();

        //   C o m p r e s s   t h e   t a b l e
        //
        // We can no longer rely on row locations, so we do a full table scan
        // instead to detect any violations.
        s.executeUpdate("create table t(i int, " +
                "  constraint c check (i > 0) initially deferred)");

        s.executeUpdate("insert into t values -1, -2, -2, -3");
        s.executeUpdate("delete from t where i=-2");
        s.executeUpdate("call syscs_util.syscs_compress_table('APP', 'T', 0)");
        assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T, getConnection());

        s.executeUpdate("create table t(i int, " +
                "  constraint c check (i > 0) initially deferred)");
        commit();
        s.executeUpdate("insert into t values -1, -2, -2, -3");
        s.executeUpdate("delete from t where i=-2");
        s.executeUpdate("call syscs_util.syscs_inplace_compress_table(" +
                "'APP', 'T', 1, 1, 1)");
        assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T, getConnection());
        s.executeUpdate("drop table t");
        commit();


        //
        //   F O R E I G N   K E Y  C O N S T R A I N T
        //

        //   D r o p   t h e   c o n s t r a i n t
        s.executeUpdate("create table t(i int, constraint c foreign key(i) " +
                "references referenced(i) initially deferred)");
        s.executeUpdate("insert into t values 1,2,3");
        assertStatementError(LANG_DEFERRED_FK_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("alter table t drop constraint c");
        commit();
        s.executeUpdate("drop table t");
        commit();

        //   T r u n c a t e   t h e   r e f e r e n c i n g   t a b l e
        s.executeUpdate("create table t(i int, constraint c foreign key(i) " +
                "references referenced(i) initially deferred)");

        s.executeUpdate("insert into t values 1,2,3");
        assertStatementError(LANG_DEFERRED_FK_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("truncate table t");
        commit();
        s.executeUpdate("insert into t values 1,2,3");
        assertStatementError(LANG_DEFERRED_FK_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("drop table t");
        commit();

        //   C o m p r e s s   t h e   r e f e r e n c i n g   t a b l e

        // Compress by recreating the conglomerate
        s.executeUpdate("create table t(i int, constraint c foreign key(i) " +
                "references referenced(i) initially deferred)");
        s.executeUpdate("insert into referenced(i) values 4,5,6");
        s.executeUpdate("insert into t values 4,5,6,7");
        s.executeUpdate("delete from t where i=5");
        s.executeUpdate("call syscs_util.syscs_compress_table('APP', 'T', 0)");
        assertCommitError(LANG_DEFERRED_FK_VIOLATION_T, getConnection());

        // In-place compress
        s.executeUpdate("create table t(i int, constraint c foreign key(i) " +
                "references referenced(i) initially deferred)");
        s.executeUpdate("insert into referenced(i) values 4,5,6");
        s.executeUpdate("insert into t values 4,5,6,7");
        s.executeUpdate("delete from t where i=5");
        // s.executeUpdate("call syscs_util.syscs_inplace_compress_table(" +
        //                 "    'APP', 'T', 1,1,1)");
        assertStatementError(
            LOCK_TIMEOUT, s,
            "call syscs_util.syscs_inplace_compress_table('APP', 'T', 1,1,1)");
        // assertCommitError(LANG_DEFERRED_FK_VIOLATION_T, getConnection());


        //   C o m p r e s s   t h e   r e f e r e n c e d   t a b l e
        //
        // Compress by recreating the conglomerate
        s.executeUpdate(
            "create table t(i int, constraint c foreign key(i) " +
            "references referenced(i) ON DELETE NO ACTION initially deferred)");
        s.executeUpdate("insert into referenced(i) values 4,5,6");
        s.executeUpdate("insert into t values 4,5,6");
        s.executeUpdate("delete from referenced where i=5");
        assertStatementError(LANG_DEFERRED_FK_VIOLATION_S, s,
                             "set constraints c immediate");
        s.executeUpdate("call syscs_util.syscs_compress_table('APP', 'T', 0)");
        assertCommitError(LANG_DEFERRED_FK_VIOLATION_T, getConnection());

        // In-place compress
        s.executeUpdate(
            "create table t(i int, constraint c foreign key(i) " +
            "references referenced(i) ON DELETE NO ACTION initially deferred)");
        s.executeUpdate("insert into referenced(i) values 4,5,6");
        s.executeUpdate("insert into t values 4,5,6");
        s.executeUpdate("delete from referenced where i=5");
        assertStatementError(LANG_DEFERRED_FK_VIOLATION_S, s,
                             "set constraints c immediate");
        // s.executeUpdate("call syscs_util.syscs_inplace_compress_table(" +
        //                 "    'APP', 'T', 1,1,1)");
        assertStatementError(
           LOCK_TIMEOUT, s,
           "call syscs_util.syscs_inplace_compress_table('APP', 'T', 1, 1, 1)");
        // assertCommitError(LANG_DEFERRED_FK_VIOLATION_T, getConnection());
    }

    /**
     * Import uses other code paths than normal insert, so test it. Not very
     * useful with deferred constraints, however, since the IMPORT performs an
     * implicit commit at the end. However, the implementation goes through the
     * motions of deferring the checking, and the actual checking happens at
     * commit time. So, if the implicit commit is lifted in the future, the
     * deferred constraints should work. For now, the only net effect is to
     * delay the violation detection, so we should recommend immediate checking
     * in conjunction with import.
     *
     * @throws SQLException
     */
    public void testImport() throws SQLException {
        final Statement s = createStatement();

        s.executeUpdate("create table t(i int)");

        try {

            // Try the test cases below with both "replace" and "append"
            // semantics
            for (int addOrReplace = 0; addOrReplace < 2; addOrReplace++) {
                //
                //  P R I M A R Y   C O N S T R A I N T
                //
                s.executeUpdate("alter table t alter column i not null");
                s.executeUpdate(
                    "alter table t " +
                    "add constraint c primary key(i) " +
                    "    deferrable initially immediate");
                commit();

                s.executeUpdate("set constraints c deferred");

                // import and implicit commit leads to checking
                assertStatementError(
                    LANG_DEFERRED_DUP_VIOLATION_T, s,
                    "call SYSCS_UTIL.SYSCS_IMPORT_TABLE (" +
                    "    'APP' , 'T' , '" + expImpDataFile + "'," +
                    "    null, null , null, " + addOrReplace + ")");

                //
                //   U N I Q U E   N O T   N U L L   C O N S T R A I N T
                //
                s.executeUpdate("alter table t alter column i not null");
                s.executeUpdate("alter table t drop constraint c");
                s.executeUpdate
                    ("alter table t " +
                     "add constraint c unique(i) " +
                     "    deferrable initially immediate");
                commit();

                s.executeUpdate("set constraints c deferred");

                // import and implicit commit leads to checking
                assertStatementError(
                    LANG_DEFERRED_DUP_VIOLATION_T, s,
                    "call SYSCS_UTIL.SYSCS_IMPORT_TABLE (" +
                    "    'APP' , 'T' , '" + expImpDataFile + "'," +
                    "    null, null , null, " + addOrReplace + ")");

                //
                //   n u l l a b l e   U N I Q U E   C O N S T R A I N T
                //
                s.executeUpdate("alter table t alter column i null");
                s.executeUpdate("alter table t drop constraint c");
                s.executeUpdate(
                    "alter table t " +
                    "add constraint c unique(i) initially deferred");
                commit();

                // import and implicit commit leads to checking
                assertStatementError(
                    LANG_DEFERRED_DUP_VIOLATION_T, s,
                    "call SYSCS_UTIL.SYSCS_IMPORT_TABLE (" +
                    "    'APP' , 'T' , '" + expImpDataFile + "'," +
                    "    null, null , null, " + addOrReplace + ")");

                // Import OK data with multiple NULLs should still work with
                // nullable UNIQUE deferred constraint
                s.executeUpdate(
                        "call SYSCS_UTIL.SYSCS_IMPORT_TABLE (" +
                        "    'APP' , 'T' , '" +
                        expImpDataWithNullsFile + "'," +
                        "    null, null , null, " + addOrReplace + ")");
                s.executeUpdate("alter table t drop constraint c");
                s.executeUpdate("truncate table t");
                commit();

                //
                //   C H E C K   C O N S T R A I N T
                //
                s.executeUpdate(
                    "alter table t " +
                    "add constraint c check (i > 0) initially deferred");

                // import and implicit commit leads to checking
                assertStatementError(
                    LANG_DEFERRED_CHECK_VIOLATION_T, s,
                    "call SYSCS_UTIL.SYSCS_IMPORT_TABLE (" +
                    "    'APP' , 'T' , '" + expImpDataFile + "'," +
                    "    null, null , null, " + addOrReplace + ")");

                s.executeUpdate("truncate table t");
                commit();
            }
        } finally {
            dropTable("t");
            commit();
        }
    }

    // Adapted from UniqueConstraintSetNullTest which exposed an error
    // when we ran all regressions with default deferrable: when a NOT NULL
    // clause was dropped, the test used to drop and recreate the index to
    // be non-unique was incomplete in the deferrable case.
    public void testDropNotNullOnUniqueColumn() throws SQLException {
        final Statement s = createStatement();

        s.executeUpdate("create table constraintest (" +
                "val1 varchar (20) not null, " +
                "val2 varchar (20))");

        s.executeUpdate("alter table constraintest add constraint " +
                "u_con unique (val1) deferrable initially immediate");

        s.executeUpdate("alter table constraintest alter column val1 null");

        s.executeUpdate("insert into constraintest(val1) values 'name1'");

        assertStatementError(
            LANG_DUPLICATE_KEY_CONSTRAINT, s,
            "insert into constraintest(val1) values 'name1'");

        final PreparedStatement ps = prepareStatement(
                "insert into constraintest(val1) values (?)");
        ps.setString(1, null);
        ps.executeUpdate();
        ps.setString(1, null);
        ps.executeUpdate();
    }


    public void testDerby6374() throws SQLException {
        final Statement s = createStatement();

        s.executeUpdate("create table t(i int)");

        try {
            // Try the test cases below with both "replace" and not with
            // the import statement:
            for (int addOrReplace = 0; addOrReplace < 2; addOrReplace++) {

                // Import duplicate data into a table a nullable
                // UNIQUE constraint
                s.executeUpdate("alter table t add constraint c unique(i)");
                commit();

                assertStatementError(
                    LANG_DUPLICATE_KEY_CONSTRAINT, s,
                    "call SYSCS_UTIL.SYSCS_IMPORT_TABLE (" +
                    "    'APP' , 'T' , '" + expImpDataFile + "'," +
                    "    null, null , null, " + addOrReplace + ")");

                s.executeUpdate("alter table t drop constraint c");
            }
        } finally {
            dropTable("t");
            commit();
        }
    }

    public void testXA() throws SQLException, XAException {
        final XADataSource xads = J2EEDataSource.getXADataSource();
        J2EEDataSource.setBeanProperty(xads, "databaseName", "wombat");

        final int UNIQUE_PK = 0; // loop iteration 0
        final int CHECK = 1;     // loop iteration 1

        final String[] expectedError = {
            LANG_DEFERRED_DUP_VIOLATION_T,
            LANG_DEFERRED_CHECK_VIOLATION_T};

        for (int i = UNIQUE_PK; i <= CHECK; i++) {
            final XAConnection xaconn = xads.getXAConnection();

            try {
                final XAResource xar = xaconn.getXAResource();
                final Connection conn = xaconn.getConnection();
                conn.setAutoCommit(false);

                final Statement s = conn.createStatement();

                //
                // Do XA rollback when we have a violation; expect normal
                // operation.
                //
                Xid xid = (i == UNIQUE_PK) ?
                        doXAWorkUniquePK(s, xar) :
                        doXAWorkCheck(s, xar);

                xar.rollback(xid);
                assertXidRolledBack(xar, xid);

                //
                // Do an XA prepare when we have a violation; expect exception
                // and rollback.
                //
                xid = (i == UNIQUE_PK) ?
                        doXAWorkUniquePK(s, xar) :
                        doXAWorkCheck(s, xar);

                try {
                    xar.prepare(xid);
                    fail("Expected XA prepare to fail due to " +
                         "constraint violation");
                } catch (XAException xe) {
                    assertEquals(XAException.XA_RBINTEGRITY, xe.errorCode);

                    if (!usingDerbyNetClient()) {
                        Throwable t = xe.getCause();
                        assertTrue(t != null && t instanceof SQLException);
                        assertSQLState(expectedError[i], (SQLException)t);
                    }

                    assertXidRolledBack(xar, xid);
                }

                //
                // Do XA commit (1PC, no prepare) when we have a violation;
                // expect exception and rollback.
                //
                xid = (i == UNIQUE_PK) ?
                        doXAWorkUniquePK(s, xar) :
                        doXAWorkCheck(s, xar);

                try {
                    xar.commit(xid, true);
                    fail("Expected XA commit to fail due to " +
                         "constraint violation");
                } catch (XAException xe) {
                    if (xe.errorCode == -3) {
                        System.err.println("huff");
                    } else {
                        assertEquals(XAException.XA_RBINTEGRITY, xe.errorCode);

                        if (!usingDerbyNetClient()) {
                            Throwable t = xe.getCause();
                            assertTrue(t != null && t instanceof SQLException);
                            assertSQLState(expectedError[i], (SQLException)t);
                        }
                    }

                    assertXidRolledBack(xar, xid);
                }

            } finally {
                if (usingDerbyNetClient()) {
                    xaconn.getConnection().rollback();
                }
                xaconn.close();
            }
        }
    }

    // Exposed a bug when running regression suites with default
    // deferrable: compress recreates the index.
    public void testCompressTableOKUnique() throws SQLException {
        final Statement stmt = createStatement();
        stmt.executeUpdate(
                "create table table1(" +
                "name1 int unique deferrable initially immediate, " +
                "name2 int unique not null, " +
                "name3 int primary key)");
        try {
            stmt.execute(
                "call syscs_util.syscs_compress_table('APP','TABLE1',1)");
            stmt.executeUpdate(
                "insert into table1 values(1,11,111)");

            // The following should run into problem because of constraint
            // on name1
            assertStatementError(
                LANG_DUPLICATE_KEY_CONSTRAINT, stmt,
                "insert into table1 values(1,22,222)");

            // The following should run into problem because of constraint
            // on name2
            assertStatementError(
                LANG_DUPLICATE_KEY_CONSTRAINT, stmt,
                "insert into table1 values(3,11,333)");

            // The following should run into problem because of constraint
            // on name3
            assertStatementError(
                LANG_DUPLICATE_KEY_CONSTRAINT, stmt,
                "insert into table1 values(4,44,111)");

	    // Test the DERBY-6773 support:
            try {
                stmt.execute( "insert into table1 values(1,22,222)");
                fail();
            }
            catch ( DerbySQLIntegrityConstraintViolationException dsicve ) {
                assertSQLState(LANG_DUPLICATE_KEY_CONSTRAINT, dsicve);
                assertEquals( "TABLE1", dsicve.getTableName() );
                assertTrue( dsicve.getConstraintName().startsWith( "SQL" ) );
            }
        } finally {
            stmt.executeUpdate("drop table table1");
        }
    }


    final static long NO_OF_INSERTED_ROWS = (1024L * 4);
    public void testManySimilarDuplicates() throws SQLException {
        if (usingDerbyNetClient()) {
            // skip, too heavy fixture to do twice...
            return;
        }

        final Connection c = getConnection();
        c.setAutoCommit(false);

        final Statement s = c.createStatement();
        try {
            s.executeUpdate(
                "create table t (i varchar(256), " +
                "    constraint c primary key(i) initially deferred)");

            final PreparedStatement ps =
                c.prepareStatement("insert into t values ?");

            char[] value = new char[256];
            Arrays.fill(value, 'a');
            ps.setString(1, String.valueOf(value));

            for (long l=0; l < NO_OF_INSERTED_ROWS; l++) {
                ps.executeUpdate();
            }
            c.commit();
            fail();
        } catch (SQLException e) {
            assertSQLState(LANG_DEFERRED_DUP_VIOLATION_T, e);
            s.executeUpdate("call syscs_util.syscs_checkpoint_database()");
        }
    }

    /**
     * Remove all duplicates except the last
     * @throws java.sql.SQLException
     */
    public void testAlmostRemovedAllDups() throws SQLException {
        final Statement s = createStatement();
        s.executeUpdate(
            "create table t(i int, j int, " +
            "    constraint c primary key (i) initially deferred)");

        try {
            final PreparedStatement ps = prepareStatement(
                "insert into t values (?,?)");

            for (int i=0; i < 10; i++) {
                ps.setInt(1, 1);
                ps.setInt(2, i);
                ps.executeUpdate();
            }

            // leave one row
            s.executeUpdate("delete from t where j > 0");
            commit(); // should work

            s.executeUpdate("truncate table t");

            // make many different duplicates and delete all except the last
            // two rows, i.e. one duplicate left.
            for (int i=0; i < 10; i++) {
                ps.setInt(1, i);
                ps.setInt(2, i);
                ps.executeUpdate();

                ps.setInt(1, i);
                ps.setInt(2, i);
                ps.executeUpdate();
            }

            s.execute("delete from t where i < 9");
            JDBC.assertFullResultSet(s.executeQuery("select * from t"),
                new String[][]{
                    {"9","9"},
                    {"9","9"}});
            commit();
        } catch (SQLException e) {
            assertSQLState(LANG_DEFERRED_DUP_VIOLATION_T, e);
        } finally {
            dropTable("t");
            commit();
        }
    }

    private static void setupTab1(final Connection c) throws SQLException {
        final Statement stmt = c.createStatement();
        stmt.execute(
                "create table tab1 (i integer)");
        stmt.executeUpdate(
                "alter table tab1 add constraint con1 unique (i) deferrable");
        final PreparedStatement ps = c.prepareStatement("insert into tab1 " +
                "values (?)");

        for (int i = 0; i < 10; i++) {
            ps.setInt(1, i);
            ps.executeUpdate();
        }

        ps.close();
        stmt.close();
        c.commit();
    }


    private static void dropTab1(final Connection c) throws SQLException {
        final Statement stmt = c.createStatement();

        try {
            stmt.execute("drop table tab1");
            c.commit();
        } catch (SQLException e) {
            // ignore so we get to see original exception if there is one
        }
    }

    /**
     * Test inserting a duplicate record while original is deleted in a
     * transaction and later committed.
     * <p/>
     * This test was lifted from UniqueConstraintMultiThrededTest
     * except that here we run it with a deferrable constraint. We
     * include it her e since it exposed a bug during implementation
     * of deferrable constraints: we check a deferrable constraint
     * <em>after</em> the insert (cf. {@code IndexChanger}) by using a
     * BTree scan.  Iff the constraint mode is deferred, we treat any
     * lock or deadlock timeout as if it were a duplicate, allowing us
     * to defer the check till commit time, as so possibly gain more
     * concurrency. To get speed in this case, the scan returns
     * immediately if it can't get a lock.  The error was that, if the
     * constraint mode is <em>not</em> deferred (i.e. immediate), we
     * should wait for the lock, and we didn't. This was exposed by
     * this test since the 2 seconds wait makes it work in the normal
     * case (the lock would be released), but in the no-wait scan, we
     * saw a the lock time-out error.
     * @throws java.lang.Exception
     */
    public void testLockingForUniquePKWithCommit () throws Exception {
        setupTab1(getConnection());

        try {
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    executeThreads(
                        this,
                        (int)Math.pow(2,i),
                        (int)Math.pow(2,j), true);
                }
            }
        } finally {
            dropTab1(getConnection());
        }
    }

    /**
     * Test inserting a duplicate record while original is deleted in
     * a transaction and later rolled back.
     * <p/>
     * See also comment for {@link #testLockingForUniquePKWithCommit() }.
     *
     * @throws java.lang.Exception
     */
    public void testLockingForUniquePKWithRollback () throws Exception {
        setupTab1(getConnection());

        try {
            for (int i = 0; i < 4; i++) {
                for (int j = 0; j < 4; j++) {
                    executeThreads(
                        this,
                        (int)Math.pow(2,i),
                        (int)Math.pow(2,j), false);
                }
            }
        } finally {
            dropTab1(getConnection());
        }
    }


    /**
     * A bit of white box testing to cover different code paths. SOmetimes, on
     * INSERT and UPDATE, the actual writing of the rows is deferred, e.g.
     * due to a "self" select, or due to the presence of triggers.
     *
     * @throws SQLException
     */
    public void testCheckConstraintsWithDeferredRows () throws
            SQLException {

        final Statement s = createStatement();

        try {
            s.executeUpdate(
                "create table tab1 (c1 int, " +
                "constraint c check (c1 > 0) deferrable initially deferred)");
            commit();

            //
            // I N S E R T,   D E F E R R E D   P R O C E S S I N G
            //

            // INSERT from self causes the violation
            s.executeUpdate("insert into tab1 values (4)");
            s.executeUpdate("insert into tab1 values (3)");
            s.executeUpdate("insert into tab1 select c1-3 from tab1");
            assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T,
                              getConnection());

            //
            // U P D A T E,   D E F E R R E D   P R O C E S S I N G
            //

            // correlated query to force deferred update processing:
            s.executeUpdate("insert into tab1 values (2)");
            s.executeUpdate("update tab1 as grr set c1=-1 where c1 = 2 and " +
                    "((select max(c1) from tab1 where grr.c1 > 0) > 0)");

            assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T,
                              getConnection());

            // Correlated query to force deferred update processing but with
            // trigger which causes another code path.
            s.executeUpdate("insert into tab1 values (2)");
            s.executeUpdate("create table trigtab(i int)");
            s.executeUpdate("create trigger mytrigger " +
                    "after update on tab1 insert into trigtab values 1");
            s.executeUpdate("update tab1 as grr set c1=-1 where c1 = 2 and " +
                    "((select max(c1) from tab1 where grr.c1 > 0) > 0)");

            assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T,
                              getConnection());
        } finally {
            // clean up
            dropTable("tab1");
            dropTable("trigtab");
            commit();
        }
    }

    final static String DID = "deferrable initially deferred";
    /**
     * We can have several constraints broken on one row write,
     * test is we register all correctly
     *
     * @throws SQLException
     */
    public void testSeveralCheckConstraints () throws SQLException {
        final Statement s = createStatement();

        try {
            s.executeUpdate(
                "create table t(" +
                "i int, constraint ci check (i > 0) " + DID + ", " +
                "j int, constraint cj check (j > 0) " + DID + ", " +
                "k int, constraint ck check (k > 0) " + DID + ")");
            commit();

            final String[] setStrings = {
                "j = -j, k = -k",
                "i = -i, k = -k",
                "i = -i, j = -j"};

            final String[] makeImmediate = {"ci", "cj", "ck"};

            // All constraints are broken, make all except one good and
            // check that the one still barfs when made immediate.
            for (int i = 0; i < 3; i++) {
                s.executeUpdate("insert into t values (-1, -2, -3)");
                s.executeUpdate("update t set " + setStrings[i]);

                try {
                    // We force checking of only the column which is still
                    // broken
                    s.executeUpdate("set constraints " + makeImmediate[i] +
                                    " immediate");
                    fail("expected violation: " + i);
                } catch (SQLException e) {
                    assertSQLState(LANG_DEFERRED_CHECK_VIOLATION_S, e);
                    assertTrue(e.getMessage().contains(
                                   makeImmediate[i].toUpperCase()));
                }
                rollback();
            }

            // Check that we accumulate the set of broken constraints
            // when many are violated on separate occasions
            s.executeUpdate("insert into t values (-1,  2,  3)");
            s.executeUpdate("insert into t values ( 1, -2,  3)");
            s.executeUpdate("insert into t values ( 1,  2, -3)");

            for (int i = 0; i < 3; i++) {
                try {
                    s.executeUpdate("set constraints " + makeImmediate[i] +
                                    " immediate");
                    fail("expected violation: " + i);
                } catch (SQLException e) {
                    assertSQLState(LANG_DEFERRED_CHECK_VIOLATION_S, e);
                    assertTrue(e.getMessage().contains(
                                   makeImmediate[i].toUpperCase()));
                }
            }

            rollback();

            // Violations on the same the same row many times
            s.executeUpdate("insert into t values (-1,  2,  3)");
            s.executeUpdate("update t set i=-1");
            s.executeUpdate("update t set i=-1");
            assertCommitError(LANG_DEFERRED_CHECK_VIOLATION_T,
                              getConnection());

        } finally {
            dropTable("t");
            commit();
        }
    }

    /**
     * Deletes a record in a transaction and tries to insert the same
     * from a different transaction. Once second transaction goes on wait
     * first transaction is committed or rolled back based on third
     * parameter (boolean commit).
     *
     * @param thisTest the test object to operate on
     * @param isolation1 isolation level for 1st thread
     * @param isolation2 isolation level for 2nd thread
     * @param commit whether or not to commit
     *
     * (Lifted from UniqueConstraintMultiThrededTest to test with deferrable
     * constraint.)
     *
     * @throws java.lang.Exception
     */
    private static void executeThreads (
        final ConstraintCharacteristicsTest thisTest,
        final int isolation1,
        final int isolation2,
        final boolean commit) throws Exception {

        final Connection con1 = thisTest.openDefaultConnection();
        con1.setTransactionIsolation(isolation1);
        final Connection con2 = thisTest.openDefaultConnection();

        try {
            con2.setTransactionIsolation(isolation2);
            final DBOperations dbo1 = new DBOperations (con1, 5);
            final DBOperations dbo2 = new DBOperations (con2, 5);
            dbo1.delete();
            final Thread t = new Thread (dbo2);
            t.start();

            Thread.sleep((WAIT_TIMEOUT_DURATION * 1000) / 2 );

            if (commit) {
                dbo1.rollback();
                t.join();
                assertSQLState(
                    "isolation levels: " + isolation1 + " " + isolation2,
                    LANG_DUPLICATE_KEY_CONSTRAINT,
                    dbo2.getException());
            } else {
                dbo1.commit();
                t.join();
                assertNull("isolation levels: " + isolation1
                            + " " + isolation2 + ": exception " + 
                        dbo2.getException(), dbo2.getException());
            }
            assertNull("unexpected failure: " + isolation1
                        + " " + isolation2 + ": exception " + 
                        dbo2.getUnexpectedException(), 
                    dbo2.getUnexpectedException());
        }
        finally {
            con1.commit();
            con2.commit();
            con1.close();
            con2.close();
        }

    }


    private Xid doXAWorkUniquePK(final Statement s, final XAResource xar)
            throws SQLException, XAException {
        final Xid xid = XATestUtil.getXid(1,05,32);
        // Start work on a transaction branch
        xar.start(xid, XAResource.TMNOFLAGS);

        // Create the table and insert some records which violate a deferred
        // constraint into it.
        s.executeUpdate(
            "create table derby532xa(i int, " +
            "    constraint derby532xa_c primary key(i) initially deferred)");
        s.executeUpdate("insert into derby532xa values 1,1,2");

        // End work on a transaction branch
        xar.end(xid, XAResource.TMSUCCESS);
        return xid;
    }

    private Xid doXAWorkCheck(final Statement s, final XAResource xar)
            throws SQLException, XAException {
        final Xid xid = XATestUtil.getXid(1,05,32);
        // Start work on a transaction branch
        xar.start(xid, XAResource.TMNOFLAGS);

        // Create the table and insert some records which violate a deferred
        // constraint into it.
        s.executeUpdate(
            "create table derby532xa(i int, " +
            "    constraint derby532xa_c check(i > 0) initially deferred)");
        s.executeUpdate("insert into derby532xa values -1, 1,-2");

        // End work on a transaction branch
        xar.end(xid, XAResource.TMSUCCESS);
        return xid;
    }

    private void assertXidRolledBack(final XAResource xar, final Xid xid) {
        try {
            xar.rollback(xid);
            fail("expected the transaction to be unknown");
        } catch (XAException xe) {
            assertEquals(xe.errorCode, XAException.XAER_NOTA);
        }
    }

    /**
     * Format rows to single string in syntax suitable for VALUES statement:
     * "{@code (v1,v2,..,vn), (v1,v2,..,vn),....}"
     *
     * @param rs result set strings
     * @return  the formatted string
     */
    private static String rs2Values(final String[][] rs) {
        final StringBuilder sb = new StringBuilder();

        for (String[] row : rs) {
            sb.append('(');
            for (String v : row) {
                sb.append(v);
                sb.append(',');
            }
            sb.deleteCharAt(sb.length() - 1); // trailing comma
            sb.append("),");
        }
        sb.deleteCharAt(sb.length() - 1); // trailing comma
        return sb.toString();
    }

    private static void checkConsistencyOfBaseTableAndIndex(Statement s)
            throws SQLException {
        JDBC.assertFullResultSet(
            s.executeQuery("values SYSCS_UTIL.SYSCS_CHECK_TABLE('APP', 'T')"),
            new String[][] {{"1"}});
    }

    private final static String[] tableConstraintTypes = {
            " foreign key (i) references referenced(i)",
            " primary key(i)",
            " unique(i)",
            " check(i<3)"
        };

    private final static String[] columnConstraintTypes = {
            " references referenced(i)",
            " primary key",
            " unique",
            " check(i<3)"
        };

    // Each of the three characteristics can have 3 values
    // corresponding to {default, on, off}. This translates into 3 x 3
    // x 3 = 27 syntax permutations, classified below with their
    // corresponding dictionary state.
    //
    private final static String[][] defaultCharacteristics = {
        {" not deferrable initially immediate enforced", "E"},
        {" not deferrable initially immediate", "E"},
        {" not deferrable enforced", "E"},
        {" not deferrable", "E"},
        {" initially immediate enforced", "E"},
        {" initially immediate", "E"},
        {" enforced", "E"},
        {"", "E"}};

    private final static String[][] nonDefaultCharacteristics = {
        {" deferrable", "i"},
        {" deferrable initially immediate", "i"},
        {" deferrable enforced", "i"},
        {" deferrable initially immediate enforced", "i"},
        {" deferrable initially deferred", "e"},
        {" deferrable initially deferred enforced", "e"},
        {" initially deferred enforced", "e"},
        {" initially deferred", "e"},
        {" deferrable not enforced", "j"},
        {" deferrable initially immediate not enforced", "j"},
        {" deferrable initially deferred not enforced", "d"},
        {" initially deferred not enforced", "d"},
        {" not enforced", "D"},
        {" initially immediate not enforced", "D"},
        {" not deferrable not enforced", "D"},
        {" not deferrable initially immediate not enforced", "D"}
    };

    private final static String[] illegalCharacteristics = {
        " not deferrable initially deferred",
        " not deferrable initially deferred enforced",
        " not deferrable initially deferred not enforced"
    };

    private final static String[] illegalAlterCharacteristics;

    static {
        final List<String> characteristics = new ArrayList<String>();
        characteristics.addAll(Arrays.asList(defaultCharacteristics[0]));
        characteristics.addAll(Arrays.asList(nonDefaultCharacteristics[0]));
        characteristics.addAll(Arrays.asList(illegalCharacteristics));
        characteristics.remove(" not enforced");
        characteristics.remove(" enforced");
        characteristics.remove("");
        illegalAlterCharacteristics = characteristics.toArray(new String[0]);
    }

    private final static Map<String, String[]> inverseState =
            new HashMap<String, String[]>();

    static {
        inverseState.put("E", new String[]{"E", "D"});
        inverseState.put("D", new String[]{"E", "D"});
        inverseState.put("i", new String[]{"i", "j"});
        inverseState.put("j", new String[]{"i", "j"});
        inverseState.put("i", new String[]{"i", "j"});
        inverseState.put("e", new String[]{"e", "d"});
        inverseState.put("d", new String[]{"e", "d"});
    }

    /**
     * Assert that we fail with feature not implemented
     * until feature is implemented (for characteristics that are not Derby
     * default).
     *
     * @param s statement

     * @throws SQLException
     */
    private static void assertTableLevelNonDefaultAccepted(
            final Statement s) throws SQLException {

        for (String ct : tableConstraintTypes) {
            for (String[] ch : nonDefaultCharacteristics) {
                // Only primary key and unique implemented
                if (ch[0].contains("not enforced")) {

                    assertStatementError(NOT_IMPLEMENTED,
                            s,
                            "create table t(i int, constraint c " +
                                    ct + ch[0] + ")");
                } else {
                    s.executeUpdate("create table t(i int, constraint c " +
                                    ct + ch[0] + ")");
                    s.executeUpdate("drop table t");
                }
            }
        }
    }

    /**
     * Assert that we allow non defaults
     *
     * @param s statement

     * @throws SQLException
     */
    private static void assertColumnLevelNonDefaultAccepted(
            final Statement s) throws SQLException {

        for (String ct : columnConstraintTypes) {
            for (String[] ch : nonDefaultCharacteristics) {
                // Only primary key and unique implemented
                if (ch[0].contains("not enforced")) {

                    assertStatementError(NOT_IMPLEMENTED,
                            s,
                            "create table t(i int " +
                                    ct + ch[0] + ")");
                } else {
                    s.executeUpdate("create table t(i int " +
                            ct + ch[0] + ")");
                    s.executeUpdate("drop table t");
                }
            }
        }
    }

    /**
     * Assert that we accept characteristics that merely specify the default
     * behavior anyway.
     *
     * @param c connection
     * @param s statement
     *
     * @throws SQLException
     */
    private static void assertTableLevelDefaultBehaviorAccepted (
            final Connection c,
            final Statement s) throws SQLException {

        for (String ct : tableConstraintTypes) {
            for (String[] ch : defaultCharacteristics) {
                assertUpdateCount(s, 0,
                    "create table t(i int, constraint c " + ct + ch[0] + ")");
                c.rollback();
            }
        }
    }

    /**
     * Assert that we accept characteristics that merely specify the default
     * behavior anyway.
     *
     * @param c connection
     * @param s statement
     *
     * @throws SQLException
     */
    private static void assertColumnLevelDefaultBehaviorAccepted (
            final Connection c,
            final Statement s) throws SQLException {

        for (String ct : columnConstraintTypes) {
            for (String ch[] : defaultCharacteristics) {
                assertUpdateCount(s, 0,
                    "create table t(i int " + ct + ch[0] + ")");
                c.rollback();
            }
        }
    }


    /**
     * Check that the dictionary state resulting from {@code characteristics}
     * equals {@code}.
     *
     * @param s                Statement to use
     * @param characteristics  A table level constraint characteristics string
     * @param code             Character encoding for characteristics
     *
     * @throws SQLException
     */
    private void assertDictState(
            final Statement s,
            final String characteristics,
            final String code) throws SQLException {

        for (String ct: tableConstraintTypes) {
            try {
                s.executeUpdate(
                    "create table t(i int, constraint c " + ct + " " +
                    characteristics + ")");

                if (characteristics.contains("not enforced")) {
                    fail();
                } else {
                    JDBC.assertFullResultSet(
                        s.executeQuery(
                            "select state from sys.sysconstraints " +
                            "    where constraintname = 'C'"),
                        new String[][]{{code}});
                    rollback();
                }
            } catch (SQLException e) {
                if (characteristics.contains("not enforced")) {
                    assertSQLState(NOT_IMPLEMENTED, e);
                } else {
                    throw e;
                }
            }
        }
    }

    /**
     * Check that the altered dictionary state resulting from new
     * {@code characteristics} equals {@code}.
     *
     * @param s                The statement object to use
     * @param enforcement  String containing ENFORCED or NOT ENFORCED
     *
     * @throws SQLException
     */
    private void assertAlterDictState(
            final Statement s,
            final String enforcement) throws SQLException {

        final String oldState = getOldState(s);
        final String newState = computeNewState(oldState, enforcement);

        if (!enforcement.contains("not enforced")) {
            s.executeUpdate("alter table t alter constraint c " +
                    enforcement);
            JDBC.assertFullResultSet(
                    s.executeQuery("select state from sys.sysconstraints " +
                            "    where constraintname = 'C'"),
                    new String[][]{{newState}});
        } else {
            assertStatementError(NOT_IMPLEMENTED, s,
                    "alter table t alter constraint c " + enforcement);
        }
    }

    private String getOldState(final Statement s) throws SQLException {
        final ResultSet rs = s.executeQuery(
                "select state from sys.sysconstraints " +
                "    where constraintname = 'C'");
        try {
            rs.next();
            return rs.getString(1);
        } finally {
            rs.close();
        }
    }


    private String computeNewState(String oldState, String enforcement) {
        return inverseState.get(oldState)[
            enforcement.equals("enforced") ? 0 : 1];
    }

    private void assertCreateInconsistentCharacteristics(
            final Statement s,
            final String characteristics) throws SQLException {

        for (String ct: tableConstraintTypes) {
            try {
                s.executeUpdate(
                    "create table t(i int, constraint c " + ct + " " +
                    characteristics + ")");
                fail("wrong characteristics unexpectedly passed muster");
                rollback();
            } catch (SQLException e) {
                assertSQLState(LANG_INCONSISTENT_C_CHARACTERISTICS, e);
            }
        }
    }

    private void assertAlterInconsistentCharacteristics(
            final Statement s,
            final String characteristics) throws SQLException {

        try {
            s.executeUpdate("alter table t alter constraint c " +
                    characteristics);
            fail("wrong characteristics unexpectedly passed muster");
            rollback();
        } catch (SQLException e) {
            assertSQLState(LANG_SYNTAX_ERROR, e);
        }
    }

    private void declareCalledNested(final Statement s) throws SQLException {
        s.executeUpdate(
            "create procedure calledNested(isCheckConstraint boolean)" +
            "  language java parameter style java" +
            "  external name '" +
            this.getClass().getName() +
            ".calledNested' modifies sql data");
    }

    private void declareCalledNestedFk(final Statement s) throws SQLException {
        s.executeUpdate(
            "create procedure calledNestedFk()" +
            "  language java parameter style java" +
            "  external name '" +
            this.getClass().getName() +
            ".calledNestedFk' modifies sql data");
    }
    private void declareCalledNestedSetImmediate(final Statement s) 
            throws SQLException {
        s.executeUpdate(
            "create procedure calledNestedSetImmediate()" +
            "  language java parameter style java" +
            "  external name '" +
            this.getClass().getName() +
            ".calledNestedSetImmediate' modifies sql data");
    }

    public static void calledNested(final boolean isCheckConstraint)
            throws SQLException
    {
        final Connection c =
            DriverManager.getConnection("jdbc:default:connection");
        final Statement cStmt = c.createStatement();

        cStmt.executeUpdate("set constraints c deferred");
        cStmt.executeUpdate("insert into t values " +
                rs2Values(isCheckConstraint ?
                        negatedInitialContents :
                        initialContents));
        c.close();
    }

    public static void calledNestedFk() throws SQLException
    {
        final Connection c =
            DriverManager.getConnection("jdbc:default:connection");
        final Statement cStmt = c.createStatement();

        cStmt.executeUpdate("set constraints c deferred");
        cStmt.executeUpdate("insert into t select i*2, j*2 from t");
        c.close();
    }

    public static void calledNestedSetImmediate() throws SQLException
    {
        final Connection c =
            DriverManager.getConnection("jdbc:default:connection");
        final Statement cStmt = c.createStatement();

        try {
            cStmt.executeUpdate("set constraints c immediate");
        } finally { 
            c.close();
        }
    }

    private void dontThrow(Statement st, String stm) {
        try {
            st.executeUpdate(stm);
        } catch (SQLException e) {
            // ignore, best effort here
            println("\"" + stm+ "\"failed: " + e);
        }
    }

    /**
     * DERBY-6670 test cases. The violation information would be released when
     * we dropped a constraint. Unfortunately, an undo in the form of a
     * rollback to save point would not redo the row operations (so as to
     * regenerate the violation information), but just undo the conglomerate
     * delete (which still isn't physically deleted. So, we'd need the
     * violation information back too. After this fix, we do not release the
     * violation information until commit/rollback, just make it robust to
     * disappearance of constraints and their associated tables/schemas.
     *
     * @throws SQLException
     */
    public void testDerby6670_a() throws SQLException {
        final Connection c = getConnection();
        Statement s = createStatement();

        String[] types = new String[]{"pk", "fk", "check"};

        for (String type : types) {
            String expectedErr = null;

            try {
                if (type.equals("pk")) {
                    s.execute("create table derby6670_1(x int primary key " +
                              "    initially deferred)");
                    s.execute("insert into derby6670_1 values 1,1,1,1");
                    expectedErr = LANG_DEFERRED_DUP_VIOLATION_T;

                } else if (type.equals("fk")) {
                    s.execute("create table derby6670_11(x int primary key)");
                    s.execute("create table derby6670_1(x int " +
                              "  references derby6670_11 initially deferred)");
                    s.execute("insert into derby6670_1 values 1");
                    expectedErr = LANG_DEFERRED_FK_VIOLATION_T;

                } else if (type.equals("check")) {
                    s.execute("create table derby6670_1(x int check (x < 0) " +
                              "    initially deferred)");
                    s.execute("insert into derby6670_1 values 1");
                    expectedErr = LANG_DEFERRED_CHECK_VIOLATION_T;
                }

                Savepoint sp = c.setSavepoint();
                s.execute("drop table derby6670_1");
                c.rollback(sp);

                // Since there are four identical rows in DERBY6670_1, this
                // call should fail because the primary key was violated. It
                // did not prior to DERBY-6670.
                try {
                    commit();
                    fail();
                } catch (SQLException e) {
                    assertSQLState(expectedErr, e);
                }

                // In savepoint, create table and make a violation, then roll
                // back.  Commit should work since no violation exists.
                sp = c.setSavepoint();

                if (type.equals("pk")) {
                    s.execute("create table derby6670_2(x int primary key " +
                              "    initially deferred)");
                    s.execute("insert into derby6670_2 values 1,1,1,1");

                } else if (type.equals("fk")) {
                    s.execute("create table derby6670_22(x int primary key)");
                    s.execute("create table derby6670_2(x int " +
                              "  references derby6670_22 initially deferred)");
                    s.execute("insert into derby6670_2 values 1");

                } else if (type.equals("check")) {
                    s.execute("create table derby6670_2(x int)");
                    s.execute("alter table derby6670_2 add constraint c " +
                              "  check(x > 0) deferrable initially deferred");
                    s.execute("insert into derby6670_2 values -1");
                }

                c.rollback(sp);
                commit();

                // In a savepoint, add constraint with offending rows. After
                // rollback, the commit should work since no violations exist.

                s.execute("create table derby6670_3(x int not null)");
                commit();
                sp = c.setSavepoint();

                if (type.equals("pk")) {
                    s.execute("alter table derby6670_3 add constraint c " +
                              "  primary key(x) deferrable " +
                              "  initially deferred");
                    s.execute("insert into derby6670_3 values 1,1");

                } else if (type.equals("fk")) {
                    s.execute("create table derby6670_33(x int primary key)");
                    s.execute("alter table derby6670_3 add constraint c " +
                              "   foreign key(x) references derby6670_33 " +
                              "   deferrable initially deferred");
                    s.execute("insert into derby6670_3 values -1");

                } else if (type.equals("check")) {
                    s.execute("alter table derby6670_3 add constraint c " +
                              "   check(x > 0) deferrable initially deferred");
                    s.execute("insert into derby6670_3 values -1");
                }

                c.rollback(sp);
                commit();

                // In a savepoint, drop a constraint, then rollback. We should
                // still see violation at commit.
                s.execute("create table derby6670_4(x int not null)");
                c.commit();

                if (type.equals("pk")) {
                    s.execute("alter table derby6670_4 add constraint c " +
                              "  primary key(x) deferrable " +
                              "  initially deferred");
                    s.execute("insert into derby6670_4 values 1,1");

                } else if (type.equals("fk")) {
                    s.execute("create table derby6670_44(x int primary key)");
                    s.execute("alter table derby6670_4 add constraint c " +
                              "   foreign key(x) references derby6670_44 " +
                              "   deferrable initially deferred");
                    s.execute("insert into derby6670_4 values -1");

                } else if (type.equals("check")) {
                    s.execute("alter table derby6670_4 add constraint c " +
                              "   check(x > 0) deferrable initially deferred");
                    s.execute("insert into derby6670_4 values -1");
                }

                sp = c.setSavepoint();
                s.execute("alter table derby6670_4 drop constraint c");
                c.rollback(sp);

                try {
                    c.commit();
                    fail();
                } catch (SQLException e) {
                    assertSQLState(expectedErr, e);
                }
            } finally {
                for (int i = 1; i <= 4; i++) {
                    dropTable("derby6670_" + i);
                }
                c.commit();
            }
        }
    }


    /**
     * Similarly to what happened for dropping of constraints, when we revert
     * from deferred constraint mode to immediate, and no violations are seen,
     * we used to drop the violation information, if any. Again, this is not
     * safe iff a rollback to a savepoint re-introduces the violations. This
     * test would fail prior to DERBY-6670.
     * @throws SQLException test error
     */
    public void testDerby6670_b() throws SQLException {
        final Connection c = getConnection();
        final Statement s = createStatement();

        String[] forms = new String[]{"c", "all"};

        for (String form : forms) {
            s.execute("create table t1(x int primary key, " +
                      "    constraint c check(x > 0) initially deferred)");
            s.execute("insert into t1 values -1");

            Savepoint sp = c.setSavepoint();
            s.execute("delete from t1");
            s.execute("set constraints " + form + " immediate");
            c.rollback(sp);

            try {
                // Used to succeed because we released violation information of
                // the successful constraint when moving to immediate mode
                c.commit();
                fail();

            } catch (SQLException e) {
                assertSQLState(LANG_DEFERRED_CHECK_VIOLATION_T, e);
            }
        }
    }


    /**
     * DERBY-6666. Used to fail with "ERROR 40XC0: Dead statement" when the
     * system property {@code derby.language.logQueryPlan} is set to {@code
     * true}, which it is is here.
     *
     * @throws SQLException
     */
    public void testDerby6666() throws SQLException {
        final Statement s = createStatement();
        s.executeUpdate("create table t1(x int primary key)");
        s.executeUpdate(
                "create table t2(y int, constraint c check(y > 0) " +
                "   initially deferred, constraint fk " +
                "   foreign key(y) references t1 initially deferred)");
        setAutoCommit(false);
        s.executeUpdate("insert into t1 values -1, 1");
        s.executeUpdate("insert into t2 values 1");
        s.executeUpdate("update t2 set y = -1");

        try {
            commit();
            fail();
        } catch (SQLException e) {
            assertSQLState(LANG_DEFERRED_CHECK_VIOLATION_T, e);
        }

    }
    
    /**
     * DERBY-6773: check the Derby-specific subclass of the standard
     * SQLIntegrityConstraintViolationException.
     */
    public void testDerby6773() throws Exception
    {
        final Statement s = createStatement();
        s.executeUpdate( "create table Application " +
                         "   (id bigint generated by default as identity," +
                         "    name varchar(255)," +
                         "    shortName varchar(32)," +
                         "    userId varchar(32)," +
                         "    primary key (id))" );
        s.executeUpdate( "create unique index UK_APPLICATION_SHORTNAME " +
                         "    on Application (shortName)" );
        s.executeUpdate( "create unique index UK_APPLICATION_NAME " +
                         "    on Application (name)" );
        s.executeUpdate( "insert into Application (name, shortName, userId) " +
                         "    VALUES ('fooApp', 'Foo Application 0', 'me')" );
        try
        {
            s.executeUpdate(
		         "insert into Application (name, shortName, userId) " +
                         "    VALUES ('fooApp', 'Foo Application 1', 'me')" );
            fail();
        }
        catch ( DerbySQLIntegrityConstraintViolationException dsicve )
        {
            assertEquals( "APPLICATION", dsicve.getTableName() );
            assertEquals( "UK_APPLICATION_NAME", dsicve.getConstraintName() );
        }

        try
        {
            s.executeUpdate(
		         "insert into Application (name, shortName, userId) " +
                         "    VALUES ('BarApp', 'Foo Application 0', 'me')" );
            fail();
        }
        catch ( DerbySQLIntegrityConstraintViolationException dsicve )
        {
            assertEquals( "APPLICATION", dsicve.getTableName() );
            assertEquals( "UK_APPLICATION_SHORTNAME",
                          dsicve.getConstraintName() );
        }
    }

    /**
     * Privileged lookup of the LCC from a Connection.
     */
    public  static  LanguageConnectionContext    getLCC( final Connection conn )
    {
        return AccessController.doPrivileged
            (
             new PrivilegedAction<LanguageConnectionContext>()
             {
                 public LanguageConnectionContext run()
                 {
                     final ContextManager contextManager =
                         ((EmbedConnection)conn).getContextManager();
                     return (LanguageConnectionContext)
                         contextManager.getContext( "LanguageConnectionContext" );
                 }
             }
             );
    }
}

