embulk-output-jdbc 0.4.1 → 0.4.2

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/build.gradle +2 -2
  3. data/classpath/embulk-output-jdbc-0.4.2.jar +0 -0
  4. data/lib/embulk/output/jdbc.rb +3 -3
  5. data/src/main/java/org/embulk/output/JdbcOutputPlugin.java +137 -138
  6. data/src/main/java/org/embulk/output/jdbc/AbstractJdbcOutputPlugin.java +972 -973
  7. data/src/main/java/org/embulk/output/jdbc/BatchInsert.java +52 -53
  8. data/src/main/java/org/embulk/output/jdbc/JdbcColumn.java +116 -116
  9. data/src/main/java/org/embulk/output/jdbc/JdbcColumnOption.java +34 -34
  10. data/src/main/java/org/embulk/output/jdbc/JdbcOutputConnection.java +492 -492
  11. data/src/main/java/org/embulk/output/jdbc/JdbcOutputConnector.java +8 -8
  12. data/src/main/java/org/embulk/output/jdbc/JdbcSchema.java +60 -60
  13. data/src/main/java/org/embulk/output/jdbc/JdbcUtils.java +153 -155
  14. data/src/main/java/org/embulk/output/jdbc/StandardBatchInsert.java +200 -200
  15. data/src/main/java/org/embulk/output/jdbc/ToString.java +54 -54
  16. data/src/main/java/org/embulk/output/jdbc/ToStringMap.java +34 -35
  17. data/src/main/java/org/embulk/output/jdbc/setter/BigDecimalColumnSetter.java +68 -70
  18. data/src/main/java/org/embulk/output/jdbc/setter/BooleanColumnSetter.java +53 -54
  19. data/src/main/java/org/embulk/output/jdbc/setter/ByteColumnSetter.java +75 -76
  20. data/src/main/java/org/embulk/output/jdbc/setter/ColumnSetter.java +44 -45
  21. data/src/main/java/org/embulk/output/jdbc/setter/ColumnSetterFactory.java +196 -196
  22. data/src/main/java/org/embulk/output/jdbc/setter/ColumnSetterVisitor.java +95 -96
  23. data/src/main/java/org/embulk/output/jdbc/setter/DefaultValueSetter.java +48 -48
  24. data/src/main/java/org/embulk/output/jdbc/setter/DoubleColumnSetter.java +60 -61
  25. data/src/main/java/org/embulk/output/jdbc/setter/FloatColumnSetter.java +60 -61
  26. data/src/main/java/org/embulk/output/jdbc/setter/IntColumnSetter.java +75 -76
  27. data/src/main/java/org/embulk/output/jdbc/setter/LongColumnSetter.java +71 -72
  28. data/src/main/java/org/embulk/output/jdbc/setter/NStringColumnSetter.java +58 -59
  29. data/src/main/java/org/embulk/output/jdbc/setter/NullColumnSetter.java +53 -53
  30. data/src/main/java/org/embulk/output/jdbc/setter/NullDefaultValueSetter.java +105 -105
  31. data/src/main/java/org/embulk/output/jdbc/setter/PassThroughColumnSetter.java +58 -59
  32. data/src/main/java/org/embulk/output/jdbc/setter/ShortColumnSetter.java +75 -76
  33. data/src/main/java/org/embulk/output/jdbc/setter/SkipColumnSetter.java +43 -43
  34. data/src/main/java/org/embulk/output/jdbc/setter/SqlDateColumnSetter.java +58 -59
  35. data/src/main/java/org/embulk/output/jdbc/setter/SqlTimeColumnSetter.java +58 -59
  36. data/src/main/java/org/embulk/output/jdbc/setter/SqlTimestampColumnSetter.java +58 -58
  37. data/src/main/java/org/embulk/output/jdbc/setter/StringColumnSetter.java +58 -59
  38. data/src/test/java/org/embulk/output/TestJdbcOutputPlugin.java +5 -5
  39. metadata +3 -3
  40. data/classpath/embulk-output-jdbc-0.4.1.jar +0 -0
@@ -1,53 +1,52 @@
1
- package org.embulk.output.jdbc;
2
-
3
- import java.math.BigDecimal;
4
- import java.util.Calendar;
5
- import java.io.IOException;
6
- import java.sql.SQLException;
7
- import java.sql.PreparedStatement;
8
- import org.embulk.spi.time.Timestamp;
9
-
10
- public interface BatchInsert
11
- {
12
- public void prepare(String loadTable, JdbcSchema insertSchema) throws SQLException;
13
-
14
- public int getBatchWeight();
15
-
16
- public void add() throws IOException, SQLException;
17
-
18
- public void close() throws IOException, SQLException;
19
-
20
- public void flush() throws IOException, SQLException;
21
-
22
- public void finish() throws IOException, SQLException;
23
-
24
- public void setNull(int sqlType) throws IOException, SQLException;
25
-
26
- public void setBoolean(boolean v) throws IOException, SQLException;
27
-
28
- public void setByte(byte v) throws IOException, SQLException;
29
-
30
- public void setShort(short v) throws IOException, SQLException;
31
-
32
- public void setInt(int v) throws IOException, SQLException;
33
-
34
- public void setLong(long v) throws IOException, SQLException;
35
-
36
- public void setFloat(float v) throws IOException, SQLException;
37
-
38
- public void setDouble(double v) throws IOException, SQLException;
39
-
40
- public void setBigDecimal(BigDecimal v) throws IOException, SQLException;
41
-
42
- public void setString(String v) throws IOException, SQLException;
43
-
44
- public void setNString(String v) throws IOException, SQLException;
45
-
46
- public void setBytes(byte[] v) throws IOException, SQLException;
47
-
48
- public void setSqlDate(Timestamp v, Calendar cal) throws IOException, SQLException;
49
-
50
- public void setSqlTime(Timestamp v, Calendar cal) throws IOException, SQLException;
51
-
52
- public void setSqlTimestamp(Timestamp v, Calendar cal) throws IOException, SQLException;
53
- }
1
+ package org.embulk.output.jdbc;
2
+
3
+ import java.math.BigDecimal;
4
+ import java.util.Calendar;
5
+ import java.io.IOException;
6
+ import java.sql.SQLException;
7
+ import org.embulk.spi.time.Timestamp;
8
+
9
+ public interface BatchInsert
10
+ {
11
+ public void prepare(String loadTable, JdbcSchema insertSchema) throws SQLException;
12
+
13
+ public int getBatchWeight();
14
+
15
+ public void add() throws IOException, SQLException;
16
+
17
+ public void close() throws IOException, SQLException;
18
+
19
+ public void flush() throws IOException, SQLException;
20
+
21
+ public void finish() throws IOException, SQLException;
22
+
23
+ public void setNull(int sqlType) throws IOException, SQLException;
24
+
25
+ public void setBoolean(boolean v) throws IOException, SQLException;
26
+
27
+ public void setByte(byte v) throws IOException, SQLException;
28
+
29
+ public void setShort(short v) throws IOException, SQLException;
30
+
31
+ public void setInt(int v) throws IOException, SQLException;
32
+
33
+ public void setLong(long v) throws IOException, SQLException;
34
+
35
+ public void setFloat(float v) throws IOException, SQLException;
36
+
37
+ public void setDouble(double v) throws IOException, SQLException;
38
+
39
+ public void setBigDecimal(BigDecimal v) throws IOException, SQLException;
40
+
41
+ public void setString(String v) throws IOException, SQLException;
42
+
43
+ public void setNString(String v) throws IOException, SQLException;
44
+
45
+ public void setBytes(byte[] v) throws IOException, SQLException;
46
+
47
+ public void setSqlDate(Timestamp v, Calendar cal) throws IOException, SQLException;
48
+
49
+ public void setSqlTime(Timestamp v, Calendar cal) throws IOException, SQLException;
50
+
51
+ public void setSqlTimestamp(Timestamp v, Calendar cal) throws IOException, SQLException;
52
+ }
@@ -1,116 +1,116 @@
1
- package org.embulk.output.jdbc;
2
-
3
- import com.google.common.base.Optional;
4
- import com.fasterxml.jackson.annotation.JsonCreator;
5
- import com.fasterxml.jackson.annotation.JsonProperty;
6
- import com.fasterxml.jackson.annotation.JsonIgnore;
7
-
8
- public class JdbcColumn
9
- {
10
- private final String name;
11
- private final String simpleTypeName;
12
- private final int sqlType;
13
- private final int sizeTypeParameter;
14
- private final int scaleTypeParameter;
15
- private final Optional<String> declaredType;
16
- private final boolean isNotNull;
17
- private final boolean isUniqueKey;
18
-
19
- @JsonCreator
20
- public JdbcColumn(
21
- @JsonProperty("name") String name,
22
- @JsonProperty("sqlType") int sqlType,
23
- @JsonProperty("simpleTypeName") String simpleTypeName,
24
- @JsonProperty("sizeTypeParameter") int sizeTypeParameter,
25
- @JsonProperty("scaleTypeParameter") int scaleTypeParameter,
26
- @JsonProperty("declaredType") Optional<String> declaredType,
27
- @JsonProperty("notNull") boolean isNotNull,
28
- @JsonProperty("uniqueKey") boolean isUniqueKey)
29
- {
30
- this.name = name;
31
- this.simpleTypeName = simpleTypeName;
32
- this.sqlType = sqlType;
33
- this.sizeTypeParameter = sizeTypeParameter;
34
- this.scaleTypeParameter = scaleTypeParameter;
35
- this.declaredType = declaredType;
36
- this.isNotNull = isNotNull;
37
- this.isUniqueKey = isUniqueKey;
38
- }
39
-
40
- public static JdbcColumn newGenericTypeColumn(String name, int sqlType,
41
- String simpleTypeName, int sizeTypeParameter, int scaleTypeParameter,
42
- boolean isNotNull, boolean isUniqueKey)
43
- {
44
- return new JdbcColumn(name, sqlType,
45
- simpleTypeName, sizeTypeParameter, scaleTypeParameter, Optional.<String>absent(),
46
- isNotNull, isUniqueKey);
47
- }
48
-
49
- public static JdbcColumn newTypeDeclaredColumn(String name, int sqlType,
50
- String declaredType, boolean isNotNull, boolean isUniqueKey)
51
- {
52
- return new JdbcColumn(name, sqlType,
53
- declaredType, 0, 0, Optional.of(declaredType),
54
- isNotNull, isUniqueKey);
55
- }
56
-
57
- @JsonIgnore
58
- public static JdbcColumn skipColumn()
59
- {
60
- return new JdbcColumn(null, 0, null, 0, 0, Optional.<String>absent(), false, false);
61
- }
62
-
63
- @JsonIgnore
64
- public boolean isSkipColumn()
65
- {
66
- return name == null;
67
- }
68
-
69
- @JsonProperty("name")
70
- public String getName()
71
- {
72
- return name;
73
- }
74
-
75
- @JsonProperty("sqlType")
76
- public int getSqlType()
77
- {
78
- return sqlType;
79
- }
80
-
81
- @JsonProperty("simpleTypeName")
82
- public String getSimpleTypeName()
83
- {
84
- return simpleTypeName;
85
- }
86
-
87
- @JsonProperty("sizeTypeParameter")
88
- public int getSizeTypeParameter()
89
- {
90
- return sizeTypeParameter;
91
- }
92
-
93
- @JsonProperty("scaleTypeParameter")
94
- public int getScaleTypeParameter()
95
- {
96
- return scaleTypeParameter;
97
- }
98
-
99
- @JsonProperty("declaredType")
100
- public Optional<String> getDeclaredType()
101
- {
102
- return declaredType;
103
- }
104
-
105
- @JsonProperty("notNull")
106
- public boolean isNotNull()
107
- {
108
- return isNotNull;
109
- }
110
-
111
- @JsonProperty("uniqueKey")
112
- public boolean isUniqueKey()
113
- {
114
- return isUniqueKey;
115
- }
116
- }
1
+ package org.embulk.output.jdbc;
2
+
3
+ import com.google.common.base.Optional;
4
+ import com.fasterxml.jackson.annotation.JsonCreator;
5
+ import com.fasterxml.jackson.annotation.JsonProperty;
6
+ import com.fasterxml.jackson.annotation.JsonIgnore;
7
+
8
+ public class JdbcColumn
9
+ {
10
+ private final String name;
11
+ private final String simpleTypeName;
12
+ private final int sqlType;
13
+ private final int sizeTypeParameter;
14
+ private final int scaleTypeParameter;
15
+ private final Optional<String> declaredType;
16
+ private final boolean isNotNull;
17
+ private final boolean isUniqueKey;
18
+
19
+ @JsonCreator
20
+ public JdbcColumn(
21
+ @JsonProperty("name") String name,
22
+ @JsonProperty("sqlType") int sqlType,
23
+ @JsonProperty("simpleTypeName") String simpleTypeName,
24
+ @JsonProperty("sizeTypeParameter") int sizeTypeParameter,
25
+ @JsonProperty("scaleTypeParameter") int scaleTypeParameter,
26
+ @JsonProperty("declaredType") Optional<String> declaredType,
27
+ @JsonProperty("notNull") boolean isNotNull,
28
+ @JsonProperty("uniqueKey") boolean isUniqueKey)
29
+ {
30
+ this.name = name;
31
+ this.simpleTypeName = simpleTypeName;
32
+ this.sqlType = sqlType;
33
+ this.sizeTypeParameter = sizeTypeParameter;
34
+ this.scaleTypeParameter = scaleTypeParameter;
35
+ this.declaredType = declaredType;
36
+ this.isNotNull = isNotNull;
37
+ this.isUniqueKey = isUniqueKey;
38
+ }
39
+
40
+ public static JdbcColumn newGenericTypeColumn(String name, int sqlType,
41
+ String simpleTypeName, int sizeTypeParameter, int scaleTypeParameter,
42
+ boolean isNotNull, boolean isUniqueKey)
43
+ {
44
+ return new JdbcColumn(name, sqlType,
45
+ simpleTypeName, sizeTypeParameter, scaleTypeParameter, Optional.<String>absent(),
46
+ isNotNull, isUniqueKey);
47
+ }
48
+
49
+ public static JdbcColumn newTypeDeclaredColumn(String name, int sqlType,
50
+ String declaredType, boolean isNotNull, boolean isUniqueKey)
51
+ {
52
+ return new JdbcColumn(name, sqlType,
53
+ declaredType, 0, 0, Optional.of(declaredType),
54
+ isNotNull, isUniqueKey);
55
+ }
56
+
57
+ @JsonIgnore
58
+ public static JdbcColumn skipColumn()
59
+ {
60
+ return new JdbcColumn(null, 0, null, 0, 0, Optional.<String>absent(), false, false);
61
+ }
62
+
63
+ @JsonIgnore
64
+ public boolean isSkipColumn()
65
+ {
66
+ return name == null;
67
+ }
68
+
69
+ @JsonProperty("name")
70
+ public String getName()
71
+ {
72
+ return name;
73
+ }
74
+
75
+ @JsonProperty("sqlType")
76
+ public int getSqlType()
77
+ {
78
+ return sqlType;
79
+ }
80
+
81
+ @JsonProperty("simpleTypeName")
82
+ public String getSimpleTypeName()
83
+ {
84
+ return simpleTypeName;
85
+ }
86
+
87
+ @JsonProperty("sizeTypeParameter")
88
+ public int getSizeTypeParameter()
89
+ {
90
+ return sizeTypeParameter;
91
+ }
92
+
93
+ @JsonProperty("scaleTypeParameter")
94
+ public int getScaleTypeParameter()
95
+ {
96
+ return scaleTypeParameter;
97
+ }
98
+
99
+ @JsonProperty("declaredType")
100
+ public Optional<String> getDeclaredType()
101
+ {
102
+ return declaredType;
103
+ }
104
+
105
+ @JsonProperty("notNull")
106
+ public boolean isNotNull()
107
+ {
108
+ return isNotNull;
109
+ }
110
+
111
+ @JsonProperty("uniqueKey")
112
+ public boolean isUniqueKey()
113
+ {
114
+ return isUniqueKey;
115
+ }
116
+ }
@@ -1,34 +1,34 @@
1
- package org.embulk.output.jdbc;
2
-
3
- import com.google.common.base.Optional;
4
- import org.joda.time.DateTimeZone;
5
- import org.jruby.embed.ScriptingContainer;
6
- import org.embulk.config.Task;
7
- import org.embulk.config.Config;
8
- import org.embulk.config.ConfigDefault;
9
- import org.embulk.config.ConfigInject;
10
- import org.embulk.spi.time.TimestampFormat;
11
-
12
- public interface JdbcColumnOption
13
- extends Task
14
- {
15
- @Config("type")
16
- @ConfigDefault("null")
17
- public Optional<String> getType();
18
-
19
- @Config("value_type")
20
- @ConfigDefault("\"coerce\"")
21
- public String getValueType();
22
-
23
- @Config("timestamp_format")
24
- @ConfigDefault("\"%Y-%m-%d %H:%M:%S.%6N\"")
25
- public TimestampFormat getTimestampFormat();
26
-
27
- @Config("timezone")
28
- @ConfigDefault("null")
29
- public Optional<DateTimeZone> getTimeZone();
30
-
31
- // required by TimestampFormatter
32
- @ConfigInject
33
- public ScriptingContainer getJRuby();
34
- }
1
+ package org.embulk.output.jdbc;
2
+
3
+ import com.google.common.base.Optional;
4
+ import org.joda.time.DateTimeZone;
5
+ import org.jruby.embed.ScriptingContainer;
6
+ import org.embulk.config.Task;
7
+ import org.embulk.config.Config;
8
+ import org.embulk.config.ConfigDefault;
9
+ import org.embulk.config.ConfigInject;
10
+ import org.embulk.spi.time.TimestampFormat;
11
+
12
+ public interface JdbcColumnOption
13
+ extends Task
14
+ {
15
+ @Config("type")
16
+ @ConfigDefault("null")
17
+ public Optional<String> getType();
18
+
19
+ @Config("value_type")
20
+ @ConfigDefault("\"coerce\"")
21
+ public String getValueType();
22
+
23
+ @Config("timestamp_format")
24
+ @ConfigDefault("\"%Y-%m-%d %H:%M:%S.%6N\"")
25
+ public TimestampFormat getTimestampFormat();
26
+
27
+ @Config("timezone")
28
+ @ConfigDefault("null")
29
+ public Optional<DateTimeZone> getTimeZone();
30
+
31
+ // required by TimestampFormatter
32
+ @ConfigInject
33
+ public ScriptingContainer getJRuby();
34
+ }
@@ -1,492 +1,492 @@
1
- package org.embulk.output.jdbc;
2
-
3
- import java.util.List;
4
- import java.nio.charset.Charset;
5
- import java.nio.charset.StandardCharsets;
6
- import java.sql.Connection;
7
- import java.sql.DatabaseMetaData;
8
- import java.sql.PreparedStatement;
9
- import java.sql.ResultSet;
10
- import java.sql.SQLException;
11
- import java.sql.Statement;
12
- import org.slf4j.Logger;
13
- import com.google.common.base.Optional;
14
- import org.embulk.spi.Exec;
15
-
16
- public class JdbcOutputConnection
17
- implements AutoCloseable
18
- {
19
- private final Logger logger = Exec.getLogger(JdbcOutputConnection.class);
20
- protected final Connection connection;
21
- protected final String schemaName;
22
- protected final DatabaseMetaData databaseMetaData;
23
- protected String identifierQuoteString;
24
-
25
- public JdbcOutputConnection(Connection connection, String schemaName)
26
- throws SQLException
27
- {
28
- this.connection = connection;
29
- this.schemaName = schemaName;
30
- this.databaseMetaData = connection.getMetaData();
31
- this.identifierQuoteString = databaseMetaData.getIdentifierQuoteString();
32
- if (schemaName != null) {
33
- setSearchPath(schemaName);
34
- }
35
- }
36
-
37
- @Override
38
- public void close() throws SQLException
39
- {
40
- connection.close();
41
- }
42
-
43
- public String getSchemaName()
44
- {
45
- return schemaName;
46
- }
47
-
48
- public DatabaseMetaData getMetaData() throws SQLException
49
- {
50
- return databaseMetaData;
51
- }
52
-
53
- public Charset getTableNameCharset() throws SQLException
54
- {
55
- return StandardCharsets.UTF_8;
56
- }
57
-
58
- protected void setSearchPath(String schema) throws SQLException
59
- {
60
- Statement stmt = connection.createStatement();
61
- try {
62
- String sql = "SET search_path TO " + quoteIdentifierString(schema);
63
- executeUpdate(stmt, sql);
64
- commitIfNecessary(connection);
65
- } finally {
66
- stmt.close();
67
- }
68
- }
69
-
70
- public boolean tableExists(String tableName) throws SQLException
71
- {
72
- try (ResultSet rs = connection.getMetaData().getTables(null, schemaName, tableName, null)) {
73
- return rs.next();
74
- }
75
- }
76
-
77
- public void dropTableIfExists(String tableName) throws SQLException
78
- {
79
- Statement stmt = connection.createStatement();
80
- try {
81
- dropTableIfExists(stmt, tableName);
82
- commitIfNecessary(connection);
83
- } catch (SQLException ex) {
84
- throw safeRollback(connection, ex);
85
- } finally {
86
- stmt.close();
87
- }
88
- }
89
-
90
- protected void dropTableIfExists(Statement stmt, String tableName) throws SQLException
91
- {
92
- String sql = String.format("DROP TABLE IF EXISTS %s", quoteIdentifierString(tableName));
93
- executeUpdate(stmt, sql);
94
- }
95
-
96
- public void dropTable(String tableName) throws SQLException
97
- {
98
- Statement stmt = connection.createStatement();
99
- try {
100
- dropTable(stmt, tableName);
101
- commitIfNecessary(connection);
102
- } catch (SQLException ex) {
103
- throw safeRollback(connection, ex);
104
- } finally {
105
- stmt.close();
106
- }
107
- }
108
-
109
- protected void dropTable(Statement stmt, String tableName) throws SQLException
110
- {
111
- String sql = String.format("DROP TABLE %s", quoteIdentifierString(tableName));
112
- executeUpdate(stmt, sql);
113
- }
114
-
115
- public void createTableIfNotExists(String tableName, JdbcSchema schema) throws SQLException
116
- {
117
- Statement stmt = connection.createStatement();
118
- try {
119
- String sql = buildCreateTableIfNotExistsSql(tableName, schema);
120
- executeUpdate(stmt, sql);
121
- commitIfNecessary(connection);
122
- } catch (SQLException ex) {
123
- throw safeRollback(connection, ex);
124
- } finally {
125
- stmt.close();
126
- }
127
- }
128
-
129
- protected String buildCreateTableIfNotExistsSql(String name, JdbcSchema schema)
130
- {
131
- StringBuilder sb = new StringBuilder();
132
-
133
- sb.append("CREATE TABLE IF NOT EXISTS ");
134
- quoteIdentifierString(sb, name);
135
- sb.append(buildCreateTableSchemaSql(schema));
136
- return sb.toString();
137
- }
138
-
139
- protected String buildCreateTableSchemaSql(JdbcSchema schema)
140
- {
141
- StringBuilder sb = new StringBuilder();
142
-
143
- sb.append(" (");
144
- for (int i=0; i < schema.getCount(); i++) {
145
- if (i != 0) { sb.append(", "); }
146
- quoteIdentifierString(sb, schema.getColumnName(i));
147
- sb.append(" ");
148
- String typeName = getCreateTableTypeName(schema.getColumn(i));
149
- sb.append(typeName);
150
- }
151
- sb.append(")");
152
-
153
- return sb.toString();
154
- }
155
-
156
- public static enum ColumnDeclareType
157
- {
158
- SIMPLE,
159
- SIZE,
160
- SIZE_AND_SCALE,
161
- SIZE_AND_OPTIONAL_SCALE,
162
- };
163
-
164
- protected String getCreateTableTypeName(JdbcColumn c)
165
- {
166
- if (c.getDeclaredType().isPresent()) {
167
- return c.getDeclaredType().get();
168
- } else {
169
- return buildColumnTypeName(c);
170
- }
171
- }
172
-
173
- protected String buildColumnTypeName(JdbcColumn c)
174
- {
175
- String simpleTypeName = c.getSimpleTypeName();
176
- switch (getColumnDeclareType(simpleTypeName, c)) {
177
- case SIZE:
178
- return String.format("%s(%d)", simpleTypeName, c.getSizeTypeParameter());
179
- case SIZE_AND_SCALE:
180
- if (c.getScaleTypeParameter() < 0) {
181
- return String.format("%s(%d,0)", simpleTypeName, c.getSizeTypeParameter());
182
- } else {
183
- return String.format("%s(%d,%d)", simpleTypeName, c.getSizeTypeParameter(), c.getScaleTypeParameter());
184
- }
185
- case SIZE_AND_OPTIONAL_SCALE:
186
- if (c.getScaleTypeParameter() < 0) {
187
- return String.format("%s(%d)", simpleTypeName, c.getSizeTypeParameter());
188
- } else {
189
- return String.format("%s(%d,%d)", simpleTypeName, c.getSizeTypeParameter(), c.getScaleTypeParameter());
190
- }
191
- default: // SIMPLE
192
- return simpleTypeName;
193
- }
194
- }
195
-
196
- // TODO
197
- private static final String[] STANDARD_SIZE_TYPE_NAMES = new String[] {
198
- "CHAR",
199
- "VARCHAR", "CHAR VARYING", "CHARACTER VARYING", "LONGVARCHAR",
200
- "NCHAR",
201
- "NVARCHAR", "NCHAR VARYING", "NATIONAL CHAR VARYING", "NATIONAL CHARACTER VARYING",
202
- "BINARY",
203
- "VARBINARY", "BINARY VARYING", "LONGVARBINARY",
204
- "BIT",
205
- "VARBIT", "BIT VARYING",
206
- "FLOAT", // SQL standard's FLOAT[(p)] optionally accepts precision
207
- };
208
-
209
- private static final String[] STANDARD_SIZE_AND_SCALE_TYPE_NAMES = new String[] {
210
- "DECIMAL",
211
- };
212
-
213
- protected ColumnDeclareType getColumnDeclareType(String convertedTypeName, JdbcColumn col)
214
- {
215
- for (String x : STANDARD_SIZE_TYPE_NAMES) {
216
- if (x.equals(convertedTypeName)) {
217
- return ColumnDeclareType.SIZE;
218
- }
219
- }
220
-
221
- for (String x : STANDARD_SIZE_AND_SCALE_TYPE_NAMES) {
222
- if (x.equals(convertedTypeName)) {
223
- return ColumnDeclareType.SIZE_AND_SCALE;
224
- }
225
- }
226
-
227
- return ColumnDeclareType.SIMPLE;
228
- }
229
-
230
- public PreparedStatement prepareBatchInsertStatement(String toTable, JdbcSchema toTableSchema, Optional<List<String>> mergeKeys) throws SQLException
231
- {
232
- String sql;
233
- if (mergeKeys.isPresent()) {
234
- sql = buildPreparedMergeSql(toTable, toTableSchema, mergeKeys.get());
235
- } else {
236
- sql = buildPreparedInsertSql(toTable, toTableSchema);
237
- }
238
- logger.info("Prepared SQL: {}", sql);
239
- return connection.prepareStatement(sql);
240
- }
241
-
242
- protected String buildPreparedInsertSql(String toTable, JdbcSchema toTableSchema) throws SQLException
243
- {
244
- StringBuilder sb = new StringBuilder();
245
-
246
- sb.append("INSERT INTO ");
247
- quoteIdentifierString(sb, toTable);
248
-
249
- sb.append(" (");
250
- for (int i=0; i < toTableSchema.getCount(); i++) {
251
- if(i != 0) { sb.append(", "); }
252
- quoteIdentifierString(sb, toTableSchema.getColumnName(i));
253
- }
254
- sb.append(") VALUES (");
255
- for(int i=0; i < toTableSchema.getCount(); i++) {
256
- if(i != 0) { sb.append(", "); }
257
- sb.append("?");
258
- }
259
- sb.append(")");
260
-
261
- return sb.toString();
262
- }
263
-
264
- protected String buildPreparedMergeSql(String toTable, JdbcSchema toTableSchema, List<String> mergeKeys) throws SQLException
265
- {
266
- throw new UnsupportedOperationException("not implemented");
267
- }
268
-
269
- protected void collectInsert(List<String> fromTables, JdbcSchema schema, String toTable,
270
- boolean truncateDestinationFirst) throws SQLException
271
- {
272
- Statement stmt = connection.createStatement();
273
- try {
274
- if (truncateDestinationFirst) {
275
- String sql = buildTruncateSql(toTable);
276
- executeUpdate(stmt, sql);
277
- }
278
- String sql = buildCollectInsertSql(fromTables, schema, toTable);
279
- executeUpdate(stmt, sql);
280
- commitIfNecessary(connection);
281
- } catch (SQLException ex) {
282
- throw safeRollback(connection, ex);
283
- } finally {
284
- stmt.close();
285
- }
286
- }
287
-
288
- protected String buildTruncateSql(String table)
289
- {
290
- StringBuilder sb = new StringBuilder();
291
-
292
- sb.append("DELETE FROM ");
293
- quoteIdentifierString(sb, table);
294
-
295
- return sb.toString();
296
- }
297
-
298
- protected String buildCollectInsertSql(List<String> fromTables, JdbcSchema schema, String toTable)
299
- {
300
- StringBuilder sb = new StringBuilder();
301
-
302
- sb.append("INSERT INTO ");
303
- quoteIdentifierString(sb, toTable);
304
- sb.append(" (");
305
- for (int i=0; i < schema.getCount(); i++) {
306
- if (i != 0) { sb.append(", "); }
307
- quoteIdentifierString(sb, schema.getColumnName(i));
308
- }
309
- sb.append(") ");
310
- for (int i=0; i < fromTables.size(); i++) {
311
- if (i != 0) { sb.append(" UNION ALL "); }
312
- sb.append("SELECT ");
313
- for (int j=0; j < schema.getCount(); j++) {
314
- if (j != 0) { sb.append(", "); }
315
- quoteIdentifierString(sb, schema.getColumnName(j));
316
- }
317
- sb.append(" FROM ");
318
- quoteIdentifierString(sb, fromTables.get(i));
319
- }
320
-
321
- return sb.toString();
322
- }
323
-
324
- protected void collectMerge(List<String> fromTables, JdbcSchema schema, String toTable, List<String> mergeKeys) throws SQLException
325
- {
326
- Statement stmt = connection.createStatement();
327
- try {
328
- String sql = buildCollectMergeSql(fromTables, schema, toTable, mergeKeys);
329
- executeUpdate(stmt, sql);
330
- commitIfNecessary(connection);
331
- } catch (SQLException ex) {
332
- throw safeRollback(connection, ex);
333
- } finally {
334
- stmt.close();
335
- }
336
- }
337
-
338
- protected String buildCollectMergeSql(List<String> fromTables, JdbcSchema schema, String toTable, List<String> mergeKeys) throws SQLException
339
- {
340
- throw new UnsupportedOperationException("not implemented");
341
- }
342
-
343
- public void replaceTable(String fromTable, JdbcSchema schema, String toTable) throws SQLException
344
- {
345
- Statement stmt = connection.createStatement();
346
- try {
347
- dropTableIfExists(stmt, toTable);
348
-
349
- StringBuilder sb = new StringBuilder();
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);
356
-
357
- commitIfNecessary(connection);
358
- } catch (SQLException ex) {
359
- throw safeRollback(connection, ex);
360
- } finally {
361
- stmt.close();
362
- }
363
- }
364
-
365
- protected void quoteIdentifierString(StringBuilder sb, String str)
366
- {
367
- sb.append(quoteIdentifierString(str, identifierQuoteString));
368
- }
369
-
370
- protected String quoteIdentifierString(String str)
371
- {
372
- return quoteIdentifierString(str, identifierQuoteString);
373
- }
374
-
375
- protected String quoteIdentifierString(String str, String quoteString)
376
- {
377
- // TODO if identifierQuoteString.equals(" ") && str.contains([^a-zA-Z0-9_connection.getMetaData().getExtraNameCharacters()])
378
- // TODO if str.contains(identifierQuoteString);
379
- return quoteString + str + quoteString;
380
- }
381
-
382
- // PostgreSQL JDBC driver implements isValid() method. But the
383
- // implementation throws following exception:
384
- // "java.io.IOException: Method org.postgresql.jdbc4.Jdbc4Connection.isValid(int) is not yet implemented."
385
- //
386
- // So, checking mechanism doesn't work at all.
387
- // Thus here just runs "SELECT 1" to check connectivity.
388
- //
389
- public boolean isValidConnection(int timeout) throws SQLException
390
- {
391
- Statement stmt = connection.createStatement();
392
- try {
393
- stmt.executeQuery("SELECT 1").close();
394
- return true;
395
- } catch (SQLException ex) {
396
- return false;
397
- } finally {
398
- stmt.close();
399
- }
400
- }
401
-
402
- protected String[] getDeterministicSqlStates()
403
- {
404
- return new String[0];
405
- }
406
-
407
- protected int[] getDeterministicErrorCodes()
408
- {
409
- return new int[0];
410
- }
411
-
412
- protected Class[] getDeterministicRootCauses()
413
- {
414
- return new Class[] {
415
- // Don't retry on UnknownHostException.
416
- java.net.UnknownHostException.class,
417
-
418
- //// we should not retry on connect() error?
419
- //java.net.ConnectException.class,
420
- };
421
- }
422
-
423
- public boolean isRetryableException(SQLException exception)
424
- {
425
- String sqlState = exception.getSQLState();
426
- for (String deterministic : getDeterministicSqlStates()) {
427
- if (sqlState.equals(deterministic)) {
428
- return false;
429
- }
430
- }
431
-
432
- int errorCode = exception.getErrorCode();
433
- for (int deterministic : getDeterministicErrorCodes()) {
434
- if (errorCode == deterministic) {
435
- return false;
436
- }
437
- }
438
-
439
- Throwable rootCause = getRootCause(exception);
440
- for (Class deterministic : getDeterministicRootCauses()) {
441
- if (deterministic.equals(rootCause.getClass())) {
442
- return false;
443
- }
444
- }
445
-
446
- return true;
447
- }
448
-
449
- private Throwable getRootCause(Throwable e) {
450
- while (e.getCause() != null) {
451
- e = e.getCause();
452
- }
453
- return e;
454
- }
455
-
456
- protected int executeUpdate(Statement stmt, String sql) throws SQLException
457
- {
458
- logger.info("SQL: " + sql);
459
- long startTime = System.currentTimeMillis();
460
- int count = stmt.executeUpdate(sql);
461
- double seconds = (System.currentTimeMillis() - startTime) / 1000.0;
462
- if (count == 0) {
463
- logger.info(String.format("> %.2f seconds", seconds));
464
- } else {
465
- logger.info(String.format("> %.2f seconds (%,d rows)", seconds, count));
466
- }
467
- return count;
468
- }
469
-
470
- protected void commitIfNecessary(Connection con) throws SQLException
471
- {
472
- if (!con.getAutoCommit()) {
473
- con.commit();
474
- }
475
- }
476
-
477
- protected SQLException safeRollback(Connection con, SQLException cause)
478
- {
479
- try {
480
- if (!con.getAutoCommit()) {
481
- con.rollback();
482
- }
483
- return cause;
484
- } catch (SQLException ex) {
485
- if (cause != null) {
486
- cause.addSuppressed(ex);
487
- return cause;
488
- }
489
- return ex;
490
- }
491
- }
492
- }
1
+ package org.embulk.output.jdbc;
2
+
3
+ import java.util.List;
4
+ import java.nio.charset.Charset;
5
+ import java.nio.charset.StandardCharsets;
6
+ import java.sql.Connection;
7
+ import java.sql.DatabaseMetaData;
8
+ import java.sql.PreparedStatement;
9
+ import java.sql.ResultSet;
10
+ import java.sql.SQLException;
11
+ import java.sql.Statement;
12
+ import org.slf4j.Logger;
13
+ import com.google.common.base.Optional;
14
+ import org.embulk.spi.Exec;
15
+
16
+ public class JdbcOutputConnection
17
+ implements AutoCloseable
18
+ {
19
+ private final Logger logger = Exec.getLogger(JdbcOutputConnection.class);
20
+ protected final Connection connection;
21
+ protected final String schemaName;
22
+ protected final DatabaseMetaData databaseMetaData;
23
+ protected String identifierQuoteString;
24
+
25
+ public JdbcOutputConnection(Connection connection, String schemaName)
26
+ throws SQLException
27
+ {
28
+ this.connection = connection;
29
+ this.schemaName = schemaName;
30
+ this.databaseMetaData = connection.getMetaData();
31
+ this.identifierQuoteString = databaseMetaData.getIdentifierQuoteString();
32
+ if (schemaName != null) {
33
+ setSearchPath(schemaName);
34
+ }
35
+ }
36
+
37
+ @Override
38
+ public void close() throws SQLException
39
+ {
40
+ connection.close();
41
+ }
42
+
43
+ public String getSchemaName()
44
+ {
45
+ return schemaName;
46
+ }
47
+
48
+ public DatabaseMetaData getMetaData() throws SQLException
49
+ {
50
+ return databaseMetaData;
51
+ }
52
+
53
+ public Charset getTableNameCharset() throws SQLException
54
+ {
55
+ return StandardCharsets.UTF_8;
56
+ }
57
+
58
+ protected void setSearchPath(String schema) throws SQLException
59
+ {
60
+ Statement stmt = connection.createStatement();
61
+ try {
62
+ String sql = "SET search_path TO " + quoteIdentifierString(schema);
63
+ executeUpdate(stmt, sql);
64
+ commitIfNecessary(connection);
65
+ } finally {
66
+ stmt.close();
67
+ }
68
+ }
69
+
70
+ public boolean tableExists(String tableName) throws SQLException
71
+ {
72
+ try (ResultSet rs = connection.getMetaData().getTables(null, schemaName, tableName, null)) {
73
+ return rs.next();
74
+ }
75
+ }
76
+
77
+ public void dropTableIfExists(String tableName) throws SQLException
78
+ {
79
+ Statement stmt = connection.createStatement();
80
+ try {
81
+ dropTableIfExists(stmt, tableName);
82
+ commitIfNecessary(connection);
83
+ } catch (SQLException ex) {
84
+ throw safeRollback(connection, ex);
85
+ } finally {
86
+ stmt.close();
87
+ }
88
+ }
89
+
90
+ protected void dropTableIfExists(Statement stmt, String tableName) throws SQLException
91
+ {
92
+ String sql = String.format("DROP TABLE IF EXISTS %s", quoteIdentifierString(tableName));
93
+ executeUpdate(stmt, sql);
94
+ }
95
+
96
+ public void dropTable(String tableName) throws SQLException
97
+ {
98
+ Statement stmt = connection.createStatement();
99
+ try {
100
+ dropTable(stmt, tableName);
101
+ commitIfNecessary(connection);
102
+ } catch (SQLException ex) {
103
+ throw safeRollback(connection, ex);
104
+ } finally {
105
+ stmt.close();
106
+ }
107
+ }
108
+
109
+ protected void dropTable(Statement stmt, String tableName) throws SQLException
110
+ {
111
+ String sql = String.format("DROP TABLE %s", quoteIdentifierString(tableName));
112
+ executeUpdate(stmt, sql);
113
+ }
114
+
115
+ public void createTableIfNotExists(String tableName, JdbcSchema schema) throws SQLException
116
+ {
117
+ Statement stmt = connection.createStatement();
118
+ try {
119
+ String sql = buildCreateTableIfNotExistsSql(tableName, schema);
120
+ executeUpdate(stmt, sql);
121
+ commitIfNecessary(connection);
122
+ } catch (SQLException ex) {
123
+ throw safeRollback(connection, ex);
124
+ } finally {
125
+ stmt.close();
126
+ }
127
+ }
128
+
129
+ protected String buildCreateTableIfNotExistsSql(String name, JdbcSchema schema)
130
+ {
131
+ StringBuilder sb = new StringBuilder();
132
+
133
+ sb.append("CREATE TABLE IF NOT EXISTS ");
134
+ quoteIdentifierString(sb, name);
135
+ sb.append(buildCreateTableSchemaSql(schema));
136
+ return sb.toString();
137
+ }
138
+
139
+ protected String buildCreateTableSchemaSql(JdbcSchema schema)
140
+ {
141
+ StringBuilder sb = new StringBuilder();
142
+
143
+ sb.append(" (");
144
+ for (int i=0; i < schema.getCount(); i++) {
145
+ if (i != 0) { sb.append(", "); }
146
+ quoteIdentifierString(sb, schema.getColumnName(i));
147
+ sb.append(" ");
148
+ String typeName = getCreateTableTypeName(schema.getColumn(i));
149
+ sb.append(typeName);
150
+ }
151
+ sb.append(")");
152
+
153
+ return sb.toString();
154
+ }
155
+
156
+ public static enum ColumnDeclareType
157
+ {
158
+ SIMPLE,
159
+ SIZE,
160
+ SIZE_AND_SCALE,
161
+ SIZE_AND_OPTIONAL_SCALE,
162
+ };
163
+
164
+ protected String getCreateTableTypeName(JdbcColumn c)
165
+ {
166
+ if (c.getDeclaredType().isPresent()) {
167
+ return c.getDeclaredType().get();
168
+ } else {
169
+ return buildColumnTypeName(c);
170
+ }
171
+ }
172
+
173
+ protected String buildColumnTypeName(JdbcColumn c)
174
+ {
175
+ String simpleTypeName = c.getSimpleTypeName();
176
+ switch (getColumnDeclareType(simpleTypeName, c)) {
177
+ case SIZE:
178
+ return String.format("%s(%d)", simpleTypeName, c.getSizeTypeParameter());
179
+ case SIZE_AND_SCALE:
180
+ if (c.getScaleTypeParameter() < 0) {
181
+ return String.format("%s(%d,0)", simpleTypeName, c.getSizeTypeParameter());
182
+ } else {
183
+ return String.format("%s(%d,%d)", simpleTypeName, c.getSizeTypeParameter(), c.getScaleTypeParameter());
184
+ }
185
+ case SIZE_AND_OPTIONAL_SCALE:
186
+ if (c.getScaleTypeParameter() < 0) {
187
+ return String.format("%s(%d)", simpleTypeName, c.getSizeTypeParameter());
188
+ } else {
189
+ return String.format("%s(%d,%d)", simpleTypeName, c.getSizeTypeParameter(), c.getScaleTypeParameter());
190
+ }
191
+ default: // SIMPLE
192
+ return simpleTypeName;
193
+ }
194
+ }
195
+
196
+ // TODO
197
+ private static final String[] STANDARD_SIZE_TYPE_NAMES = new String[] {
198
+ "CHAR",
199
+ "VARCHAR", "CHAR VARYING", "CHARACTER VARYING", "LONGVARCHAR",
200
+ "NCHAR",
201
+ "NVARCHAR", "NCHAR VARYING", "NATIONAL CHAR VARYING", "NATIONAL CHARACTER VARYING",
202
+ "BINARY",
203
+ "VARBINARY", "BINARY VARYING", "LONGVARBINARY",
204
+ "BIT",
205
+ "VARBIT", "BIT VARYING",
206
+ "FLOAT", // SQL standard's FLOAT[(p)] optionally accepts precision
207
+ };
208
+
209
+ private static final String[] STANDARD_SIZE_AND_SCALE_TYPE_NAMES = new String[] {
210
+ "DECIMAL",
211
+ };
212
+
213
+ protected ColumnDeclareType getColumnDeclareType(String convertedTypeName, JdbcColumn col)
214
+ {
215
+ for (String x : STANDARD_SIZE_TYPE_NAMES) {
216
+ if (x.equals(convertedTypeName)) {
217
+ return ColumnDeclareType.SIZE;
218
+ }
219
+ }
220
+
221
+ for (String x : STANDARD_SIZE_AND_SCALE_TYPE_NAMES) {
222
+ if (x.equals(convertedTypeName)) {
223
+ return ColumnDeclareType.SIZE_AND_SCALE;
224
+ }
225
+ }
226
+
227
+ return ColumnDeclareType.SIMPLE;
228
+ }
229
+
230
+ public PreparedStatement prepareBatchInsertStatement(String toTable, JdbcSchema toTableSchema, Optional<List<String>> mergeKeys) throws SQLException
231
+ {
232
+ String sql;
233
+ if (mergeKeys.isPresent()) {
234
+ sql = buildPreparedMergeSql(toTable, toTableSchema, mergeKeys.get());
235
+ } else {
236
+ sql = buildPreparedInsertSql(toTable, toTableSchema);
237
+ }
238
+ logger.info("Prepared SQL: {}", sql);
239
+ return connection.prepareStatement(sql);
240
+ }
241
+
242
+ protected String buildPreparedInsertSql(String toTable, JdbcSchema toTableSchema) throws SQLException
243
+ {
244
+ StringBuilder sb = new StringBuilder();
245
+
246
+ sb.append("INSERT INTO ");
247
+ quoteIdentifierString(sb, toTable);
248
+
249
+ sb.append(" (");
250
+ for (int i=0; i < toTableSchema.getCount(); i++) {
251
+ if(i != 0) { sb.append(", "); }
252
+ quoteIdentifierString(sb, toTableSchema.getColumnName(i));
253
+ }
254
+ sb.append(") VALUES (");
255
+ for(int i=0; i < toTableSchema.getCount(); i++) {
256
+ if(i != 0) { sb.append(", "); }
257
+ sb.append("?");
258
+ }
259
+ sb.append(")");
260
+
261
+ return sb.toString();
262
+ }
263
+
264
+ protected String buildPreparedMergeSql(String toTable, JdbcSchema toTableSchema, List<String> mergeKeys) throws SQLException
265
+ {
266
+ throw new UnsupportedOperationException("not implemented");
267
+ }
268
+
269
+ protected void collectInsert(List<String> fromTables, JdbcSchema schema, String toTable,
270
+ boolean truncateDestinationFirst) throws SQLException
271
+ {
272
+ Statement stmt = connection.createStatement();
273
+ try {
274
+ if (truncateDestinationFirst) {
275
+ String sql = buildTruncateSql(toTable);
276
+ executeUpdate(stmt, sql);
277
+ }
278
+ String sql = buildCollectInsertSql(fromTables, schema, toTable);
279
+ executeUpdate(stmt, sql);
280
+ commitIfNecessary(connection);
281
+ } catch (SQLException ex) {
282
+ throw safeRollback(connection, ex);
283
+ } finally {
284
+ stmt.close();
285
+ }
286
+ }
287
+
288
+ protected String buildTruncateSql(String table)
289
+ {
290
+ StringBuilder sb = new StringBuilder();
291
+
292
+ sb.append("DELETE FROM ");
293
+ quoteIdentifierString(sb, table);
294
+
295
+ return sb.toString();
296
+ }
297
+
298
+ protected String buildCollectInsertSql(List<String> fromTables, JdbcSchema schema, String toTable)
299
+ {
300
+ StringBuilder sb = new StringBuilder();
301
+
302
+ sb.append("INSERT INTO ");
303
+ quoteIdentifierString(sb, toTable);
304
+ sb.append(" (");
305
+ for (int i=0; i < schema.getCount(); i++) {
306
+ if (i != 0) { sb.append(", "); }
307
+ quoteIdentifierString(sb, schema.getColumnName(i));
308
+ }
309
+ sb.append(") ");
310
+ for (int i=0; i < fromTables.size(); i++) {
311
+ if (i != 0) { sb.append(" UNION ALL "); }
312
+ sb.append("SELECT ");
313
+ for (int j=0; j < schema.getCount(); j++) {
314
+ if (j != 0) { sb.append(", "); }
315
+ quoteIdentifierString(sb, schema.getColumnName(j));
316
+ }
317
+ sb.append(" FROM ");
318
+ quoteIdentifierString(sb, fromTables.get(i));
319
+ }
320
+
321
+ return sb.toString();
322
+ }
323
+
324
+ protected void collectMerge(List<String> fromTables, JdbcSchema schema, String toTable, List<String> mergeKeys) throws SQLException
325
+ {
326
+ Statement stmt = connection.createStatement();
327
+ try {
328
+ String sql = buildCollectMergeSql(fromTables, schema, toTable, mergeKeys);
329
+ executeUpdate(stmt, sql);
330
+ commitIfNecessary(connection);
331
+ } catch (SQLException ex) {
332
+ throw safeRollback(connection, ex);
333
+ } finally {
334
+ stmt.close();
335
+ }
336
+ }
337
+
338
+ protected String buildCollectMergeSql(List<String> fromTables, JdbcSchema schema, String toTable, List<String> mergeKeys) throws SQLException
339
+ {
340
+ throw new UnsupportedOperationException("not implemented");
341
+ }
342
+
343
+ public void replaceTable(String fromTable, JdbcSchema schema, String toTable) throws SQLException
344
+ {
345
+ Statement stmt = connection.createStatement();
346
+ try {
347
+ dropTableIfExists(stmt, toTable);
348
+
349
+ StringBuilder sb = new StringBuilder();
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);
356
+
357
+ commitIfNecessary(connection);
358
+ } catch (SQLException ex) {
359
+ throw safeRollback(connection, ex);
360
+ } finally {
361
+ stmt.close();
362
+ }
363
+ }
364
+
365
+ protected void quoteIdentifierString(StringBuilder sb, String str)
366
+ {
367
+ sb.append(quoteIdentifierString(str, identifierQuoteString));
368
+ }
369
+
370
+ protected String quoteIdentifierString(String str)
371
+ {
372
+ return quoteIdentifierString(str, identifierQuoteString);
373
+ }
374
+
375
+ protected String quoteIdentifierString(String str, String quoteString)
376
+ {
377
+ // TODO if identifierQuoteString.equals(" ") && str.contains([^a-zA-Z0-9_connection.getMetaData().getExtraNameCharacters()])
378
+ // TODO if str.contains(identifierQuoteString);
379
+ return quoteString + str + quoteString;
380
+ }
381
+
382
+ // PostgreSQL JDBC driver implements isValid() method. But the
383
+ // implementation throws following exception:
384
+ // "java.io.IOException: Method org.postgresql.jdbc4.Jdbc4Connection.isValid(int) is not yet implemented."
385
+ //
386
+ // So, checking mechanism doesn't work at all.
387
+ // Thus here just runs "SELECT 1" to check connectivity.
388
+ //
389
+ public boolean isValidConnection(int timeout) throws SQLException
390
+ {
391
+ Statement stmt = connection.createStatement();
392
+ try {
393
+ stmt.executeQuery("SELECT 1").close();
394
+ return true;
395
+ } catch (SQLException ex) {
396
+ return false;
397
+ } finally {
398
+ stmt.close();
399
+ }
400
+ }
401
+
402
+ protected String[] getDeterministicSqlStates()
403
+ {
404
+ return new String[0];
405
+ }
406
+
407
+ protected int[] getDeterministicErrorCodes()
408
+ {
409
+ return new int[0];
410
+ }
411
+
412
+ protected Class[] getDeterministicRootCauses()
413
+ {
414
+ return new Class[] {
415
+ // Don't retry on UnknownHostException.
416
+ java.net.UnknownHostException.class,
417
+
418
+ //// we should not retry on connect() error?
419
+ //java.net.ConnectException.class,
420
+ };
421
+ }
422
+
423
+ public boolean isRetryableException(SQLException exception)
424
+ {
425
+ String sqlState = exception.getSQLState();
426
+ for (String deterministic : getDeterministicSqlStates()) {
427
+ if (sqlState.equals(deterministic)) {
428
+ return false;
429
+ }
430
+ }
431
+
432
+ int errorCode = exception.getErrorCode();
433
+ for (int deterministic : getDeterministicErrorCodes()) {
434
+ if (errorCode == deterministic) {
435
+ return false;
436
+ }
437
+ }
438
+
439
+ Throwable rootCause = getRootCause(exception);
440
+ for (Class deterministic : getDeterministicRootCauses()) {
441
+ if (deterministic.equals(rootCause.getClass())) {
442
+ return false;
443
+ }
444
+ }
445
+
446
+ return true;
447
+ }
448
+
449
+ private Throwable getRootCause(Throwable e) {
450
+ while (e.getCause() != null) {
451
+ e = e.getCause();
452
+ }
453
+ return e;
454
+ }
455
+
456
+ protected int executeUpdate(Statement stmt, String sql) throws SQLException
457
+ {
458
+ logger.info("SQL: " + sql);
459
+ long startTime = System.currentTimeMillis();
460
+ int count = stmt.executeUpdate(sql);
461
+ double seconds = (System.currentTimeMillis() - startTime) / 1000.0;
462
+ if (count == 0) {
463
+ logger.info(String.format("> %.2f seconds", seconds));
464
+ } else {
465
+ logger.info(String.format("> %.2f seconds (%,d rows)", seconds, count));
466
+ }
467
+ return count;
468
+ }
469
+
470
+ protected void commitIfNecessary(Connection con) throws SQLException
471
+ {
472
+ if (!con.getAutoCommit()) {
473
+ con.commit();
474
+ }
475
+ }
476
+
477
+ protected SQLException safeRollback(Connection con, SQLException cause)
478
+ {
479
+ try {
480
+ if (!con.getAutoCommit()) {
481
+ con.rollback();
482
+ }
483
+ return cause;
484
+ } catch (SQLException ex) {
485
+ if (cause != null) {
486
+ cause.addSuppressed(ex);
487
+ return cause;
488
+ }
489
+ return ex;
490
+ }
491
+ }
492
+ }