/*
 * Derby - Class org.apache.derbyTesting.perf.basic.jdbc.SelectDistinctTest
 *
 * 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.perf.basic.jdbc;

import java.io.UnsupportedEncodingException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.JDBCPerfTestCase;

/**
 * Performance tests for SELECT DISTINCT.
 */
public class SelectDistinctTest extends JDBCPerfTestCase {

    /** The prepared statement to be used in the test body. */
    private PreparedStatement ps;

    /**
     * Name of the test case for CHAR(5), and the name of the table used by
     * that test.
     */
    private static final String TEST_CHAR5 = "selectDistinctChar5";

    /**
     * The number of distinct rows to be expected in the test for CHAR(5).
     * Since the table contains all permutations of "ABCDE", there should be
     * 5! distinct rows.
     */
    private static final int EXPECTED_DISTINCT_CHAR5 = 5 * 4 * 3 * 2;

    /**
     * Name of the test case for CHAR(5) FOR BIT DATA, and the name of the
     * table used by that test.
     */
    private static final String TEST_BINARY5 = "selectDistinctBinary5";

    /**
     * The number of distinct rows to be expected in the test for CHAR(5) FOR
     * BIT DATA. Should be the same as in the test for CHAR(5).
     */
    private static final int EXPECTED_DISTINCT_BINARY5 =
            EXPECTED_DISTINCT_CHAR5;

    /**
     * Create a test case.
     *
     * @param name the name of the test method, and the name of the table used
     * in the test
     * @param iterations the number of iterations in each test
     * @param repeats the number of times each test should be repeated
     */
    private SelectDistinctTest(String name, int iterations, int repeats) {
        super(name, iterations, repeats);
    }

    /**
     * Create the suite of all test cases in this class.
     *
     * @return all test cases in this class
     */
    public static Test suite() {
        BaseTestSuite suite = new BaseTestSuite("SelectDistinctTest");
        suite.addTest(new SelectDistinctTest(TEST_CHAR5, 2000, 4));
        suite.addTest(new SelectDistinctTest(TEST_BINARY5, 2000, 4));

        return new CleanDatabaseTestSetup(suite) {
            protected void decorateSQL(Statement s) throws SQLException {
                s.execute("CREATE TABLE " + TEST_CHAR5 + "(COL CHAR(5))");
                // insert all permutations of 'ABCDE'
                s.execute("INSERT INTO " + TEST_CHAR5 +
                        " SELECT V1.X||V2.X||V3.X||V4.X||V5.X FROM " +
                        "(VALUES 'A', 'B', 'C', 'D', 'E') V1(X), " +
                        "(VALUES 'A', 'B', 'C', 'D', 'E') V2(X), " +
                        "(VALUES 'A', 'B', 'C', 'D', 'E') V3(X), " +
                        "(VALUES 'A', 'B', 'C', 'D', 'E') V4(X), " +
                        "(VALUES 'A', 'B', 'C', 'D', 'E') V5(X) " +
                        "WHERE (SELECT COUNT(*) FROM " +
                        "(VALUES 'A', 'B', 'C', 'D', 'E') VV(X) " +
                        "WHERE VV.X NOT IN (V1.X,V2.X,V3.X,V4.X,V5.X)) = 0");
                // make some duplicates
                for (int i = 0; i < 3; i++) {
                    s.execute("INSERT INTO " + TEST_CHAR5 + " " +
                            "SELECT * FROM " + TEST_CHAR5);
                }

                s.execute("CREATE TABLE " + TEST_BINARY5 +
                        "(COL CHAR(5) FOR BIT DATA)");
                s.execute("CREATE FUNCTION CHAR_TO_BINARY(STR CHAR(5)) " +
                        "RETURNS CHAR(5) FOR BIT DATA " +
                        "LANGUAGE JAVA PARAMETER STYLE JAVA " +
                        "EXTERNAL NAME '" + SelectDistinctTest.class.getName() +
                        ".charToBinary' NO SQL");
                s.execute("INSERT INTO " + TEST_BINARY5 +
                        " SELECT CHAR_TO_BINARY(COL) FROM " + TEST_CHAR5);
            }
        };
    }

    /**
     * Set up the test environment.
     */
    protected void setUp() throws SQLException {
        ps = prepareStatement("SELECT DISTINCT * FROM " + getName());
    }

    /**
     * Tear down the test environment.
     */
    protected void tearDown() throws Exception {
        super.tearDown();
        ps = null; // automatically closed in super.tearDown()
    }

    /**
     * Stored function that converts an SQL CHAR value to an SQL CHAR FOR BIT
     * DATA value.
     *
     * @param s the string to convert to binary data
     * @return binary UTF-8 representation of the string
     */
    public static byte[] charToBinary(String s)
            throws UnsupportedEncodingException {
        return s.getBytes("UTF-8");
    }

    /**
     * Test case for SELECT DISTINCT on a CHAR(5) column.
     */
    public void selectDistinctChar5() throws SQLException {
        JDBC.assertDrainResults(ps.executeQuery(), EXPECTED_DISTINCT_CHAR5);
    }

    /**
     * Test case for SELECT DISTINCT on a CHAR(5) FOR BIT DATA column.
     */
    public void selectDistinctBinary5() throws SQLException {
        JDBC.assertDrainResults(ps.executeQuery(), EXPECTED_DISTINCT_BINARY5);
    }
}
