embulk-output-oracle 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -3
  3. data/build.gradle +3 -0
  4. data/classpath/{embulk-output-jdbc-0.2.1.jar → embulk-output-jdbc-0.2.2.jar} +0 -0
  5. data/classpath/embulk-output-oracle-0.2.2.jar +0 -0
  6. data/lib/embulk/linux_x64/libembulk-output-oracle.so +0 -0
  7. data/lib/embulk/win_x64/embulk-output-oracle.dll +0 -0
  8. data/src/main/cpp/common/dir-path-load.cpp +424 -0
  9. data/src/main/cpp/common/dir-path-load.h +36 -0
  10. data/src/main/cpp/common/embulk-output-oracle.cpp +196 -0
  11. data/src/main/cpp/common/org_embulk_output_oracle_oci_OCI.h +77 -0
  12. data/src/main/cpp/linux/build.sh +21 -0
  13. data/src/main/cpp/win/build.bat +32 -0
  14. data/src/main/cpp/win/dllmain.cpp +25 -0
  15. data/src/main/cpp/win/embulk-output-oracle.sln +39 -0
  16. data/src/main/cpp/win/embulk-output-oracle.vcxproj +176 -0
  17. data/src/main/java/org/embulk/output/OracleOutputPlugin.java +51 -10
  18. data/src/main/java/org/embulk/output/oracle/DirectBatchInsert.java +289 -0
  19. data/src/main/java/org/embulk/output/oracle/InsertMethod.java +8 -0
  20. data/src/main/java/org/embulk/output/oracle/OracleCharset.java +19 -0
  21. data/src/main/java/org/embulk/output/oracle/OracleOutputConnection.java +60 -1
  22. data/src/main/java/org/embulk/output/oracle/OracleOutputConnector.java +7 -5
  23. data/src/main/java/org/embulk/output/oracle/TimestampFormat.java +37 -0
  24. data/src/main/java/org/embulk/output/oracle/oci/ColumnDefinition.java +26 -0
  25. data/src/main/java/org/embulk/output/oracle/oci/OCI.java +139 -0
  26. data/src/main/java/org/embulk/output/oracle/oci/OCIManager.java +64 -0
  27. data/src/main/java/org/embulk/output/oracle/oci/OCIWrapper.java +96 -0
  28. data/src/main/java/org/embulk/output/oracle/oci/RowBuffer.java +99 -0
  29. data/src/main/java/org/embulk/output/oracle/oci/TableDefinition.java +24 -0
  30. data/src/main/java/org/embulk/output/oracle/setter/OracleColumnSetterFactory.java +14 -14
  31. data/src/test/cpp/common/embulk-output-oracle-test.cpp +69 -0
  32. data/src/test/cpp/linux/build.sh +19 -0
  33. data/src/test/cpp/win/build.bat +29 -0
  34. data/src/test/cpp/win/embulk-output-oracle-test.vcxproj +155 -0
  35. data/src/test/java/org/embulk/input/filesplit/LocalFileSplitInputPlugin.java +187 -0
  36. data/src/test/java/org/embulk/input/filesplit/PartialFile.java +50 -0
  37. data/src/test/java/org/embulk/input/filesplit/PartialFileInputStream.java +154 -0
  38. data/src/test/java/org/embulk/output/oracle/ChildFirstClassLoader.java +42 -0
  39. data/src/test/java/org/embulk/output/oracle/EmbulkPluginTester.java +120 -0
  40. data/src/test/java/org/embulk/output/oracle/EmptyConfigSource.java +100 -0
  41. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTest.java +161 -0
  42. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTestImpl.java +413 -0
  43. data/src/test/java/org/embulk/output/oracle/TimestampFormatTest.java +57 -0
  44. data/src/test/resources/data/test1/test1.csv +3 -0
  45. data/src/test/resources/yml/test-insert-direct.yml +26 -0
  46. data/src/test/resources/yml/test-insert-oci-split.yml +26 -0
  47. data/src/test/resources/yml/test-insert-oci.yml +26 -0
  48. data/src/test/resources/yml/test-insert.yml +25 -0
  49. data/src/test/resources/yml/test-replace-long-name.yml +25 -0
  50. data/src/test/resources/yml/test-replace.yml +25 -0
  51. data/src/test/resources/yml/test-url.yml +24 -0
  52. metadata +46 -4
  53. data/classpath/embulk-output-oracle-0.2.1.jar +0 -0
@@ -12,16 +12,18 @@ public class OracleOutputConnector
12
12
  {
13
13
  private final String url;
14
14
  private final Properties properties;
15
+ private final boolean direct;
15
16
 
16
- public OracleOutputConnector(String url, Properties properties)
17
+ public OracleOutputConnector(String url, Properties properties, boolean direct)
17
18
  {
18
19
  try {
19
- Class.forName("oracle.jdbc.OracleDriver");
20
+ Class.forName("oracle.jdbc.OracleDriver");
20
21
  } catch (Exception ex) {
21
22
  throw new RuntimeException(ex);
22
23
  }
23
24
  this.url = url;
24
25
  this.properties = properties;
26
+ this.direct = direct;
25
27
  }
26
28
 
27
29
  @Override
@@ -29,12 +31,12 @@ public class OracleOutputConnector
29
31
  {
30
32
  Connection c = DriverManager.getConnection(url, properties);
31
33
  if (c == null) {
32
- // driver.connect returns null when url is "jdbc:mysql://...".
33
- throw new SQLException("Invalid url : " + url);
34
+ // driver.connect returns null when url is "jdbc:mysql://...".
35
+ throw new SQLException("Invalid url : " + url);
34
36
  }
35
37
 
36
38
  try {
37
- OracleOutputConnection con = new OracleOutputConnection(c, autoCommit);
39
+ OracleOutputConnection con = new OracleOutputConnection(c, autoCommit, direct);
38
40
  c = null;
39
41
  return con;
40
42
 
@@ -0,0 +1,37 @@
1
+ package org.embulk.output.oracle;
2
+
3
+ import java.sql.Timestamp;
4
+ import java.text.FieldPosition;
5
+ import java.text.SimpleDateFormat;
6
+ import java.util.Date;
7
+
8
+
9
+ public class TimestampFormat extends SimpleDateFormat
10
+ {
11
+
12
+ private final int scale;
13
+
14
+ public TimestampFormat(String pattern, int scale)
15
+ {
16
+ super(pattern);
17
+
18
+ this.scale = scale;
19
+ }
20
+
21
+ @Override
22
+ public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos)
23
+ {
24
+ StringBuffer buffer = super.format(date, toAppendTo, pos);
25
+ if (scale > 0) {
26
+ buffer.append('.');
27
+ String nanos = Integer.toString(((Timestamp)date).getNanos());
28
+ int zeros = Math.min(scale, 9 - nanos.length());
29
+ for (int i = 0; i < zeros; i++) {
30
+ buffer.append('0');
31
+ }
32
+ buffer.append(nanos.substring(0, scale - zeros));
33
+ }
34
+ return buffer;
35
+ }
36
+
37
+ }
@@ -0,0 +1,26 @@
1
+ package org.embulk.output.oracle.oci;
2
+
3
+ public class ColumnDefinition
4
+ {
5
+ public static int SQLT_CHR = 1;
6
+ public static int SQLT_INT = 3;
7
+
8
+ public final String columnName;
9
+ public final int columnType;
10
+ public final int columnSize;
11
+ public final String columnDateFormat;
12
+
13
+
14
+ public ColumnDefinition(String columnName, int columnType, int columnSize, String columnDateFormat)
15
+ {
16
+ this.columnName = columnName;
17
+ this.columnType = columnType;
18
+ this.columnSize = columnSize;
19
+ this.columnDateFormat = columnDateFormat;
20
+ }
21
+
22
+ public ColumnDefinition(String columnName, int columnType, int columnSize)
23
+ {
24
+ this(columnName, columnType, columnSize, null);
25
+ }
26
+ }
@@ -0,0 +1,139 @@
1
+ package org.embulk.output.oracle.oci;
2
+
3
+ import java.io.File;
4
+ import java.io.IOException;
5
+ import java.net.MalformedURLException;
6
+ import java.net.URISyntaxException;
7
+ import java.net.URL;
8
+
9
+ import org.embulk.spi.Exec;
10
+ import org.slf4j.Logger;
11
+
12
+ import com.google.common.io.Files;
13
+
14
+
15
+ public class OCI
16
+ {
17
+ private static final Logger logger = Exec.getLogger(OCI.class);
18
+
19
+ private static final String PLUGIN_NAME = "embulk-output-oracle";
20
+
21
+ static {
22
+ try {
23
+ loadLibrary();
24
+ } catch (URISyntaxException | IOException e) {
25
+ throw new RuntimeException(e);
26
+ }
27
+ }
28
+
29
+ public native byte[] createContext();
30
+
31
+ public native byte[] getLasetMessage(byte[] context);
32
+
33
+ public native boolean open(byte[] context, String dbName, String userName, String password);
34
+
35
+ public native boolean prepareLoad(byte[] context, TableDefinition tableDefinition);
36
+
37
+ public native boolean loadBuffer(byte[] context, byte[] buffer, int rowCount);
38
+
39
+ public native boolean commit(byte[] context);
40
+
41
+ public native boolean rollback(byte[] context);
42
+
43
+ public native void close(byte[] context);
44
+
45
+ private static void loadLibrary() throws URISyntaxException, IOException
46
+ {
47
+ loadLibrary(getPluginRoot());
48
+ }
49
+
50
+ private static File getPluginRoot() throws MalformedURLException, URISyntaxException
51
+ {
52
+ URL url = OCI.class.getResource("/" + OCI.class.getName().replace('.', '/') + ".class");
53
+ if (url.toString().startsWith("jar:")) {
54
+ url = new URL(url.toString().replaceAll("^jar:", "").replaceAll("![^!]*$", ""));
55
+ }
56
+
57
+ File folder = new File(url.toURI()).getParentFile();
58
+ for (;; folder = folder.getParentFile()) {
59
+ if (folder == null) {
60
+ String message = String.format("OCI : %s folder not found.", PLUGIN_NAME);
61
+ throw new RuntimeException(message);
62
+ }
63
+
64
+ if (folder.getName().startsWith(PLUGIN_NAME)) {
65
+ return folder;
66
+ }
67
+ }
68
+ }
69
+
70
+ private static void loadLibrary(File folder) throws IOException
71
+ {
72
+ File lib = new File(new File(folder, "lib"), "embulk");
73
+
74
+ String osName = System.getProperty("os.name");
75
+ String osArch = System.getProperty("os.arch");
76
+
77
+ String libraryName = System.mapLibraryName(PLUGIN_NAME);
78
+ File libFolder = null;
79
+
80
+ if (osName.startsWith("Windows")) {
81
+ if (osArch.endsWith("64")) {
82
+ libFolder = new File(lib, "win_x64");
83
+ } else if (osArch.equals("x86")) {
84
+ libFolder = new File(lib, "win_x86");
85
+ }
86
+ } else if (osName.equals("Linux")) {
87
+ if (osArch.endsWith("64")) {
88
+ libFolder = new File(lib, "linux_x64");
89
+ } else if (osArch.equals("x86")) {
90
+ libFolder = new File(lib, "linux_x86");
91
+ }
92
+ }
93
+
94
+ if (libFolder == null) {
95
+ logger.error(String.format("OCI : library \"%s\" for %s %s doesn't exist in lib folder.", libraryName, osName, osArch));
96
+ return;
97
+ }
98
+
99
+ File libFile = new File(libFolder, libraryName);
100
+ if (!libFile.exists()) {
101
+ logger.error(String.format("OCI : library \"%s\" doesn't exist.", libFile.getAbsolutePath()));
102
+ return;
103
+ }
104
+
105
+ logger.info(String.format("OCI : load library \"%s\".", libFile.getAbsolutePath()));
106
+
107
+ File tempFolder = new File(lib, "temp");
108
+ tempFolder.mkdirs();
109
+
110
+ long currentTime = System.currentTimeMillis();
111
+ File[] files = tempFolder.listFiles();
112
+ if (files != null) {
113
+ for (File file : files) {
114
+ // delete old temporary files.
115
+ // if another plugin is using a file, it cannot be deleted.
116
+ // don't delete a recent file because another plugin may intend to use it.
117
+ if (file.isFile() && file.getName().startsWith(PLUGIN_NAME) && file.lastModified() < currentTime - 60000) {
118
+ file.delete();
119
+ }
120
+ }
121
+ }
122
+
123
+ String extension = libraryName.replaceAll("^[^\\.]*", "");
124
+ for (int i = 0; i < 10; i++) {
125
+ File tempLibFile = new File(tempFolder, PLUGIN_NAME + "-" + currentTime + "-" + i + extension);
126
+ if (tempLibFile.createNewFile()) {
127
+ // copy and load the library because different plugins cannot load the same library.
128
+ logger.info(String.format("OCI : create temporary library \"%s\".", tempLibFile.getAbsolutePath()));
129
+ Files.copy(libFile, tempLibFile);
130
+ System.load(tempLibFile.getAbsolutePath());
131
+ tempLibFile.deleteOnExit();
132
+ // but may not be deleted because loaded as a library.
133
+ return;
134
+ }
135
+ }
136
+
137
+ logger.error("OCI : cannot create temporary library.");
138
+ }
139
+ }
@@ -0,0 +1,64 @@
1
+ package org.embulk.output.oracle.oci;
2
+
3
+ import java.sql.SQLException;
4
+ import java.util.HashMap;
5
+ import java.util.Map;
6
+
7
+ import org.embulk.spi.Exec;
8
+ import org.slf4j.Logger;
9
+
10
+ public class OCIManager
11
+ {
12
+ private static class OCIWrapperAndCounter {
13
+ public OCIWrapper oci;
14
+ public int counter;
15
+ }
16
+
17
+
18
+ private final Logger logger = Exec.getLogger(getClass());
19
+
20
+ private Map<Object, OCIWrapperAndCounter> ociAndCounters = new HashMap<Object, OCIWrapperAndCounter>();
21
+
22
+
23
+ public OCIWrapper open(Object key, String dbName, String userName, String password, TableDefinition tableDefinition) throws SQLException
24
+ {
25
+ synchronized(ociAndCounters) {
26
+ OCIWrapperAndCounter ociAndCounter;
27
+ if (ociAndCounters.containsKey(key)) {
28
+ ociAndCounter = ociAndCounters.get(key);
29
+ } else {
30
+ logger.info(String.format("OCI : open for %s.", key));
31
+ ociAndCounter = new OCIWrapperAndCounter();
32
+ ociAndCounter.oci = new OCIWrapper();
33
+ ociAndCounter.oci.open(dbName, userName, password);
34
+ ociAndCounter.oci.prepareLoad(tableDefinition);
35
+ ociAndCounters.put(key, ociAndCounter);
36
+ }
37
+ ociAndCounter.counter++;
38
+ return ociAndCounter.oci;
39
+ }
40
+ }
41
+
42
+ public OCIWrapper get(Object key)
43
+ {
44
+ synchronized(ociAndCounters) {
45
+ return ociAndCounters.get(key).oci;
46
+ }
47
+ }
48
+
49
+ public void close(Object key) throws SQLException
50
+ {
51
+ synchronized(ociAndCounters) {
52
+ OCIWrapperAndCounter ociAndCounter = ociAndCounters.get(key);
53
+ if (ociAndCounter != null) {
54
+ ociAndCounter.counter--;
55
+ if (ociAndCounter.counter == 0) {
56
+ logger.info(String.format("OCI : close for %s.", key));
57
+ ociAndCounter.oci.close();
58
+ ociAndCounters.remove(key);
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ }
@@ -0,0 +1,96 @@
1
+ package org.embulk.output.oracle.oci;
2
+
3
+ import java.nio.charset.Charset;
4
+ import java.sql.SQLException;
5
+
6
+ import org.embulk.spi.Exec;
7
+ import org.slf4j.Logger;
8
+
9
+
10
+ public class OCIWrapper implements AutoCloseable
11
+ {
12
+ private final Logger logger = Exec.getLogger(getClass());
13
+
14
+ private final OCI oci = new OCI();
15
+ // used for messages
16
+ private final Charset defaultCharset;
17
+ private byte[] context;
18
+ private boolean errorOccured;
19
+ private boolean committedOrRollbacked;
20
+
21
+
22
+ public OCIWrapper()
23
+ {
24
+ // enable to change default encoding for test
25
+ defaultCharset = Charset.forName(System.getProperty("file.encoding"));
26
+ context = oci.createContext();
27
+ }
28
+
29
+ public void open(String dbName, String userName, String password) throws SQLException
30
+ {
31
+ if (!oci.open(context, dbName, userName, password)) {
32
+ throwException();
33
+ }
34
+ }
35
+
36
+ public void prepareLoad(TableDefinition tableDefinition) throws SQLException
37
+ {
38
+ if (!oci.prepareLoad(context, tableDefinition)) {
39
+ throwException();
40
+ }
41
+ }
42
+
43
+ public void loadBuffer(byte[] buffer, int rowCount) throws SQLException
44
+ {
45
+ if (!oci.loadBuffer(context, buffer, rowCount)) {
46
+ throwException();
47
+ }
48
+ }
49
+
50
+ public void commit() throws SQLException
51
+ {
52
+ committedOrRollbacked = true;
53
+ logger.info("OCI : start to commit.");
54
+ if (!oci.commit(context)) {
55
+ throwException();
56
+ }
57
+ }
58
+
59
+ public void rollback() throws SQLException
60
+ {
61
+ committedOrRollbacked = true;
62
+ logger.info("OCI : start to rollback.");
63
+ if (!oci.rollback(context)) {
64
+ throwException();
65
+ }
66
+ }
67
+
68
+ private void throwException() throws SQLException
69
+ {
70
+ errorOccured = true;
71
+ String message = new String(oci.getLasetMessage(context), defaultCharset);
72
+ logger.error(message);
73
+ throw new SQLException(message);
74
+ }
75
+
76
+
77
+ @Override
78
+ public void close() throws SQLException
79
+ {
80
+ if (context != null) {
81
+ try {
82
+ if (!committedOrRollbacked) {
83
+ if (errorOccured) {
84
+ rollback();
85
+ } else {
86
+ commit();
87
+ }
88
+ }
89
+ } finally {
90
+ oci.close(context);
91
+ context = null;
92
+ }
93
+ }
94
+ }
95
+
96
+ }
@@ -0,0 +1,99 @@
1
+ package org.embulk.output.oracle.oci;
2
+
3
+ import java.nio.ByteBuffer;
4
+ import java.nio.charset.Charset;
5
+
6
+ public class RowBuffer
7
+ {
8
+ private final TableDefinition table;
9
+ private final int rowCount;
10
+ private final byte[] buffer;
11
+ private int currentRow = 0;
12
+ private int currentColumn = 0;
13
+ private int currentPosition = 0;
14
+ private final Charset charset;
15
+
16
+
17
+ public RowBuffer(TableDefinition table, int rowCount, Charset charset)
18
+ {
19
+ this.table = table;
20
+ this.rowCount = rowCount;
21
+ this.charset = charset;
22
+
23
+ int rowSize = 0;
24
+ for (ColumnDefinition column : table.columns) {
25
+ rowSize += column.columnSize;
26
+ }
27
+
28
+ buffer = new byte[rowSize * rowCount];
29
+ }
30
+
31
+ public void addValue(int value)
32
+ {
33
+ if (isFull()) {
34
+ throw new IllegalStateException();
35
+ }
36
+
37
+ buffer[currentPosition] = (byte)value;
38
+ buffer[currentPosition + 1] = (byte)(value >> 8);
39
+ buffer[currentPosition + 2] = (byte)(value >> 16);
40
+ buffer[currentPosition + 3] = (byte)(value >> 24);
41
+
42
+ next();
43
+ }
44
+
45
+ public void addValue(String value)
46
+ {
47
+ if (isFull()) {
48
+ throw new IllegalStateException();
49
+ }
50
+
51
+ ByteBuffer bytes = charset.encode(value);
52
+ int length = bytes.remaining();
53
+ // TODO:warning or error if truncated
54
+ bytes.get(buffer, currentPosition, length);
55
+ if (length < table.columns[currentColumn].columnSize) {
56
+ buffer[currentPosition + length] = 0;
57
+ }
58
+
59
+ next();
60
+ }
61
+
62
+ private void next()
63
+ {
64
+ currentPosition += table.columns[currentColumn].columnSize;
65
+ currentColumn++;
66
+ if (currentColumn == table.columns.length) {
67
+ currentColumn = 0;
68
+ currentRow++;
69
+ }
70
+ }
71
+
72
+ public byte[] getBuffer()
73
+ {
74
+ return buffer;
75
+ }
76
+
77
+ public int getCurrentColumn()
78
+ {
79
+ return currentColumn;
80
+ }
81
+
82
+ public int getRowCount()
83
+ {
84
+ return currentRow;
85
+ }
86
+
87
+ public boolean isFull()
88
+ {
89
+ return currentRow >= rowCount;
90
+ }
91
+
92
+ public void clear()
93
+ {
94
+ currentPosition = 0;
95
+ currentRow = 0;
96
+ currentColumn = 0;
97
+ }
98
+
99
+ }