embulk-output-oracle 0.6.3 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +43 -0
  3. data/build.gradle +2 -2
  4. data/classpath/{embulk-output-jdbc-0.6.3.jar → embulk-output-jdbc-0.6.4.jar} +0 -0
  5. data/classpath/embulk-output-oracle-0.6.4.jar +0 -0
  6. data/lib/embulk/native/x86_64-linux/libembulk-output-oracle-oci.so +0 -0
  7. data/lib/embulk/native/x86_64-windows/embulk-output-oracle-oci.dll +0 -0
  8. data/src/main/cpp/common/embulk-output-oracle-oci.cpp +27 -0
  9. data/src/main/cpp/linux/build.sh +15 -0
  10. data/src/main/cpp/windows/build.bat +26 -0
  11. data/src/main/cpp/windows/dllmain.cpp +25 -0
  12. data/src/main/cpp/windows/embulk-output-oracle-oci.sln +20 -0
  13. data/src/main/cpp/windows/embulk-output-oracle-oci.vcxproj +171 -0
  14. data/src/main/java/org/embulk/output/oracle/DirectBatchInsert.java +5 -39
  15. data/src/main/java/org/embulk/output/oracle/oci/BulkOCI.java +15 -0
  16. data/src/main/java/org/embulk/output/oracle/oci/OCI.java +1 -0
  17. data/src/main/java/org/embulk/output/oracle/oci/OCIManager.java +3 -2
  18. data/src/main/java/org/embulk/output/oracle/oci/OCIWrapper.java +103 -37
  19. data/src/main/java/org/embulk/output/oracle/oci/PrimitiveBulkOCI.java +47 -0
  20. data/src/main/java/org/embulk/output/oracle/oci/RowBuffer.java +50 -46
  21. data/src/main/java/org/embulk/output/oracle/oci/TableDefinition.java +9 -0
  22. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTest.java +622 -133
  23. data/src/test/resources/oracle/data/test3/test3.csv +9999 -0
  24. data/src/test/resources/oracle/yml/test-insert-direct-oci-method-large.yml +29 -0
  25. metadata +19 -8
  26. data/classpath/embulk-output-oracle-0.6.3.jar +0 -0
  27. data/src/test/java/org/embulk/output/oracle/OracleOutputPluginTestImpl.java +0 -620
@@ -0,0 +1,15 @@
1
+ package org.embulk.output.oracle.oci;
2
+
3
+ import jnr.ffi.Pointer;
4
+ import jnr.ffi.types.u_int16_t;
5
+ import jnr.ffi.types.u_int32_t;
6
+
7
+ public interface BulkOCI {
8
+
9
+ short embulk_output_oracle_OCIDirPathColArrayEntriesSet(Pointer dpca,
10
+ Pointer errhp,
11
+ @u_int16_t short columnCount,
12
+ @u_int32_t int rowCount,
13
+ Pointer data,
14
+ Pointer sizes);
15
+ }
@@ -32,6 +32,7 @@ public interface OCI
32
32
  static int OCI_ATTR_SCHEMA_NAME = 9;
33
33
  static int OCI_ATTR_CHARSET_ID = 31;
34
34
  static int OCI_ATTR_DATEFORMAT = 75;
35
+ static int OCI_ATTR_BUF_SIZE = 77;
35
36
  static int OCI_ATTR_NUM_ROWS = 81;
36
37
  static int OCI_ATTR_NUM_COLS = 102;
37
38
  static int OCI_ATTR_LIST_COLUMNS = 103;
@@ -20,7 +20,8 @@ public class OCIManager
20
20
  private Map<Object, OCIWrapperAndCounter> ociAndCounters = new HashMap<Object, OCIWrapperAndCounter>();
21
21
 
22
22
 
23
- public OCIWrapper open(Object key, String dbName, String userName, String password, TableDefinition tableDefinition) throws SQLException
23
+ public OCIWrapper open(Object key, String dbName, String userName, String password, TableDefinition tableDefinition, int bufferSize)
24
+ throws SQLException
24
25
  {
25
26
  synchronized(ociAndCounters) {
26
27
  OCIWrapperAndCounter ociAndCounter;
@@ -31,7 +32,7 @@ public class OCIManager
31
32
  ociAndCounter = new OCIWrapperAndCounter();
32
33
  ociAndCounter.oci = new OCIWrapper();
33
34
  ociAndCounter.oci.open(dbName, userName, password);
34
- ociAndCounter.oci.prepareLoad(tableDefinition);
35
+ ociAndCounter.oci.prepareLoad(tableDefinition, bufferSize);
35
36
  ociAndCounters.put(key, ociAndCounter);
36
37
  }
37
38
  ociAndCounter.counter++;
@@ -1,13 +1,17 @@
1
1
  package org.embulk.output.oracle.oci;
2
2
 
3
+ import java.io.File;
4
+ import java.net.MalformedURLException;
5
+ import java.net.URISyntaxException;
6
+ import java.net.URL;
3
7
  import java.nio.ByteBuffer;
4
8
  import java.nio.charset.Charset;
5
9
  import java.sql.SQLException;
6
10
 
7
11
  import jnr.ffi.LibraryLoader;
12
+ import jnr.ffi.Platform;
8
13
  import jnr.ffi.Pointer;
9
14
  import jnr.ffi.Runtime;
10
- import jnr.ffi.provider.BoundedMemoryIO;
11
15
  import jnr.ffi.provider.jffi.ArrayMemoryIO;
12
16
  import jnr.ffi.provider.jffi.ByteBufferMemoryIO;
13
17
 
@@ -17,7 +21,10 @@ import org.slf4j.Logger;
17
21
 
18
22
  public class OCIWrapper
19
23
  {
24
+ private static final String PLUGIN_NAME = "embulk-output-oracle";
25
+
20
26
  private static OCI oci;
27
+ private static BulkOCI bulkOci;
21
28
 
22
29
  private final Logger logger = Exec.getLogger(getClass());
23
30
 
@@ -31,6 +38,8 @@ public class OCIWrapper
31
38
 
32
39
  private TableDefinition tableDefinition;
33
40
  private int maxRowCount;
41
+ private long totalRows;
42
+ private int loadCount;
34
43
 
35
44
  private boolean errorOccured;
36
45
  private boolean committedOrRollbacked;
@@ -43,14 +52,17 @@ public class OCIWrapper
43
52
 
44
53
  synchronized (OCIWrapper.class) {
45
54
  if (oci == null) {
46
- oci = loadLibrary();
55
+ oci = loadOCILibrary();
56
+ }
57
+ if (bulkOci == null) {
58
+ bulkOci = loadBulkOCILibrary(oci);
47
59
  }
48
60
  }
49
61
  }
50
62
 
51
- private OCI loadLibrary()
63
+ private OCI loadOCILibrary()
52
64
  {
53
- logger.info("Loading OCI library.");
65
+ logger.info("OCI : Loading OCI library.");
54
66
 
55
67
  // "oci" for Windows, "clntsh" for Linux
56
68
  StringBuilder libraryNames = new StringBuilder();
@@ -69,12 +81,57 @@ public class OCIWrapper
69
81
  throw new UnsatisfiedLinkError("Cannot find library: " + libraryNames);
70
82
  }
71
83
 
84
+ private BulkOCI loadBulkOCILibrary(OCI oci)
85
+ {
86
+ String libraryName = "embulk-output-oracle-oci";
87
+ logger.info("OCI : Loading " + libraryName + " library.");
88
+
89
+ Platform platform = Platform.getNativePlatform();
90
+
91
+ File folder = getPluginRoot();
92
+ folder = new File(new File(new File(new File(folder ,"lib"), "embulk"), "native"), platform.getName());
93
+
94
+ File file = new File(folder, System.mapLibraryName(libraryName));
95
+ if (!file.exists()) {
96
+ logger.info("OCI : Library '" + file.getAbsolutePath() + "' doesn't exist, so Java implementation is used instead.");
97
+ return new PrimitiveBulkOCI(oci);
98
+ }
99
+
100
+ logger.info("OCI : Library '" + file.getAbsolutePath() + "' is found.");
101
+ return LibraryLoader.create(BulkOCI.class).search(folder.getAbsolutePath()).failImmediately().load(libraryName);
102
+ }
103
+
104
+ private File getPluginRoot()
105
+ {
106
+ try {
107
+ URL url = getClass().getResource("/" + getClass().getName().replace('.', '/') + ".class");
108
+ if (url.toString().startsWith("jar:")) {
109
+ url = new URL(url.toString().replaceAll("^jar:", "").replaceAll("![^!]*$", ""));
110
+ }
111
+
112
+ File folder = new File(url.toURI()).getParentFile();
113
+ for (;; folder = folder.getParentFile()) {
114
+ if (folder == null) {
115
+ String message = String.format("OCI : %s folder not found.", PLUGIN_NAME);
116
+ throw new RuntimeException(message);
117
+ }
118
+
119
+ if (folder.getName().startsWith(PLUGIN_NAME)) {
120
+ return folder;
121
+ }
122
+ }
123
+ } catch (MalformedURLException | URISyntaxException e) {
124
+ throw new RuntimeException(e);
125
+ }
126
+ }
127
+
72
128
  public void open(String dbName, String userName, String password) throws SQLException
73
129
  {
74
130
  Pointer envHandlePointer = createPointerPointer();
131
+ // OCI_THREADED is not needed because synchronized in Java side.
75
132
  check("OCIEnvCreate", oci.OCIEnvCreate(
76
133
  envHandlePointer,
77
- OCI.OCI_THREADED | OCI.OCI_OBJECT,
134
+ /*OCI.OCI_THREADED |*/ OCI.OCI_OBJECT,
78
135
  null,
79
136
  null,
80
137
  null,
@@ -126,10 +183,27 @@ public class OCIWrapper
126
183
  dpHandle = dpHandlePointer.getPointer(0);
127
184
  }
128
185
 
129
- public void prepareLoad(TableDefinition tableDefinition) throws SQLException
186
+ public void prepareLoad(TableDefinition tableDefinition, int bufferSize) throws SQLException
130
187
  {
131
188
  this.tableDefinition = tableDefinition;
132
189
 
190
+ check("OCIAttrSet(OCI_ATTR_BUF_SIZE)", oci.OCIAttrSet(
191
+ dpHandle,
192
+ OCI.OCI_HTYPE_DIRPATH_CTX,
193
+ createPointer(bufferSize),
194
+ 4,
195
+ OCI.OCI_ATTR_BUF_SIZE,
196
+ errHandle));
197
+
198
+ int numRows = Math.max(bufferSize / tableDefinition.getRowSize(), 16);
199
+ check("OCIAttrSet(OCI_ATTR_NUM_ROWS)", oci.OCIAttrSet(
200
+ dpHandle,
201
+ OCI.OCI_HTYPE_DIRPATH_CTX,
202
+ createPointer(numRows),
203
+ 4,
204
+ OCI.OCI_ATTR_NUM_ROWS,
205
+ errHandle));
206
+
133
207
  if (tableDefinition.getSchemaName() != null) {
134
208
  Pointer schemaName = createPointer(tableDefinition.getSchemaName());
135
209
  check("OCIAttrSet(OCI_ATTR_NAME)", oci.OCIAttrSet(
@@ -269,7 +343,6 @@ public class OCIWrapper
269
343
  dpstrHandle = dpstrHandlePointer.getPointer(0);
270
344
 
271
345
  Pointer maxRowCountPointer = createPointer(0);
272
-
273
346
  check("OCIAttrGet(OCI_ATTR_NUM_ROWS)", oci.OCIAttrGet(
274
347
  dpcaHandle,
275
348
  OCI.OCI_HTYPE_DIRPATH_COLUMN_ARRAY,
@@ -278,42 +351,31 @@ public class OCIWrapper
278
351
  OCI.OCI_ATTR_NUM_ROWS,
279
352
  errHandle));
280
353
  maxRowCount = maxRowCountPointer.getInt(0);
354
+ logger.info(String.format("OCI : DirectPathColumnArray.numRows = %,d", maxRowCount));
281
355
  }
282
356
 
283
- public void loadBuffer(RowBuffer rowBuffer) throws SQLException
284
- {
285
- Pointer pointer = new ByteBufferMemoryIO(Runtime.getSystemRuntime(), rowBuffer.getBuffer());
286
- short[] sizes = rowBuffer.getSizes();
357
+ public int getMaxRowCount() {
358
+ return maxRowCount;
359
+ }
287
360
 
288
- int i = 0;
289
- int position = 0;
290
- int rowCount = 0;
291
- for (int row = 0; row < rowBuffer.getRowCount(); row++) {
292
- for (short col = 0; col < tableDefinition.getColumnCount(); col++) {
293
- short size = sizes[i++];
361
+ public void loadBuffer(ByteBuffer buffer, ByteBuffer sizes, int rowCount) throws SQLException
362
+ {
363
+ logger.info(String.format("Loading %,d rows", rowCount));
364
+ long startTime = System.currentTimeMillis();
294
365
 
295
- check("OCIDirPathColArrayEntrySet", oci.OCIDirPathColArrayEntrySet(
296
- dpcaHandle,
297
- errHandle,
298
- rowCount,
299
- col,
300
- new BoundedMemoryIO(pointer, position, size),
301
- size,
302
- OCI.OCI_DIRPATH_COL_COMPLETE));
303
-
304
- position += size;
305
- }
366
+ check("OCIDirPathColArrayEntriesSet", bulkOci.embulk_output_oracle_OCIDirPathColArrayEntriesSet(
367
+ dpcaHandle,
368
+ errHandle,
369
+ (short)tableDefinition.getColumnCount(),
370
+ rowCount,
371
+ new ByteBufferMemoryIO(Runtime.getSystemRuntime(), buffer),
372
+ new ByteBufferMemoryIO(Runtime.getSystemRuntime(), sizes)));
306
373
 
307
- rowCount++;
308
- if (rowCount == maxRowCount) {
309
- loadRows(rowCount);
310
- rowCount = 0;
311
- }
312
- }
374
+ loadRows(rowCount);
313
375
 
314
- if (rowCount > 0) {
315
- loadRows(rowCount);
316
- }
376
+ totalRows += rowCount;
377
+ double seconds = (System.currentTimeMillis() - startTime) / 1000.0;
378
+ logger.info(String.format("> %.2f seconds (loaded %,d rows in total)", seconds, totalRows));
317
379
  }
318
380
 
319
381
  private void loadRows(int rowCount) throws SQLException
@@ -334,6 +396,7 @@ public class OCIWrapper
334
396
  check("OCIDirPathColArrayToStream", result);
335
397
  }
336
398
 
399
+ loadCount++;
337
400
  check("OCIDirPathLoadStream", oci.OCIDirPathLoadStream(
338
401
  dpHandle,
339
402
  dpstrHandle,
@@ -358,6 +421,9 @@ public class OCIWrapper
358
421
  public void commit() throws SQLException
359
422
  {
360
423
  committedOrRollbacked = true;
424
+ if (loadCount > 0) {
425
+ logger.info(String.format("OCI : OCIDirPathLoadStream : %,d rows x %,d times.", totalRows / loadCount, loadCount));
426
+ }
361
427
  logger.info("OCI : start to commit.");
362
428
 
363
429
  try {
@@ -0,0 +1,47 @@
1
+ package org.embulk.output.oracle.oci;
2
+
3
+ import jnr.ffi.Pointer;
4
+ import jnr.ffi.types.u_int16_t;
5
+ import jnr.ffi.types.u_int32_t;
6
+
7
+ public class PrimitiveBulkOCI implements BulkOCI {
8
+
9
+ private final OCI oci;
10
+
11
+
12
+ public PrimitiveBulkOCI(OCI oci)
13
+ {
14
+ this.oci = oci;
15
+ }
16
+
17
+ public short embulk_output_oracle_OCIDirPathColArrayEntriesSet(
18
+ Pointer dpca,
19
+ Pointer errhp,
20
+ @u_int16_t short columnCount,
21
+ @u_int32_t int rowCount,
22
+ Pointer data,
23
+ Pointer sizes) {
24
+
25
+ int index = 0;
26
+ long offset = 0;
27
+ for (int row = 0; row < rowCount; row++) {
28
+ for (short column = 0; column < columnCount; column++) {
29
+ short size = sizes.getShort(index++ * 2);
30
+ short result = oci.OCIDirPathColArrayEntrySet(
31
+ dpca,
32
+ errhp,
33
+ row,
34
+ column,
35
+ data.slice(offset),
36
+ size,
37
+ OCI.OCI_DIRPATH_COL_COMPLETE);
38
+ if (result != OCI.OCI_SUCCESS) {
39
+ return result;
40
+ }
41
+ offset += size;
42
+ }
43
+ }
44
+
45
+ return OCI.OCI_SUCCESS;
46
+ }
47
+ }
@@ -2,6 +2,7 @@ package org.embulk.output.oracle.oci;
2
2
 
3
3
  import java.math.BigDecimal;
4
4
  import java.nio.ByteBuffer;
5
+ import java.nio.ByteOrder;
5
6
  import java.nio.charset.Charset;
6
7
  import java.sql.SQLException;
7
8
 
@@ -12,48 +13,46 @@ import org.embulk.output.oracle.oci.TableDefinition;
12
13
 
13
14
  public class RowBuffer
14
15
  {
16
+ // this value was calculated by tests
17
+ private static final double OPTIMAL_LOAD_TIME = 10;
18
+ private static final int MIN_ROW_COUNT_TO_LOAD = 100;
19
+
20
+ private final OCIWrapper oci;
15
21
  private final TableDefinition table;
16
- private final int rowCount;
22
+ private final int maxRowCount;
17
23
 
18
- private int currentRow = 0;
24
+ private int rowCount = 0;
19
25
  private int currentColumn = 0;
20
26
 
21
- private final short[] sizes;
27
+ private int rowCountToLoad;
28
+ private int totalLoadedRowCount = 0;
29
+ private int loadCount = 0;
30
+ private double totalLoadTime = 0;
31
+
32
+ private final ByteBuffer sizes;
33
+ private final ByteBuffer defaultSizes;
22
34
  private final ByteBuffer buffer;
23
35
  private final ByteBuffer defaultBuffer;
24
36
 
25
- public RowBuffer(TableDefinition table, int rowCount)
37
+ public RowBuffer(OCIWrapper oci, TableDefinition table)
26
38
  {
39
+ this.oci = oci;
27
40
  this.table = table;
28
- this.rowCount = rowCount;
29
-
30
- int rowSize = 0;
31
- for (int i = 0; i < table.getColumnCount(); i++) {
32
- rowSize += table.getColumn(i).getDataSize();
33
- }
41
+ maxRowCount = oci.getMaxRowCount();
42
+ rowCountToLoad = maxRowCount;
34
43
 
44
+ ByteOrder byteOrder = Runtime.getSystemRuntime().byteOrder();
35
45
  // should be direct because used by native library
36
- buffer = ByteBuffer.allocateDirect(rowSize * rowCount).order(Runtime.getSystemRuntime().byteOrder());
46
+ buffer = ByteBuffer.allocateDirect(table.getRowSize() * maxRowCount).order(byteOrder);
37
47
  // position is not updated
38
- defaultBuffer = buffer.duplicate();
39
-
40
- sizes = new short[table.getColumnCount() * rowCount];
41
- }
42
-
43
- public ByteBuffer getBuffer() {
44
- return defaultBuffer;
45
- }
48
+ defaultBuffer = buffer.duplicate().order(byteOrder);
46
49
 
47
- public short[] getSizes() {
48
- return sizes;
50
+ sizes = ByteBuffer.allocateDirect(table.getColumnCount() * maxRowCount * 2).order(byteOrder);
51
+ defaultSizes = sizes.duplicate().order(byteOrder);
49
52
  }
50
53
 
51
- public void addValue(int value)
54
+ public void addValue(int value) throws SQLException
52
55
  {
53
- if (isFull()) {
54
- throw new IllegalStateException();
55
- }
56
-
57
56
  buffer.putInt(value);
58
57
 
59
58
  next((short)4);
@@ -61,10 +60,6 @@ public class RowBuffer
61
60
 
62
61
  public void addValue(String value) throws SQLException
63
62
  {
64
- if (isFull()) {
65
- throw new IllegalStateException();
66
- }
67
-
68
63
  ColumnDefinition column = table.getColumn(currentColumn);
69
64
  Charset charset = column.getCharset().getJavaCharset();
70
65
 
@@ -88,14 +83,18 @@ public class RowBuffer
88
83
  addValue(value.toPlainString());
89
84
  }
90
85
 
91
- private void next(short size)
86
+ private void next(short size) throws SQLException
92
87
  {
93
- sizes[currentRow * table.getColumnCount() + currentColumn] = size;
88
+ sizes.putShort(size);
94
89
 
95
90
  currentColumn++;
96
91
  if (currentColumn == table.getColumnCount()) {
97
92
  currentColumn = 0;
98
- currentRow++;
93
+ rowCount++;
94
+
95
+ if (rowCount >= rowCountToLoad) {
96
+ flush();
97
+ }
99
98
  }
100
99
  }
101
100
 
@@ -104,21 +103,26 @@ public class RowBuffer
104
103
  return currentColumn;
105
104
  }
106
105
 
107
- public int getRowCount()
108
- {
109
- return currentRow;
110
- }
111
-
112
- public boolean isFull()
106
+ public void flush() throws SQLException
113
107
  {
114
- return currentRow >= rowCount;
115
- }
108
+ if (rowCount > 0) {
109
+ synchronized (oci) {
110
+ long time = System.currentTimeMillis();
111
+ oci.loadBuffer(defaultBuffer, defaultSizes, rowCount);
112
+ totalLoadTime += System.currentTimeMillis() - time;
113
+ totalLoadedRowCount += rowCount;
114
+ loadCount += 1;
115
+ }
116
+
117
+ rowCount = 0;
118
+ currentColumn = 0;
119
+ buffer.clear();
120
+ sizes.clear();
116
121
 
117
- public void clear()
118
- {
119
- currentRow = 0;
120
- currentColumn = 0;
121
- buffer.clear();
122
+ if (loadCount >= 4) {
123
+ rowCountToLoad = Math.min(Math.max((int)(totalLoadedRowCount / totalLoadTime * OPTIMAL_LOAD_TIME), MIN_ROW_COUNT_TO_LOAD), maxRowCount);
124
+ }
125
+ }
122
126
  }
123
127
 
124
128
  }