/* 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. */ /* Copyright (C) 2008 * Evan Jones * Massachusetts Institute of Technology * * 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; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import java.math.BigDecimal; import java.util.Date; import junit.framework.TestCase; import org.voltcore.utils.CoreUtils; import org.voltdb.VoltTable.ColumnInfo; import org.voltdb.client.ClientResponse; import org.voltdb.types.TimestampType; public class TestVoltProcedure extends TestCase { static class DateProcedure extends NullProcedureWrapper { public static VoltTable[] run(Date arg1) { arg = arg1; return new VoltTable[0]; } public static Date arg; } static class TimestampProcedure extends NullProcedureWrapper { public static VoltTable[] run(TimestampType arg1) { arg = arg1; return new VoltTable[0]; } public static TimestampType arg; } static class StringProcedure extends NullProcedureWrapper { public static VoltTable[] run(String arg1) { arg = arg1; return new VoltTable[0]; } public static String arg; } static class DecimalProcedure extends NullProcedureWrapper { public static VoltTable[] run(BigDecimal arg1) { arg = arg1; return new VoltTable[0]; } public static BigDecimal arg; } static class ByteProcedure extends NullProcedureWrapper { public static VoltTable[] run(byte arg1) { arg = arg1; return new VoltTable[0]; } public static byte arg; } static class ShortProcedure extends NullProcedureWrapper { public static VoltTable[] run(short arg1) { arg = arg1; return new VoltTable[0]; } public static short arg; } static class IntegerProcedure extends NullProcedureWrapper { public static VoltTable[] run(int arg1) { arg = arg1; return new VoltTable[0]; } public static int arg; } static class LongProcedure extends NullProcedureWrapper { public static VoltTable[] run(long arg1) { arg = arg1; return new VoltTable[0]; } public static long arg; } static class DoubleProcedure extends NullProcedureWrapper { public static VoltTable[] run(double arg1) { arg = arg1; return new VoltTable[0]; } public static double arg; } static class BoxedByteProcedure extends NullProcedureWrapper { public static VoltTable[] run(Byte arg1) { arg = arg1; return new VoltTable[0]; } public static Byte arg; } static class BoxedShortProcedure extends NullProcedureWrapper { public static VoltTable[] run(Short arg1) { arg = arg1; return new VoltTable[0]; } public static Short arg; } static class BoxedIntegerProcedure extends NullProcedureWrapper { public static VoltTable[] run(Integer arg1) { arg = arg1; return new VoltTable[0]; } public static Integer arg; } static class BoxedLongProcedure extends NullProcedureWrapper { public static VoltTable[] run(Long arg1) { arg = arg1; return new VoltTable[0]; } public static Long arg; } static class BoxedDoubleProcedure extends NullProcedureWrapper { public static VoltTable[] run(Double arg1) { arg = arg1; return new VoltTable[0]; } public static Double arg; } static class LongArrayProcedure extends NullProcedureWrapper { public static VoltTable[] run(long[] arg1) { arg = arg1; return new VoltTable[0]; } public static long[] arg; } static class NPEProcedure extends NullProcedureWrapper { public static VoltTable[] run(String arg) { return new VoltTable[arg.length()]; } } // See ENG-807 static class UnexpectedFailureFourProcedure extends NullProcedureWrapper { public static VoltTable[] run(String arg) { String[] haha = {"Amusingly", "Horrible", "Message"}; String boom = haha[4]; return new VoltTable[boom.length()]; } } static class LargeNumberOfTablesProc extends NullProcedureWrapper { public static VoltTable[] run(String arg) { ColumnInfo columnInfo = new ColumnInfo("intcol", VoltType.INTEGER); VoltTable table = new VoltTable(columnInfo); table.addRow(10); int count = Short.MAX_VALUE + 1; VoltTable[] results = new VoltTable[count]; for (int i=0; i<count; i++) { results[i] = table; } return results; } } class GetClusterIdProcedure extends NullProcedureWrapper { public VoltTable[] run(Object arg) { clusterId = getClusterId(); return new VoltTable[0]; } public int clusterId; } static class NullProcedureWrapper extends VoltProcedure { VoltTable runQueryStatement(SQLStmt stmt, Object... params) { assert false; return null; } long runDMLStatement(SQLStmt stmt, Object... params) { assert false; return -1; } void addQueryStatement(SQLStmt stmt, Object... args) { assert false; } void addDMLStatement(SQLStmt stmt, Object... args) { assert false; } VoltTable[] executeQueryBatch() { assert false; return null; } long[] executeDMLBatch() { assert false; return null; } } MockVoltDB manager; SiteProcedureConnection site; MockStatsAgent agent; ParameterSet nullParam; private long executionSiteId; private final int expectedClusterId = 5; @Override public void setUp() { manager = new MockVoltDB(); final long executionSiteId = CoreUtils.getHSIdFromHostAndSite( 0, 42); this.executionSiteId = executionSiteId; manager.addSite(executionSiteId, 0); agent = new MockStatsAgent(); manager.setStatsAgent(agent); VoltDB.replaceVoltDBInstanceForTest(manager); manager.addProcedureForTest(DateProcedure.class.getName()); manager.addProcedureForTest(TimestampProcedure.class.getName()); manager.addProcedureForTest(StringProcedure.class.getName()); manager.addProcedureForTest(DecimalProcedure.class.getName()); manager.addProcedureForTest(ByteProcedure.class.getName()); manager.addProcedureForTest(ShortProcedure.class.getName()); manager.addProcedureForTest(IntegerProcedure.class.getName()); manager.addProcedureForTest(LongProcedure.class.getName()); manager.addProcedureForTest(DoubleProcedure.class.getName()); manager.addProcedureForTest(BoxedByteProcedure.class.getName()); manager.addProcedureForTest(BoxedShortProcedure.class.getName()); manager.addProcedureForTest(BoxedIntegerProcedure.class.getName()); manager.addProcedureForTest(BoxedLongProcedure.class.getName()); manager.addProcedureForTest(BoxedDoubleProcedure.class.getName()); manager.addProcedureForTest(LongArrayProcedure.class.getName()); manager.addProcedureForTest(NPEProcedure.class.getName()); manager.addProcedureForTest(LargeNumberOfTablesProc.class.getName()); manager.addProcedureForTest(UnexpectedFailureFourProcedure.class.getName()); manager.addProcedureForTest(GetClusterIdProcedure.class.getName()); site = mock(SiteProcedureConnection.class); doReturn(42).when(site).getCorrespondingPartitionId(); doReturn(executionSiteId).when(site).getCorrespondingSiteId(); doReturn(expectedClusterId).when(site).getCorrespondingClusterId(); nullParam = ParameterSet.fromArrayNoCopy(new Object[]{null}); } @Override public void tearDown() throws Exception { manager.shutdown(null); } /** * XXX (Ning) I'm not sure this test is still useful since we don't support * Java Date object anymore. */ public void testNullDate() { ClientResponse r = call(DateProcedure.class); assertEquals(null, DateProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullTimestamp() { ClientResponse r = call(TimestampProcedure.class); assertEquals(null, TimestampProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullString() { ClientResponse r = call(StringProcedure.class); assertEquals(null, StringProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullDecimal() { ClientResponse r = call(DecimalProcedure.class); assertEquals(null, DecimalProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullByte() { ClientResponse r = call(ByteProcedure.class); assertEquals(VoltType.NULL_TINYINT, ByteProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullShort() { ClientResponse r = call(ShortProcedure.class); assertEquals(VoltType.NULL_SMALLINT, ShortProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullInteger() { ClientResponse r = call(IntegerProcedure.class); assertEquals(VoltType.NULL_INTEGER, IntegerProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullLong() { ClientResponse r = call(LongProcedure.class); assertEquals(VoltType.NULL_BIGINT, LongProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullDouble() { ClientResponse r = call(DoubleProcedure.class); assertEquals(VoltType.NULL_FLOAT, DoubleProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullBoxedByte() { ClientResponse r = call(BoxedByteProcedure.class); assertEquals(null, BoxedByteProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullBoxedShort() { ClientResponse r = call(BoxedShortProcedure.class); assertEquals(null, BoxedShortProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullBoxedInteger() { ClientResponse r = call(BoxedIntegerProcedure.class); assertEquals(null, BoxedIntegerProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullBoxedLong() { ClientResponse r = call(BoxedLongProcedure.class); assertEquals(null, BoxedLongProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullBoxedDouble() { ClientResponse r = call(BoxedDoubleProcedure.class); assertEquals(null, BoxedDoubleProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullLongArray() { ClientResponse r = call(LongArrayProcedure.class); assertEquals(null, LongArrayProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testNullPointerException() { ClientResponse r = call(NPEProcedure.class); assertEquals(ClientResponse.UNEXPECTED_FAILURE, r.getStatus()); System.out.println(r.getStatusString()); assertTrue(r.getStatusString().contains("java.lang.NullPointerException")); } public void testLargeNumberOfTablesError() { ClientResponse r = call(LargeNumberOfTablesProc.class); assertEquals(ClientResponse.GRACEFUL_FAILURE, r.getStatus()); System.out.println(r.getStatusString()); assertTrue(r.getStatusString().contains("Exceeded maximum number of VoltTables")); } public void testNegativeWiderType() { ClientResponse r = callWithArgs(LongProcedure.class, Integer.valueOf(-1000)); assertEquals(-1000L, LongProcedure.arg); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } public void testUnexpectedFailureFour() { ClientResponse r = call(UnexpectedFailureFourProcedure.class); assertEquals(ClientResponse.UNEXPECTED_FAILURE, r.getStatus()); System.out.println(r.getStatusString()); assertTrue(r.getStatusString().contains("java.lang.ArrayIndexOutOfBoundsException")); } public void testProcedureStatsCollector() { NullProcedureWrapper wrapper = new LongProcedure(); ProcedureRunner runner = new ProcedureRunner( wrapper, site, null, VoltDB.instance().getCatalogContext().database.getProcedures().get(LongProcedure.class.getName()), null); ParameterSet params = ParameterSet.fromArrayNoCopy(1L); assertNotNull(agent.m_selector); assertNotNull(agent.m_source); assertEquals(agent.m_selector, StatsSelector.PROCEDURE); assertEquals(agent.m_catalogId, executionSiteId); Object statsRow[][] = agent.m_source.getStatsRows(false, 0L); assertNotNull(statsRow); assertEquals( 0, statsRow.length); for (int ii = 1; ii < 200; ii++) { runner.setupTransaction(null); runner.call(params.toArray()); statsRow = agent.m_source.getStatsRows(false, 0L); assertEquals(statsRow[0][7], new Long(ii)); } assertTrue(((Long)statsRow[0][7]).longValue() > 0L); assertTrue(((Long)statsRow[0][8]).longValue() > 0L); assertFalse(statsRow[0][9].equals(0)); assertFalse(statsRow[0][10].equals(0)); assertTrue(((Long)statsRow[0][10]) > 0L); } public void testGetClusterId() { GetClusterIdProcedure gcip = new GetClusterIdProcedure(); ProcedureRunner runner = new ProcedureRunner( gcip, site, null, VoltDB.instance().getCatalogContext().database.getProcedures().get(GetClusterIdProcedure.class.getName()), null); runner.setupTransaction(null); ClientResponse r = runner.call((Object) null); assertEquals(expectedClusterId, gcip.clusterId); assertEquals(ClientResponse.SUCCESS, r.getStatus()); } private ClientResponse call(Class<? extends NullProcedureWrapper> procedure) { return callWithArgs(procedure, (Object) null); } private ClientResponse callWithArgs(Class<? extends NullProcedureWrapper> procedure, Object... args) { NullProcedureWrapper wrapper = null; try { wrapper = procedure.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } ProcedureRunner runner = new ProcedureRunner( wrapper, site, null, VoltDB.instance().getCatalogContext().database.getProcedures().get(LongProcedure.class.getName()), null); runner.setupTransaction(null); return runner.call(args); } private class MockStatsAgent extends StatsAgent { public StatsSource m_source = null; public StatsSelector m_selector = null; public long m_catalogId = 0; @Override public void registerStatsSource(StatsSelector selector, long catalogId, StatsSource source) { m_source = source; m_selector = selector; m_catalogId = catalogId; } @Override public ProcedureStatsCollector registerProcedureStatsSource(long catalogId, ProcedureStatsCollector source) { m_source = source; m_selector = StatsSelector.PROCEDURE; m_catalogId = catalogId; return source; } } }