embulk-output-jdbc 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/build.gradle +2 -2
  3. data/classpath/{embulk-output-jdbc-0.4.0.jar → embulk-output-jdbc-0.4.1.jar} +0 -0
  4. data/lib/embulk/output/jdbc.rb +3 -3
  5. data/src/main/java/org/embulk/output/JdbcOutputPlugin.java +138 -138
  6. data/src/main/java/org/embulk/output/jdbc/AbstractJdbcOutputPlugin.java +973 -972
  7. data/src/main/java/org/embulk/output/jdbc/BatchInsert.java +53 -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 +155 -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 +35 -35
  17. data/src/main/java/org/embulk/output/jdbc/setter/BigDecimalColumnSetter.java +70 -70
  18. data/src/main/java/org/embulk/output/jdbc/setter/BooleanColumnSetter.java +54 -54
  19. data/src/main/java/org/embulk/output/jdbc/setter/ByteColumnSetter.java +76 -76
  20. data/src/main/java/org/embulk/output/jdbc/setter/ColumnSetter.java +45 -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 +96 -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 +61 -61
  25. data/src/main/java/org/embulk/output/jdbc/setter/FloatColumnSetter.java +61 -61
  26. data/src/main/java/org/embulk/output/jdbc/setter/IntColumnSetter.java +76 -76
  27. data/src/main/java/org/embulk/output/jdbc/setter/LongColumnSetter.java +72 -72
  28. data/src/main/java/org/embulk/output/jdbc/setter/NStringColumnSetter.java +59 -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 +59 -59
  32. data/src/main/java/org/embulk/output/jdbc/setter/ShortColumnSetter.java +76 -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 +59 -59
  35. data/src/main/java/org/embulk/output/jdbc/setter/SqlTimeColumnSetter.java +59 -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 +59 -59
  38. data/src/test/java/org/embulk/output/TestJdbcOutputPlugin.java +5 -5
  39. metadata +3 -3
@@ -1,53 +1,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 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 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,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
+ }