/* This file is part of VoltDB.
 * Copyright (C) 2008-2017 VoltDB Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

package org.voltdb.regressionsuites;

import java.io.IOException;

import org.voltdb.BackendTarget;
import org.voltdb.VoltTable;
import org.voltdb.client.Client;
import org.voltdb.compiler.VoltProjectBuilder;

public class TestBooleanLiteralsSuite extends RegressionSuite {

    static String[] conditions = {"1=1", "TRUE", "6-1>=0", "1=0", "FALSE", "1>2"};
    static boolean isConditionTrue(int condId) {
        return condId < 3;
    }
    static boolean isTablePartitioned(int tableId) {
        return tableId > 2;
    }

    private void populateTable(int tableId, Client client) throws Exception{
        String procName = "T" + tableId + ".Insert";
        for (int i=0; i<3; i++) {
            client.callProcedure(procName, 2*i+1);
        }
    }

    public void testBooleanLiteralsInQueries() throws Exception {
        String sqlBody;
        Client client = getClient();
        for (int i=1; i<=4; i++) {
            populateTable(i, client);
        }

        Object[][] emptyTable = {};
        Object[][] tableResult = {{1}, {3}, {5}};
        Object[][] joinResult = {{1,1}, {1,3}, {1,5}, {3,1}, {3,3}, {3,5}, {5,1}, {5,3}, {5,5}};
        Object[][] caseWhenFalseResult = {{-1}, {-1}, {-1}};
        String whereQuery = "SELECT AINT FROM T%d WHERE %%s";
        String joinQuery = "SELECT * FROM T%d a JOIN T%d b ON %%s";
        String caseWhenQuery = "SELECT CASE WHEN %%s THEN AINT ELSE -1 END FROM T%d ORDER BY AINT";
        subTestBooleanLiteralsInQuery(String.format(whereQuery, 1),
                                      tableResult, emptyTable, client); // replicated
        subTestBooleanLiteralsInQuery(String.format(whereQuery, 3),
                                      tableResult, emptyTable, client); // partitioned
        subTestBooleanLiteralsInQuery(String.format(joinQuery, 1, 2),
                                      joinResult, emptyTable, client); // replicated join replicated.
        subTestBooleanLiteralsInQuery(String.format(joinQuery, 2, 3),
                                      joinResult, emptyTable, client); // replicated join partitioned.
        subTestBooleanLiteralsInQuery(String.format(joinQuery, 3, 2),
                                      joinResult, emptyTable, client); // partitioned join replicated.
        // We do not test a partitioned table joining another partitioned table on a constant boolean value here.
        // Instead, we test this case in TestPlansJoin for proper error message.
        // The query is not plannable because the planner cannot guarantee that all rows would be in a single partition.
        subTestBooleanLiteralsInQuery(String.format(caseWhenQuery, 1),
                                      tableResult, caseWhenFalseResult, client); // replicated
        subTestBooleanLiteralsInQuery(String.format(caseWhenQuery, 3),
                                      tableResult, caseWhenFalseResult, client); // partitioned
    }

    private void subTestBooleanLiteralsInQuery(String sqlBody,
                                               Object[][] conditionTrueResult,
                                               Object[][] conditionFalseResult,
                                               Client client) throws Exception {
        VoltTable vt;
        for (int i=0; i<conditions.length; i++) {
            String sql = String.format(sqlBody, conditions[i]);
            vt = client.callProcedure("@AdHoc", sql).getResults()[0];
            assertContentOfTable((isConditionTrue(i) ? conditionTrueResult : conditionFalseResult), vt);
        }
    }

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

    static String tableDDL = "CREATE TABLE T%d (" +
                             "AINT INTEGER DEFAULT 0 NOT NULL," +
                             "PRIMARY KEY (AINT) );";
    static String partitionDDL = "PARTITION TABLE T%d ON COLUMN AINT;";

    static private void addTableToProject(VoltProjectBuilder project, int tableId, boolean partitioned) {
        try {
            project.addLiteralSchema(String.format(tableDDL, tableId));
            if (partitioned) {
                project.addLiteralSchema(String.format(partitionDDL, tableId));
            }
        } catch (IOException e) {
            assertFalse(true);
        }
    }

    static public junit.framework.Test suite() {
        VoltServerConfig config = null;
        MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder(TestBooleanLiteralsSuite.class);
        boolean success;

        VoltProjectBuilder project = new VoltProjectBuilder();
        // T1 and T2 are replicated table, while T3 and T4 are partitioned.
        for (int i=1; i<=4; i++) {
            addTableToProject(project, i, isTablePartitioned(i));
        }

        // CONFIG #1: Local Site/Partitions running on JNI backend
        config = new LocalCluster("bool-voltdbBackend.jar", 2, 1, 0, BackendTarget.NATIVE_EE_JNI);
        // alternative to enable for debugging */ config = new LocalCluster("IPC-onesite.jar", 1, 1, 0, BackendTarget.NATIVE_EE_IPC);
        success = config.compile(project);
        assertTrue(success);
        builder.addServerConfig(config);

        // CONFIG #2: HSQL
        config = new LocalCluster("bool-hsqlBackend.jar", 2, 1, 0, BackendTarget.HSQLDB_BACKEND);
        success = config.compile(project);
        assertTrue(success);
        builder.addServerConfig(config);

        return builder;
    }
}

@@ -1956,32 +1956,8 @@
 n %7B%0A
-        String sqlBody;%0A
     
@@ -2372,24 +2372,38 @@
 %25d WHERE %25%25s
+ ORDER BY AINT
 %22;%0A        S
@@ -2458,16 +2458,40 @@
 b ON %25%25s
+ ORDER BY a.AINT, b.AINT
 %22;%0A     

Fix test so that it passes in memcheck (row order issue) (#4366)