src/test/java/org/embulk/output/oracle/OracleOutputPluginTest.java in embulk-output-oracle-0.6.3 vs src/test/java/org/embulk/output/oracle/OracleOutputPluginTest.java in embulk-output-oracle-0.6.4

- old
+ new

@@ -1,297 +1,786 @@ package org.embulk.output.oracle; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; +import java.lang.reflect.Constructor; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; import java.util.List; +import java.util.TimeZone; +import org.embulk.exec.PartialExecutionException; import org.embulk.input.filesplit.LocalFileSplitInputPlugin; +import org.embulk.output.AbstractJdbcOutputPluginTest; +import org.embulk.output.OracleOutputPlugin; import org.embulk.output.tester.EmbulkPluginTester; -import org.embulk.plugin.PluginClassLoader; import org.embulk.spi.InputPlugin; import org.embulk.spi.OutputPlugin; import org.junit.BeforeClass; import org.junit.Test; -public class OracleOutputPluginTest +public class OracleOutputPluginTest extends AbstractJdbcOutputPluginTest { - private static class NamedObject - { - public final String name; - public final Object value; - public NamedObject(String name, Object value) - { - this.name = name; - this.value = value; - } + private static boolean canTest; + private static EmbulkPluginTester tester = new EmbulkPluginTester(); + static { + tester.addPlugin(OutputPlugin.class, "oracle", OracleOutputPlugin.class); + tester.addPlugin(InputPlugin.class, "filesplit", LocalFileSplitInputPlugin.class); } - private static List<NamedObject> testObjects = new ArrayList<NamedObject>(); - private static NamedObject test12c; - private static NamedObject test11g; - private static EmbulkPluginTester tester = new EmbulkPluginTester(); - - @BeforeClass public static void beforeClass() throws Exception { if (System.getProperty("path.separator").equals(";")) { // for Windows System.setProperty("file.encoding", "MS932"); } - tester.addPlugin(InputPlugin.class, "filesplit", LocalFileSplitInputPlugin.class); - - test12c = createTest("oracle12", "/driver/12c/ojdbc7.jar"); - if (test12c == null) { - System.out.println("Warning: you should put ojdbc7.jar (version 12c) on 'test/resources/driver/12c' directory."); - } else { - testObjects.add(test12c); + try { + Class.forName("oracle.jdbc.OracleDriver"); + } catch (ClassNotFoundException | NoClassDefFoundError e) { + System.err.println("Warning: you should put 'ojdbc7.jar' in 'embulk-input-oracle/driver' directory in order to test."); + return; } - test11g = createTest("oracle11", "/driver/11g/ojdbc6.jar"); - if (test11g == null) { - System.out.println("Warning: you should put ojdbc6.jar (version 11g Release 2) on 'test/resources/driver/11g' directory."); - } else { - testObjects.add(test11g); + try (Connection connection = new OracleOutputPluginTest().connect()) { + String version = connection.getMetaData().getDriverVersion(); + System.out.println("Driver version = " + version); + canTest = true; + + } catch (SQLException e) { + System.err.println(e); + System.err.println("Warning: prepare a schema on Oracle 12c (database = 'TESTDB', user = 'TEST_USER', password = 'test_pw', charset = UTF-8)."); + // for example + // CREATE USER EMBULK_USER IDENTIFIED BY "embulk_pw"; + // GRANT DBA TO EMBULK_USER; } } - private static NamedObject createTest(String pluginName, String jdbcDriverPath) throws Exception + @Test + public void testInsert() throws Exception { - // Load OracleOutputPluginTestImpl, Oracle JDBC Driver, embulk-output-oracle by another ClassLoader - // in order to test for different driver versions. - - List<URL> urls = new ArrayList<URL>(); - - File testRoot = new File(OracleOutputPluginTest.class.getResource("/oracle/").toURI()).getParentFile(); - String pluginClassName = "org.embulk.output.OracleOutputPlugin"; - URL pluginClassUrl = OracleOutputPluginTest.class.getResource("/" +pluginClassName.replace('.', '/') + ".class"); - File root = new File(pluginClassUrl.toURI()).getParentFile().getParentFile().getParentFile().getParentFile(); - - urls.add(root.toURI().toURL()); - urls.add(testRoot.toURI().toURL()); - - URL jdbcDriverUrl = OracleOutputPluginTest.class.getResource(jdbcDriverPath); - if (jdbcDriverUrl == null) { - return null; + if (!canTest) { + return; } - urls.add(jdbcDriverUrl); - ClassLoader classLoader = new PluginClassLoader(urls, OracleOutputPluginTest.class.getClassLoader(), - Arrays.asList(EmbulkPluginTester.class.getPackage().getName()), new ArrayList<String>()); - Thread.currentThread().setContextClassLoader(classLoader); - tester.addPlugin(OutputPlugin.class, pluginName, classLoader.loadClass(pluginClassName)); + String table = "TEST1"; - Class<?> testClass = classLoader.loadClass(OracleOutputPluginTest.class.getName() + "Impl"); - final Object testObject = testClass.newInstance(); - invoke(testObject, "setTester", tester); - invoke(testObject, "setPluginName", pluginName); + dropTable(table); + createTable(table); - final String version = (String)invoke(testObject, "beforeClass"); - if (version == null) { - return null; - } - return new NamedObject(version, testObject); - } + run("/oracle/yml/test-insert.yml"); - private static void invoke(String methodName) throws Exception - { - for (NamedObject testObject : testObjects) { - invoke(testObject, methodName); - } + assertTable(table); } - private static Object invoke(NamedObject testObject, String methodName) throws Exception + @Test + public void testInsertCreate() throws Exception { - if (testObject != null) { - System.out.println("*** " + testObject.name + " ***"); - return invoke(testObject.value, methodName); + if (!canTest) { + return; } - return null; - } - private static Object invoke(Object testObject, String methodName, Object... arguments) throws Exception - { - if (testObject != null) { - Thread.currentThread().setContextClassLoader(testObject.getClass().getClassLoader()); - Class<?>[] parameterTypes = new Class<?>[arguments.length]; - for (int i = 0; i < arguments.length; i++) { - parameterTypes[i] = arguments[i].getClass(); - } - Method method = testObject.getClass().getMethod(methodName, parameterTypes); - return method.invoke(testObject, arguments); - } - return null; - } + String table = "TEST1"; - @Test - public void testInsert() throws Exception - { - // cannot test with Oracle 11g JDBC driver for Oracle 12c, - // because the driver returns sqlType=1111 for NCHAR/NVARCHAR2, - // and ColumnSetterFactory#newCoalesceColumnSetter throws Exception. - // even if setting {value_type: string} for NCHAR/NVARCHAR2, - // PreparedStatement#setNull(parameterIndex, sqlType=1111) throws Exception. - invoke(test12c, "testInsert"); - } + dropTable(table); - @Test - public void testInsertCreate() throws Exception - { - invoke("testInsertCreate"); + run("/oracle/yml/test-insert.yml"); + + assertGeneratedTable1(table); } @Test public void testInsertEmpty() throws Exception { - invoke(test12c, "testInsertEmpty"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + new File(convertPath("/oracle/data/"), "test2").mkdir(); + run("/oracle/yml/test-insert-empty.yml"); + + assertTableEmpty(table); } @Test public void testTruncateInsert() throws Exception { - invoke(test12c, "testTruncateInsert"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + insertRecord(table); + + run("/oracle/yml/test-truncate-insert.yml"); + + assertTable(table); } @Test public void testTruncateInsertOCIMethod() throws Exception { - invoke(test12c, "testTruncateInsertOCIMethod"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + insertRecord(table); + + run("/oracle/yml/test-truncate-insert-oci-method.yml"); + + assertTable(table); } @Test public void testTruncateInsertCreate() throws Exception { - invoke("testTruncateInsertCreate"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + + run("/oracle/yml/test-truncate-insert.yml"); + + assertGeneratedTable1(table); } @Test public void testInsertDirect() throws Exception { - invoke(test12c, "testInsertDirect"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-insert-direct.yml"); + + assertTable(table); } @Test public void testInsertDirectDuplicate() throws Exception { - invoke(test12c, "testInsertDirectDuplicate"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + insertRecord(table, "A002"); + + try { + run("/oracle/yml/test-insert-direct.yml"); + fail("Exception expected."); + } catch (Exception e) { + System.out.println(e); + } } @Test - public void testInsertDirectCreate() throws Exception + public void testInsertDirectEmpty() throws Exception { - invoke("testInsertDirectCreate"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + new File(convertPath("/oracle/data/"), "test2").mkdir(); + run("/oracle/yml/test-insert-direct-empty.yml"); + + assertTableEmpty(table); } @Test - public void testInsertDirectEmpty() throws Exception + public void testInsertDirectCreate() throws Exception { - invoke(test12c, "testInsertDirectEmpty"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + + run("/oracle/yml/test-insert-direct.yml"); + + assertGeneratedTable1(table); } @Test public void testInsertDirectDirectMethod() throws Exception { - invoke(test12c, "testInsertDirectDirectMethod"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + try { + run("/oracle/yml/test-insert-direct-direct-method.yml"); + } catch (PartialExecutionException e) { + if (e.getCause() != null && e.getCause().getClass().equals(RuntimeException.class) + && e.getCause().getCause() != null && e.getCause().getCause().getClass().equals(AssertionError.class)) { + // ignore error + e.printStackTrace(); + System.err.println("The 'direct' mode works if running embulk directly, but fails if using EmbulkPluginTester."); + return; + } + throw e; + } + + assertTable(table); } @Test public void testInsertDirectOCIMethod() throws Exception { - invoke(test12c, "testInsertDirectOCIMethod"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-insert-direct-oci-method.yml"); + + assertTable(table); } @Test + public void testInsertDirectOCIMethodLarge() throws Exception + { + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-insert-direct-oci-method-large.yml"); + + List<List<Object>> rows = select(table); + assertEquals(9999, rows.size()); + for (int i = 0; i < rows.size(); i++) { + assertEquals(String.format("%04d", i + 1), rows.get(i).get(0)); + } + } + + @Test public void testInsertDirectOCIMethodDuplicate() throws Exception { - invoke(test12c, "testInsertDirectOCIMethodDuplicate"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + insertRecord(table, "A002"); + + try { + run("/oracle/yml/test-insert-direct-oci-method.yml"); + fail("Exception expected."); + } catch (Exception e) { + System.out.println(e); + } } @Test public void testInsertDirectOCIMethodMultibyte() throws Exception { - invoke(test12c, "testInsertDirectOCIMethodMultibyte"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-insert-direct-oci-method-multibyte.yml"); + + assertTable(table); } @Test public void testInsertDirectOCIMethodMultibyteDuplicate() throws Exception { - invoke(test12c, "testInsertDirectOCIMethodMultibyteDuplicate"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + insertRecord(table, "A002"); + + try { + run("/oracle/yml/test-insert-direct-oci-method-multibyte.yml"); + fail("Exception expected."); + } catch (Exception e) { + System.out.println(e); + } } @Test public void testInsertDirectOCIMethodSplit() throws Exception { - invoke(test12c, "testInsertDirectOCIMethodSplit"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-insert-direct-oci-method-split.yml"); + + assertTable(table); } @Test public void testUrl() throws Exception { - invoke(test12c, "testUrl"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-url.yml"); + + assertTable(table); } @Test public void testLowerTable() throws Exception { - invoke(test12c, "testLowerTable"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-lower-table.yml"); + + assertTable(table); } @Test public void testLowerColumn() throws Exception { - invoke(test12c, "testLowerColumn"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-lower-column.yml"); + + assertTable(table); } @Test public void testLowerColumnOptions() throws Exception { - invoke(test12c, "testLowerColumnOptions"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-lower-column-options.yml"); + + assertTable(table); } @Test public void testReplace() throws Exception { - invoke(test12c, "testReplace"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-replace.yml"); + + assertGeneratedTable2(table); } @Test public void testReplaceOCIMethod() throws Exception { - invoke(test12c, "testReplaceOCIMethod"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-replace-oci-method.yml"); + + assertGeneratedTable2(table); } @Test public void testReplaceEmpty() throws Exception { - invoke(test12c, "testReplaceEmpty"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-replace-empty.yml"); + + assertTableEmpty(table); } @Test - public void testReplaceLongName() throws Exception + public void testReplaceCreate() throws Exception { - invoke(test12c, "testReplaceLongName"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + + run("/oracle/yml/test-replace.yml"); + + assertGeneratedTable2(table); } + @Test - public void testReplaceLongNameMultibyte() throws Exception + public void testReplaceLongName() throws Exception { - invoke(test12c, "testReplaceLongNameMultibyte"); + if (!canTest) { + return; + } + + String table = "TEST12345678901234567890123456"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-replace-long-name.yml"); + + assertGeneratedTable2(table); } @Test - public void testReplaceCreate() throws Exception + public void testReplaceLongNameMultibyte() throws Exception { - invoke(test12c, "testReplaceCreate"); + if (!canTest) { + return; + } + + String table = "TEST123456789012345678"; + + run("/oracle/yml/test-replace-long-name-multibyte.yml"); + + assertGeneratedTable2(table); } @Test public void testStringTimestamp() throws Exception { - invoke(test12c, "testStringTimestamp"); + if (!canTest) { + return; + } + + String table = "TEST1"; + + dropTable(table); + createTable(table); + + run("/oracle/yml/test-string-timestamp.yml"); + + assertTable(table); } + + private void createTable(String table) throws SQLException + { + String sql = String.format("CREATE TABLE %s (" + + "ID CHAR(4)," + + "VARCHAR2_ITEM VARCHAR2(6)," + + "NVARCHAR2_ITEM NVARCHAR2(6)," + + "INTEGER_ITEM NUMBER(4,0)," + + "NUMBER_ITEM NUMBER(10,2)," + + "DATE_ITEM DATE," + + "TIMESTAMP_ITEM TIMESTAMP," + + "PRIMARY KEY (ID))", table); + executeSQL(sql); + } + + private void insertRecord(String table) throws SQLException + { + insertRecord(table, "9999"); + } + + private void insertRecord(String table, String id) throws SQLException + { + executeSQL(String.format("INSERT INTO %s VALUES('%s', NULL, NULL, NULL, NULL, NULL, NULL)", table, id)); + } + + private void assertTable(String table) throws Exception + { + // datetime of UTC will be inserted by embulk. + // datetime of default timezone will be selected by JDBC. + TimeZone timeZone = TimeZone.getDefault(); + List<List<Object>> rows = select(table); + + /* + A001,ABCDE,abcde,,0,123.45,2015/03/05,2015/03/05 12:34:56 + A002,AB,abcdef,-9999,-99999999.99,2015/03/06,2015/03/06 23:59:59 + A003,,,,,, + */ + + assertEquals(3, rows.size()); + Iterator<List<Object>> i1 = rows.iterator(); + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A001", i2.next()); + assertEquals("ABCDE", i2.next()); + assertEquals("abcde", i2.next()); + assertEquals(new BigDecimal("0"), i2.next()); + assertEquals(new BigDecimal("123.45"), i2.next()); + assertEquals(toTimestamp("2015/03/05 00:00:00", timeZone), i2.next()); + assertEquals(toOracleTimestamp("2015/03/05 12:34:56", timeZone), i2.next()); + } + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A002", i2.next()); + assertEquals("AB", i2.next()); + assertEquals("abcdef", i2.next()); + assertEquals(new BigDecimal("-9999"), i2.next()); + assertEquals(new BigDecimal("-99999999.99"), i2.next()); + assertEquals(toTimestamp("2015/03/06 00:00:00", timeZone), i2.next()); + assertEquals(toOracleTimestamp("2015/03/06 23:59:59", timeZone), i2.next()); + } + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A003", i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + } + } + + private void assertTableEmpty(String table) throws Exception + { + List<List<Object>> rows = select(table); + assertEquals(0, rows.size()); + } + + private void assertGeneratedTable1(String table) throws Exception + { + // datetime of UTC will be inserted by embulk. + // datetime of default timezone will be selected by JDBC. + TimeZone timeZone = TimeZone.getDefault(); + List<List<Object>> rows = select(table); + + /* + A001,ABCDE,abcde,0,123.45,2015/03/05,2015/03/05 12:34:56 + A002,AB,abcdef,-9999,-99999999.99,2015/03/06,2015/03/06 23:59:59 + A003,,,,,, + */ + + assertEquals(3, rows.size()); + Iterator<List<Object>> i1 = rows.iterator(); + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A001", i2.next()); + assertEquals("ABCDE", i2.next()); + assertEquals("abcde", i2.next()); + assertEquals(new BigDecimal("0"), i2.next()); + assertEquals("123.45", i2.next()); + assertEquals(toOracleTimestamp("2015/03/05 00:00:00", timeZone), i2.next()); + assertEquals(toOracleTimestamp("2015/03/05 12:34:56", timeZone), i2.next()); + } + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A002", i2.next()); + assertEquals("AB", i2.next()); + assertEquals("abcdef", i2.next()); + assertEquals(new BigDecimal("-9999"), i2.next()); + assertEquals("-99999999.99", i2.next()); + assertEquals(toOracleTimestamp("2015/03/06 00:00:00", timeZone), i2.next()); + assertEquals(toOracleTimestamp("2015/03/06 23:59:59", timeZone), i2.next()); + } + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A003", i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + } + } + + private void assertGeneratedTable2(String table) throws Exception + { + // datetime of UTC will be inserted by embulk. + // datetime of default timezone will be selected by JDBC. + TimeZone timeZone = TimeZone.getDefault(); + List<List<Object>> rows = select(table); + + /* + A001,ABCDE,abcde,0,123.45,2015/03/05,2015/03/05 12:34:56 + A002,AB,abcdef,-9999,-99999999.99,2015/03/06,2015/03/06 23:59:59 + A003,,,,,, + */ + + assertEquals(3, rows.size()); + Iterator<List<Object>> i1 = rows.iterator(); + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A001", i2.next()); + assertEquals("ABCDE", i2.next()); + assertEquals("abcde", i2.next()); + assertEquals(new BigDecimal("0"), i2.next()); + assertEquals(new BigDecimal("123.45"), i2.next()); + assertEquals(toTimestamp("2015/03/05 00:00:00", timeZone), i2.next()); + assertEquals(toOracleTimestamp("2015/03/05 12:34:56", timeZone), i2.next()); + } + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A002", i2.next()); + assertEquals("AB", i2.next()); + assertEquals("abcdef", i2.next()); + assertEquals(new BigDecimal("-9999"), i2.next()); + assertEquals(new BigDecimal("-99999999.99"), i2.next()); + assertEquals(toTimestamp("2015/03/06 00:00:00", timeZone), i2.next()); + assertEquals(toOracleTimestamp("2015/03/06 23:59:59", timeZone), i2.next()); + } + { + Iterator<Object> i2 = i1.next().iterator(); + assertEquals("A003", i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + assertEquals(null, i2.next()); + } + } + + @Override + protected Object getValue(ResultSet resultSet, int index) throws SQLException + { + if (resultSet.getMetaData().getColumnTypeName(index).equals("CLOB")) { + return resultSet.getString(index); + } + return super.getValue(resultSet, index); + } + + + private Timestamp toTimestamp(String s, TimeZone timeZone) + { + for (String formatString : new String[]{"yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd"}) { + DateFormat dateFormat = new SimpleDateFormat(formatString); + dateFormat.setTimeZone(timeZone); + try { + Date date = dateFormat.parse(s); + return new Timestamp(date.getTime()); + } catch (ParseException e) { + // NOP + } + } + throw new IllegalArgumentException(s); + } + + private Object toOracleTimestamp(String s, TimeZone timeZone) throws Exception + { + Class<?> timestampClass = Class.forName("oracle.sql.TIMESTAMP"); + Constructor<?> constructor = timestampClass.getConstructor(Timestamp.class); + return constructor.newInstance(toTimestamp(s, timeZone)); + } + + @Override + protected Connection connect() throws SQLException + { + return DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:TESTDB", "TEST_USER", "test_pw"); + } + + private void run(String ymlName) throws Exception + { + tester.run(convertYml(ymlName)); + } + }