embulk-output-mysql 0.7.7 → 0.8.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/README.md +21 -0
- data/build.gradle +3 -3
- data/classpath/embulk-output-jdbc-0.8.0.jar +0 -0
- data/classpath/embulk-output-mysql-0.8.0.jar +0 -0
- data/default_jdbc_driver/mysql-connector-java-5.1.44.jar +0 -0
- data/src/main/java/org/embulk/output/MySQLOutputPlugin.java +56 -16
- data/src/main/java/org/embulk/output/MySQLTimeZoneComparison.java +107 -0
- data/src/main/java/org/embulk/output/mysql/MySQLOutputConnection.java +14 -5
- data/src/main/java/org/embulk/output/mysql/MySQLOutputConnector.java +3 -8
- data/src/test/java/org/embulk/output/mysql/AfterLoadTest.java +136 -0
- data/src/test/java/org/embulk/output/mysql/BeforeLoadTest.java +124 -0
- data/src/test/java/org/embulk/output/mysql/CreateTableTest.java +79 -0
- data/src/test/java/org/embulk/output/mysql/MySQLTests.java +69 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/setup.sql +8 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test1.csv +4 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_expected.diff +2 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_insert_after_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_insert_after_load_expected.csv +5 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_insert_direct_after_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_merge_after_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_merge_after_load_expected.csv +5 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_merge_direct_after_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_replace_after_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_replace_after_load_expected.csv +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_truncate_insert_after_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/after_load/test_truncate_insert_after_load_expected.csv +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/setup.sql +8 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test1.csv +4 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_expected.diff +2 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_insert_before_load.yml +3 -0
- data/src/test/resources/{mysql/data/test1.csv → org/embulk/output/mysql/test/expect/before_load/test_insert_before_load_expected.csv} +1 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_insert_direct_before_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_merge_before_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_merge_before_load_expected.csv +4 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_merge_direct_before_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_truncate_insert_before_load.yml +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/before_load/test_truncate_insert_before_load_expected.csv +4 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/create_table/setup.sql +1 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/create_table/test1.csv +4 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/create_table/test_table_constraint.yml +5 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/create_table/test_table_constraint_expected.csv +3 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/create_table/test_table_option.yml +5 -0
- data/src/test/resources/org/embulk/output/mysql/test/expect/create_table/test_table_option_expected.csv +3 -0
- metadata +40 -18
- data/classpath/embulk-output-jdbc-0.7.7.jar +0 -0
- data/classpath/embulk-output-mysql-0.7.7.jar +0 -0
- data/classpath/mysql-connector-java-5.1.34.jar +0 -0
- data/src/test/java/org/embulk/output/mysql/MySQLOutputPluginTest.java +0 -569
- data/src/test/resources/mysql/yml/test-insert-after-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-insert-before-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-insert-direct-after-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-insert-direct-before-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-merge-after-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-merge-before-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-merge-direct-after-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-merge-direct-before-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-replace-after-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-truncate-insert-after-load.yml +0 -22
- data/src/test/resources/mysql/yml/test-truncate-insert-before-load.yml +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6da5ba42634421a68e79de5f9c753a76d8406786
|
|
4
|
+
data.tar.gz: 7f80247c0f4c5fa8b973d9dca3914151f90815e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be36e44971e9d805842958a0acd90f69cbde13c141df4e727aff2fcd05158452d463ebfe7d36ff476643e76a71f0793494dc694e1687083ea46892ef5e59955f
|
|
7
|
+
data.tar.gz: 2bbf372969dd29e6368d32da9c29db14322f9035c4291696853d7c8d6f7a75261dae5086b063a09efe5435fa53a57359eb00d8f22b127ec9d493affd86600d46
|
data/README.md
CHANGED
|
@@ -10,12 +10,17 @@ MySQL output plugin for Embulk loads records to MySQL.
|
|
|
10
10
|
|
|
11
11
|
## Configuration
|
|
12
12
|
|
|
13
|
+
- **driver_path**: path to the jar file of the MySQL JDBC driver. If not set, the bundled JDBC driver (MySQL Connector/J 5.1.44) will be used (string). NOTE: the bundled JDBC driver version was upgrade from 5.1.34 to 5.1.44 at embulk-output-mysql 0.8.0 . Please check release notes for MySQL Connector/J (https://dev.mysql.com/doc/relnotes/connector-j/5.1/en/news-5-1.html) .
|
|
13
14
|
- **host**: database host name (string, required)
|
|
14
15
|
- **port**: database port number (integer, default: 3306)
|
|
15
16
|
- **user**: database login user name (string, required)
|
|
16
17
|
- **password**: database login password (string, default: "")
|
|
18
|
+
- **ssl**: use SSL to connect to the database (string, default: `disable`. `enable` uses SSL without server-side validation and `verify` checks the certificate. For compatibility reasons, `true` behaves as `enable` and `false` behaves as `disable`.)
|
|
17
19
|
- **database**: destination database name (string, required)
|
|
20
|
+
- **temp_database**: database name for intermediate tables. by default, intermediate tables will be created in the database specified by `database`. (string, optional)
|
|
18
21
|
- **table**: destination table name (string, required)
|
|
22
|
+
- **create_table_constraint** table constraint added to `CREATE TABLE` statement, like `CREATE TABLE <table_name> (<column1> <type1>, <column2> <type2>, ..., <create_table_constraint>) <create_table_option>`.
|
|
23
|
+
- **create_table_option** table option added to `CREATE TABLE` statement, like `CREATE TABLE <table_name> (<column1> <type1>, <column2> <type2>, ..., <create_table_constraint>) <create_table_option>`.
|
|
19
24
|
- **options**: extra connection properties (hash, default: {})
|
|
20
25
|
- **retry_limit** max retry count for database operations (integer, default: 12)
|
|
21
26
|
- **retry_wait** initial retry wait time in milliseconds (integer, default: 1000 (1 second))
|
|
@@ -118,3 +123,19 @@ out:
|
|
|
118
123
|
```
|
|
119
124
|
$ ./gradlew gem
|
|
120
125
|
```
|
|
126
|
+
|
|
127
|
+
Running tests:
|
|
128
|
+
|
|
129
|
+
You need to create 'mysql.yml' as follows.
|
|
130
|
+
```
|
|
131
|
+
type: mysql
|
|
132
|
+
host: localhost
|
|
133
|
+
port: 3306
|
|
134
|
+
database: database
|
|
135
|
+
user: user
|
|
136
|
+
password: pass
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
$ EMBULK_OUTPUT_MYSQL_TEST_CONFIG=mysql.yml ./gradlew :embulk-output-mysql:check --info
|
|
141
|
+
```
|
data/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
dependencies {
|
|
2
2
|
compile project(':embulk-output-jdbc')
|
|
3
|
-
compile 'mysql:mysql-connector-java:5.1.
|
|
3
|
+
compile 'mysql:mysql-connector-java:5.1.44'
|
|
4
|
+
defaultJdbcDriver 'mysql:mysql-connector-java:5.1.44'
|
|
4
5
|
|
|
5
|
-
testCompile 'org.embulk:embulk-standards:0.8.
|
|
6
|
-
testCompile project(':embulk-output-jdbc').sourceSets.test.output
|
|
6
|
+
testCompile 'org.embulk:embulk-standards:0.8.22'
|
|
7
7
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,14 +3,21 @@ package org.embulk.output;
|
|
|
3
3
|
import java.util.Properties;
|
|
4
4
|
import java.io.IOException;
|
|
5
5
|
import java.sql.SQLException;
|
|
6
|
+
|
|
6
7
|
import com.google.common.base.Optional;
|
|
8
|
+
|
|
7
9
|
import org.embulk.config.Config;
|
|
8
10
|
import org.embulk.config.ConfigDefault;
|
|
9
11
|
import org.embulk.output.jdbc.AbstractJdbcOutputPlugin;
|
|
12
|
+
import org.embulk.output.jdbc.Ssl;
|
|
10
13
|
import org.embulk.output.jdbc.BatchInsert;
|
|
14
|
+
import org.embulk.output.jdbc.JdbcOutputConnection;
|
|
11
15
|
import org.embulk.output.jdbc.MergeConfig;
|
|
16
|
+
import org.embulk.output.jdbc.TableIdentifier;
|
|
17
|
+
import org.embulk.output.mysql.MySQLOutputConnection;
|
|
12
18
|
import org.embulk.output.mysql.MySQLOutputConnector;
|
|
13
19
|
import org.embulk.output.mysql.MySQLBatchInsert;
|
|
20
|
+
import org.embulk.spi.Schema;
|
|
14
21
|
|
|
15
22
|
public class MySQLOutputPlugin
|
|
16
23
|
extends AbstractJdbcOutputPlugin
|
|
@@ -18,6 +25,10 @@ public class MySQLOutputPlugin
|
|
|
18
25
|
public interface MySQLPluginTask
|
|
19
26
|
extends PluginTask
|
|
20
27
|
{
|
|
28
|
+
@Config("driver_path")
|
|
29
|
+
@ConfigDefault("null")
|
|
30
|
+
public Optional<String> getDriverPath();
|
|
31
|
+
|
|
21
32
|
@Config("host")
|
|
22
33
|
public String getHost();
|
|
23
34
|
|
|
@@ -34,6 +45,15 @@ public class MySQLOutputPlugin
|
|
|
34
45
|
|
|
35
46
|
@Config("database")
|
|
36
47
|
public String getDatabase();
|
|
48
|
+
|
|
49
|
+
@Config("temp_database")
|
|
50
|
+
@ConfigDefault("null")
|
|
51
|
+
public Optional<String> getTempDatabase();
|
|
52
|
+
|
|
53
|
+
@Config("ssl")
|
|
54
|
+
@ConfigDefault("\"disable\"") // backward compatibility
|
|
55
|
+
public Ssl getSsl();
|
|
56
|
+
|
|
37
57
|
}
|
|
38
58
|
|
|
39
59
|
@Override
|
|
@@ -55,6 +75,8 @@ public class MySQLOutputPlugin
|
|
|
55
75
|
{
|
|
56
76
|
MySQLPluginTask t = (MySQLPluginTask) task;
|
|
57
77
|
|
|
78
|
+
loadDriver("com.mysql.jdbc.Driver", t.getDriverPath());
|
|
79
|
+
|
|
58
80
|
String url = String.format("jdbc:mysql://%s:%d/%s",
|
|
59
81
|
t.getHost(), t.getPort(), t.getDatabase());
|
|
60
82
|
|
|
@@ -70,21 +92,21 @@ public class MySQLOutputPlugin
|
|
|
70
92
|
// Socket options TCP_KEEPCNT, TCP_KEEPIDLE, and TCP_KEEPINTVL are not configurable.
|
|
71
93
|
props.setProperty("tcpKeepAlive", "true");
|
|
72
94
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
switch (t.getSsl()) {
|
|
96
|
+
case DISABLE:
|
|
97
|
+
props.setProperty("useSSL", "false");
|
|
98
|
+
break;
|
|
99
|
+
case ENABLE:
|
|
100
|
+
props.setProperty("useSSL", "true");
|
|
101
|
+
props.setProperty("requireSSL", "true");
|
|
102
|
+
props.setProperty("verifyServerCertificate", "false");
|
|
103
|
+
break;
|
|
104
|
+
case VERIFY:
|
|
105
|
+
props.setProperty("useSSL", "true");
|
|
106
|
+
props.setProperty("requireSSL", "true");
|
|
107
|
+
props.setProperty("verifyServerCertificate", "true");
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
88
110
|
|
|
89
111
|
if (!retryableMetadataOperation) {
|
|
90
112
|
// non-retryable batch operation uses longer timeout
|
|
@@ -97,12 +119,21 @@ public class MySQLOutputPlugin
|
|
|
97
119
|
// TODO validate task.getMergeKeys is null
|
|
98
120
|
|
|
99
121
|
props.setProperty("user", t.getUser());
|
|
100
|
-
logger.info("Connecting to {} options {}", url, props);
|
|
101
122
|
props.setProperty("password", t.getPassword());
|
|
123
|
+
logConnectionProperties(url, props);
|
|
102
124
|
|
|
103
125
|
return new MySQLOutputConnector(url, props);
|
|
104
126
|
}
|
|
105
127
|
|
|
128
|
+
@Override
|
|
129
|
+
protected TableIdentifier buildIntermediateTableId(JdbcOutputConnection con, PluginTask task, String tableName) {
|
|
130
|
+
MySQLPluginTask t = (MySQLPluginTask) task;
|
|
131
|
+
if (t.getTempDatabase().isPresent()) {
|
|
132
|
+
return new TableIdentifier(t.getTempDatabase().get(), null, tableName);
|
|
133
|
+
}
|
|
134
|
+
return super.buildIntermediateTableId(con, task, tableName);
|
|
135
|
+
}
|
|
136
|
+
|
|
106
137
|
@Override
|
|
107
138
|
protected BatchInsert newBatchInsert(PluginTask task, Optional<MergeConfig> mergeConfig) throws IOException, SQLException
|
|
108
139
|
{
|
|
@@ -122,4 +153,13 @@ public class MySQLOutputPlugin
|
|
|
122
153
|
return false;
|
|
123
154
|
}
|
|
124
155
|
}
|
|
156
|
+
|
|
157
|
+
@Override
|
|
158
|
+
protected void doBegin(JdbcOutputConnection con,
|
|
159
|
+
PluginTask task, final Schema schema, int taskCount) throws SQLException
|
|
160
|
+
{
|
|
161
|
+
MySQLOutputConnection mySQLCon = (MySQLOutputConnection)con;
|
|
162
|
+
mySQLCon.compareTimeZone();
|
|
163
|
+
super.doBegin(con,task,schema,taskCount);
|
|
164
|
+
}
|
|
125
165
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
package org.embulk.output;
|
|
2
|
+
|
|
3
|
+
import org.embulk.spi.Exec;
|
|
4
|
+
import org.slf4j.Logger;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import java.sql.Connection;
|
|
8
|
+
import java.sql.ResultSet;
|
|
9
|
+
import java.sql.SQLException;
|
|
10
|
+
import java.sql.Statement;
|
|
11
|
+
import java.util.Date;
|
|
12
|
+
import java.util.Locale;
|
|
13
|
+
import java.util.TimeZone;
|
|
14
|
+
|
|
15
|
+
public class MySQLTimeZoneComparison {
|
|
16
|
+
|
|
17
|
+
private static final int ONE_HOUR_SEC = 3600;
|
|
18
|
+
private static final int ONE_MIN_SEC = 60;
|
|
19
|
+
|
|
20
|
+
private Connection connection;
|
|
21
|
+
|
|
22
|
+
private final Logger logger = Exec.getLogger(getClass());
|
|
23
|
+
|
|
24
|
+
public MySQLTimeZoneComparison(Connection connection)
|
|
25
|
+
{
|
|
26
|
+
this.connection = connection;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public void compareTimeZone()
|
|
30
|
+
throws SQLException
|
|
31
|
+
{
|
|
32
|
+
TimeZone serverTimeZone = null;
|
|
33
|
+
try {
|
|
34
|
+
serverTimeZone = getServerTimeZone();
|
|
35
|
+
}
|
|
36
|
+
catch (SQLException ex) {
|
|
37
|
+
logger.warn("Can't get server TimeZone.");
|
|
38
|
+
logger.warn(String.format(Locale.ENGLISH, "SQLException raised %s", ex.toString()));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
TimeZone clientTimeZone = TimeZone.getDefault();
|
|
42
|
+
Date today = new Date();
|
|
43
|
+
int clientOffset = clientTimeZone.getRawOffset();
|
|
44
|
+
|
|
45
|
+
if (clientTimeZone.inDaylightTime(today)) {
|
|
46
|
+
clientOffset += clientTimeZone.getDSTSavings();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//
|
|
50
|
+
// Compare offset only. Although I expect to return true, the following code return false,
|
|
51
|
+
//
|
|
52
|
+
// TimeZone tz_jst = TimeZone.getTimeZone("JST");
|
|
53
|
+
// TimeZone tz_gmt9 = TimeZone.getTimeZone("GMT+9");
|
|
54
|
+
// tz_jst.hasSameRules(tz_gmt9) // return false.
|
|
55
|
+
//
|
|
56
|
+
if (clientOffset != serverTimeZone.getRawOffset()) {
|
|
57
|
+
logger.warn(String.format(Locale.ENGLISH,
|
|
58
|
+
"The client timezone(%s) is different from the server timezone(%s). The plugin will store wrong datetime values.",
|
|
59
|
+
clientTimeZone.getID(), serverTimeZone.getID()));
|
|
60
|
+
logger.warn(String.format(Locale.ENGLISH,
|
|
61
|
+
"You may need to set options `useLegacyDatetimeCode` and `serverTimezone`"));
|
|
62
|
+
logger.warn(String.format(Locale.ENGLISH,
|
|
63
|
+
"Example. `options: { useLegacyDatetimeCode: false, serverTimezone: UTC }`"));
|
|
64
|
+
}
|
|
65
|
+
logger.warn(String.format(Locale.ENGLISH, "The plugin will set `useLegacyDatetimeCode=false` by default in future."));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private TimeZone getServerTimeZone()
|
|
69
|
+
throws SQLException
|
|
70
|
+
{
|
|
71
|
+
//
|
|
72
|
+
// First, I used `@@system_time_zone`. but It return non Time Zone Abbreviations name on a specific platform.
|
|
73
|
+
// So, This method calculate GMT offset with query.
|
|
74
|
+
//
|
|
75
|
+
String query = "select TIME_TO_SEC(timediff(now(),utc_timestamp()));";
|
|
76
|
+
Statement stmt = connection.createStatement();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
ResultSet rs = stmt.executeQuery(query);
|
|
80
|
+
if (rs.next()) {
|
|
81
|
+
int offsetSeconds = rs.getInt(1);
|
|
82
|
+
return fromGMTOffsetSeconds(offsetSeconds);
|
|
83
|
+
}
|
|
84
|
+
throw new SQLException(String.format(Locale.ENGLISH,
|
|
85
|
+
"The timezone comparison query(%s) doesn't return the result.",query));
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
stmt.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private TimeZone fromGMTOffsetSeconds(int offsetSeconds)
|
|
93
|
+
{
|
|
94
|
+
if (offsetSeconds == 0) {
|
|
95
|
+
return TimeZone.getTimeZone("UTC");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
String sign = offsetSeconds > 0 ? "+" : "-";
|
|
99
|
+
int absOffsetSec = Math.abs(offsetSeconds);
|
|
100
|
+
int tzHour = absOffsetSec / ONE_HOUR_SEC;
|
|
101
|
+
int tzMin = absOffsetSec % ONE_HOUR_SEC / ONE_MIN_SEC;
|
|
102
|
+
String tzName = String.format(Locale.ENGLISH, "GMT%s%02d:%02d", sign, tzHour, tzMin);
|
|
103
|
+
return TimeZone.getTimeZone(tzName);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
}
|
|
@@ -4,10 +4,12 @@ import java.util.List;
|
|
|
4
4
|
import java.sql.Connection;
|
|
5
5
|
import java.sql.SQLException;
|
|
6
6
|
|
|
7
|
+
import org.embulk.output.MySQLTimeZoneComparison;
|
|
7
8
|
import org.embulk.output.jdbc.JdbcColumn;
|
|
8
9
|
import org.embulk.output.jdbc.JdbcSchema;
|
|
9
10
|
import org.embulk.output.jdbc.JdbcOutputConnection;
|
|
10
11
|
import org.embulk.output.jdbc.MergeConfig;
|
|
12
|
+
import org.embulk.output.jdbc.TableIdentifier;
|
|
11
13
|
|
|
12
14
|
public class MySQLOutputConnection
|
|
13
15
|
extends JdbcOutputConnection
|
|
@@ -20,12 +22,12 @@ public class MySQLOutputConnection
|
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
@Override
|
|
23
|
-
protected String buildPreparedMergeSql(
|
|
25
|
+
protected String buildPreparedMergeSql(TableIdentifier toTable, JdbcSchema toTableSchema, MergeConfig mergeConfig) throws SQLException
|
|
24
26
|
{
|
|
25
27
|
StringBuilder sb = new StringBuilder();
|
|
26
28
|
|
|
27
29
|
sb.append("INSERT INTO ");
|
|
28
|
-
|
|
30
|
+
quoteTableIdentifier(sb, toTable);
|
|
29
31
|
sb.append(" (");
|
|
30
32
|
for (int i = 0; i < toTableSchema.getCount(); i++) {
|
|
31
33
|
if(i != 0) { sb.append(", "); }
|
|
@@ -58,12 +60,12 @@ public class MySQLOutputConnection
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
@Override
|
|
61
|
-
protected String buildCollectMergeSql(List<
|
|
63
|
+
protected String buildCollectMergeSql(List<TableIdentifier> fromTables, JdbcSchema schema, TableIdentifier toTable, MergeConfig mergeConfig) throws SQLException
|
|
62
64
|
{
|
|
63
65
|
StringBuilder sb = new StringBuilder();
|
|
64
66
|
|
|
65
67
|
sb.append("INSERT INTO ");
|
|
66
|
-
|
|
68
|
+
quoteTableIdentifier(sb, toTable);
|
|
67
69
|
sb.append(" (");
|
|
68
70
|
for (int i = 0; i < schema.getCount(); i++) {
|
|
69
71
|
if (i != 0) { sb.append(", "); }
|
|
@@ -78,7 +80,7 @@ public class MySQLOutputConnection
|
|
|
78
80
|
quoteIdentifierString(sb, schema.getColumnName(j));
|
|
79
81
|
}
|
|
80
82
|
sb.append(" FROM ");
|
|
81
|
-
|
|
83
|
+
quoteTableIdentifier(sb, fromTables.get(i));
|
|
82
84
|
}
|
|
83
85
|
sb.append(" ON DUPLICATE KEY UPDATE ");
|
|
84
86
|
if (mergeConfig.getMergeRule().isPresent()) {
|
|
@@ -110,4 +112,11 @@ public class MySQLOutputConnection
|
|
|
110
112
|
return super.buildColumnTypeName(c);
|
|
111
113
|
}
|
|
112
114
|
}
|
|
115
|
+
|
|
116
|
+
public void compareTimeZone() throws SQLException
|
|
117
|
+
{
|
|
118
|
+
MySQLTimeZoneComparison timeZoneComparison = new MySQLTimeZoneComparison(connection);
|
|
119
|
+
timeZoneComparison.compareTimeZone();
|
|
120
|
+
}
|
|
121
|
+
|
|
113
122
|
}
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
package org.embulk.output.mysql;
|
|
2
2
|
|
|
3
3
|
import java.util.Properties;
|
|
4
|
-
import java.sql.Driver;
|
|
5
4
|
import java.sql.Connection;
|
|
5
|
+
import java.sql.DriverManager;
|
|
6
6
|
import java.sql.SQLException;
|
|
7
|
+
|
|
7
8
|
import org.embulk.output.jdbc.JdbcOutputConnector;
|
|
8
9
|
|
|
9
10
|
public class MySQLOutputConnector
|
|
10
11
|
implements JdbcOutputConnector
|
|
11
12
|
{
|
|
12
|
-
private final Driver driver;
|
|
13
13
|
private final String url;
|
|
14
14
|
private final Properties properties;
|
|
15
15
|
|
|
16
16
|
public MySQLOutputConnector(String url, Properties properties)
|
|
17
17
|
{
|
|
18
|
-
try {
|
|
19
|
-
this.driver = new com.mysql.jdbc.Driver(); // new com.mysql.jdbc.Driver throws SQLException
|
|
20
|
-
} catch (SQLException ex) {
|
|
21
|
-
throw new RuntimeException(ex);
|
|
22
|
-
}
|
|
23
18
|
this.url = url;
|
|
24
19
|
this.properties = properties;
|
|
25
20
|
}
|
|
@@ -27,7 +22,7 @@ public class MySQLOutputConnector
|
|
|
27
22
|
@Override
|
|
28
23
|
public MySQLOutputConnection connect(boolean autoCommit) throws SQLException
|
|
29
24
|
{
|
|
30
|
-
Connection c =
|
|
25
|
+
Connection c = DriverManager.getConnection(url, properties);
|
|
31
26
|
try {
|
|
32
27
|
MySQLOutputConnection con = new MySQLOutputConnection(c, autoCommit);
|
|
33
28
|
c = null;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
package org.embulk.output.mysql;
|
|
2
|
+
|
|
3
|
+
import static org.embulk.output.mysql.MySQLTests.execute;
|
|
4
|
+
import static org.embulk.output.mysql.MySQLTests.selectRecords;
|
|
5
|
+
import static org.hamcrest.Matchers.is;
|
|
6
|
+
import static org.junit.Assert.assertThat;
|
|
7
|
+
|
|
8
|
+
import java.io.File;
|
|
9
|
+
import java.net.URISyntaxException;
|
|
10
|
+
import java.net.URL;
|
|
11
|
+
import java.nio.file.FileSystems;
|
|
12
|
+
import java.nio.file.Path;
|
|
13
|
+
|
|
14
|
+
import org.embulk.config.ConfigDiff;
|
|
15
|
+
import org.embulk.config.ConfigSource;
|
|
16
|
+
import org.embulk.output.MySQLOutputPlugin;
|
|
17
|
+
import org.embulk.spi.OutputPlugin;
|
|
18
|
+
import org.embulk.test.EmbulkTests;
|
|
19
|
+
import org.embulk.test.TestingEmbulk;
|
|
20
|
+
import org.junit.Before;
|
|
21
|
+
import org.junit.Rule;
|
|
22
|
+
import org.junit.Test;
|
|
23
|
+
|
|
24
|
+
import com.google.common.io.Resources;
|
|
25
|
+
|
|
26
|
+
public class AfterLoadTest
|
|
27
|
+
{
|
|
28
|
+
private static final String BASIC_RESOURCE_PATH = "org/embulk/output/mysql/test/expect/after_load/";
|
|
29
|
+
|
|
30
|
+
private static ConfigSource loadYamlResource(TestingEmbulk embulk, String fileName)
|
|
31
|
+
{
|
|
32
|
+
return embulk.loadYamlResource(BASIC_RESOURCE_PATH + fileName);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private static String readResource(String fileName)
|
|
36
|
+
{
|
|
37
|
+
return EmbulkTests.readResource(BASIC_RESOURCE_PATH + fileName);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Rule
|
|
41
|
+
public TestingEmbulk embulk = TestingEmbulk.builder()
|
|
42
|
+
.registerPlugin(OutputPlugin.class, "mysql", MySQLOutputPlugin.class)
|
|
43
|
+
.build();
|
|
44
|
+
|
|
45
|
+
private ConfigSource baseConfig;
|
|
46
|
+
|
|
47
|
+
@Before
|
|
48
|
+
public void setup()
|
|
49
|
+
{
|
|
50
|
+
baseConfig = MySQLTests.baseConfig();
|
|
51
|
+
execute(readResource("setup.sql")); // setup rows
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Test
|
|
55
|
+
public void testInsertAfterLoad() throws Exception
|
|
56
|
+
{
|
|
57
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
58
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
59
|
+
|
|
60
|
+
Path in1 = toPath("test1.csv");
|
|
61
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_insert_after_load.yml")), in1);
|
|
62
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_insert_after_load_expected.csv")));
|
|
63
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
public void testInsertDirectAfterLoad() throws Exception
|
|
68
|
+
{
|
|
69
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
70
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
71
|
+
|
|
72
|
+
Path in1 = toPath("test1.csv");
|
|
73
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_insert_direct_after_load.yml")), in1);
|
|
74
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_insert_after_load_expected.csv")));
|
|
75
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Test
|
|
79
|
+
public void testTruncateInsertAfterLoad() throws Exception
|
|
80
|
+
{
|
|
81
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
82
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
83
|
+
|
|
84
|
+
Path in1 = toPath("test1.csv");
|
|
85
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_replace_after_load.yml")), in1);
|
|
86
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_replace_after_load_expected.csv")));
|
|
87
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@Test
|
|
91
|
+
public void testReplaceAfterLoad() throws Exception
|
|
92
|
+
{
|
|
93
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
94
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
95
|
+
|
|
96
|
+
Path in1 = toPath("test1.csv");
|
|
97
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_truncate_insert_after_load.yml")), in1);
|
|
98
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_truncate_insert_after_load_expected.csv")));
|
|
99
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@Test
|
|
103
|
+
public void testMergeAfterLoad() throws Exception
|
|
104
|
+
{
|
|
105
|
+
execute("insert into test1 values('A002', 1, 'y')");
|
|
106
|
+
execute("insert into test1 values('A003', 1, 'y')");
|
|
107
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
108
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
109
|
+
|
|
110
|
+
Path in1 = toPath("test1.csv");
|
|
111
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_merge_after_load.yml")), in1);
|
|
112
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_merge_after_load_expected.csv")));
|
|
113
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@Test
|
|
117
|
+
public void testMergeDirectAfterLoad() throws Exception
|
|
118
|
+
{
|
|
119
|
+
execute("insert into test1 values('A002', 1, 'y')");
|
|
120
|
+
execute("insert into test1 values('A003', 1, 'y')");
|
|
121
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
122
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
123
|
+
|
|
124
|
+
Path in1 = toPath("test1.csv");
|
|
125
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_merge_direct_after_load.yml")), in1);
|
|
126
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_merge_after_load_expected.csv")));
|
|
127
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private Path toPath(String fileName) throws URISyntaxException
|
|
131
|
+
{
|
|
132
|
+
URL url = Resources.getResource(BASIC_RESOURCE_PATH + fileName);
|
|
133
|
+
return FileSystems.getDefault().getPath(new File(url.toURI()).getAbsolutePath());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
package org.embulk.output.mysql;
|
|
2
|
+
|
|
3
|
+
import static org.embulk.output.mysql.MySQLTests.execute;
|
|
4
|
+
import static org.embulk.output.mysql.MySQLTests.selectRecords;
|
|
5
|
+
import static org.hamcrest.Matchers.is;
|
|
6
|
+
import static org.junit.Assert.assertThat;
|
|
7
|
+
|
|
8
|
+
import java.io.File;
|
|
9
|
+
import java.net.URISyntaxException;
|
|
10
|
+
import java.net.URL;
|
|
11
|
+
import java.nio.file.FileSystems;
|
|
12
|
+
import java.nio.file.Path;
|
|
13
|
+
|
|
14
|
+
import org.embulk.config.ConfigDiff;
|
|
15
|
+
import org.embulk.config.ConfigSource;
|
|
16
|
+
import org.embulk.output.MySQLOutputPlugin;
|
|
17
|
+
import org.embulk.spi.OutputPlugin;
|
|
18
|
+
import org.embulk.test.EmbulkTests;
|
|
19
|
+
import org.embulk.test.TestingEmbulk;
|
|
20
|
+
import org.junit.Before;
|
|
21
|
+
import org.junit.Rule;
|
|
22
|
+
import org.junit.Test;
|
|
23
|
+
|
|
24
|
+
import com.google.common.io.Resources;
|
|
25
|
+
|
|
26
|
+
public class BeforeLoadTest
|
|
27
|
+
{
|
|
28
|
+
private static final String BASIC_RESOURCE_PATH = "org/embulk/output/mysql/test/expect/before_load/";
|
|
29
|
+
|
|
30
|
+
private static ConfigSource loadYamlResource(TestingEmbulk embulk, String fileName)
|
|
31
|
+
{
|
|
32
|
+
return embulk.loadYamlResource(BASIC_RESOURCE_PATH + fileName);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private static String readResource(String fileName)
|
|
36
|
+
{
|
|
37
|
+
return EmbulkTests.readResource(BASIC_RESOURCE_PATH + fileName);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Rule
|
|
41
|
+
public TestingEmbulk embulk = TestingEmbulk.builder()
|
|
42
|
+
.registerPlugin(OutputPlugin.class, "mysql", MySQLOutputPlugin.class)
|
|
43
|
+
.build();
|
|
44
|
+
|
|
45
|
+
private ConfigSource baseConfig;
|
|
46
|
+
|
|
47
|
+
@Before
|
|
48
|
+
public void setup()
|
|
49
|
+
{
|
|
50
|
+
baseConfig = MySQLTests.baseConfig();
|
|
51
|
+
execute(readResource("setup.sql")); // setup rows
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Test
|
|
55
|
+
public void testInsertBeforeLoad() throws Exception
|
|
56
|
+
{
|
|
57
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
58
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
59
|
+
|
|
60
|
+
Path in1 = toPath("test1.csv");
|
|
61
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_insert_before_load.yml")), in1);
|
|
62
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_insert_before_load_expected.csv")));
|
|
63
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
public void testInsertDirectBeforeLoad() throws Exception
|
|
68
|
+
{
|
|
69
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
70
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
71
|
+
|
|
72
|
+
Path in1 = toPath("test1.csv");
|
|
73
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_insert_direct_before_load.yml")), in1);
|
|
74
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_insert_before_load_expected.csv")));
|
|
75
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@Test
|
|
79
|
+
public void testTruncateInsertBeforeLoad() throws Exception
|
|
80
|
+
{
|
|
81
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
82
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
83
|
+
|
|
84
|
+
Path in1 = toPath("test1.csv");
|
|
85
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_truncate_insert_before_load.yml")), in1);
|
|
86
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_truncate_insert_before_load_expected.csv")));
|
|
87
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@Test
|
|
91
|
+
public void testMergeBeforeLoad() throws Exception
|
|
92
|
+
{
|
|
93
|
+
execute("insert into test1 values('A002', 1, 'y')");
|
|
94
|
+
execute("insert into test1 values('A003', 1, 'y')");
|
|
95
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
96
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
97
|
+
|
|
98
|
+
Path in1 = toPath("test1.csv");
|
|
99
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_merge_before_load.yml")), in1);
|
|
100
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_merge_before_load_expected.csv")));
|
|
101
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Test
|
|
105
|
+
public void testMergeDirectBeforeLoad() throws Exception
|
|
106
|
+
{
|
|
107
|
+
execute("insert into test1 values('A002', 1, 'y')");
|
|
108
|
+
execute("insert into test1 values('A003', 1, 'y')");
|
|
109
|
+
execute("insert into test1 values('B001', 0, 'z')");
|
|
110
|
+
execute("insert into test1 values('B002', 9, 'z')");
|
|
111
|
+
|
|
112
|
+
Path in1 = toPath("test1.csv");
|
|
113
|
+
TestingEmbulk.RunResult result1 = embulk.runOutput(baseConfig.merge(loadYamlResource(embulk, "test_merge_direct_before_load.yml")), in1);
|
|
114
|
+
assertThat(selectRecords(embulk, "test1"), is(readResource("test_merge_before_load_expected.csv")));
|
|
115
|
+
//assertThat(result1.getConfigDiff(), is((ConfigDiff) loadYamlResource(embulk, "test_expected.diff")));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private Path toPath(String fileName) throws URISyntaxException
|
|
119
|
+
{
|
|
120
|
+
URL url = Resources.getResource(BASIC_RESOURCE_PATH + fileName);
|
|
121
|
+
return FileSystems.getDefault().getPath(new File(url.toURI()).getAbsolutePath());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|