embulk-output-jdbc 0.4.5 → 0.5.0
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.
- checksums.yaml +4 -4
- data/classpath/embulk-output-jdbc-0.5.0.jar +0 -0
- data/src/main/java/org/embulk/output/jdbc/AbstractJdbcOutputPlugin.java +54 -8
- data/src/main/java/org/embulk/output/jdbc/JdbcOutputConnection.java +12 -7
- data/src/main/java/org/embulk/output/jdbc/setter/SqlDateColumnSetter.java +1 -1
- data/src/main/java/org/embulk/output/jdbc/setter/SqlTimeColumnSetter.java +1 -1
- data/src/main/java/org/embulk/output/jdbc/setter/SqlTimestampColumnSetter.java +1 -1
- data/src/test/java/org/embulk/output/AbstractJdbcOutputPluginTest.java +117 -0
- data/src/test/java/org/embulk/output/tester/EmbulkPluginTester.java +78 -0
- metadata +5 -3
- data/classpath/embulk-output-jdbc-0.4.5.jar +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22fb481777c119acfc41002c96d50fb4bd336afd
|
4
|
+
data.tar.gz: 7757ee98f9ff666421f81efe882020df6f22aa7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e5fb5117260aaa6b8b42b3f0582b33fb5f842c3f20b349f2ca13120904053428f2e0edb7c87fdee1a32332243c421e1700d4ef09b03bcb9b224f8562a8c3399
|
7
|
+
data.tar.gz: 29f4a992a7fce0809993b044f7833d1baf620a1a1dc3527acfba917671ac784ce6bf8f20498c38fb56232fe03730dbdf600e5dd16764dbf8695bf29c72d480a0
|
Binary file
|
@@ -13,8 +13,10 @@ import java.sql.Types;
|
|
13
13
|
import java.sql.ResultSet;
|
14
14
|
import java.sql.DatabaseMetaData;
|
15
15
|
import java.sql.SQLException;
|
16
|
+
|
16
17
|
import org.slf4j.Logger;
|
17
18
|
import org.joda.time.DateTimeZone;
|
19
|
+
|
18
20
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
19
21
|
import com.fasterxml.jackson.annotation.JsonValue;
|
20
22
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
@@ -25,6 +27,7 @@ import com.google.common.base.Throwables;
|
|
25
27
|
import com.google.common.collect.Lists;
|
26
28
|
import com.google.common.collect.ImmutableList;
|
27
29
|
import com.google.common.collect.ImmutableSet;
|
30
|
+
|
28
31
|
import org.embulk.config.Config;
|
29
32
|
import org.embulk.config.ConfigDefault;
|
30
33
|
import org.embulk.config.ConfigDiff;
|
@@ -47,6 +50,7 @@ import org.embulk.output.jdbc.setter.ColumnSetter;
|
|
47
50
|
import org.embulk.output.jdbc.setter.ColumnSetterFactory;
|
48
51
|
import org.embulk.output.jdbc.setter.ColumnSetterVisitor;
|
49
52
|
import org.embulk.spi.util.RetryExecutor.Retryable;
|
53
|
+
|
50
54
|
import static org.embulk.spi.util.RetryExecutor.retryExecutor;
|
51
55
|
import static org.embulk.output.jdbc.JdbcSchema.filterSkipColumns;
|
52
56
|
|
@@ -102,9 +106,30 @@ public abstract class AbstractJdbcOutputPlugin
|
|
102
106
|
public void setIntermediateTables(Optional<List<String>> names);
|
103
107
|
}
|
104
108
|
|
109
|
+
public static enum LengthSemantics
|
110
|
+
{
|
111
|
+
BYTES {
|
112
|
+
@Override
|
113
|
+
public int countLength(Charset charset, String s)
|
114
|
+
{
|
115
|
+
return charset.encode(s).remaining();
|
116
|
+
}
|
117
|
+
},
|
118
|
+
CHARACTERS {
|
119
|
+
@Override
|
120
|
+
public int countLength(Charset charset, String s)
|
121
|
+
{
|
122
|
+
return s.length();
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
public abstract int countLength(Charset charset, String s);
|
127
|
+
}
|
128
|
+
|
105
129
|
public static class Features
|
106
130
|
{
|
107
131
|
private int maxTableNameLength = 64;
|
132
|
+
private LengthSemantics tableNameLengthSemantics = LengthSemantics.BYTES;
|
108
133
|
private Set<Mode> supportedModes = ImmutableSet.copyOf(Mode.values());
|
109
134
|
private boolean ignoreMergeKeys = false;
|
110
135
|
|
@@ -124,6 +149,18 @@ public abstract class AbstractJdbcOutputPlugin
|
|
124
149
|
return this;
|
125
150
|
}
|
126
151
|
|
152
|
+
public LengthSemantics getTableNameLengthSemantics()
|
153
|
+
{
|
154
|
+
return tableNameLengthSemantics;
|
155
|
+
}
|
156
|
+
|
157
|
+
@JsonProperty
|
158
|
+
public Features setTableNameLengthSemantics(LengthSemantics tableNameLengthSemantics)
|
159
|
+
{
|
160
|
+
this.tableNameLengthSemantics = tableNameLengthSemantics;
|
161
|
+
return this;
|
162
|
+
}
|
163
|
+
|
127
164
|
@JsonProperty
|
128
165
|
public Set<Mode> getSupportedModes()
|
129
166
|
{
|
@@ -412,12 +449,14 @@ public abstract class AbstractJdbcOutputPlugin
|
|
412
449
|
// direct modify mode doesn't need intermediate tables.
|
413
450
|
ImmutableList.Builder<String> intermTableNames = ImmutableList.builder();
|
414
451
|
if (mode.tempTablePerTask()) {
|
415
|
-
String namePrefix = generateIntermediateTableNamePrefix(task.getTable(), con, 3,
|
452
|
+
String namePrefix = generateIntermediateTableNamePrefix(task.getTable(), con, 3,
|
453
|
+
task.getFeatures().getMaxTableNameLength(), task.getFeatures().getTableNameLengthSemantics());
|
416
454
|
for (int i=0; i < taskCount; i++) {
|
417
455
|
intermTableNames.add(namePrefix + String.format("%03d", i));
|
418
456
|
}
|
419
457
|
} else {
|
420
|
-
String name = generateIntermediateTableNamePrefix(task.getTable(), con, 0,
|
458
|
+
String name = generateIntermediateTableNamePrefix(task.getTable(), con, 0,
|
459
|
+
task.getFeatures().getMaxTableNameLength(), task.getFeatures().getTableNameLengthSemantics());
|
421
460
|
intermTableNames.add(name);
|
422
461
|
}
|
423
462
|
// create the intermediate tables here
|
@@ -452,7 +491,7 @@ public abstract class AbstractJdbcOutputPlugin
|
|
452
491
|
|
453
492
|
// validate column_options
|
454
493
|
newColumnSetters(
|
455
|
-
|
494
|
+
newColumnSetterFactory(null, task.getDefaultTimeZone()), // TODO create a dummy BatchInsert
|
456
495
|
task.getTargetTableSchema(), schema,
|
457
496
|
task.getColumnOptions());
|
458
497
|
|
@@ -491,7 +530,13 @@ public abstract class AbstractJdbcOutputPlugin
|
|
491
530
|
}
|
492
531
|
}
|
493
532
|
|
494
|
-
protected
|
533
|
+
protected ColumnSetterFactory newColumnSetterFactory(BatchInsert batch, DateTimeZone defaultTimeZone)
|
534
|
+
{
|
535
|
+
return new ColumnSetterFactory(batch, defaultTimeZone);
|
536
|
+
}
|
537
|
+
|
538
|
+
protected String generateIntermediateTableNamePrefix(String baseTableName, JdbcOutputConnection con,
|
539
|
+
int suffixLength, int maxLength, LengthSemantics lengthSemantics) throws SQLException
|
495
540
|
{
|
496
541
|
Charset tableNameCharset = con.getTableNameCharset();
|
497
542
|
String tableName = baseTableName;
|
@@ -500,7 +545,7 @@ public abstract class AbstractJdbcOutputPlugin
|
|
500
545
|
|
501
546
|
// way to count length of table name varies by DBMSs (bytes or characters),
|
502
547
|
// so truncate swap table name by one character.
|
503
|
-
while (!checkTableNameLength(tableName + "_" + uniqueSuffix, tableNameCharset, suffixLength, maxLength)) {
|
548
|
+
while (!checkTableNameLength(tableName + "_" + uniqueSuffix, tableNameCharset, suffixLength, maxLength, lengthSemantics)) {
|
504
549
|
if (uniqueSuffix.length() > 8 + suffix.length()) {
|
505
550
|
// truncate transaction unique name
|
506
551
|
// (include 8 characters of the transaction name at least)
|
@@ -572,9 +617,10 @@ public abstract class AbstractJdbcOutputPlugin
|
|
572
617
|
});
|
573
618
|
}
|
574
619
|
|
575
|
-
private boolean checkTableNameLength(String tableName, Charset tableNameCharset,
|
620
|
+
private boolean checkTableNameLength(String tableName, Charset tableNameCharset,
|
621
|
+
int suffixLength, int maxLength, LengthSemantics lengthSemantics)
|
576
622
|
{
|
577
|
-
return
|
623
|
+
return lengthSemantics.countLength(tableNameCharset, tableName) + suffixLength <= maxLength;
|
578
624
|
}
|
579
625
|
|
580
626
|
protected void doCommit(JdbcOutputConnection con, PluginTask task, int taskCount)
|
@@ -773,7 +819,7 @@ public abstract class AbstractJdbcOutputPlugin
|
|
773
819
|
PageReader reader = new PageReader(schema);
|
774
820
|
|
775
821
|
List<ColumnSetter> columnSetters = newColumnSetters(
|
776
|
-
|
822
|
+
newColumnSetterFactory(batch, task.getDefaultTimeZone()),
|
777
823
|
task.getTargetTableSchema(), schema,
|
778
824
|
task.getColumnOptions());
|
779
825
|
JdbcSchema insertIntoSchema = filterSkipColumns(task.getTargetTableSchema());
|
@@ -153,6 +153,16 @@ public class JdbcOutputConnection
|
|
153
153
|
return sb.toString();
|
154
154
|
}
|
155
155
|
|
156
|
+
protected String buildRenameTableSql(String fromTable, String toTable)
|
157
|
+
{
|
158
|
+
StringBuilder sb = new StringBuilder();
|
159
|
+
sb.append("ALTER TABLE ");
|
160
|
+
quoteIdentifierString(sb, fromTable);
|
161
|
+
sb.append(" RENAME TO ");
|
162
|
+
quoteIdentifierString(sb, toTable);
|
163
|
+
return sb.toString();
|
164
|
+
}
|
165
|
+
|
156
166
|
public static enum ColumnDeclareType
|
157
167
|
{
|
158
168
|
SIMPLE,
|
@@ -208,6 +218,7 @@ public class JdbcOutputConnection
|
|
208
218
|
|
209
219
|
private static final String[] STANDARD_SIZE_AND_SCALE_TYPE_NAMES = new String[] {
|
210
220
|
"DECIMAL",
|
221
|
+
"NUMERIC",
|
211
222
|
};
|
212
223
|
|
213
224
|
protected ColumnDeclareType getColumnDeclareType(String convertedTypeName, JdbcColumn col)
|
@@ -346,13 +357,7 @@ public class JdbcOutputConnection
|
|
346
357
|
try {
|
347
358
|
dropTableIfExists(stmt, toTable);
|
348
359
|
|
349
|
-
|
350
|
-
sb.append("ALTER TABLE ");
|
351
|
-
quoteIdentifierString(sb, fromTable);
|
352
|
-
sb.append(" RENAME TO ");
|
353
|
-
quoteIdentifierString(sb, toTable);
|
354
|
-
String sql = sb.toString();
|
355
|
-
executeUpdate(stmt, sql);
|
360
|
+
executeUpdate(stmt, buildRenameTableSql(fromTable, toTable));
|
356
361
|
|
357
362
|
commitIfNecessary(connection);
|
358
363
|
} catch (SQLException ex) {
|
@@ -10,7 +10,7 @@ import org.embulk.output.jdbc.BatchInsert;
|
|
10
10
|
public class SqlDateColumnSetter
|
11
11
|
extends ColumnSetter
|
12
12
|
{
|
13
|
-
|
13
|
+
protected final Calendar calendar;
|
14
14
|
|
15
15
|
public SqlDateColumnSetter(BatchInsert batch, JdbcColumn column,
|
16
16
|
DefaultValueSetter defaultValue,
|
@@ -10,7 +10,7 @@ import org.embulk.output.jdbc.BatchInsert;
|
|
10
10
|
public class SqlTimeColumnSetter
|
11
11
|
extends ColumnSetter
|
12
12
|
{
|
13
|
-
|
13
|
+
protected final Calendar calendar;
|
14
14
|
|
15
15
|
public SqlTimeColumnSetter(BatchInsert batch, JdbcColumn column,
|
16
16
|
DefaultValueSetter defaultValue,
|
@@ -10,7 +10,7 @@ import org.embulk.output.jdbc.BatchInsert;
|
|
10
10
|
public class SqlTimestampColumnSetter
|
11
11
|
extends ColumnSetter
|
12
12
|
{
|
13
|
-
|
13
|
+
protected final Calendar calendar;
|
14
14
|
|
15
15
|
public SqlTimestampColumnSetter(BatchInsert batch, JdbcColumn column,
|
16
16
|
DefaultValueSetter defaultValue,
|
@@ -0,0 +1,117 @@
|
|
1
|
+
package org.embulk.output;
|
2
|
+
|
3
|
+
import java.io.File;
|
4
|
+
import java.net.URISyntaxException;
|
5
|
+
import java.nio.charset.Charset;
|
6
|
+
import java.sql.Connection;
|
7
|
+
import java.sql.ResultSet;
|
8
|
+
import java.sql.SQLException;
|
9
|
+
import java.sql.Statement;
|
10
|
+
import java.util.ArrayList;
|
11
|
+
import java.util.Collections;
|
12
|
+
import java.util.Comparator;
|
13
|
+
import java.util.List;
|
14
|
+
import java.util.regex.Matcher;
|
15
|
+
import java.util.regex.Pattern;
|
16
|
+
|
17
|
+
import com.google.common.io.Files;
|
18
|
+
|
19
|
+
public abstract class AbstractJdbcOutputPluginTest
|
20
|
+
{
|
21
|
+
protected void dropTable(String table) throws SQLException
|
22
|
+
{
|
23
|
+
String sql = String.format("DROP TABLE %s", table);
|
24
|
+
executeSQL(sql, true);
|
25
|
+
}
|
26
|
+
|
27
|
+
protected List<List<Object>> select(String table) throws SQLException
|
28
|
+
{
|
29
|
+
try (Connection connection = connect()) {
|
30
|
+
try (Statement statement = connection.createStatement()) {
|
31
|
+
List<List<Object>> rows = new ArrayList<List<Object>>();
|
32
|
+
String sql = String.format("SELECT * FROM %s", table);
|
33
|
+
System.out.println(sql);
|
34
|
+
try (ResultSet resultSet = statement.executeQuery(sql)) {
|
35
|
+
while (resultSet.next()) {
|
36
|
+
List<Object> row = new ArrayList<Object>();
|
37
|
+
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
|
38
|
+
row.add(getValue(resultSet, i));
|
39
|
+
}
|
40
|
+
rows.add(row);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
// cannot sort by CLOB, so sort by Java
|
44
|
+
Collections.sort(rows, new Comparator<List<Object>>() {
|
45
|
+
@Override
|
46
|
+
public int compare(List<Object> o1, List<Object> o2) {
|
47
|
+
return o1.toString().compareTo(o2.toString());
|
48
|
+
}
|
49
|
+
});
|
50
|
+
return rows;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
}
|
55
|
+
|
56
|
+
protected Object getValue(ResultSet resultSet, int index) throws SQLException
|
57
|
+
{
|
58
|
+
return resultSet.getObject(index);
|
59
|
+
}
|
60
|
+
|
61
|
+
protected void executeSQL(String sql) throws SQLException
|
62
|
+
{
|
63
|
+
executeSQL(sql, false);
|
64
|
+
}
|
65
|
+
|
66
|
+
protected void executeSQL(String sql, boolean ignoreError) throws SQLException
|
67
|
+
{
|
68
|
+
try (Connection connection = connect()) {
|
69
|
+
try {
|
70
|
+
connection.setAutoCommit(true);
|
71
|
+
|
72
|
+
try (Statement statement = connection.createStatement()) {
|
73
|
+
System.out.println(String.format("Execute SQL : \"%s\".", sql));
|
74
|
+
statement.execute(sql);
|
75
|
+
}
|
76
|
+
|
77
|
+
} catch (SQLException e) {
|
78
|
+
if (!ignoreError) {
|
79
|
+
throw e;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
protected String convertYml(String ymlName) throws Exception
|
86
|
+
{
|
87
|
+
StringBuilder builder = new StringBuilder();
|
88
|
+
Pattern pathPrefixPattern = Pattern.compile("^ *path(_prefix)?: '(.*)'$");
|
89
|
+
for (String line : Files.readLines(convertPath(ymlName), Charset.defaultCharset())) {
|
90
|
+
line = convertYmlLine(line);
|
91
|
+
Matcher matcher = pathPrefixPattern.matcher(line);
|
92
|
+
if (matcher.matches()) {
|
93
|
+
int group = 2;
|
94
|
+
builder.append(line.substring(0, matcher.start(group)));
|
95
|
+
builder.append(convertPath(matcher.group(group)).getAbsolutePath());
|
96
|
+
builder.append(line.substring(matcher.end(group)));
|
97
|
+
} else {
|
98
|
+
builder.append(line);
|
99
|
+
}
|
100
|
+
builder.append(System.lineSeparator());
|
101
|
+
}
|
102
|
+
return builder.toString();
|
103
|
+
}
|
104
|
+
|
105
|
+
protected String convertYmlLine(String line)
|
106
|
+
{
|
107
|
+
return line;
|
108
|
+
}
|
109
|
+
|
110
|
+
protected File convertPath(String name) throws URISyntaxException
|
111
|
+
{
|
112
|
+
return new File(getClass().getResource(name).toURI());
|
113
|
+
}
|
114
|
+
|
115
|
+
protected abstract Connection connect() throws SQLException;
|
116
|
+
|
117
|
+
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
package org.embulk.output.tester;
|
2
|
+
|
3
|
+
import java.util.ArrayList;
|
4
|
+
import java.util.List;
|
5
|
+
|
6
|
+
import org.embulk.EmbulkEmbed;
|
7
|
+
import org.embulk.EmbulkEmbed.Bootstrap;
|
8
|
+
import org.embulk.config.ConfigSource;
|
9
|
+
import org.embulk.plugin.InjectedPluginSource;
|
10
|
+
|
11
|
+
import com.google.inject.Binder;
|
12
|
+
import com.google.inject.Module;
|
13
|
+
|
14
|
+
public class EmbulkPluginTester
|
15
|
+
{
|
16
|
+
private static class PluginDefinition
|
17
|
+
{
|
18
|
+
public final Class<?> iface;
|
19
|
+
public final String name;
|
20
|
+
public final Class<?> impl;
|
21
|
+
|
22
|
+
|
23
|
+
public PluginDefinition(Class<?> iface, String name, Class<?> impl)
|
24
|
+
{
|
25
|
+
this.iface = iface;
|
26
|
+
this.name = name;
|
27
|
+
this.impl = impl;
|
28
|
+
}
|
29
|
+
|
30
|
+
}
|
31
|
+
|
32
|
+
private final List<PluginDefinition> plugins = new ArrayList<PluginDefinition>();
|
33
|
+
|
34
|
+
private EmbulkEmbed embulk;
|
35
|
+
|
36
|
+
public EmbulkPluginTester()
|
37
|
+
{
|
38
|
+
}
|
39
|
+
|
40
|
+
public EmbulkPluginTester(Class<?> iface, String name, Class<?> impl)
|
41
|
+
{
|
42
|
+
addPlugin(iface, name, impl);
|
43
|
+
}
|
44
|
+
|
45
|
+
public void addPlugin(Class<?> iface, String name, Class<?> impl)
|
46
|
+
{
|
47
|
+
plugins.add(new PluginDefinition(iface, name, impl));
|
48
|
+
}
|
49
|
+
|
50
|
+
public void run(String yml) throws Exception
|
51
|
+
{
|
52
|
+
if (embulk == null) {
|
53
|
+
Bootstrap bootstrap = new EmbulkEmbed.Bootstrap();
|
54
|
+
bootstrap.addModules(new Module()
|
55
|
+
{
|
56
|
+
@Override
|
57
|
+
public void configure(Binder binder)
|
58
|
+
{
|
59
|
+
for (PluginDefinition plugin : plugins) {
|
60
|
+
InjectedPluginSource.registerPluginTo(binder, plugin.iface, plugin.name, plugin.impl);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
});
|
64
|
+
embulk = bootstrap.initializeCloseable();
|
65
|
+
}
|
66
|
+
ConfigSource config = embulk.newConfigLoader().fromYamlString(yml);
|
67
|
+
embulk.run(config);
|
68
|
+
}
|
69
|
+
|
70
|
+
public void destroy()
|
71
|
+
{
|
72
|
+
if (embulk != null) {
|
73
|
+
embulk.destroy();
|
74
|
+
embulk = null;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: embulk-output-jdbc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
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-01-
|
11
|
+
date: 2016-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Inserts or updates records to a table.
|
14
14
|
email:
|
@@ -52,8 +52,10 @@ files:
|
|
52
52
|
- src/main/java/org/embulk/output/jdbc/setter/SqlTimeColumnSetter.java
|
53
53
|
- src/main/java/org/embulk/output/jdbc/setter/SqlTimestampColumnSetter.java
|
54
54
|
- src/main/java/org/embulk/output/jdbc/setter/StringColumnSetter.java
|
55
|
+
- src/test/java/org/embulk/output/AbstractJdbcOutputPluginTest.java
|
55
56
|
- src/test/java/org/embulk/output/TestJdbcOutputPlugin.java
|
56
|
-
-
|
57
|
+
- src/test/java/org/embulk/output/tester/EmbulkPluginTester.java
|
58
|
+
- classpath/embulk-output-jdbc-0.5.0.jar
|
57
59
|
homepage: https://github.com/embulk/embulk-output-jdbc
|
58
60
|
licenses:
|
59
61
|
- Apache 2.0
|
Binary file
|