embulk-output-oracle 0.7.5 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44663c92d50300afd8216bc26987e9723688eaef
4
- data.tar.gz: 2d3379ffeb559914ab552dfd55bbd5a681feb8aa
3
+ metadata.gz: 6bae783332773b7e53b34d8676ad6592d4354618
4
+ data.tar.gz: cb529aeca71d8c03ba1009c491e8816d41814e6b
5
5
  SHA512:
6
- metadata.gz: 834f776e6b4c08e781ce108f58402d0e83f1cfac21fbdc7bcd295e6f73e469f1628bec55f7fcc7e44dae154ea4a178501ebb4f5bc054f85a5140faefc775d945
7
- data.tar.gz: f1102c5ebef6d1649543a181d1d47f4b0e56ac5186aea5a07433b0abc8472653104154c715c1a029de61da894793ba8f870f9e81c713c10ddea577632904d698
6
+ metadata.gz: c91850c74c82a2e5d52d4b0ba78f7ba6dd53f1b9a88efc933c0aeb22ae60091887410dade93de799d8fe6136e9c1e2289eea506d0bf2a67f202d0a9dc05c768c
7
+ data.tar.gz: 4a205e6af57486fb767e3c50825737008985dc184978cb0dbcb16c3da79ae11615329697265674a9529db98df8bb7b965cdf6033cb995b90f347a716512083f7
data/README.md CHANGED
@@ -22,7 +22,9 @@ Oracle output plugin for Embulk loads records to Oracle.
22
22
  - **retry_limit** max retry count for database operations (integer, default: 12)
23
23
  - **retry_wait** initial retry wait time in milliseconds (integer, default: 1000 (1 second))
24
24
  - **max_retry_wait** upper limit of retry wait, which will be doubled at every retry (integer, default: 1800000 (30 minutes))
25
- - **mode**: "insert", "insert_direct", "truncate_insert", or "replace". See below. (string, required)
25
+ - **mode**: "insert", "insert_direct", "truncate_insert", "replace" or "merge". See below. (string, required)
26
+ - **merge_keys**: key column names for merging records in merge mode (string array, required in merge mode if table doesn't have primary key)
27
+ - **merge_rule**: list of column assignments for updating existing records used in merge mode, for example `foo = T.foo + S.foo` (`T` means target table and `S` means source table). (string array, default: always overwrites with new values)
26
28
  - **insert_method**: see below
27
29
  - **batch_size**: size of a single batch insert (integer, default: 16777216)
28
30
  - **default_timezone**: If input column type (embulk type) is timestamp, this plugin needs to format the timestamp into a SQL string. This default_timezone option is used to control the timezone. You can overwrite timezone for each columns using column_options option. (string, default: `UTC`)
@@ -51,6 +53,10 @@ Oracle output plugin for Embulk loads records to Oracle.
51
53
  * Behavior: This mode writes rows to an intermediate table first. If all those tasks run correctly, drops the target table and alters the name of the intermediate table into the target table name.
52
54
  * Transactional: Yes.
53
55
  * Resumable: No.
56
+ * **merge**:
57
+ * Behavior: This mode writes rows to some intermediate tables first. If all those tasks run correctly, runs `MERGE INTO ... WHEN MATCHED THEN UPDATE ... WHEN NOT MATCHED THEN INSERT ...` query. Namely, if merge keys of a record in the intermediate tables already exist in the target table, the target record is updated by the intermediate record, otherwise the intermediate record is inserted. If the target table doesn't exist, it is created automatically.
58
+ * Transactional: Yes.
59
+ * Resumable: Yes.
54
60
 
55
61
  ### Insert methods
56
62
 
@@ -1,6 +1,5 @@
1
1
  package org.embulk.output;
2
2
 
3
- import java.util.List;
4
3
  import java.util.Properties;
5
4
  import java.io.IOException;
6
5
  import java.sql.SQLException;
@@ -70,7 +69,7 @@ public class OracleOutputPlugin
70
69
  {
71
70
  return new Features()
72
71
  .setMaxTableNameLength(30)
73
- .setSupportedModes(ImmutableSet.of(Mode.INSERT, Mode.INSERT_DIRECT, Mode.TRUNCATE_INSERT, Mode.REPLACE))
72
+ .setSupportedModes(ImmutableSet.of(Mode.INSERT, Mode.INSERT_DIRECT, Mode.MERGE, Mode.TRUNCATE_INSERT, Mode.REPLACE))
74
73
  .setIgnoreMergeKeys(false);
75
74
  }
76
75
 
@@ -8,11 +8,13 @@ import java.sql.SQLException;
8
8
  import java.sql.Statement;
9
9
  import java.util.Arrays;
10
10
  import java.util.HashMap;
11
+ import java.util.List;
11
12
  import java.util.Map;
12
13
 
13
14
  import org.embulk.output.jdbc.JdbcOutputConnection;
14
15
  import org.embulk.output.jdbc.JdbcColumn;
15
16
  import org.embulk.output.jdbc.JdbcSchema;
17
+ import org.embulk.output.jdbc.MergeConfig;
16
18
 
17
19
  public class OracleOutputConnection
18
20
  extends JdbcOutputConnection
@@ -195,4 +197,73 @@ public class OracleOutputConnection
195
197
  }
196
198
  return super.getColumnDeclareType(convertedTypeName, col);
197
199
  }
200
+
201
+ @Override
202
+ protected String buildCollectMergeSql(List<String> fromTables, JdbcSchema schema, String toTable, MergeConfig mergeConfig) throws SQLException
203
+ {
204
+ StringBuilder sb = new StringBuilder();
205
+
206
+ sb.append("MERGE INTO ");
207
+ sb.append(quoteIdentifierString(toTable));
208
+ sb.append(" T");
209
+ sb.append(" USING (");
210
+ for (int i = 0; i < fromTables.size(); i++) {
211
+ if (i != 0) { sb.append(" UNION ALL "); }
212
+ sb.append(" SELECT ");
213
+ sb.append(buildColumns(schema, ""));
214
+ sb.append(" FROM ");
215
+ sb.append(quoteIdentifierString(fromTables.get(i)));
216
+ }
217
+ sb.append(") S");
218
+ sb.append(" ON (");
219
+ for (int i = 0; i < mergeConfig.getMergeKeys().size(); i++) {
220
+ if (i != 0) { sb.append(" AND "); }
221
+ String mergeKey = quoteIdentifierString(mergeConfig.getMergeKeys().get(i));
222
+ sb.append("T.");
223
+ sb.append(mergeKey);
224
+ sb.append(" = S.");
225
+ sb.append(mergeKey);
226
+ }
227
+ sb.append(")");
228
+ sb.append(" WHEN MATCHED THEN");
229
+ sb.append(" UPDATE SET ");
230
+ if (mergeConfig.getMergeRule().isPresent()) {
231
+ for (int i = 0; i < mergeConfig.getMergeRule().get().size(); i++) {
232
+ if (i != 0) { sb.append(", "); }
233
+ sb.append(mergeConfig.getMergeRule().get().get(i));
234
+ }
235
+ } else {
236
+ int index = 0;
237
+ for (int i = 0; i < schema.getCount(); i++) {
238
+ String rawColumn = schema.getColumnName(i);
239
+ if (mergeConfig.getMergeKeys().contains(rawColumn)) {
240
+ continue;
241
+ }
242
+ if (index++ != 0) { sb.append(", "); }
243
+ String column = quoteIdentifierString(rawColumn);
244
+ sb.append(column);
245
+ sb.append(" = S.");
246
+ sb.append(column);
247
+ }
248
+ }
249
+ sb.append(" WHEN NOT MATCHED THEN");
250
+ sb.append(" INSERT (");
251
+ sb.append(buildColumns(schema, ""));
252
+ sb.append(") VALUES (");
253
+ sb.append(buildColumns(schema, "S."));
254
+ sb.append(")");
255
+
256
+ return sb.toString();
257
+ }
258
+
259
+ private String buildColumns(JdbcSchema schema, String prefix)
260
+ {
261
+ StringBuilder sb = new StringBuilder();
262
+ for (int i = 0; i < schema.getCount(); i++) {
263
+ if (i != 0) { sb.append(", "); }
264
+ sb.append(prefix);
265
+ sb.append(quoteIdentifierString(schema.getColumnName(i)));
266
+ }
267
+ return sb.toString();
268
+ }
198
269
  }
@@ -329,6 +329,195 @@ public class OracleOutputPluginTest extends AbstractJdbcOutputPluginTest
329
329
  assertTable(table);
330
330
  }
331
331
 
332
+ @Test
333
+ public void testMerge() throws Exception
334
+ {
335
+ if (!enabled) {
336
+ return;
337
+ }
338
+
339
+ String table = "TEST1";
340
+
341
+ dropTable(table);
342
+ executeSQL("CREATE TABLE TEST1 ("
343
+ + "ID CHAR(4),"
344
+ + "VARCHAR2_ITEM VARCHAR2(6),"
345
+ + "NUMBER_ITEM NUMBER(10,2),"
346
+ + "PRIMARY KEY (ID))");
347
+ executeSQL("INSERT INTO TEST1 VALUES('A001', 'AAA', 12.34)");
348
+ executeSQL("INSERT INTO TEST1 VALUES('A003', NULL, NULL)");
349
+ executeSQL("INSERT INTO TEST1 VALUES('A005', 'EEE', 56.78)");
350
+ executeSQL("INSERT INTO TEST1 VALUES('A006', 'FFF', 0)");
351
+
352
+ test("/oracle/yml/test-merge.yml");
353
+
354
+ List<List<Object>> rows = select(table);
355
+ assertEquals(6, rows.size());
356
+ Iterator<List<Object>> i1 = rows.iterator();
357
+ {
358
+ Iterator<Object> i2 = i1.next().iterator();
359
+ assertEquals("A001", i2.next());
360
+ assertEquals("aaa", i2.next());
361
+ assertEquals(new BigDecimal("99.99"), i2.next());
362
+ }
363
+ {
364
+ Iterator<Object> i2 = i1.next().iterator();
365
+ assertEquals("A002", i2.next());
366
+ assertEquals("bbb", i2.next());
367
+ assertEquals(new BigDecimal("88.88"), i2.next());
368
+ }
369
+ {
370
+ Iterator<Object> i2 = i1.next().iterator();
371
+ assertEquals("A003", i2.next());
372
+ assertEquals("ccc", i2.next());
373
+ assertEquals(new BigDecimal("77.77"), i2.next());
374
+ }
375
+ {
376
+ Iterator<Object> i2 = i1.next().iterator();
377
+ assertEquals("A004", i2.next());
378
+ assertEquals(null, i2.next());
379
+ assertEquals(null, i2.next());
380
+ }
381
+ {
382
+ Iterator<Object> i2 = i1.next().iterator();
383
+ assertEquals("A005", i2.next());
384
+ assertEquals(null, i2.next());
385
+ assertEquals(null, i2.next());
386
+ }
387
+ {
388
+ Iterator<Object> i2 = i1.next().iterator();
389
+ assertEquals("A006", i2.next());
390
+ assertEquals("FFF", i2.next());
391
+ assertEquals(new BigDecimal("0"), i2.next());
392
+ }
393
+ }
394
+
395
+ @Test
396
+ public void testMergeWithKeys() throws Exception
397
+ {
398
+ if (!enabled) {
399
+ return;
400
+ }
401
+
402
+ String table = "TEST1";
403
+
404
+ dropTable(table);
405
+ executeSQL("CREATE TABLE TEST1 ("
406
+ + "ID CHAR(4),"
407
+ + "VARCHAR2_ITEM VARCHAR2(6),"
408
+ + "NUMBER_ITEM NUMBER(10,2),"
409
+ + "PRIMARY KEY (ID))");
410
+ executeSQL("INSERT INTO TEST1 VALUES('A001', 'AAA', 11.11)");
411
+ executeSQL("INSERT INTO TEST1 VALUES('A002', 'BBB', 22.22)");
412
+ executeSQL("INSERT INTO TEST1 VALUES('A003', 'CCC', 33.33)");
413
+
414
+ test("/oracle/yml/test-merge-keys.yml");
415
+
416
+ List<List<Object>> rows = select(table);
417
+ assertEquals(5, rows.size());
418
+ Iterator<List<Object>> i1 = rows.iterator();
419
+ {
420
+ Iterator<Object> i2 = i1.next().iterator();
421
+ assertEquals("A001", i2.next());
422
+ assertEquals("AAA", i2.next());
423
+ assertEquals(new BigDecimal("11.11"), i2.next());
424
+ }
425
+ {
426
+ Iterator<Object> i2 = i1.next().iterator();
427
+ assertEquals("A004", i2.next());
428
+ assertEquals("BBB", i2.next());
429
+ assertEquals(new BigDecimal("22.21"), i2.next());
430
+ }
431
+ {
432
+ Iterator<Object> i2 = i1.next().iterator();
433
+ assertEquals("A005", i2.next());
434
+ assertEquals("BBB", i2.next());
435
+ assertEquals(new BigDecimal("22.22"), i2.next());
436
+ }
437
+ {
438
+ Iterator<Object> i2 = i1.next().iterator();
439
+ assertEquals("A006", i2.next());
440
+ assertEquals("BBB", i2.next());
441
+ assertEquals(new BigDecimal("22.23"), i2.next());
442
+ }
443
+ {
444
+ Iterator<Object> i2 = i1.next().iterator();
445
+ assertEquals("A007", i2.next());
446
+ assertEquals("CCC", i2.next());
447
+ assertEquals(new BigDecimal("33.33"), i2.next());
448
+ }
449
+ }
450
+
451
+ @Test
452
+ public void testMergeWithRule() throws Exception
453
+ {
454
+ if (!enabled) {
455
+ return;
456
+ }
457
+
458
+ String table = "TEST1";
459
+
460
+ dropTable(table);
461
+ executeSQL("CREATE TABLE TEST1 ("
462
+ + "ID CHAR(4),"
463
+ + "VARCHAR2_ITEM VARCHAR2(6),"
464
+ + "NUMBER_ITEM NUMBER(10,2),"
465
+ + "PRIMARY KEY (ID))");
466
+ executeSQL("INSERT INTO TEST1 VALUES('A002', 'BBB', 22.22)");
467
+ executeSQL("INSERT INTO TEST1 VALUES('A004', 'DDD', 44.44)");
468
+ executeSQL("INSERT INTO TEST1 VALUES('A006', 'FFF', 66.66)");
469
+
470
+ /*
471
+ A001,aaa,99.99
472
+ A002,bbb,88.88
473
+ A003,ccc,77.77
474
+ A004,,
475
+ A005,,
476
+ */
477
+
478
+ test("/oracle/yml/test-merge-rule.yml");
479
+
480
+ List<List<Object>> rows = select(table);
481
+ assertEquals(6, rows.size());
482
+ Iterator<List<Object>> i1 = rows.iterator();
483
+ {
484
+ Iterator<Object> i2 = i1.next().iterator();
485
+ assertEquals("A001", i2.next());
486
+ assertEquals("aaa", i2.next());
487
+ assertEquals(new BigDecimal("99.99"), i2.next());
488
+ }
489
+ {
490
+ Iterator<Object> i2 = i1.next().iterator();
491
+ assertEquals("A002", i2.next());
492
+ assertEquals("x", i2.next());
493
+ assertEquals(new BigDecimal("111.1"), i2.next());
494
+ }
495
+ {
496
+ Iterator<Object> i2 = i1.next().iterator();
497
+ assertEquals("A003", i2.next());
498
+ assertEquals("ccc", i2.next());
499
+ assertEquals(new BigDecimal("77.77"), i2.next());
500
+ }
501
+ {
502
+ Iterator<Object> i2 = i1.next().iterator();
503
+ assertEquals("A004", i2.next());
504
+ assertEquals("x", i2.next());
505
+ assertEquals(null, i2.next());
506
+ }
507
+ {
508
+ Iterator<Object> i2 = i1.next().iterator();
509
+ assertEquals("A005", i2.next());
510
+ assertEquals(null, i2.next());
511
+ assertEquals(null, i2.next());
512
+ }
513
+ {
514
+ Iterator<Object> i2 = i1.next().iterator();
515
+ assertEquals("A006", i2.next());
516
+ assertEquals("FFF", i2.next());
517
+ assertEquals(new BigDecimal("66.66"), i2.next());
518
+ }
519
+ }
520
+
332
521
  @Test
333
522
  public void testUrl() throws Exception
334
523
  {
@@ -0,0 +1,5 @@
1
+ A001,aaa,99.99
2
+ A002,bbb,88.88
3
+ A003,ccc,77.77
4
+ A004,,
5
+ A005,,
@@ -0,0 +1,4 @@
1
+ A004,BBB,22.21
2
+ A005,BBB,22.22
3
+ A006,BBB,22.23
4
+ A007,CCC,33.33
@@ -0,0 +1,23 @@
1
+ in:
2
+ type: file
3
+ path_prefix: '/oracle/data/test5/test5.csv'
4
+ parser:
5
+ charset: UTF-8
6
+ newline: CRLF
7
+ type: csv
8
+ delimiter: ','
9
+ quote: ''
10
+ columns:
11
+ - {name: ID, type: string}
12
+ - {name: VARCHAR2_ITEM, type: string}
13
+ - {name: NUMBER_ITEM, type: string}
14
+ out:
15
+ type: oracle
16
+ host: #host#
17
+ database: #database#
18
+ user: #user#
19
+ password: #password#
20
+ table: TEST1
21
+ mode: merge
22
+ merge_keys: [VARCHAR2_ITEM,NUMBER_ITEM]
23
+ #driver_path: driver/ojdbc7.jar
@@ -0,0 +1,23 @@
1
+ in:
2
+ type: file
3
+ path_prefix: '/oracle/data/test4/test4.csv'
4
+ parser:
5
+ charset: UTF-8
6
+ newline: CRLF
7
+ type: csv
8
+ delimiter: ','
9
+ quote: ''
10
+ columns:
11
+ - {name: ID, type: string}
12
+ - {name: VARCHAR2_ITEM, type: string}
13
+ - {name: NUMBER_ITEM, type: string}
14
+ out:
15
+ type: oracle
16
+ host: #host#
17
+ database: #database#
18
+ user: #user#
19
+ password: #password#
20
+ table: TEST1
21
+ mode: merge
22
+ merge_rule: ["VARCHAR2_ITEM = 'x'", "NUMBER_ITEM = T.NUMBER_ITEM + S.NUMBER_ITEM"]
23
+ #driver_path: driver/ojdbc7.jar
@@ -0,0 +1,22 @@
1
+ in:
2
+ type: file
3
+ path_prefix: '/oracle/data/test4/test4.csv'
4
+ parser:
5
+ charset: UTF-8
6
+ newline: CRLF
7
+ type: csv
8
+ delimiter: ','
9
+ quote: ''
10
+ columns:
11
+ - {name: ID, type: string}
12
+ - {name: VARCHAR2_ITEM, type: string}
13
+ - {name: NUMBER_ITEM, type: string}
14
+ out:
15
+ type: oracle
16
+ host: #host#
17
+ database: #database#
18
+ user: #user#
19
+ password: #password#
20
+ table: TEST1
21
+ mode: merge
22
+ #driver_path: driver/ojdbc7.jar
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-output-oracle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.5
4
+ version: 0.7.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-10 00:00:00.000000000 Z
11
+ date: 2017-03-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Inserts or updates records to a table.
14
14
  email:
@@ -19,8 +19,8 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - README.md
21
21
  - build.gradle
22
- - classpath/embulk-output-jdbc-0.7.5.jar
23
- - classpath/embulk-output-oracle-0.7.5.jar
22
+ - classpath/embulk-output-jdbc-0.7.6.jar
23
+ - classpath/embulk-output-oracle-0.7.6.jar
24
24
  - lib/embulk/native/x86_64-linux/libembulk-output-oracle-oci.so
25
25
  - lib/embulk/native/x86_64-windows/embulk-output-oracle-oci.dll
26
26
  - lib/embulk/output/oracle.rb
@@ -50,6 +50,8 @@ files:
50
50
  - src/test/java/org/embulk/output/oracle/OracleOutputPluginTest.java
51
51
  - src/test/resources/oracle/data/test1/test1.csv
52
52
  - src/test/resources/oracle/data/test3/test3.csv
53
+ - src/test/resources/oracle/data/test4/test4.csv
54
+ - src/test/resources/oracle/data/test5/test5.csv
53
55
  - src/test/resources/oracle/yml/test-insert-direct-direct-method.yml
54
56
  - src/test/resources/oracle/yml/test-insert-direct-empty.yml
55
57
  - src/test/resources/oracle/yml/test-insert-direct-oci-method-large.yml
@@ -62,6 +64,9 @@ files:
62
64
  - src/test/resources/oracle/yml/test-lower-column-options.yml
63
65
  - src/test/resources/oracle/yml/test-lower-column.yml
64
66
  - src/test/resources/oracle/yml/test-lower-table.yml
67
+ - src/test/resources/oracle/yml/test-merge-keys.yml
68
+ - src/test/resources/oracle/yml/test-merge-rule.yml
69
+ - src/test/resources/oracle/yml/test-merge.yml
65
70
  - src/test/resources/oracle/yml/test-replace-empty.yml
66
71
  - src/test/resources/oracle/yml/test-replace-long-name-multibyte.yml
67
72
  - src/test/resources/oracle/yml/test-replace-long-name.yml