/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This file contains original code and/or modifications of original code.
* Any modifications made by VoltDB Inc. are licensed under the following
* terms and conditions:
*
* 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.
*/
/**
* 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.voltdb.planner.eegentests;
import org.voltdb.catalog.Database;
/**
* This is not a jUnit test, it's a Java application, which generates EE
* Unit tests. To run the application, run this Java class as a
* java application. All tests generated from this class use the same
* DDL definitions. In this class the DDL file is testplans-ee-geneators.sql,
* set in the function setUp defined in this class. Any other way of
* getting a URL to a DDL definition is possible. All the schema definitions
* from any of the tables used in these files will be from this DDL file.
*
* After setting up the schema, follow the example below. All the
* possibilities are in the test generatedPlannerTest, where there is more
* documentation.
*
* Finally, after defining member functions to generate the tests,
* in the main() function, create a test generator object and call
* the generator member functions, as is done in this file.
*
*/
public class GenerateEETests extends EEPlanGenerator {
private static final String DDL_FILENAME = "testplans-ee-generators.sql";
@Override
public void setUp() throws Exception {
super.setUp(GenerateEETests.class.getResource(DDL_FILENAME),
"testplansgenerator",
true);
}
//
// It's better to make functions this way than to make
// static constants. The database db will often not
// be initialized when initializing a static constant.
//
private TableConfig makeAAA(Database db) {
final TableConfig AAAConfig = new TableConfig("AAA",
db,
new Integer[][] {
{ 1, 10, 101 },
{ 1, 10, 102 },
{ 1, 20, 201 },
{ 1, 20, 202 },
{ 1, 30, 301},
{ 2, 10, 101},
{ 2, 10, 102},
{ 2, 20, 201},
{ 2, 20, 202},
{ 2, 30, 301},
{ 3, 10, 101},
{ 3, 10, 102},
{ 3, 20, 201},
{ 3, 20, 202},
{ 3, 30, 301}});
return AAAConfig;
}
private TableConfig makeBBB(Database db) {
final TableConfig BBBConfig = new TableConfig("BBB",
db,
new Integer[][] {
{ 1, 10, 101 },
{ 1, 10, 102 },
{ 1, 20, 201 },
{ 1, 20, 202 },
{ 1, 30, 301},
{ 2, 10, 101},
{ 2, 10, 102},
{ 2, 20, 201},
{ 2, 20, 202},
{ 2, 30, 301},
{ 3, 10, 101},
{ 3, 10, 102},
{ 3, 20, 201},
{ 3, 20, 202},
{ 3, 30, 301} } );
return BBBConfig;
}
/**
* Tables with heterogeneous data need to have data whose
* type is Object[][].
*
* @param db
* @return
*/
private TableConfig makeXXX(Database db) {
final TableConfig XXXConfig = new TableConfig("XXX",
db,
new Object[][] {
{ 1, "alpha", "beta" },
{ 2, "gamma", "delta" } } );
return XXXConfig;
}
private TableConfig makeCCC(Database db) {
final TableConfig CCCConfig = new TableConfig("CCC",
db,
10000);
return CCCConfig;
}
public void generatedPlannerTest() throws Exception {
//
// First, get the database. This is the database
// which contains the catalog, with contains the
// processed definitions from the DDL file.
//
Database db = getDatabase();
//
// Create some table configurations. These are the parts of
// the catalog we need to generate tests, cached for easy use.
//
// It's often more helpful to create a member function which
// makes a table configuration, rather than creating a table
// configuration as a static object. This is because the
// table configuration depends on the schema, which is in
// the Database, and we won't have the Database until run time.
//
final TableConfig AAAConfig = makeAAA(db);
final TableConfig BBBConfig = makeBBB(db);
final TableConfig XXXConfig = makeXXX(db);
// This is another way of making a TableConfig. As we can see,
// a TableConfig is given by a table name, the schema extracted from
// the database db and some data.
//
final TableConfig orderByOutput = new TableConfig("test_order_by",
db,
new Object[][] {
{ 1, 10},
{ 1, 10},
{ 1, 20},
{ 1, 20},
{ 1, 30},
{ 2, 10},
{ 2, 10},
{ 2, 20},
{ 2, 20},
{ 2, 30},
{ 3, 10},
{ 3, 10},
{ 3, 20},
{ 3, 20},
{ 3, 30} } );
final TableConfig joinOutput = new TableConfig("test_join",
db,
new Integer[][] {
{ 1, 10, 101},
{ 1, 10, 101},
{ 1, 10, 101},
{ 1, 10, 102},
{ 1, 10, 102},
{ 1, 10, 102},
{ 1, 20, 201},
{ 1, 20, 201},
{ 1, 20, 201},
{ 1, 20, 202},
{ 1, 20, 202},
{ 1, 20, 202},
{ 1, 30, 301},
{ 1, 30, 301},
{ 1, 30, 301},
{ 2, 10, 101},
{ 2, 10, 101},
{ 2, 10, 101},
{ 2, 10, 102},
{ 2, 10, 102},
{ 2, 10, 102},
{ 2, 20, 201},
{ 2, 20, 201},
{ 2, 20, 201},
{ 2, 20, 202},
{ 2, 20, 202},
{ 2, 20, 202},
{ 2, 30, 301},
{ 2, 30, 301},
{ 2, 30, 301},
{ 3, 10, 101},
{ 3, 10, 101},
{ 3, 10, 101},
{ 3, 10, 102},
{ 3, 10, 102},
{ 3, 10, 102},
{ 3, 20, 201},
{ 3, 20, 201},
{ 3, 20, 201},
{ 3, 20, 202},
{ 3, 20, 202},
{ 3, 20, 202},
{ 3, 30, 301},
{ 3, 30, 301},
{ 3, 30, 301} } );
//
// This a third kind of configuration creates a table whose contents
// are generated randomly. It will have 10000 rows, with random
// values of the right type for each of the columns.
//
final TableConfig CCCConfig = new TableConfig("CCC",
db,
10000);
//
// Create a DB config, which contains all the tables.
// Note that input tables, like AAAConfig or BBBConfig,
// and result tables, like orderByOutput or
// joinOutput, need to be added. Any number of tables
// can be added.
DBConfig dbc = new DBConfig(getClass(),
GenerateEETests.class.getResource(DDL_FILENAME),
getCatalogString(),
AAAConfig,
BBBConfig,
CCCConfig,
XXXConfig,
orderByOutput,
joinOutput);
// Add a test. This test runs the select statement
// and expects the result to be orderByOutput.
dbc.addTest(new TestConfig("test_order_by",
"select A, B from AAA order by A, B;",
false,
orderByOutput));
// Add another test. This test runs the select
// statement and expects the result to be joinOutput.
dbc.addTest(new TestConfig("test_join",
"select AAA.A, AAA.B, BBB.C from AAA join BBB on AAA.C = BBB.C order by AAA.A, AAA.B, AAA.C;",
false,
joinOutput));
// In this case we don't care about the output
// at all. We just want to run the test. This could
// be used under gdb, where the output is long or
// non-deterministic, or to do profiling, where we don't
// care about specifying the output, and it will not
// be validated at all.
dbc.addTest(new TestConfig("test_cache", "select * from CCC;", false));
// Now, write the tests in the file executors/TestGeneratedPlans.cpp.
generateTests("executors", "TestGeneratedPlans", dbc);
}
TableConfig makeTConfig(Database db) {
TableConfig TConfig = new TableConfig("T",
db,
new Integer[][] {
// A B C
//-------------
{ 1, 1, 1},
{ 1, 1, 2},
{ 1, 1, 3},
{ 1, 1, 4},
{ 1, 1, 5},
//======================================
{ 1, 2, 1},
{ 1, 2, 2},
{ 1, 2, 3},
{ 1, 2, 4},
{ 1, 2, 5},
//======================================
{ 1, 3, 1},
{ 1, 3, 2},
{ 1, 3, 3},
{ 1, 3, 4},
{ 1, 3, 5},
//--------------------------------------
{ 2, 1, 1},
{ 2, 1, 2},
{ 2, 1, 3},
{ 2, 1, 4},
{ 2, 1, 5},
//======================================
{ 2, 2, 1},
{ 2, 2, 2},
{ 2, 2, 3},
{ 2, 2, 4},
{ 2, 2, 5},
//======================================
{ 2, 3, 1},
{ 2, 3, 2},
{ 2, 3, 3},
{ 2, 3, 4},
{ 2, 3, 5}});
return TConfig;
}
private TableConfig makeTestOutput(Database db) {
TableConfig testOutput = new TableConfig("test_output",
db,
new Integer[][] {
{ 1, 1, 0},
{ 1, 1, 0},
{ 1, 1, 0},
{ 1, 1, 0},
{ 1, 1, 0},
//======================================
{ 1, 2, 0},
{ 1, 2, 0},
{ 1, 2, 0},
{ 1, 2, 0},
{ 1, 2, 0},
//======================================
{ 1, 3, 0},
{ 1, 3, 0},
{ 1, 3, 0},
{ 1, 3, 0},
{ 1, 3, 0},
//--------------------------------------
{ 2, 1, 0},
{ 2, 1, 0},
{ 2, 1, 0},
{ 2, 1, 0},
{ 2, 1, 0},
//======================================
{ 2, 2, 0},
{ 2, 2, 0},
{ 2, 2, 0},
{ 2, 2, 0},
{ 2, 2, 0},
//======================================
{ 2, 3, 0},
{ 2, 3, 0},
{ 2, 3, 0},
{ 2, 3, 0},
{ 2, 3, 0} } );
return testOutput;
}
public void generatedMaxPlan() throws Exception {
Database db = getDatabase();
TableConfig TConfig = makeTConfig(db);
TableConfig testOutput = makeTestOutput(db);
DBConfig maxDB = new DBConfig(getClass(),
GenerateEETests.class.getResource(DDL_FILENAME),
getCatalogString(),
TConfig,
testOutput);
maxDB.addTest(new TestConfig("test_max_first_row",
"select A, B, max(-1 * abs(1-C)) over (partition by A order by B) as R from T ORDER BY A, B, R;",
false,
testOutput));
maxDB.addTest(new TestConfig("test_max_middle_row",
"select A, B, max(-1 * abs(3-C)) over (partition by A order by B) as R from T ORDER BY A, B, R;",
false,
testOutput));
maxDB.addTest(new TestConfig("test_max_last_row",
"select A, B, max(-1 * abs(5-C)) over (partition by A order by B) as R from T ORDER BY A, B, R;",
false,
testOutput));
generateTests("executors", "TestWindowedMax", maxDB);
}
public void generatedMinPlan() throws Exception {
Database db = getDatabase();
TableConfig TConfig = makeTConfig(db);
TableConfig testOutput = makeTestOutput(db);
DBConfig minDB = new DBConfig(getClass(),
GenerateEETests.class.getResource(DDL_FILENAME),
getCatalogString(),
TConfig,
testOutput);
minDB.addTest(new TestConfig("test_min_last_row",
"select A, B, min(abs(5-C)) over (partition by A order by B) as R from T ORDER BY A, B, R;",
false,
testOutput));
minDB.addTest(new TestConfig("test_min_middle_row",
"select A, B, min(abs(3-C)) over (partition by A order by B) as R from T ORDER BY A, B, R;",
false,
testOutput));
minDB.addTest(new TestConfig("test_min_first_row",
"select A, B, min(abs(1-C)) over (partition by A order by B) as R from T ORDER BY A, B, R;",
false,
testOutput));
generateTests("executors", "TestWindowedMin", minDB);
}
private TableConfig makeSumOutput(Database db) {
TableConfig sumOutput = new TableConfig("test_sum_output",
db,
new Integer[][] {
{ 1, 1, 20},
{ 1, 1, 20},
{ 1, 1, 20},
{ 1, 1, 20},
{ 1, 1, 20},
//======================================
{ 1, 2, 45},
{ 1, 2, 45},
{ 1, 2, 45},
{ 1, 2, 45},
{ 1, 2, 45},
//======================================
{ 1, 3, 75},
{ 1, 3, 75},
{ 1, 3, 75},
{ 1, 3, 75},
{ 1, 3, 75},
//--------------------------------------
{ 2, 1, 20},
{ 2, 1, 20},
{ 2, 1, 20},
{ 2, 1, 20},
{ 2, 1, 20},
//======================================
{ 2, 2, 45},
{ 2, 2, 45},
{ 2, 2, 45},
{ 2, 2, 45},
{ 2, 2, 45},
//======================================
{ 2, 3, 75},
{ 2, 3, 75},
{ 2, 3, 75},
{ 2, 3, 75},
{ 2, 3, 75} } );
return sumOutput;
}
public void generatedSumPlan() throws Exception {
Database db = getDatabase();
TableConfig TConfig = makeTConfig(db);
TableConfig sumOutput = makeSumOutput(db);
DBConfig sumDB = new DBConfig(getClass(),
GenerateEETests.class.getResource(DDL_FILENAME),
getCatalogString(),
TConfig,
sumOutput);
sumDB.addTest(new TestConfig("test_min_last_row",
"select A, B, sum(B+C) over (partition by A order by B) as R from T ORDER BY A, B, R;",
false,
sumOutput));
generateTests("executors", "TestWindowedSum", sumDB);
}
private TableConfig makeCountInput(Database db) {
TableConfig countInput = new TableConfig("T",
db,
new Integer[][] {
// A B C
//-------------
{ 1, 1, 101},
{ 1, 1, 102},
//======================================
{ 1, 2, 201},
{ 1, 2, 202},
//======================================
{ 1, 3, 203},
//--------------------------------------
{ 2, 1, 1101},
{ 2, 1, 1102},
//======================================
{ 2, 2, 1201},
{ 2, 2, 1202},
//======================================
{ 2, 3, 1203},
//--------------------------------------
{ 20, 1, 2101},
{ 20, 1, 2102},
//======================================
{ 20, 2, 2201},
{ 20, 2, 2202},
//======================================
{ 20, 3, 2203} } );
return countInput;
}
private TableConfig makeCountOutput(Database db) {
TableConfig countOutput = new TableConfig("count_output",
db,
new Integer[][] {
// A B C count
//--------------------------------------
{ 1, 1, 101, 2},
{ 1, 1, 102, 2},
//======================================
{ 1, 2, 201, 4},
{ 1, 2, 202, 4},
//======================================
{ 1, 3, 203, 5},
//--------------------------------------
{ 2, 1, 1101, 2},
{ 2, 1, 1102, 2},
//======================================
{ 2, 2, 1201, 4},
{ 2, 2, 1202, 4},
//======================================
{ 2, 3, 1203, 5},
//--------------------------------------
{ 20, 1, 2101, 2},
{ 20, 1, 2102, 2},
//======================================
{ 20, 2, 2201, 4},
{ 20, 2, 2202, 4},
//======================================
{ 20, 3, 2203, 5}});
return countOutput;
}
public void generatedCountPlan() throws Exception {
Database db = getDatabase();
TableConfig countInput = makeCountInput(db);
TableConfig countOutput = makeCountOutput(db);
DBConfig countDB = new DBConfig(getClass(),
GenerateEETests.class.getResource(DDL_FILENAME),
getCatalogString(),
countInput,
countOutput);
String sqlStmt;
sqlStmt = "select A, B, C, count(*) over (partition by A order by B) as R from T ORDER BY A, B, C, R;";
countDB.addTest(new TestConfig("test_count_star",
sqlStmt,
false,
countOutput));
sqlStmt = "select A, B, C, count(A+B) over (partition by A order by B) as R from T ORDER BY A, B, C, R;";
countDB.addTest(new TestConfig("test_count",
sqlStmt,
false,
countOutput));
generateTests("executors", "TestWindowedCount", countDB);
}
private TableConfig makeRankInput(Database db) {
TableConfig rankInput = new TableConfig("T",
db,
new Integer[][] {
// A B C
//-------------
{ 1, 1, 101},
{ 1, 1, 102},
//======================================
{ 1, 2, 201},
{ 1, 2, 202},
//======================================
{ 1, 3, 203},
//--------------------------------------
{ 2, 1, 1101},
{ 2, 1, 1102},
//======================================
{ 2, 2, 1201},
{ 2, 2, 1202},
//======================================
{ 2, 3, 1203},
//--------------------------------------
{ 20, 1, 2101},
{ 20, 1, 2102},
//======================================
{ 20, 2, 2201},
{ 20, 2, 2202},
//======================================
{ 20, 3, 2203}});
return rankInput;
}
private TableConfig makeRankOutput(Database db) {
TableConfig rankOutput = new TableConfig("rank_output",
db,
new Integer[][] {
// A B C rank
//--------------------------------------
{ 1, 1, 101, 1},
{ 1, 1, 102, 1},
//======================================
{ 1, 2, 201, 3},
{ 1, 2, 202, 3},
//======================================
{ 1, 3, 203, 5},
//--------------------------------------
{ 2, 1, 1101, 1},
{ 2, 1, 1102, 1},
//======================================
{ 2, 2, 1201, 3},
{ 2, 2, 1202, 3},
//======================================
{ 2, 3, 1203, 5},
//--------------------------------------
{ 20, 1, 2101, 1},
{ 20, 1, 2102, 1},
//======================================
{ 20, 2, 2201, 3},
{ 20, 2, 2202, 3},
//======================================
{ 20, 3, 2203, 5},
//--------------------------------------
});
return rankOutput;
}
TableConfig makeRankDenseOutput(Database db) {
TableConfig rankDenseOutput = new TableConfig("rank_dense_output",
db,
new Integer[][] {
// A B C rank
//--------------------------------------
{ 1, 1, 101, 1},
{ 1, 1, 102, 1},
//======================================
{ 1, 2, 201, 2},
{ 1, 2, 202, 2},
//======================================
{ 1, 3, 203, 3},
//--------------------------------------
{ 2, 1, 1101, 1},
{ 2, 1, 1102, 1},
//======================================
{ 2, 2, 1201, 2},
{ 2, 2, 1202, 2},
//======================================
{ 2, 3, 1203, 3},
//--------------------------------------
{ 20, 1, 2101, 1},
{ 20, 1, 2102, 1},
//======================================
{ 20, 2, 2201, 2},
{ 20, 2, 2202, 2},
//======================================
{ 20, 3, 2203, 3},
//--------------------------------------
});
return rankDenseOutput;
}
public void generatedRankPlan() throws Exception {
Database db = getDatabase();
TableConfig rankInput = makeRankInput(db);
TableConfig rankOutput = makeRankOutput(db);
TableConfig rankDenseOutput = makeRankDenseOutput(db);
DBConfig rankDB = new DBConfig(getClass(),
GenerateEETests.class.getResource(DDL_FILENAME),
getCatalogString(),
rankInput,
rankOutput,
rankDenseOutput);
String sqlStmt;
sqlStmt = "select A, B, C, rank() over (partition by A order by B) as R from T ORDER BY A, B, C, R;";
rankDB.addTest(new TestConfig("test_rank",
sqlStmt,
false,
rankOutput));
sqlStmt = "select A, B, C, dense_rank() over (partition by A order by B) as R from T ORDER BY A, B, C, R;";
rankDB.addTest(new TestConfig("test_dense_rank",
sqlStmt,
false,
rankDenseOutput));
generateTests("executors", "TestWindowedRank", rankDB);
}
public void generatedStringPlan() throws Exception {
Database db = getDatabase();
// We test a particular possible bug in the test framework.
// If the found values are shorter than the expected values
// comparing the two may cause a read to non-existent memory.
final TableConfig CCCConfig = new TableConfig("CCC",
db,
new Object[][] {
{ 100, "alpha", "beta" }
});
final TableConfig tooLongAnswerConfig = new TableConfig("CCCLongAns",
db,
new Object[][] {
{ 100, "alphaalpha", "betabeta" }
});
final TableConfig tooShortAnswerConfig = new TableConfig("CCCShortAns",
db,
new Object[][] {
{ 100, "al", "be" }
});
DBConfig GSDB = new DBConfig(getClass(),
GenerateEETests.class.getResource(DDL_FILENAME),
getCatalogString(),
CCCConfig,
tooShortAnswerConfig,
tooLongAnswerConfig);
GSDB.addTest(new TestConfig("test_long_string",
"select * from CCC;",
true,
tooLongAnswerConfig));
GSDB.addTest(new TestConfig("test_short_string",
"select * from CCC;",
true,
tooShortAnswerConfig));
generateTests("executors", "TestGeneratedString", GSDB);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
public static void main(String args[]) {
GenerateEETests tg = new GenerateEETests();
tg.processArgs(args);
try {
tg.setUp();
tg.generatedPlannerTest();
tg.generatedCountPlan();
tg.generatedMinPlan();
tg.generatedMaxPlan();
tg.generatedSumPlan();
tg.generatedRankPlan();
tg.generatedStringPlan();
} catch (Exception e) {
System.err.printf("Unexpected exception: %s\n", e.getMessage());
e.printStackTrace();
}
}
}