embulk-output-jdbc 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|