embulk-output-sqlserver 0.7.1 → 0.7.2

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: d974a42bda50d1cc40c33c3d25e15ef4a74fbc91
4
- data.tar.gz: c9f3cc3c294b6528344aed949cd991cd5d781f48
3
+ metadata.gz: 83928aeb58e8e44cd1e3ddd359a5f0bc91a8b480
4
+ data.tar.gz: ebcde6ea3c960f1a962750e41e43732fe9bcff60
5
5
  SHA512:
6
- metadata.gz: a6460579ddcf2544670f3fef6a8a8e376cfc39bb2c5aef1efb167c5863832f2f8792dd81769cf767814135292cf9ca79496a173174252246686bb3635608c8ef
7
- data.tar.gz: 56502e2371187fdeeb45d0f15ce5687d549a86cba5122fda35273fef6f6fe6eecba48744a2f2c2f6d583cad317a845a8363d7f8996868d386b499de1d82b5eac
6
+ metadata.gz: 0321e417ca209e078b1c9f183f3332f23068f7b6ae0abfd2115f2c15ebbd3c6aa9b6a0b97c2f6e45a44114fd462234e6765e3c882929fa76d91fd2cc651141e6
7
+ data.tar.gz: 8a939954de682d79658f854fd62ccbd42857be89c579c7ed22b1d44993c2ed0653910d6e4529bd82403558ca05b92c7be1999257d369908f665b965fe0fb6937
data/README.md CHANGED
@@ -28,7 +28,9 @@ embulk "-J-Djava.library.path=C:\drivers" run input-sqlserver.yml
28
28
  - **retry_limit** max retry count for database operations (integer, default: 12)
29
29
  - **retry_wait** initial retry wait time in milliseconds (integer, default: 1000 (1 second))
30
30
  - **max_retry_wait** upper limit of retry wait, which will be doubled at every retry (integer, default: 1800000 (30 minutes))
31
- - **mode**: "insert", "insert_direct", "truncate_insert" or "replace". See below. (string, required)
31
+ - **mode**: "insert", "insert_direct", "truncate_insert" , "replace" or "merge". See below. (string, required)
32
+ - **merge_keys**: key column names for merging records in merge mode (string array, required in merge mode if table doesn't have primary key)
33
+ - **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)
32
34
  - **insert_method**: see below
33
35
  - **batch_size**: size of a single batch insert (integer, default: 16777216)
34
36
  - **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`)
@@ -57,6 +59,10 @@ embulk "-J-Djava.library.path=C:\drivers" run input-sqlserver.yml
57
59
  * 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.
58
60
  * Transactional: No. If fails, the target table could be dropped (because SQL Server can't rollback DDL).
59
61
  * Resumable: No.
62
+ * **merge**:
63
+ * 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.
64
+ * Transactional: Yes.
65
+ * Resumable: Yes.
60
66
 
61
67
  ### Insert methods
62
68
 
@@ -2,6 +2,7 @@ package org.embulk.output;
2
2
 
3
3
  import com.google.common.base.Optional;
4
4
  import com.google.common.collect.ImmutableSet;
5
+
5
6
  import org.embulk.config.Config;
6
7
  import org.embulk.config.ConfigDefault;
7
8
  import org.embulk.config.ConfigException;
@@ -103,7 +104,7 @@ public class SQLServerOutputPlugin
103
104
  {
104
105
  return new Features()
105
106
  .setMaxTableNameLength(128)
106
- .setSupportedModes(ImmutableSet.of(Mode.INSERT, Mode.INSERT_DIRECT, Mode.TRUNCATE_INSERT, Mode.REPLACE))
107
+ .setSupportedModes(ImmutableSet.of(Mode.INSERT, Mode.INSERT_DIRECT, Mode.MERGE, Mode.TRUNCATE_INSERT, Mode.REPLACE))
107
108
  .setIgnoreMergeKeys(false);
108
109
  }
109
110
 
@@ -4,10 +4,12 @@ import java.sql.Connection;
4
4
  import java.sql.SQLException;
5
5
  import java.sql.Statement;
6
6
  import java.util.Arrays;
7
+ import java.util.List;
7
8
 
8
9
  import org.embulk.output.jdbc.JdbcColumn;
9
10
  import org.embulk.output.jdbc.JdbcOutputConnection;
10
11
  import org.embulk.output.jdbc.JdbcSchema;
12
+ import org.embulk.output.jdbc.MergeConfig;
11
13
 
12
14
  public class SQLServerOutputConnection
13
15
  extends JdbcOutputConnection
@@ -113,12 +115,67 @@ public class SQLServerOutputConnection
113
115
  return super.getColumnDeclareType(convertedTypeName, col);
114
116
  }
115
117
 
116
- /*
117
118
  @Override
118
- public Charset getTableNameCharset() throws SQLException
119
+ protected String buildCollectMergeSql(List<String> fromTables, JdbcSchema schema, String toTable, MergeConfig mergeConfig) throws SQLException
119
120
  {
120
- return getOracleCharset().getJavaCharset();
121
+ StringBuilder sb = new StringBuilder();
122
+
123
+ sb.append("MERGE INTO ");
124
+ sb.append(quoteIdentifierString(toTable));
125
+ sb.append(" AS T");
126
+ sb.append(" USING (");
127
+ for (int i = 0; i < fromTables.size(); i++) {
128
+ if (i != 0) { sb.append(" UNION ALL "); }
129
+ sb.append(" SELECT ");
130
+ sb.append(buildColumns(schema, ""));
131
+ sb.append(" FROM ");
132
+ sb.append(quoteIdentifierString(fromTables.get(i)));
133
+ }
134
+ sb.append(") AS S");
135
+ sb.append(" ON (");
136
+ for (int i = 0; i < mergeConfig.getMergeKeys().size(); i++) {
137
+ if (i != 0) { sb.append(" AND "); }
138
+ String mergeKey = quoteIdentifierString(mergeConfig.getMergeKeys().get(i));
139
+ sb.append("T.");
140
+ sb.append(mergeKey);
141
+ sb.append(" = S.");
142
+ sb.append(mergeKey);
143
+ }
144
+ sb.append(")");
145
+ sb.append(" WHEN MATCHED THEN");
146
+ sb.append(" UPDATE SET ");
147
+ if (mergeConfig.getMergeRule().isPresent()) {
148
+ for (int i = 0; i < mergeConfig.getMergeRule().get().size(); i++) {
149
+ if (i != 0) { sb.append(", "); }
150
+ sb.append(mergeConfig.getMergeRule().get().get(i));
151
+ }
152
+ } else {
153
+ for (int i = 0; i < schema.getCount(); i++) {
154
+ if (i != 0) { sb.append(", "); }
155
+ String column = quoteIdentifierString(schema.getColumnName(i));
156
+ sb.append(column);
157
+ sb.append(" = S.");
158
+ sb.append(column);
159
+ }
160
+ }
161
+ sb.append(" WHEN NOT MATCHED THEN");
162
+ sb.append(" INSERT (");
163
+ sb.append(buildColumns(schema, ""));
164
+ sb.append(") VALUES (");
165
+ sb.append(buildColumns(schema, "S."));
166
+ sb.append(");");
167
+
168
+ return sb.toString();
121
169
  }
122
170
 
123
- */
171
+ private String buildColumns(JdbcSchema schema, String prefix)
172
+ {
173
+ StringBuilder sb = new StringBuilder();
174
+ for (int i = 0; i < schema.getCount(); i++) {
175
+ if (i != 0) { sb.append(", "); }
176
+ sb.append(prefix);
177
+ sb.append(quoteIdentifierString(schema.getColumnName(i)));
178
+ }
179
+ return sb.toString();
180
+ }
124
181
  }
@@ -34,7 +34,7 @@ public class SQLServerOutputPluginTest extends AbstractJdbcOutputPluginTest
34
34
  try {
35
35
  Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
36
36
  } catch (ClassNotFoundException e) {
37
- System.err.println("Warning: you should put 'sqljdbc41.jar' in 'embulk-input-sqlserver/driver' directory in order to test.");
37
+ System.err.println("Warning: you should put 'sqljdbc41.jar' in 'embulk-output-sqlserver/driver' directory in order to test.");
38
38
  return;
39
39
  }
40
40
 
@@ -358,6 +358,145 @@ public class SQLServerOutputPluginTest extends AbstractJdbcOutputPluginTest
358
358
  }
359
359
  }
360
360
 
361
+ @Test
362
+ public void testMerge() throws Exception
363
+ {
364
+ if (!enabled) {
365
+ return;
366
+ }
367
+
368
+ String table = "TEST6";
369
+
370
+ dropTable(table);
371
+ executeSQL(String.format("CREATE TABLE %S (ITEM1 INT, ITEM2 INT, ITEM3 VARCHAR(4), PRIMARY KEY(ITEM1, ITEM2))", table));
372
+ executeSQL(String.format("INSERT INTO %S VALUES(10, 20, 'A')", table));
373
+ executeSQL(String.format("INSERT INTO %S VALUES(10, 21, 'B')", table));
374
+ executeSQL(String.format("INSERT INTO %S VALUES(11, 20, 'C')", table));
375
+
376
+ test("/sqlserver/yml/test-merge.yml");
377
+
378
+ assertMergedTable(table);
379
+ }
380
+
381
+ @Test
382
+ public void testMergeWithKeys() throws Exception
383
+ {
384
+ if (!enabled) {
385
+ return;
386
+ }
387
+
388
+ String table = "TEST6b";
389
+
390
+ dropTable(table);
391
+ executeSQL(String.format("CREATE TABLE %S (ITEM1 INT, ITEM2 INT, ITEM3 VARCHAR(4))", table));
392
+ executeSQL(String.format("INSERT INTO %S VALUES(10, 20, 'A')", table));
393
+ executeSQL(String.format("INSERT INTO %S VALUES(10, 21, 'B')", table));
394
+ executeSQL(String.format("INSERT INTO %S VALUES(11, 20, 'C')", table));
395
+
396
+ test("/sqlserver/yml/test-merge-keys.yml");
397
+
398
+ assertMergedTable(table);
399
+ }
400
+
401
+ @Test
402
+ public void testMergeWithRule() throws Exception
403
+ {
404
+ if (!enabled) {
405
+ return;
406
+ }
407
+
408
+ String table = "TEST6";
409
+
410
+ dropTable(table);
411
+ executeSQL(String.format("CREATE TABLE %S (ITEM1 INT, ITEM2 INT, ITEM3 VARCHAR(4), PRIMARY KEY(ITEM1, ITEM2))", table));
412
+ executeSQL(String.format("INSERT INTO %S VALUES(10, 20, 'A')", table));
413
+ executeSQL(String.format("INSERT INTO %S VALUES(10, 21, 'B')", table));
414
+ executeSQL(String.format("INSERT INTO %S VALUES(11, 20, 'C')", table));
415
+
416
+ test("/sqlserver/yml/test-merge-rule.yml");
417
+
418
+ List<List<Object>> rows = select(table);
419
+ assertEquals(6, rows.size());
420
+ {
421
+ List<Object> row = rows.get(0);
422
+ assertEquals(Integer.valueOf(10), row.get(0));
423
+ assertEquals(Integer.valueOf(20), row.get(1));
424
+ assertEquals("A", row.get(2));
425
+ }
426
+ {
427
+ List<Object> row = rows.get(1);
428
+ assertEquals(Integer.valueOf(10), row.get(0));
429
+ assertEquals(Integer.valueOf(21), row.get(1));
430
+ assertEquals("Baa", row.get(2));
431
+ }
432
+ {
433
+ List<Object> row = rows.get(2);
434
+ assertEquals(Integer.valueOf(10), row.get(0));
435
+ assertEquals(Integer.valueOf(22), row.get(1));
436
+ assertEquals("dd", row.get(2));
437
+ }
438
+ {
439
+ List<Object> row = rows.get(3);
440
+ assertEquals(Integer.valueOf(11), row.get(0));
441
+ assertEquals(Integer.valueOf(10), row.get(1));
442
+ assertEquals("bb", row.get(2));
443
+ }
444
+ {
445
+ List<Object> row = rows.get(4);
446
+ assertEquals(Integer.valueOf(11), row.get(0));
447
+ assertEquals(Integer.valueOf(20), row.get(1));
448
+ assertEquals("C", row.get(2));
449
+ }
450
+ {
451
+ List<Object> row = rows.get(5);
452
+ assertEquals(Integer.valueOf(12), row.get(0));
453
+ assertEquals(Integer.valueOf(20), row.get(1));
454
+ assertEquals("cc", row.get(2));
455
+ }
456
+ }
457
+
458
+ private void assertMergedTable(String table) throws Exception
459
+ {
460
+ List<List<Object>> rows = select(table);
461
+ assertEquals(6, rows.size());
462
+ {
463
+ List<Object> row = rows.get(0);
464
+ assertEquals(Integer.valueOf(10), row.get(0));
465
+ assertEquals(Integer.valueOf(20), row.get(1));
466
+ assertEquals("A", row.get(2));
467
+ }
468
+ {
469
+ List<Object> row = rows.get(1);
470
+ assertEquals(Integer.valueOf(10), row.get(0));
471
+ assertEquals(Integer.valueOf(21), row.get(1));
472
+ assertEquals("aa", row.get(2));
473
+ }
474
+ {
475
+ List<Object> row = rows.get(2);
476
+ assertEquals(Integer.valueOf(10), row.get(0));
477
+ assertEquals(Integer.valueOf(22), row.get(1));
478
+ assertEquals("dd", row.get(2));
479
+ }
480
+ {
481
+ List<Object> row = rows.get(3);
482
+ assertEquals(Integer.valueOf(11), row.get(0));
483
+ assertEquals(Integer.valueOf(10), row.get(1));
484
+ assertEquals("bb", row.get(2));
485
+ }
486
+ {
487
+ List<Object> row = rows.get(4);
488
+ assertEquals(Integer.valueOf(11), row.get(0));
489
+ assertEquals(Integer.valueOf(20), row.get(1));
490
+ assertEquals("C", row.get(2));
491
+ }
492
+ {
493
+ List<Object> row = rows.get(5);
494
+ assertEquals(Integer.valueOf(12), row.get(0));
495
+ assertEquals(Integer.valueOf(20), row.get(1));
496
+ assertEquals("cc", row.get(2));
497
+ }
498
+ }
499
+
361
500
 
362
501
  private void assertTable(int skip, String table) throws Exception
363
502
  {
@@ -0,0 +1,4 @@
1
+ 10,21,aa
2
+ 11,10,bb
3
+ 12,20,cc
4
+ 10,22,dd
@@ -0,0 +1,23 @@
1
+ in:
2
+ type: file
3
+ path_prefix: '/sqlserver/data/test6/test6.csv'
4
+ parser:
5
+ charset: UTF-8
6
+ newline: CRLF
7
+ type: csv
8
+ delimiter: ','
9
+ quote: ''
10
+ columns:
11
+ - {name: ITEM1, type: long}
12
+ - {name: ITEM2, type: long}
13
+ - {name: ITEM3, type: string}
14
+ out:
15
+ type: sqlserver
16
+ host: #host#
17
+ database: #database#
18
+ user: #user#
19
+ password: #password#
20
+ table: TEST6B
21
+ mode: merge
22
+ merge_keys: [ITEM1,ITEM2]
23
+
@@ -0,0 +1,22 @@
1
+ in:
2
+ type: file
3
+ path_prefix: '/sqlserver/data/test6/test6.csv'
4
+ parser:
5
+ charset: UTF-8
6
+ newline: CRLF
7
+ type: csv
8
+ delimiter: ','
9
+ quote: ''
10
+ columns:
11
+ - {name: ITEM1, type: long}
12
+ - {name: ITEM2, type: long}
13
+ - {name: ITEM3, type: string}
14
+ out:
15
+ type: sqlserver
16
+ host: #host#
17
+ database: #database#
18
+ user: #user#
19
+ password: #password#
20
+ table: TEST6
21
+ mode: merge
22
+ merge_rule: ["ITEM3 = T.ITEM3 + S.ITEM3"]
@@ -0,0 +1,21 @@
1
+ in:
2
+ type: file
3
+ path_prefix: '/sqlserver/data/test6/test6.csv'
4
+ parser:
5
+ charset: UTF-8
6
+ newline: CRLF
7
+ type: csv
8
+ delimiter: ','
9
+ quote: ''
10
+ columns:
11
+ - {name: ITEM1, type: long}
12
+ - {name: ITEM2, type: long}
13
+ - {name: ITEM3, type: string}
14
+ out:
15
+ type: sqlserver
16
+ host: #host#
17
+ database: #database#
18
+ user: #user#
19
+ password: #password#
20
+ table: TEST6
21
+ mode: merge
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: embulk-output-sqlserver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-25 00:00:00.000000000 Z
11
+ date: 2016-12-19 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.1.jar
23
- - classpath/embulk-output-sqlserver-0.7.1.jar
22
+ - classpath/embulk-output-jdbc-0.7.2.jar
23
+ - classpath/embulk-output-sqlserver-0.7.2.jar
24
24
  - classpath/jtds-1.3.1.jar
25
25
  - lib/embulk/output/sqlserver.rb
26
26
  - src/main/java/org/embulk/output/SQLServerOutputPlugin.java
@@ -42,9 +42,13 @@ files:
42
42
  - src/test/resources/sqlserver/data/test3/test3.csv
43
43
  - src/test/resources/sqlserver/data/test4/test4.csv
44
44
  - src/test/resources/sqlserver/data/test5/test5.csv
45
+ - src/test/resources/sqlserver/data/test6/test6.csv
45
46
  - src/test/resources/sqlserver/yml/test-insert-direct.yml
46
47
  - src/test/resources/sqlserver/yml/test-insert.yml
47
48
  - src/test/resources/sqlserver/yml/test-jtds.yml
49
+ - src/test/resources/sqlserver/yml/test-merge-keys.yml
50
+ - src/test/resources/sqlserver/yml/test-merge-rule.yml
51
+ - src/test/resources/sqlserver/yml/test-merge.yml
48
52
  - src/test/resources/sqlserver/yml/test-native-date.yml
49
53
  - src/test/resources/sqlserver/yml/test-native-decimal.yml
50
54
  - src/test/resources/sqlserver/yml/test-native-integer.yml