/* 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.client.NoConnectionsException; import org.voltdb.client.ProcCallException; import org.voltdb.compiler.VoltProjectBuilder; /* * Functional tests of the statements compiled in the test suite * org.voltdb.planner.TestComplexGroupBySuite. */ public class TestCatchExceptionsInProcedure extends RegressionSuite { private boolean isTrue(int value) { return value == 0 ? false: true; } private void mpChecker(Client client, int hasPreviousBatch, int tryCatchContains1Batch, int hasFollowingBatch) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt; String sql; final String MPErrorMessage = "attempted to execute new batch " + "after hitting EE exception in a previous batch"; String[] procs = {"MPInsertOnReplicatedTable", "MPInsertOnPartitionTable"}; String[] tables = {"R1", "P1"}; int[] followingBatchHasExceptions = {0, 1}; for (int i = 0; i < procs.length; i++) { String proc = procs[i]; String tb = tables[i]; for (int followingBatchHasException: followingBatchHasExceptions) { try { vt = client.callProcedure(proc, hasPreviousBatch, tryCatchContains1Batch, hasFollowingBatch, followingBatchHasException).getResults()[0]; // validate returned value from the procedure calls validateRowOfLongs(vt, new long[]{-1}); } catch(Exception e) { assertTrue(e.getMessage().contains(MPErrorMessage)); } sql = "select ratio from " + tb + " order by 1;"; // empty table validateTableColumnOfScalarFloat(client, sql, new double[]{}); // empty table sql = "select count(*) from " + tb; validateTableOfScalarLongs(client, sql, new long[]{0}); client.callProcedure("@AdHoc", "truncate table " + tb); } } } public void testMPCatchException() throws IOException, ProcCallException { System.out.println("test testMPCatchException..."); Client client = getClient(); mpChecker(client, 0, 0, 0); mpChecker(client, 1, 0, 0); mpChecker(client, 0, 1, 0); mpChecker(client, 0, 0, 1); mpChecker(client, 1, 1, 0); mpChecker(client, 1, 0, 1); mpChecker(client, 0, 1, 1); mpChecker(client, 1, 1, 1); } private void spChecker(Client client, String proc, int hasPreviousBatch, int tryCatchContains1BatchFirst, int tryCatchContains1BatchSecond, int hasFollowingBatch, int followingBatchHasException, double[] expected) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt; String sql; try { // use the default value for partition column to route this procedure if (tryCatchContains1BatchSecond == -1) { vt = client.callProcedure(proc, 0, hasPreviousBatch, tryCatchContains1BatchFirst, hasFollowingBatch, followingBatchHasException).getResults()[0]; } else { vt = client.callProcedure(proc, 0, hasPreviousBatch, tryCatchContains1BatchFirst, tryCatchContains1BatchSecond, hasFollowingBatch, followingBatchHasException).getResults()[0]; } if (isTrue(followingBatchHasException)) { assertTrue(isTrue(hasFollowingBatch)); fail("Expected failure but succeeded."); } // validate returned value from the procedure calls validateRowOfLongs(vt, new long[]{-1}); } catch(Exception e) { assertTrue(e.getMessage().contains("CONSTRAINT VIOLATION")); assertTrue(e.getMessage().contains("3.2")); // violated at row (3, 3.2) assertTrue(isTrue(hasFollowingBatch) && isTrue(followingBatchHasException)); } sql = "select ratio from P1 order by 1; "; validateTableColumnOfScalarFloat(client, sql, expected); client.callProcedure("@AdHoc", "truncate table P1"); } private void spSingleTryCatchChecker(Client client, int hasPreviousBatch, int tryCatchContains1Batch, int hasFollowingBatch, int followingBatchHasException, double[] expected) throws NoConnectionsException, IOException, ProcCallException { // call the main checker spChecker(client, "SPInsertOnPartitionTable", hasPreviousBatch, tryCatchContains1Batch, -1, hasFollowingBatch, followingBatchHasException, expected); } public static double prev = 0.1; public void testSPCatchException() throws IOException, ProcCallException { System.out.println("test testSPCatchException..."); Client client = getClient(); // no previous batch spSingleTryCatchChecker(client, 0, 0, 0, 0, new double[]{1.1}); spSingleTryCatchChecker(client, 0, 0, 1, 0, new double[]{1.1, 3.1}); spSingleTryCatchChecker(client, 0, 0, 1, 1, new double[]{}); spSingleTryCatchChecker(client, 0, 1, 0, 0, new double[]{}); spSingleTryCatchChecker(client, 0, 1, 1, 0, new double[]{3.1}); spSingleTryCatchChecker(client, 0, 1, 1, 1, new double[]{}); // has previous batch spSingleTryCatchChecker(client, 1, 0, 0, 0, new double[]{prev, 1.1}); spSingleTryCatchChecker(client, 1, 0, 1, 0, new double[]{prev, 1.1, 3.1}); spSingleTryCatchChecker(client, 1, 0, 1, 1, new double[]{}); spSingleTryCatchChecker(client, 1, 1, 0, 0, new double[]{prev}); spSingleTryCatchChecker(client, 1, 1, 1, 0, new double[]{prev, 3.1}); spSingleTryCatchChecker(client, 1, 1, 1, 1, new double[]{}); } private void spMultiTryCatchChecker(Client client, int hasPreviousBatch, int tryCatchContains1BatchFirst,int tryCatchContains1BatchSecond, int hasFollowingBatch, int followingBatchHasException, double[] expected) throws NoConnectionsException, IOException, ProcCallException { // call the main checker spChecker(client, "SPMultipleTryCatchOnPartitionTable", hasPreviousBatch, tryCatchContains1BatchFirst, tryCatchContains1BatchSecond, hasFollowingBatch, followingBatchHasException, expected); } public void testSPMultiCatchException() throws IOException, ProcCallException { System.out.println("test testSPMultiCatchException..."); Client client = getClient(); // // no previous batch // spMultiTryCatchChecker(client, 0, 0, 0, 0, 0, new double[]{1.1, 2.1}); spMultiTryCatchChecker(client, 0, 1, 1, 1, 1, new double[]{}); spMultiTryCatchChecker(client, 0, 1, 0, 0, 0, new double[]{2.1}); spMultiTryCatchChecker(client, 0, 0, 1, 0, 0, new double[]{1.1}); spMultiTryCatchChecker(client, 0, 0, 0, 1, 0, new double[]{1.1, 2.1, 3.1}); spMultiTryCatchChecker(client, 0, 1, 1, 0, 0, new double[]{}); spMultiTryCatchChecker(client, 0, 1, 0, 1, 0, new double[]{2.1, 3.1}); spMultiTryCatchChecker(client, 0, 0, 1, 1, 0, new double[]{1.1, 3.1}); spMultiTryCatchChecker(client, 0, 0, 0, 1, 1, new double[]{}); spMultiTryCatchChecker(client, 0, 1, 1, 1, 0, new double[]{3.1}); spMultiTryCatchChecker(client, 0, 1, 0, 1, 1, new double[]{}); spMultiTryCatchChecker(client, 0, 0, 1, 1, 1, new double[]{}); // has previous batch spMultiTryCatchChecker(client, 1, 0, 0, 0, 0, new double[]{prev, 1.1, 2.1}); spMultiTryCatchChecker(client, 1, 1, 1, 1, 1, new double[]{}); spMultiTryCatchChecker(client, 1, 1, 0, 0, 0, new double[]{prev, 2.1}); spMultiTryCatchChecker(client, 1, 0, 1, 0, 0, new double[]{prev, 1.1}); spMultiTryCatchChecker(client, 1, 0, 0, 1, 0, new double[]{prev, 1.1, 2.1, 3.1}); spMultiTryCatchChecker(client, 1, 1, 1, 0, 0, new double[]{prev}); spMultiTryCatchChecker(client, 1, 1, 0, 1, 0, new double[]{prev, 2.1, 3.1}); spMultiTryCatchChecker(client, 1, 0, 1, 1, 0, new double[]{prev, 1.1, 3.1}); spMultiTryCatchChecker(client, 1, 0, 0, 1, 1, new double[]{}); spMultiTryCatchChecker(client, 1, 1, 1, 1, 0, new double[]{prev, 3.1}); spMultiTryCatchChecker(client, 1, 1, 0, 1, 1, new double[]{}); spMultiTryCatchChecker(client, 1, 0, 1, 1, 1, new double[]{}); } private void bigBatchChecker(Client client, int hasPreviousBatch, int duplicatedID, int hasFollowingBatch, int followingBatchHasException, double[] expected, int tableCount) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt; String sql; try { // use the default value for partition column to route this procedure vt = client.callProcedure("SPBigBatchOnPartitionTable", 0, hasPreviousBatch, duplicatedID, hasFollowingBatch, followingBatchHasException).getResults()[0]; if (isTrue(followingBatchHasException)) { assertTrue(isTrue(hasFollowingBatch)); fail("Expected failure but succeeded."); } // validate returned value from the procedure calls validateRowOfLongs(vt, new long[]{duplicatedID > BIGBATCHTESTSIZE ? 0: -1}); } catch(Exception e) { assertTrue(e.getMessage().contains("CONSTRAINT VIOLATION")); assertTrue(e.getMessage().contains("500.2")); // violated at row (3, 3.2) assertTrue(isTrue(hasFollowingBatch) && isTrue(followingBatchHasException)); } sql = "select distinct ratio from P1 order by 1; "; validateTableColumnOfScalarFloat(client, sql, expected); sql = "select count(*) from P1; "; validateTableOfScalarLongs(client, sql, new long[]{tableCount}); client.callProcedure("@AdHoc", "truncate table P1"); } private static final int BIGBATCHTESTSIZE = 300; public void testBigBatchException() throws IOException, ProcCallException { System.out.println("test testBigBatchException..."); Client client = getClient(); int[] duplicates = {123, 200, 201, 256, 300}; for (int dup: duplicates) { // exception in the first 200 batch bigBatchChecker(client, 0, dup, 0, 0, new double[]{}, 0); bigBatchChecker(client, 0, dup, 1, 0, new double[]{500.1}, 1); bigBatchChecker(client, 0, dup, 1, 1, new double[]{}, 0); bigBatchChecker(client, 1, dup, 0, 0, new double[]{prev}, 1); bigBatchChecker(client, 1, dup, 1, 0, new double[]{prev, 500.1}, 2); bigBatchChecker(client, 1, dup, 1, 1, new double[]{}, 0); } // exception in the second 200 batch int[] noDuplicates = {350, 400, 450}; for (int noDup: noDuplicates) { // exception in the first 200 batch bigBatchChecker(client, 0, noDup, 0, 0, new double[]{10.1}, BIGBATCHTESTSIZE); bigBatchChecker(client, 0, noDup, 1, 0, new double[]{10.1, 500.1}, BIGBATCHTESTSIZE+1); bigBatchChecker(client, 0, noDup, 1, 1, new double[]{}, 0); bigBatchChecker(client, 1, noDup, 0, 0, new double[]{prev, 10.1}, BIGBATCHTESTSIZE+1); bigBatchChecker(client, 1, noDup, 1, 0, new double[]{prev, 10.1, 500.1}, BIGBATCHTESTSIZE+2); bigBatchChecker(client, 1, noDup, 1, 1, new double[]{}, 0); } } private void bigBatchAdvancedChecker(Client client, int partitionKey, int hasPreviousBatch, int hasBigBatch, int bigBatchDuplicatedID, int hasFollowingBatch, int hasFollowingTryCatchBatch, int hasDupsInTryCatch, int exitAbort, double[] expected, int tableCount) throws NoConnectionsException, IOException, ProcCallException { VoltTable vt; String sql; // use the default value for partition column to route this procedure vt = client.callProcedure("SPBigBatchAdvancedOnPartitionTable", 0, hasPreviousBatch, hasBigBatch, bigBatchDuplicatedID, hasFollowingBatch, hasFollowingTryCatchBatch, hasDupsInTryCatch, exitAbort).getResults()[0]; // validate returned value from the procedure calls int result = bigBatchDuplicatedID > BIGBATCHTESTSIZE ? 0: -1; if (isTrue(hasDupsInTryCatch)) result = -2; validateRowOfLongs(vt, new long[]{result}); sql = "select distinct ratio from P1 order by 1;"; validateTableColumnOfScalarFloat(client, sql, expected); sql = "select count(*) from P1; "; validateTableOfScalarLongs(client, sql, new long[]{tableCount}); client.callProcedure("@AdHoc", "truncate table P1"); } public void testBigBatchAdvancedException() throws IOException, ProcCallException { System.out.println("test testBigBatchAdvancedException..."); Client client = getClient(); String sql; // so many more permutations bigBatchAdvancedChecker(client, 0, 1, 1, 150, 1, 1, 1, 0, new double[]{0.1, 500.1}, 2); bigBatchAdvancedChecker(client, 0, 1, 1, 250, 1, 1, 1, 0, new double[]{0.1, 500.1}, 2); bigBatchAdvancedChecker(client, 0, 0, 1, 150, 0, 0, 0, 0, new double[]{}, 0); // // Test multiple procedure call suggested from code review // // big batch roll back client.callProcedure("SPBigBatchAdvancedOnPartitionTable", 0, 1, 1, 150, 0, 0, 0, 0); // how transaction roll back try { client.callProcedure("SPBigBatchAdvancedOnPartitionTable", 0, 0, 0, 0, 0, 0, 0, 1); fail(); } catch(Exception e) { assertTrue(e.getMessage().contains("CONSTRAINT VIOLATION")); assertTrue(e.getMessage().contains("700.2")); // violated at row (700, 700.2) } sql = "select distinct ratio from P1 order by 1;"; validateTableColumnOfScalarFloat(client, sql, new double[]{0.1}); // clear the data client.callProcedure("@AdHoc", "truncate table P1"); // // Test the try catch and re-throw case // // SP try { client.callProcedure("SPCatchRethrowOnPartitionTable", 0, 1); fail(); } catch(Exception e) { assertTrue(e.getMessage().contains("User's SP constraint error message")); } sql = "select distinct ratio from P1 order by 1;"; validateTableColumnOfScalarFloat(client, sql, new double[]{}); // two batches in the try catch block try { client.callProcedure("SPCatchRethrowOnPartitionTable", 0, 0); } catch(Exception e) { assertTrue(e.getMessage().contains("User's SP constraint error message")); } sql = "select distinct ratio from P1 order by 1;"; validateTableColumnOfScalarFloat(client, sql, new double[]{}); // MP try { client.callProcedure("MPCatchRethrowOnPartitionTable", 1); fail(); } catch(Exception e) { System.err.println(e.getMessage()); assertTrue(e.getMessage().contains("User's MP constraint error message")); } sql = "select distinct ratio from P1 order by 1;"; validateTableColumnOfScalarFloat(client, sql, new double[]{}); // two batches in the try catch block try { client.callProcedure("MPCatchRethrowOnPartitionTable", 0); } catch(Exception e) { assertTrue(e.getMessage().contains("User's MP constraint error message")); } sql = "select distinct ratio from P1 order by 1;"; validateTableColumnOfScalarFloat(client, sql, new double[]{}); } public TestCatchExceptionsInProcedure(String name) { super(name); } static final Class<?>[] PROCEDURES = { org.voltdb_testprocs.regressionsuites.catchexceptions.MPInsertOnReplicatedTable.class, org.voltdb_testprocs.regressionsuites.catchexceptions.SPInsertOnPartitionTable.class, org.voltdb_testprocs.regressionsuites.catchexceptions.MPInsertOnPartitionTable.class, org.voltdb_testprocs.regressionsuites.catchexceptions.SPMultipleTryCatchOnPartitionTable.class, org.voltdb_testprocs.regressionsuites.catchexceptions.SPBigBatchOnPartitionTable.class, org.voltdb_testprocs.regressionsuites.catchexceptions.SPBigBatchAdvancedOnPartitionTable.class, org.voltdb_testprocs.regressionsuites.catchexceptions.SPCatchRethrowOnPartitionTable.class, org.voltdb_testprocs.regressionsuites.catchexceptions.MPCatchRethrowOnPartitionTable.class }; static public junit.framework.Test suite() { VoltServerConfig config = null; MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder( TestCatchExceptionsInProcedure.class); VoltProjectBuilder project = new VoltProjectBuilder(); project.addProcedures(PROCEDURES); final String literalSchema = "CREATE TABLE r1 ( " + "id INTEGER DEFAULT 0 NOT NULL, " + "num INTEGER DEFAULT 0 NOT NULL, " + "ratio FLOAT, " + "PRIMARY KEY (id) ); " + "CREATE TABLE p1 ( " + "id INTEGER DEFAULT 0 NOT NULL assumeunique, " + "num INTEGER DEFAULT 0 NOT NULL, " + "ratio FLOAT, " + "PRIMARY KEY (id, num) ); " + "PARTITION TABLE p1 ON COLUMN num; " + "" ; try { project.addLiteralSchema(literalSchema); } catch (IOException e) { assertFalse(true); } boolean success; config = new LocalCluster("catchexceptions-onesite.jar", 1, 1, 0, BackendTarget.NATIVE_EE_JNI); success = config.compile(project); assertTrue(success); builder.addServerConfig(config); // Cluster config = new LocalCluster("catchexceptions-onesite.jar", 2, 3, 1, BackendTarget.NATIVE_EE_JNI); success = config.compile(project); assertTrue(success); builder.addServerConfig(config); return builder; } }