55 | */
56 | ConfigFrom defaultPropertyFiles();
57 |
58 | /**
59 | * Adds a set of properties files to read from, which can be overridden by a specified system property.
60 | * Equivalent to:
61 | *
64 | */
65 | ConfigFrom defaultPropertyFiles(String systemPropertyKey, String... filenames);
66 |
67 | /**
68 | * Adds a set of properties files to read from, which can be overridden by a specified system property.
69 | * Equivalent to:
70 | *
75 | */
76 | ConfigFrom defaultPropertyFiles(String systemPropertyKey, CharsetDecoder decoder, String... filenames);
77 |
78 | ConfigFrom propertyFile(String... filenames);
79 |
80 | ConfigFrom propertyFile(CharsetDecoder decoder, String... filenames);
81 |
82 | ConfigFrom propertyFile(File... files);
83 |
84 | ConfigFrom propertyFile(CharsetDecoder decoder, File... files);
85 |
86 | ConfigFrom rename(String key, String newKey);
87 |
88 | ConfigFrom includeKeys(String... keys);
89 |
90 | ConfigFrom includePrefix(String... prefixes);
91 |
92 | ConfigFrom includeRegex(String regex);
93 |
94 | ConfigFrom excludeKeys(String... keys);
95 |
96 | ConfigFrom excludePrefix(String... prefixes);
97 |
98 | ConfigFrom excludeRegex(String regex);
99 |
100 | ConfigFrom removePrefix(String... prefixes);
101 |
102 | ConfigFrom addPrefix(String prefix);
103 |
104 | ConfigFrom substitutions(Config config);
105 |
106 | Config get();
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/ConfigImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.susom.database;
2 |
3 | import java.math.BigDecimal;
4 | import java.util.HashSet;
5 | import java.util.Set;
6 | import java.util.function.Function;
7 |
8 | import javax.annotation.Nonnull;
9 | import javax.annotation.Nullable;
10 |
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | /**
15 | * This class handles all of the type conversions, default values, etc.
16 | * to get from simple string key/value pairs to richer configuration.
17 | *
18 | * @author garricko
19 | */
20 | public class ConfigImpl implements Config {
21 | private static final Logger log = LoggerFactory.getLogger(ConfigFromImpl.class);
22 | private final Function provider;
23 | private final String sources;
24 | private Set failedKeys = new HashSet<>();
25 |
26 | public ConfigImpl(Function provider, String sources) {
27 | this.provider = provider;
28 | this.sources = sources;
29 | }
30 |
31 | @Override
32 | public String getString(@Nonnull String key) {
33 | return cleanString(key);
34 | }
35 |
36 | @Nonnull
37 | @Override
38 | public String getStringOrThrow(@Nonnull String key) {
39 | return nonnull(key, getString(key));
40 | }
41 |
42 | @Nonnull
43 | @Override
44 | public String getString(String key, @Nonnull String defaultValue) {
45 | String stringValue = cleanString(key);
46 | if (stringValue != null) {
47 | return stringValue;
48 | }
49 | // Make sure the default value is tidied the same way a value would be
50 | defaultValue = defaultValue.trim();
51 | if (defaultValue.length() == 0) {
52 | throw new IllegalArgumentException("Your default value is empty or just whitespace");
53 | }
54 | return defaultValue;
55 | }
56 |
57 | @Override
58 | public Integer getInteger(@Nonnull String key) {
59 | String stringValue = cleanString(key);
60 | try {
61 | return stringValue == null ? null : Integer.parseInt(stringValue);
62 | } catch (Exception e) {
63 | if (!failedKeys.contains(key)) {
64 | log.warn("Could not load config value for key (this message will only be logged once): " + key, e);
65 | failedKeys.add(key);
66 | }
67 | return null;
68 | }
69 | }
70 |
71 | @Override
72 | public int getInteger(@Nonnull String key, int defaultValue) {
73 | Integer value = getInteger(key);
74 | return value == null ? defaultValue : value;
75 | }
76 |
77 | @Override
78 | public int getIntegerOrThrow(@Nonnull String key) {
79 | return nonnull(key, getInteger(key));
80 | }
81 |
82 | @Nullable
83 | @Override
84 | public Long getLong(@Nonnull String key) {
85 | String stringValue = cleanString(key);
86 | try {
87 | return stringValue == null ? null : Long.parseLong(stringValue);
88 | } catch (Exception e) {
89 | if (!failedKeys.contains(key)) {
90 | log.warn("Could not load config value for key (this message will only be logged once): " + key, e);
91 | failedKeys.add(key);
92 | }
93 | return null;
94 | }
95 | }
96 |
97 | @Override
98 | public long getLong(@Nonnull String key, long defaultValue) {
99 | Long value = getLong(key);
100 | return value == null ? defaultValue : value;
101 | }
102 |
103 | @Override
104 | public long getLongOrThrow(@Nonnull String key) {
105 | return nonnull(key, getLong(key));
106 | }
107 |
108 | @Nullable
109 | @Override
110 | public Float getFloat(@Nonnull String key) {
111 | String stringValue = cleanString(key);
112 | try {
113 | return stringValue == null ? null : Float.parseFloat(stringValue);
114 | } catch (Exception e) {
115 | if (!failedKeys.contains(key)) {
116 | log.warn("Could not load config value for key (this message will only be logged once): " + key, e);
117 | failedKeys.add(key);
118 | }
119 | return null;
120 | }
121 | }
122 |
123 | @Override
124 | public float getFloat(@Nonnull String key, float defaultValue) {
125 | Float value = getFloat(key);
126 | return value == null ? defaultValue : value;
127 | }
128 |
129 | @Override
130 | public float getFloatOrThrow(@Nonnull String key) {
131 | return nonnull(key, getFloat(key));
132 | }
133 |
134 | @Nullable
135 | @Override
136 | public Double getDouble(@Nonnull String key) {
137 | String stringValue = cleanString(key);
138 | try {
139 | return stringValue == null ? null : Double.parseDouble(stringValue);
140 | } catch (Exception e) {
141 | if (!failedKeys.contains(key)) {
142 | log.warn("Could not load config value for key (this message will only be logged once): " + key, e);
143 | failedKeys.add(key);
144 | }
145 | return null;
146 | }
147 | }
148 |
149 | @Override
150 | public double getDouble(@Nonnull String key, double defaultValue) {
151 | Double value = getDouble(key);
152 | return value == null ? defaultValue : value;
153 | }
154 |
155 | @Override
156 | public double getDoubleOrThrow(@Nonnull String key) {
157 | return nonnull(key, getDouble(key));
158 | }
159 |
160 | @Nullable
161 | @Override
162 | public BigDecimal getBigDecimal(@Nonnull String key) {
163 | String stringValue = cleanString(key);
164 | try {
165 | return stringValue == null ? null : new BigDecimal(stringValue);
166 | } catch (Exception e) {
167 | if (!failedKeys.contains(key)) {
168 | log.warn("Could not load config value for key (this message will only be logged once): " + key, e);
169 | failedKeys.add(key);
170 | }
171 | return null;
172 | }
173 | }
174 |
175 | @Nonnull
176 | @Override
177 | public BigDecimal getBigDecimal(String key, @Nonnull BigDecimal defaultValue) {
178 | BigDecimal value = getBigDecimal(key);
179 | return value == null ? defaultValue : value;
180 | }
181 |
182 | @Nonnull
183 | @Override
184 | public BigDecimal getBigDecimalOrThrow(String key) {
185 | return nonnull(key, getBigDecimal(key));
186 | }
187 |
188 | @Override
189 | public boolean getBooleanOrFalse(@Nonnull String key) {
190 | return parseBoolean(cleanString(key), false);
191 | }
192 |
193 | @Override
194 | public boolean getBooleanOrTrue(@Nonnull String key) {
195 | return parseBoolean(cleanString(key), true);
196 | }
197 |
198 | @Override
199 | public boolean getBooleanOrThrow(@Nonnull String key) {
200 | String value = nonnull(key, cleanString(key));
201 | value = value.toLowerCase();
202 | if (value.equals("yes") || value.equals("true")) {
203 | return true;
204 | }
205 | if (value.equals("no") || value.equals("false")) {
206 | return false;
207 | }
208 | throw new ConfigMissingException("Unrecognized boolean value for config key: " + key);
209 | }
210 |
211 | @Override
212 | public String sources() {
213 | return sources;
214 | }
215 |
216 | private T nonnull(String key, T value) {
217 | if (value == null) {
218 | throw new ConfigMissingException("No value for config key: " + key);
219 | }
220 | return value;
221 | }
222 |
223 | private boolean parseBoolean(String value, boolean defaultValue) {
224 | if (value != null) {
225 | value = value.toLowerCase();
226 | if (value.equals("yes") || value.equals("true")) {
227 | return true;
228 | }
229 | if (value.equals("no") || value.equals("false")) {
230 | return false;
231 | }
232 | }
233 | return defaultValue;
234 | }
235 |
236 | private String cleanString(String key) {
237 | String value = null;
238 | try {
239 | value = provider.apply(key);
240 | if (value != null) {
241 | value = value.trim();
242 | if (value.length() == 0) {
243 | value = null;
244 | }
245 | }
246 | } catch (Exception e) {
247 | if (!failedKeys.contains(key)) {
248 | log.warn("Could not load config value for key (this message will only be logged once): " + key, e);
249 | failedKeys.add(key);
250 | }
251 | }
252 |
253 | return value;
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/ConfigInvalidException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.susom.database;
17 |
18 | /**
19 | * Indicates that a configuration value is present but not in a usable format.
20 | * For example, if the value must be an integer and the configuration value
21 | * is a non-numeric string.
22 | *
23 | * @author garricko
24 | */
25 | public class ConfigInvalidException extends DatabaseException {
26 | public ConfigInvalidException(String message) {
27 | super(message);
28 | }
29 |
30 | public ConfigInvalidException(Throwable cause) {
31 | super(cause);
32 | }
33 |
34 | public ConfigInvalidException(String message, Throwable cause) {
35 | super(message, cause);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/ConfigMissingException.java:
--------------------------------------------------------------------------------
1 | package com.github.susom.database;
2 |
3 | /**
4 | * Indicates that a configuration value is required but was not present.
5 | *
6 | * @author garricko
7 | */
8 | public class ConfigMissingException extends DatabaseException {
9 | public ConfigMissingException(String message) {
10 | super(message);
11 | }
12 |
13 | public ConfigMissingException(Throwable cause) {
14 | super(cause);
15 | }
16 |
17 | public ConfigMissingException(String message, Throwable cause) {
18 | super(message, cause);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/ConstraintViolationException.java:
--------------------------------------------------------------------------------
1 | package com.github.susom.database;
2 |
3 | /**
4 | * This exception will be thrown when a condition arises that violates
5 | * a stated invariant regarding the database. This might be a database
6 | * schema "constraint violated" as thrown by the database, or could be
7 | * caused by a violation of constraints enforced only within the code.
8 | */
9 | public class ConstraintViolationException extends DatabaseException {
10 | public ConstraintViolationException(String message) {
11 | super(message);
12 | }
13 |
14 | public ConstraintViolationException(Throwable cause) {
15 | super(cause);
16 | }
17 |
18 | public ConstraintViolationException(String message, Throwable cause) {
19 | super(message, cause);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DatabaseException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.susom.database;
18 |
19 | /**
20 | * Indicates something went wrong accessing the database. Most often this is
21 | * used to wrap SQLException to avoid declaring checked exceptions.
22 | *
23 | * @author garricko
24 | */
25 | public class DatabaseException extends RuntimeException {
26 | public DatabaseException(String message) {
27 | super(message);
28 | }
29 |
30 | public DatabaseException(Throwable cause) {
31 | super(cause);
32 | }
33 |
34 | public DatabaseException(String message, Throwable cause) {
35 | super(message, cause);
36 | }
37 |
38 | /**
39 | * Wrap an exception with a DatabaseException, taking into account all known
40 | * subtypes such that we wrap subtypes in a matching type (so we don't obscure
41 | * the type available to catch clauses).
42 | *
43 | * @param message the new wrapping exception will have this message
44 | * @param cause the exception to be wrapped
45 | * @return the exception you should throw
46 | */
47 | public static DatabaseException wrap(String message, Throwable cause) {
48 | if (cause instanceof ConstraintViolationException) {
49 | return new ConstraintViolationException(message, cause);
50 | }
51 | return new DatabaseException(message, cause);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DatabaseMock.java:
--------------------------------------------------------------------------------
1 | package com.github.susom.database;
2 |
3 | /**
4 | * Convenience class to intercept calls to Connection and return stubbed results
5 | * for testing purposes.
6 | */
7 | public interface DatabaseMock {
8 | RowStub query(String executeSql, String debugSql);
9 |
10 | Integer insert(String executeSql, String debugSql);
11 |
12 | Long insertReturningPk(String executeSql, String debugSql);
13 |
14 | RowStub insertReturning(String executeSql, String debugSql);
15 |
16 | Integer update(String executeSql, String debugSql);
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DbCode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.susom.database;
17 |
18 | import java.util.function.Supplier;
19 |
20 | /**
21 | * A block of runnable code using a transacted Database.
22 | *
23 | * @author garricko
24 | */
25 | public interface DbCode {
26 | /**
27 | * Implement this method to provide a block of code that uses the provided database
28 | * and is transacted. Whether the transaction will commit or rollback is typically
29 | * controlled by the code that invokes this method.
30 | *
31 | *
If a {@link Throwable} is thrown from this method, it will be caught, wrapped in
32 | * a DatabaseException (if it is not already one), and then propagated.
33 | */
34 | void run(Supplier dbs) throws Exception;
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DbCodeTx.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.susom.database;
17 |
18 | import java.util.function.Supplier;
19 |
20 | /**
21 | * A block of runnable code using a transacted Database.
22 | *
23 | * @author garricko
24 | */
25 | public interface DbCodeTx {
26 | /**
27 | * Implement this method to provide a block of code that uses the provided database
28 | * and is transacted. Whether the transaction will commit or rollback is typically
29 | * controlled by the code that invokes this method.
30 | *
31 | *
If a {@link Throwable} is thrown from this method, it will be caught, wrapped in
32 | * a DatabaseException (if it is not already one), and then propagated.
33 | */
34 | void run(Supplier db, Transaction tx) throws Exception;
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DbCodeTyped.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.susom.database;
17 |
18 | import java.util.function.Supplier;
19 |
20 | /**
21 | * A block of runnable code using a transacted Database.
22 | *
23 | * @author garricko
24 | */
25 | public interface DbCodeTyped {
26 | /**
27 | * Implement this method to provide a block of code that uses the provided database
28 | * and is transacted. Whether the transaction will commit or rollback is typically
29 | * controlled by the code that invokes this method.
30 | *
31 | *
If a {@link Throwable} is thrown from this method, it will be caught, wrapped in
32 | * a DatabaseException (if it is not already one), and then propagated.
33 | */
34 | T run(Supplier dbs) throws Exception;
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DbCodeTypedTx.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.susom.database;
17 |
18 | import java.util.function.Supplier;
19 |
20 | /**
21 | * A block of runnable code using a transacted Database.
22 | *
23 | * @author garricko
24 | */
25 | public interface DbCodeTypedTx {
26 | /**
27 | * Implement this method to provide a block of code that uses the provided database
28 | * and is transacted. Whether the transaction will commit or rollback is typically
29 | * controlled by the code that invokes this method.
30 | *
31 | *
If a {@link Throwable} is thrown from this method, it will be caught, wrapped in
32 | * a DatabaseException (if it is not already one), and then propagated.
33 | */
34 | T run(Supplier db, Transaction tx) throws Exception;
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/Ddl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.susom.database;
18 |
19 | /**
20 | * Interface for executing a chunk of DDL within the database.
21 | *
22 | * @author garricko
23 | */
24 | public interface Ddl {
25 | /**
26 | * Execute the DDL statement. All checked SQLExceptions get wrapped in DatabaseExceptions.
27 | */
28 | void execute();
29 |
30 | /**
31 | * This just does an execute() call and silently discards any DatabaseException
32 | * that might occur. This can be useful for things like drop statements, where
33 | * some databases don't make it easy to conditionally drop things only if they
34 | * exist.
35 | */
36 | void executeQuietly();
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DdlImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.susom.database;
18 |
19 | import java.sql.CallableStatement;
20 | import java.sql.Connection;
21 | import java.sql.Statement;
22 |
23 | import org.slf4j.Logger;
24 | import org.slf4j.LoggerFactory;
25 |
26 | /**
27 | * This is the key class for configuring (query parameters) and executing a database query.
28 | *
29 | * @author garricko
30 | */
31 | public class DdlImpl implements Ddl {
32 | private static final Logger log = LoggerFactory.getLogger(Database.class);
33 | private static final Logger logQuiet = LoggerFactory.getLogger(Database.class.getName() + ".Quiet");
34 | private final Connection connection;
35 | private final String sql;
36 | private final Options options;
37 |
38 | public DdlImpl(Connection connection, String sql, Options options) {
39 | this.connection = connection;
40 | this.sql = sql;
41 | this.options = options;
42 | }
43 |
44 | private void updateInternal(boolean quiet) {
45 | CallableStatement ps = null;
46 | Metric metric = new Metric(log.isDebugEnabled());
47 |
48 | boolean isSuccess = false;
49 | String errorCode = null;
50 | Exception logEx = null;
51 | try {
52 | ps = connection.prepareCall(sql);
53 |
54 | metric.checkpoint("prep");
55 | ps.execute();
56 | metric.checkpoint("exec");
57 | isSuccess = true;
58 | } catch (Exception e) {
59 | errorCode = options.generateErrorCode();
60 | logEx = e;
61 | throw DatabaseException.wrap(DebugSql.exceptionMessage(sql, null, errorCode, options), e);
62 | } finally {
63 | close(ps);
64 | metric.checkpoint("close");
65 | // PostgreSQL requires explicit commit since we are running with setAutoCommit(false)
66 | commitIfNecessary(connection);
67 | metric.done("commit");
68 | if (isSuccess) {
69 | DebugSql.logSuccess("DDL", log, metric, sql, null, options);
70 | } else if (quiet) {
71 | DebugSql.logWarning("DDL", logQuiet, metric, errorCode, sql, null, options, logEx);
72 | } else {
73 | DebugSql.logError("DDL", log, metric, errorCode, sql, null, options, logEx);
74 | }
75 | }
76 | }
77 |
78 | @Override
79 | public void execute() {
80 | updateInternal(false);
81 | }
82 |
83 | @Override
84 | public void executeQuietly() {
85 | try {
86 | updateInternal(true);
87 | } catch (DatabaseException e) {
88 | // Ignore, as requested
89 | }
90 | }
91 |
92 | private void close(Statement s) {
93 | if (s != null) {
94 | try {
95 | s.close();
96 | } catch (Exception e) {
97 | log.warn("Caught exception closing the Statement", e);
98 | }
99 | }
100 | }
101 |
102 | private void commitIfNecessary(Connection c) {
103 | if (c != null) {
104 | try {
105 | if (!c.getAutoCommit()) {
106 | c.commit();
107 | }
108 | } catch (Exception e) {
109 | log.warn("Caught exception on commit", e);
110 | }
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/DebugSql.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.susom.database;
18 |
19 | import java.io.InputStream;
20 | import java.io.Reader;
21 | import java.util.Arrays;
22 | import java.util.Date;
23 |
24 | import org.slf4j.Logger;
25 |
26 | import com.github.susom.database.MixedParameterSql.SecretArg;
27 |
28 | /**
29 | * Convenience class to substitute real values into a database query for debugging, logging, etc.
30 | *
31 | * WARNING!!! Never execute this SQL without manual inspection because this class does NOTHING
32 | * to prevent SQL injection or any other bad things.
33 | *
34 | * @author garricko
35 | */
36 | public class DebugSql {
37 | public static final String PARAM_SQL_SEPARATOR = "\tParamSql:\t";
38 |
39 | public static String printDebugOnlySqlString(String sql, Object[] args, Options options) {
40 | StringBuilder buf = new StringBuilder();
41 | printSql(buf, sql, args, false, true, options);
42 | return buf.toString();
43 | }
44 |
45 | public static void printSql(StringBuilder buf, String sql, Object[] args, Options options) {
46 | printSql(buf, sql, args, true, options.isLogParameters(), options);
47 | }
48 |
49 | public static void printSql(StringBuilder buf, String sql, Object[] args, boolean includeExecSql,
50 | boolean includeParameters, Options options) {
51 | Object[] argsToPrint = args;
52 | if (argsToPrint == null) {
53 | argsToPrint = new Object[0];
54 | }
55 | int batchSize = -1;
56 | if (argsToPrint.length > 0 && argsToPrint instanceof Object[][]) {
57 | // The arguments provided were from a batch - just use the first set
58 | batchSize = argsToPrint.length;
59 | argsToPrint = (Object[]) argsToPrint[0];
60 | }
61 | String[] sqlParts = sql.split("\\?");
62 | if (sqlParts.length != argsToPrint.length + (sql.endsWith("?") ? 0 : 1)) {
63 | buf.append("(wrong # args) query: ");
64 | buf.append(sql);
65 | if (args != null) {
66 | buf.append(" args: ");
67 | if (includeParameters) {
68 | buf.append(Arrays.toString(argsToPrint));
69 | } else {
70 | buf.append(argsToPrint.length);
71 | }
72 | }
73 | } else {
74 | if (includeExecSql) {
75 | buf.append(removeTabs(sql));
76 | }
77 | if (includeParameters && argsToPrint.length > 0) {
78 | if (includeExecSql) {
79 | buf.append(PARAM_SQL_SEPARATOR);
80 | }
81 | for (int i = 0; i < argsToPrint.length; i++) {
82 | buf.append(removeTabs(sqlParts[i]));
83 | Object argToPrint = argsToPrint[i];
84 | if (argToPrint instanceof String) {
85 | String argToPrintString = (String) argToPrint;
86 | int maxLength = options.maxStringLengthParam();
87 | if (argToPrintString.length() > maxLength && maxLength > 0) {
88 | buf.append("'").append(argToPrintString.substring(0, maxLength)).append("...'");
89 | } else {
90 | buf.append("'");
91 | buf.append(removeTabs(escapeSingleQuoted(argToPrintString)));
92 | buf.append("'");
93 | }
94 | } else if (argToPrint instanceof StatementAdaptor.SqlNull || argToPrint == null) {
95 | buf.append("null");
96 | } else if (argToPrint instanceof java.sql.Timestamp) {
97 | buf.append(options.flavor().dateAsSqlFunction((Date) argToPrint, options.calendarForTimestamps()));
98 | } else if (argToPrint instanceof java.sql.Date) {
99 | buf.append(options.flavor().localDateAsSqlFunction((Date) argToPrint));
100 | } else if (argToPrint instanceof Number) {
101 | buf.append(argToPrint);
102 | } else if (argToPrint instanceof Boolean) {
103 | buf.append(((Boolean) argToPrint) ? "'Y'" : "'N'");
104 | } else if (argToPrint instanceof SecretArg) {
105 | buf.append("");
106 | } else if (argToPrint instanceof InternalStringReader) {
107 | String argToPrintString = ((InternalStringReader) argToPrint).getString();
108 | int maxLength = options.maxStringLengthParam();
109 | if (argToPrintString.length() > maxLength && maxLength > 0) {
110 | buf.append("'").append(argToPrintString.substring(0, maxLength)).append("...'");
111 | } else {
112 | buf.append("'");
113 | buf.append(removeTabs(escapeSingleQuoted(argToPrintString)));
114 | buf.append("'");
115 | }
116 | } else if (argToPrint instanceof Reader || argToPrint instanceof InputStream) {
117 | buf.append("<").append(argToPrint.getClass().getName()).append(">");
118 | } else if (argToPrint instanceof byte[]) {
119 | buf.append("<").append(((byte[]) argToPrint).length).append(" bytes>");
120 | } else {
121 | buf.append("");
122 | }
123 | }
124 | if (sqlParts.length > argsToPrint.length) {
125 | buf.append(sqlParts[sqlParts.length - 1]);
126 | }
127 | }
128 | }
129 | if (batchSize != -1) {
130 | buf.append(" (first in batch of ");
131 | buf.append(batchSize);
132 | buf.append(')');
133 | }
134 | }
135 |
136 | private static String removeTabs(String s) {
137 | return s == null ? null : s.replace("\t", "");
138 | }
139 |
140 | private static String escapeSingleQuoted(String s) {
141 | return s == null ? null : s.replace("'", "''");
142 | }
143 |
144 | public static String exceptionMessage(String sql, Object[] parameters, String errorCode, Options options) {
145 | StringBuilder buf = new StringBuilder("Error executing SQL");
146 | if (errorCode != null) {
147 | buf.append(" (errorCode=").append(errorCode).append(")");
148 | }
149 | if (options.isDetailedExceptions()) {
150 | buf.append(": ");
151 | DebugSql.printSql(buf, sql, parameters, options);
152 | }
153 | return buf.toString();
154 | }
155 |
156 | public static void logSuccess(String sqlType, Logger log, Metric metric, String sql, Object[] args, Options options) {
157 | if (log.isDebugEnabled()) {
158 | String msg = logMiddle('\t', sqlType, metric, null, sql, args, options);
159 | log.debug(msg);
160 | }
161 | }
162 |
163 | public static void logWarning(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args,
164 | Options options, Throwable t) {
165 | if (log.isWarnEnabled()) {
166 | String msg = logMiddle(' ', sqlType, metric, errorCode, sql, args, options);
167 | log.warn(msg, t);
168 | }
169 | }
170 |
171 | public static void logError(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args,
172 | Options options, Throwable t) {
173 | if (log.isErrorEnabled()) {
174 | String msg = logMiddle(' ', sqlType, metric, errorCode, sql, args, options);
175 | log.error(msg, t);
176 | }
177 | }
178 |
179 | private static String logMiddle(char separator, String sqlType, Metric metric,
180 | String errorCode, String sql, Object[] args, Options options) {
181 | StringBuilder buf = new StringBuilder();
182 | if (errorCode != null) {
183 | buf.append("errorCode=").append(errorCode).append(" ");
184 | }
185 | buf.append(sqlType).append(": ");
186 | metric.printMessage(buf);
187 | buf.append(separator);
188 | printSql(buf, sql, args, options);
189 | return buf.toString();
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/InternalStringReader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.susom.database;
17 |
18 | import java.io.StringReader;
19 |
20 | /**
21 | * This class exists to distinguish cases where we are mapping String to Reader
22 | * internally, but want to be able to know they really started as a String (and
23 | * be able to get the String back for things like logging).
24 | *
25 | * @author garricko
26 | */
27 | final class InternalStringReader extends StringReader {
28 | private String s;
29 |
30 | InternalStringReader(String s) {
31 | super(s);
32 | this.s = s;
33 | }
34 |
35 | String getString() {
36 | return s;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/github/susom/database/MixedParameterSql.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014 The Board of Trustees of The Leland Stanford Junior University.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.susom.database;
18 |
19 | import java.util.ArrayList;
20 | import java.util.HashMap;
21 | import java.util.HashSet;
22 | import java.util.List;
23 | import java.util.Map;
24 | import java.util.Set;
25 |
26 | /**
27 | * Convenience class to allow use of (:mylabel) for SQL parameters in addition to
28 | * positional (?) parameters. This doesn't do any smart parsing of the SQL, it is just
29 | * looking for ':' and '?' characters. If the SQL needs to include an actual ':' or '?'
30 | * character, use two of them ('::' or '??'), and they will be replaced with a
31 | * single ':' or '?'.
32 | *
33 | * @author garricko
34 | */
35 | public class MixedParameterSql {
36 | private final String sqlToExecute;
37 | private final Object[] args;
38 |
39 | public MixedParameterSql(String sql, List