/* This file is part of VoltDB.
* Copyright (C) 2008-2014 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 java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.apache.commons.lang3.StringUtils;
import org.voltdb.BackendTarget;
import org.voltdb.VoltTable;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.NoConnectionsException;
import org.voltdb.client.ProcCallException;
import org.voltdb.compiler.VoltProjectBuilder;
public class TestInsertIntoSelectSuite extends RegressionSuite {
public TestInsertIntoSelectSuite(String name) {
super(name);
}
static final String vcDefault = "dachshund";
static final long intDefault = 121;
static private class ProcedureTemplate {
static List<String> partitionedSourceTables = null;
static List<String> replicatedSourceTables = null;
static List<String> partitionedAndReplicatedSourceTables = null;
private static List<String> getPartitionedSourceTables() {
if (partitionedSourceTables == null) {
partitionedSourceTables = new ArrayList<String>(2);
partitionedSourceTables.add("source_p1");
partitionedSourceTables.add("source_p2");
}
return partitionedSourceTables;
}
private static List<String> getReplicatedSourceTables() {
if (replicatedSourceTables == null) {
replicatedSourceTables = new ArrayList<String>(2);
replicatedSourceTables.add("source_r1");
replicatedSourceTables.add("source_r2");
}
return replicatedSourceTables;
}
private static List<String> getPartitionedAndReplicatedSourceTables() {
if (partitionedAndReplicatedSourceTables == null) {
partitionedAndReplicatedSourceTables = new ArrayList<String>();
partitionedAndReplicatedSourceTables.addAll(getPartitionedSourceTables());
partitionedAndReplicatedSourceTables.addAll(getReplicatedSourceTables());
}
return partitionedAndReplicatedSourceTables;
}
private static final String partitionedTargetTable = "target_p";
private static final String replicatedTargetTable = "target_r";
// Instance Data
private final String m_queryFormat;
private final String m_label;
private int m_comboCounter = 0;
private final Map<String, List<String>> m_procNameToStmts = new HashMap<>();
ProcedureTemplate(String label, String queryFormat) {
m_queryFormat = queryFormat;
m_label = label;
generateStatements();
}
private void formatQueryAndGenerateStatements(String queryFormat,
boolean partitionProcedure,
String targetTable,
Collection<String> sourceTables1,
Collection<String> sourceTables2) {
Stack<String> formatStack = new Stack<String>();
formatStack.push(queryFormat);
while (! formatStack.empty()) {
String format = formatStack.pop();
int numTablesNeeded = StringUtils.countMatches(format, "%s");
if (numTablesNeeded > 0) {
Collection<String> whichSet = null;
if (numTablesNeeded == 2 ) {
// First table should be from
whichSet = sourceTables1;
} else {
whichSet = sourceTables2;
}
for (String sourceTable : whichSet) {
String newFormat = format.replaceFirst("%s", sourceTable);
formatStack.push(newFormat);
}
} else {
generateStatementsForProcedure(partitionProcedure, targetTable, format);
}
}
}
private void generateStatements() {
int numParams = StringUtils.countMatches(m_queryFormat, "?");
if (numParams > 0) {
// generate stored procedures from this template that insert into a partitioned table,
// selecting from both partitioned and replicated tables.
// partitioned the stored procedures
formatQueryAndGenerateStatements(m_queryFormat, true, partitionedTargetTable,
getPartitionedAndReplicatedSourceTables(), getPartitionedAndReplicatedSourceTables());
}
// As above, except that stored procedures are not marked as single-partition
formatQueryAndGenerateStatements(m_queryFormat, false, partitionedTargetTable,
getPartitionedSourceTables(), getPartitionedAndReplicatedSourceTables());
// generated procedures that insert into replicated tables, selecting from replicated tables
formatQueryAndGenerateStatements(m_queryFormat, false, replicatedTargetTable,
getReplicatedSourceTables(), getReplicatedSourceTables());
}
private String generateProcedureName(boolean partitioned, String targetTable) {
String procName = "insert_into_select_" + m_label + "_" + targetTable;
String combo = String.format("_combo%02d", (Object)m_comboCounter);
m_comboCounter++;
procName += combo;
if (partitioned) {
procName += "_partitioned";
}
return procName;
}
private void generateStatementsForProcedure(boolean partitionProcedure, String targetTable, String query) {
String procName = generateProcedureName(partitionProcedure, targetTable);
// Create a map
//
// procedureName -> Ad hoc statement
// create procedure statement (and maybe partition procedure statement)
// verify procedure
// result set produced by HSQL?
StringBuilder adHocStmt = new StringBuilder();
adHocStmt.append("INSERT INTO " + targetTable + "\n");
adHocStmt.append(" " + query + ";\n");
StringBuilder insertProc = new StringBuilder();
insertProc.append("\nCREATE PROCEDURE " + procName + " AS\n");
insertProc.append(adHocStmt.toString());
if (partitionProcedure) {
insertProc.append("PARTITION PROCEDURE " + procName + " ON TABLE " + targetTable + " COLUMN bi;\n");
}
StringBuilder verifyProc = new StringBuilder();
verifyProc.append("\nCREATE PROCEDURE verify_" + procName + " AS \n");
verifyProc.append(" " + query + "\n");
verifyProc.append(" ORDER BY 1, 2, 3, 4;\n");
ArrayList<String> stmts = new ArrayList<>();
stmts.add(adHocStmt.toString());
stmts.add(insertProc.toString());
stmts.add(verifyProc.toString());
m_procNameToStmts.put(procName, stmts);
}
Map<String, List<String>> getGeneratedStatements() {
return m_procNameToStmts;
}
}
static final ProcedureTemplate procedureTemplates[] = new ProcedureTemplate[] {
new ProcedureTemplate("simple",
"select * from %s where bi = ?"),
new ProcedureTemplate("simple_noparam",
"select * from %s"),
new ProcedureTemplate("join",
"select t1.bi, t1.vc, t2.ii, t2.ti " +
"from %s as t1 inner join %s as t2 on t1.bi = t2.bi and t1.ii = t2.ii " +
"where t1.bi = ?"),
new ProcedureTemplate("join_noparam",
"select t1.bi, t1.vc, t2.ii, t2.ti " +
"from %s as t1 inner join %s as t2 on t1.bi = t2.bi and t1.ii = t2.ii"),
new ProcedureTemplate("subquery",
"select * " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s) as t1_subq " +
"where t1_subq.bi = ?"),
new ProcedureTemplate("subquery_noparam",
"select * " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s) as t1_subq"),
new ProcedureTemplate("subquery_inner_filter",
"select * " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s where bi = ?) as t1_subq"),
new ProcedureTemplate("subquery_inner_filter_noparam",
"select * " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s) as t1_subq"),
new ProcedureTemplate("subquery_join",
"select t1_subq.bi, t1_subq.vc, t2.ii, t2.ti " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s) as t1_subq " +
"inner join %s as t2 on t1_subq.bi = t2.bi and t1_subq.ii = t2.ii " +
"where t1_subq.bi = ?"),
new ProcedureTemplate("subquery_join_noparam",
"select t1_subq.bi, t1_subq.vc, t2.ii, t2.ti " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s) as t1_subq " +
"inner join %s as t2 on t1_subq.bi = t2.bi and t1_subq.ii = t2.ii"),
new ProcedureTemplate("join_two_subqueries",
"select t1_subq.bi, t1_subq.vc, t2_subq.ii, t2_subq.ti " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s) as t1_subq " +
"inner join (select bi, '2nd_subq + ' || vc as vc, ii, ti from %s) as t2_subq " +
"on t1_subq.bi = t2_subq.bi and t1_subq.ii = t2_subq.ii " +
"where t1_subq.bi = ?"),
new ProcedureTemplate("join_two_subqueries_noparam",
"select t1_subq.bi, t1_subq.vc, t2_subq.ii, t2_subq.ti " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from %s) as t1_subq " +
"inner join (select bi, '2nd_subq + ' || vc as vc, ii, ti from %s) as t2_subq " +
"on t1_subq.bi = t2_subq.bi and t1_subq.ii = t2_subq.ii"),
new ProcedureTemplate("nest_subqueries",
"select t1_subq.bi, t1_subq.vc, t2_subq.ii, t2_subq.ti " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from " +
"(select bi, 'nested ' || vc as vc, ii, ti from %s) as t1_subq_subq) as t1_subq " +
"inner join (select bi, '2nd_subq + ' || vc as vc, ii, ti from %s) as t2_subq " +
"on t1_subq.bi = t2_subq.bi and t1_subq.ii = t2_subq.ii " +
"where t1_subq.bi = ?"),
new ProcedureTemplate("nest_subqueries_noparam",
"select t1_subq.bi, t1_subq.vc, t2_subq.ii, t2_subq.ti " +
"from (select bi, 'subq + ' || vc as vc, ii, ti from " +
"(select bi, 'nested ' || vc as vc, ii, ti from %s) as t1_subq_subq) as t1_subq " +
"inner join (select bi, '2nd_subq + ' || vc as vc, ii, ti from %s) as t2_subq " +
"on t1_subq.bi = t2_subq.bi and t1_subq.ii = t2_subq.ii")
};
private static Map<String, List<String>> generatedStmtMap = null;
private static Map<String, List<String>> mapOfAllGeneratedStatements() {
if (generatedStmtMap == null) {
generatedStmtMap = new HashMap<>();
for (ProcedureTemplate t : procedureTemplates) {
for (Map.Entry<String, List<String>> e : t.getGeneratedStatements().entrySet()) {
generatedStmtMap.put(e.getKey(), e.getValue());
}
}
}
return generatedStmtMap;
}
private static int numberOfParametersNeeded(String procName) {
List<String> stmts = mapOfAllGeneratedStatements().get(procName);
int numParams = StringUtils.countMatches(stmts.get(0), "?");
return numParams;
}
private static List<String> generatedProcedures() {
List<String> procs = new ArrayList<>();
for (List<String> stmts : mapOfAllGeneratedStatements().values()) {
procs.add(stmts.get(1));
procs.add(stmts.get(2));
}
return procs;
}
static private String generateSchema() {
StringBuilder sb = new StringBuilder();
// Target tables: 1 partitioned, 1 replicated
// Source tables: 2 partitioned, 2 replicated
sb.append(
"CREATE TABLE target_p (bi bigint not null," +
"vc varchar(100) default '" + vcDefault +"'," +
"ii integer default " + intDefault + "," +
"ti tinyint default " + intDefault + " not null);" +
"partition table target_p on column bi;" +
"CREATE TABLE target_r (bi bigint not null," +
"vc varchar(100) default '" + vcDefault +"'," +
"ii integer default " + intDefault + "," +
"ti tinyint default " + intDefault + " not null);" +
"CREATE TABLE source_p1 (bi bigint not null," +
"vc varchar(100)," +
"ii integer," +
"ti tinyint);" +
"partition table source_p1 on column bi;" +
"CREATE TABLE source_p2 (bi bigint not null," +
"vc varchar(100)," +
"ii integer," +
"ti tinyint);" +
"partition table source_p2 on column bi;" +
"CREATE TABLE source_r1 (bi bigint not null," +
"vc varchar(100)," +
"ii integer," +
"ti tinyint);" +
"CREATE TABLE source_r2 (bi bigint not null," +
"vc varchar(4)," +
"ii integer," +
"ti tinyint);"
);
sb.append(
// select all rows from target tables, to verify inserted rows
"create procedure get_all_target_p_rows as select * from target_p order by bi, vc, ii, ti;" +
"create procedure get_all_target_r_rows as select * from target_r order by bi, vc, ii, ti;" +
// A very simple insert into select statement
"create procedure insert_p_source_p as insert into target_p (bi, vc, ii, ti) select * from source_p1 where bi = ?;" +
"partition procedure insert_p_source_p on table target_p column bi;" +
// an insert into select statement that makes use of default values
"create procedure insert_p_use_defaults as insert into target_p (bi, ti) select bi, ti from source_p1 where bi = ?;" +
"partition procedure insert_p_use_defaults on table target_p column bi;" +
// an insert into select statement with unordered columns
"create procedure insert_p_use_defaults_reorder as insert into target_p (ti, bi) select ti, bi from source_p1 where bi = ?;" +
"partition procedure insert_p_use_defaults_reorder on table target_p column bi;" +
// group by in the subquery
"create procedure insert_p_source_p_agg as insert into target_p (bi, vc, ii, ti) " +
"select bi, max(vc), max(ii), min(ti)" + " from source_p1 where bi = ? group by bi;" +
"partition procedure insert_p_source_p_agg on table target_p column bi;" +
// transpose ti, ii, columns so there are implicit integer->tinyint and tinyint->integer casts
"create procedure insert_p_source_p_cast as insert into target_p (bi, vc, ti, ii) select * from source_p1 where bi = ?;" +
"partition procedure insert_p_source_p_cast on table target_p column bi;" +
// source_p2.ii contains values that will not fit into tinyint, so this procedure should throw an out-of-range conversion exception
"create procedure insert_p_source_p_cast_out_of_range as " +
"insert into target_p (bi, vc, ti, ii) " +
"select * from source_p2 where bi = ?;" +
"partition procedure insert_p_source_p_cast_out_of_range on table target_p column bi;" +
// Implicit string->int and int->string conversion.
"create procedure insert_p_source_p_nonsensical_cast as insert into target_p (bi, ii, vc, ti) select * from source_p1 where bi = ?;" +
"partition procedure insert_p_source_p_nonsensical_cast on table target_p column bi;" +
// Target table and source table the same
"create procedure select_and_insert_into_source as " +
"insert into source_p1 (bi, vc, ti, ii) select bi, vc, ti, 1000 * ii from source_p1 where bi = ? order by bi, ti;" +
"partition procedure select_and_insert_into_source on table source_p1 column bi;" +
// HSQL seems to want a cast for the parameter
// Note that there is no filter in source_r2
"create procedure insert_param_in_select_list as " +
"insert into target_p (bi, vc, ii, ti) " +
"select cast(? as bigint), vc, ii, ti from source_r2 order by ii;" +
"partition procedure insert_param_in_select_list on table target_p column bi;" +
// try to insert into the wrong partition
"create procedure insert_wrong_partition as " +
"insert into target_p (bi, ti) select ti, cast(? as tinyint) from source_r2; " +
"partition procedure insert_wrong_partition on table target_p column bi; " +
// try to violate a not null constraint
"create procedure insert_select_violate_constraint as " +
"insert into target_p (bi, ti) " +
"select bi, case ti when 55 then null else ti end from source_p1 where bi = ? order by ti asc;" +
"partition procedure insert_select_violate_constraint on table target_p column bi; " +
""
);
// Generate CREATE STORED PROCEDURE, PARTITION STORED PROCEDURE statements from each procedure template,
// as well as verify procedures for checking results
for (String proc : generatedProcedures()) {
sb.append(proc);
}
return sb.toString();
}
static public junit.framework.Test suite() {
VoltServerConfig config = null;
final MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder(TestInsertIntoSelectSuite.class);
final VoltProjectBuilder project = new VoltProjectBuilder();
try {
project.addLiteralSchema(generateSchema());
} catch (IOException error) {
fail(error.getMessage());
}
boolean success;
// JNI
config = new LocalCluster("iisf-onesite.jar", 1, 1, 0, BackendTarget.NATIVE_EE_JNI);
success = config.compile(project);
assertTrue(success);
builder.addServerConfig(config);
// CLUSTER (disable to opt for speed over coverage...
config = new LocalCluster("iisf-cluster.jar", 2, 3, 1, BackendTarget.NATIVE_EE_JNI);
success = config.compile(project);
assertTrue(success);
builder.addServerConfig(config);
// ... disable for speed) */
config = new LocalCluster("iisf-hsql.jar", 1, 1, 0, BackendTarget.HSQLDB_BACKEND);
success = config.compile(project);
assert(success);
builder.addServerConfig(config);
return builder;
}
private static void clearTargetTables(Client client) throws Exception {
ClientResponse resp = client.callProcedure("@AdHoc", "delete from target_p");
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("@AdHoc", "delete from target_r");
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
}
private static void clearTables(Client client) throws Exception {
ClientResponse resp = client.callProcedure("@AdHoc", "delete from source_p1");
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("@AdHoc", "delete from source_p2");
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("@AdHoc", "delete from source_r1");
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("@AdHoc", "delete from source_r2");
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
clearTargetTables(client);
}
private static void initializeTables(Client client) throws Exception {
ClientResponse resp = null;
clearTables(client);
for (int i=0; i < 10; i++) {
resp = client.callProcedure("SOURCE_P1.insert", i, Long.toHexString(i), i, i);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_P1.insert", i, Long.toHexString(-i), -i, -i);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_P1.insert", i, Long.toHexString(i * 11), i * 11, i * 11);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_P1.insert", i, Long.toHexString(i * -11), i * -11, i * -11);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R1.insert", i, Long.toHexString(i), i, i);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R1.insert", i, Long.toHexString(-i), -i, -i);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R1.insert", i, Long.toHexString(i * 11), i * 11, i * 11);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R1.insert", i, Long.toHexString(i * -11), i * -11, i * -11);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
int j = i + 5;
resp = client.callProcedure("SOURCE_P2.insert", j, Long.toHexString(j), j, j);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_P2.insert", j, Long.toHexString(-j), -j, -j);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_P2.insert", j, Long.toHexString(j * 11), j * 11, (j * 11) % 128);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_P2.insert", j, Long.toHexString(j * -11), j * -11, -((j * 11) % 128));
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R2.insert", j, Long.toHexString(j), j, j);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R2.insert", j, Long.toHexString(-j).substring(0, 3), -j, -j);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R2.insert", j, Long.toHexString(j * 11), j * 11, (j * 11) % 128);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
resp = client.callProcedure("SOURCE_R2.insert", j, Long.toHexString(j * -11).substring(0, 3), j * -11, -((j * 11) % 128));
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
}
}
private static VoltTable getRows(Client client, String adHocQuery) throws NoConnectionsException, IOException, ProcCallException {
ClientResponse resp = client.callProcedure("@AdHoc", adHocQuery);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
return resp.getResults()[0];
}
public void testPartitionedTableSimple() throws Exception
{
final Client client = getClient();
ClientResponse resp;
// Running the procedure with the first parameter (100) will cause 0 rows to be inserted
// The second parameter (5) will insert 4 rows into the target table
long[] params = new long[] {100, 5};
String[] procs = new String[] {"insert_p_source_p", "insert_p_source_p_cast"};
for (long param : params) {
for (String proc : procs) {
initializeTables(client);
resp = client.callProcedure(proc, param);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
long numRowsInserted = resp.getResults()[0].asScalarLong();
// verify that the corresponding rows in both tables are the same
String selectAllSource = "select * from source_p1 where bi = " + param + " order by bi, ii";
String selectAllTarget = "select * from target_p order by bi, ii";
resp = client.callProcedure("@AdHoc", selectAllSource);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
VoltTable sourceRows = resp.getResults()[0];
resp = client.callProcedure("@AdHoc", selectAllTarget);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
VoltTable targetRows = resp.getResults()[0];
int i = 0;
while(targetRows.advanceRow()) {
assertEquals(true, sourceRows.advanceRow());
assertEquals(sourceRows.getLong(0), targetRows.getLong(0));
assertEquals(sourceRows.getString(1), targetRows.getString(1));
assertEquals(sourceRows.getLong(2), targetRows.getLong(2));
assertEquals(sourceRows.getLong(3), targetRows.getLong(3));
i++;
}
assertEquals(numRowsInserted, i);
}
}
}
public void testSelectWithAggregation() throws Exception {
final Client client = getClient();
final long partitioningValue = 7;
initializeTables(client);
ClientResponse resp = client.callProcedure("insert_p_source_p_agg", partitioningValue);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
validateTableOfScalarLongs(resp.getResults()[0], new long[] {1});
resp = client.callProcedure("@AdHoc", "select * from target_p order by bi");
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
VoltTable targetRows = resp.getResults()[0];
assertTrue(targetRows.advanceRow());
assertEquals(partitioningValue, targetRows.getLong(0));
assertEquals(Long.toHexString(-partitioningValue), targetRows.getString(1));
assertEquals(partitioningValue * 11, targetRows.getLong(2));
assertEquals(partitioningValue * -11, targetRows.getLong(3));
assertFalse(targetRows.advanceRow());
}
public void testOutOfRangeImplicitCasts() throws Exception {
final Client client = getClient();
final long partitioningValue = 14;
initializeTables(client);
verifyProcFails(client, "out of range", "insert_p_source_p_cast_out_of_range", partitioningValue);
}
public void testNonsensicalCasts() throws Exception {
final Client client = getClient();
final long partitioningValue = 5;
initializeTables(client);
verifyProcFails(client, "invalid character value",
"insert_p_source_p_nonsensical_cast", partitioningValue);
}
public void testInsertIntoSelectWithDefaults() throws Exception {
final Client client = getClient();
ClientResponse resp;
long partitioningValue = 8;
// Both inserts use the select to produce values only for a subset of columns.
String[] procs = new String[] {"insert_p_use_defaults", "insert_p_use_defaults_reorder"};
for (String proc : procs) {
initializeTables(client);
resp = client.callProcedure(proc, partitioningValue);
validateTableOfScalarLongs(resp.getResults()[0], new long[] {4});
String selectSp1 = "select * from source_p1 where bi = ? order by bi, ti";
String selectTarget = "select * from target_p order by bi, ti";
resp = client.callProcedure("@AdHoc", selectTarget);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
VoltTable targetRows = resp.getResults()[0];
resp = client.callProcedure("@AdHoc", selectSp1, partitioningValue);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
VoltTable sp1Rows = resp.getResults()[0];
while (targetRows.advanceRow()) {
assertTrue(sp1Rows.advanceRow());
assertEquals(sp1Rows.getLong(0), targetRows.getLong(0));
assertEquals(vcDefault, targetRows.getString(1));
assertEquals(intDefault, targetRows.getLong(2));
assertEquals(sp1Rows.getLong(3), targetRows.getLong(3));
}
assertFalse(sp1Rows.advanceRow());
}
}
public void testInsertIntoSelectSameTable() throws Exception {
final Client client = getClient();
initializeTables(client);
final long partitioningValue = 3;
ClientResponse resp = client.callProcedure("select_and_insert_into_source", partitioningValue);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
validateTableOfScalarLongs(resp.getResults()[0], new long[] {4});
String selectOrigRows = "select * from source_p1 where bi = ? and abs(ii) < 1000 order by bi, ii";
String selectNewRows = "select * from source_p1 where bi = ? and abs(ii) > 1000 order by bi, ii";
resp = client.callProcedure("@AdHoc", selectOrigRows, partitioningValue);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
VoltTable origRows = resp.getResults()[0];
resp = client.callProcedure("@AdHoc", selectNewRows, partitioningValue);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
VoltTable newRows = resp.getResults()[0];
while (origRows.advanceRow()) {
assertTrue(newRows.advanceRow());
assertEquals(origRows.getLong(0), newRows.getLong(0));
assertEquals(origRows.getString(1), newRows.getString(1));
assertEquals(origRows.getLong(2) * 1000, newRows.getLong(2));
assertEquals(origRows.getLong(3), newRows.getLong(3));
}
assertFalse(newRows.advanceRow());
}
public void testSelectListParam() throws Exception {
final Client client = getClient();
initializeTables(client);
final long partitioningValue = 7;
ClientResponse resp = client.callProcedure("insert_param_in_select_list", partitioningValue);
assertEquals(ClientResponse.SUCCESS, resp.getStatus());
// tables should be identical except for "bi"
VoltTable sourceRows = getRows(client, "select * from source_r2 order by ii");
VoltTable targetRows = getRows(client, "select * from target_p order by ii");
//fail("target: " + targetRows);
while (sourceRows.advanceRow()) {
assertTrue(targetRows.advanceRow());
assertEquals(partitioningValue, targetRows.getLong(0));
assertEquals(sourceRows.getString(1), targetRows.getString(1));
assertEquals(sourceRows.getLong(2), targetRows.getLong(2));
assertEquals(sourceRows.getLong(3), targetRows.getLong(3));
}
assertFalse(targetRows.advanceRow());
}
public void testViolateConstraint() throws Exception {
final Client client = getClient();
initializeTables(client);
final long partitioningValue = 5;
verifyProcFails(client, "CONSTRAINT VIOLATION", "insert_select_violate_constraint", partitioningValue);
// the insert statement violated a constraint so there should still be no data in the table
validateTableOfLongs(client , "select count(*) from target_p", new long[][] {{0}});
}
public void testInsertWrongPartitionFails() throws Exception {
if (m_config.getNodeCount() > 1) {
Client client = getClient();
initializeTables(client);
final long partitioningValue = 9;
verifyProcFails(client, "Mispartitioned tuple in single-partition insert statement.",
"insert_wrong_partition", partitioningValue);
}
}
public void testFailureToPlan() throws Exception {
// queries which try to copy rows from one partition to another should fail
Client client = getClient();
initializeTables(client);
verifyStmtFails(client, "insert into target_p " +
"select sp1.bi, sp2.vc, sp1.ii, sp2.ti " +
"from source_p1 as sp1 inner join source_p2 as sp2 " +
"on sp1.ii = sp2.ii",
"Subquery statement for table __VOLT_INSERT_SUBQUERY__ has error: "
+ "Join of multiple partitioned tables has insufficient join criteria");
verifyStmtFails(client, "insert into target_r " +
"select sr1.bi, sr1.vc, sr1.ii, sr1.ti " +
"from source_r1 as sr1, source_p1 as sp1 where sr1.bi = sp1.bi",
"statement may not access partitioned data for insertion into replicated table");
verifyStmtFails(client, "insert into target_p (vc, ii, ti) " +
"select vc, ii, ti from source_p1",
"Partitioning column must be assigned a value produced " +
"by the subquery in an INSERT INTO ... SELECT statement.");
verifyStmtFails(client, "insert into target_p " +
"select bi + 1, vc, ii, ti from source_p1",
"Partitioning could not be determined for INSERT INTO ... SELECT statement");
// two fragment plan for subquery
verifyStmtFails(client, "insert into target_p " +
"select max(bi), max(vc), ii, min(ti) from source_p2 " +
"group by source_p2.ii",
"INSERT INTO ... SELECT statement subquery is too complex");
}
public void testSelectListConstants() throws Exception {
Client client = getClient();
// This statements illustrate existing limitations
// of partitioning inference.
//
// Constants in the select list of the subquery
// do not help refine partitioning.
// In this example, the subquery is multipart, but
// we are only inserting into one partition---only two fragments
// are required in this plan.
verifyStmtFails(client, "insert into target_p " +
"select 9, vc, ii, ti " +
"from source_p1 as sp1",
"Partitioning could not be determined");
// this whole statement should be single-partition!
verifyStmtFails(client, "insert into target_p " +
"select 9, vc, ii, ti " +
"from source_p1 as sp1 where sp1.bi = 9",
"Partitioning could not be determined");
// Note however that this issue is not specific to
// INSERT INTO ... SELECT. This fails to plan as well:
verifyStmtFails(client,
"select count(*) " +
"from target_p " +
"inner join " +
"(select 9 as bi, vc, ii, ti from source_p1) as ins_sq " +
"on target_p.bi = ins_sq.bi",
"Join of multiple partitioned tables " +
"has insufficient join criteria"
);
}
public void testInsertIntoSelectGeneratedProcs() throws Exception
{
Set<Map.Entry<String, List<String>>> allEntries = mapOfAllGeneratedStatements().entrySet();
System.out.println("\n\nRUNNING testInsertIntoSelectGeneratedProcs with " +
allEntries.size() + " stored procedures\n\n");
final Client client = getClient();
initializeTables(client);
for (long partitioningValue = 4; partitioningValue < 11; partitioningValue++) {
for (Map.Entry<String, List<String>> e : allEntries) {
clearTargetTables(client);
// The strategy here is:
// Insert rows via stored procedure that invokes INSERT INTO ... <some_query>.
// Select the inserted rows back, compare with the table produced by <some_query>,
// verify the tables are equal.
// Do the same verification with ad hoc SQL.
String proc = e.getKey();
boolean needsParams = (numberOfParametersNeeded(proc) > 0);
String prefix = "Assertion failed running stored procedure " + proc + ": ";
// insert rows with stored procedure
ClientResponse resp;
if (needsParams) {
resp = client.callProcedure(proc, partitioningValue);
}
else {
resp = client.callProcedure(proc);
}
assertEquals(prefix + "procedure call failed", ClientResponse.SUCCESS, resp.getStatus());
VoltTable insertResult = resp.getResults()[0];
insertResult.advanceRow();
// make sure we actually inserted something
long numRowsInserted = insertResult.getLong(0);
// fetch the rows we just inserted
if (proc.contains("target_p")) {
resp = client.callProcedure("get_all_target_p_rows");
}
else {
resp = client.callProcedure("get_all_target_r_rows");
}
assertEquals(prefix + "could not fetch rows of target table", ClientResponse.SUCCESS, resp.getStatus());
VoltTable actualRows = resp.getResults()[0];
if (needsParams) {
resp = client.callProcedure("verify_" + proc, partitioningValue);
}
else {
resp = client.callProcedure("verify_" + proc);
}
// Fetch the rows we expect to have inserted
assertEquals(prefix + "could not verify rows of target table", ClientResponse.SUCCESS, resp.getStatus());
VoltTable expectedRows = resp.getResults()[0];
assertTablesAreEqual(prefix, expectedRows, actualRows);
int actualNumRows = actualRows.getRowCount();
assertEquals(prefix + "insert statement returned " + numRowsInserted + " but only " + actualNumRows + " rows selected from target table",
actualNumRows, numRowsInserted);
// Now try the corresponding ad hoc statement
String adHocQuery = e.getValue().get(0);
prefix = "Assertion failed running ad hoc SQL: " + adHocQuery;
clearTargetTables(client);
// insert rows with stored procedure
if (needsParams) {
resp = client.callProcedure("@AdHoc", adHocQuery, partitioningValue);
}
else {
resp = client.callProcedure("@AdHoc", adHocQuery);
}
assertEquals(prefix + "ad hoc statement failed", ClientResponse.SUCCESS, resp.getStatus());
insertResult = resp.getResults()[0];
insertResult.advanceRow();
numRowsInserted = insertResult.getLong(0);
// fetch the rows we just inserted
if (proc.contains("target_p")) {
resp = client.callProcedure("get_all_target_p_rows");
}
else {
resp = client.callProcedure("get_all_target_r_rows");
}
assertEquals(prefix + "could not fetch rows of target table", ClientResponse.SUCCESS, resp.getStatus());
actualRows = resp.getResults()[0];
expectedRows.resetRowPosition();
assertTablesAreEqual(prefix, expectedRows, actualRows);
actualNumRows = actualRows.getRowCount();
assertEquals(prefix + "insert statement returned " + numRowsInserted + " but only " + actualNumRows + " rows selected from target table",
actualNumRows, numRowsInserted);
}
}
}
}