24
24
import com .google .cloud .spanner .connection .Connection ;
25
25
import com .google .cloud .spanner .connection .StatementResult ;
26
26
import com .google .cloud .spanner .connection .StatementResult .ClientSideStatementType ;
27
+ import com .google .rpc .Code ;
27
28
import java .sql .ResultSet ;
28
29
import java .sql .SQLException ;
29
30
import java .sql .SQLWarning ;
30
31
import java .sql .Statement ;
31
32
import java .util .Arrays ;
32
33
import java .util .concurrent .TimeUnit ;
34
+ import java .util .function .Function ;
35
+ import java .util .function .Supplier ;
33
36
34
37
/** Base class for Cloud Spanner JDBC {@link Statement}s */
35
38
abstract class AbstractJdbcStatement extends AbstractJdbcWrapper implements Statement {
@@ -183,21 +186,74 @@ private ResultSet executeQuery(
183
186
QueryAnalyzeMode analyzeMode ,
184
187
QueryOption ... options )
185
188
throws SQLException {
189
+ Options .QueryOption [] queryOptions = getQueryOptions (options );
190
+ return doWithStatementTimeout (
191
+ () -> {
192
+ com .google .cloud .spanner .ResultSet resultSet ;
193
+ if (analyzeMode == null ) {
194
+ resultSet = connection .getSpannerConnection ().executeQuery (statement , queryOptions );
195
+ } else {
196
+ resultSet = connection .getSpannerConnection ().analyzeQuery (statement , analyzeMode );
197
+ }
198
+ return JdbcResultSet .of (this , resultSet );
199
+ });
200
+ }
201
+
202
+ private <T > T doWithStatementTimeout (Supplier <T > runnable ) throws SQLException {
203
+ return doWithStatementTimeout (runnable , ignore -> Boolean .TRUE );
204
+ }
205
+
206
+ private <T > T doWithStatementTimeout (
207
+ Supplier <T > runnable , Function <T , Boolean > shouldResetTimeout ) throws SQLException {
186
208
StatementTimeout originalTimeout = setTemporaryStatementTimeout ();
209
+ T result = null ;
187
210
try {
188
- com .google .cloud .spanner .ResultSet resultSet ;
189
- if (analyzeMode == null ) {
190
- resultSet =
191
- connection .getSpannerConnection ().executeQuery (statement , getQueryOptions (options ));
192
- } else {
193
- resultSet = connection .getSpannerConnection ().analyzeQuery (statement , analyzeMode );
194
- }
195
- return JdbcResultSet .of (this , resultSet );
196
- } catch (SpannerException e ) {
197
- throw JdbcSqlExceptionFactory .of (e );
211
+ result = runnable .get ();
212
+ return result ;
213
+ } catch (SpannerException spannerException ) {
214
+ throw JdbcSqlExceptionFactory .of (spannerException );
198
215
} finally {
199
- resetStatementTimeout (originalTimeout );
216
+ if (shouldResetTimeout .apply (result )) {
217
+ resetStatementTimeout (originalTimeout );
218
+ }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Executes a SQL statement on the connection of this {@link Statement} as an update (DML)
224
+ * statement.
225
+ *
226
+ * @param statement The SQL statement to execute
227
+ * @return the number of rows that was inserted/updated/deleted
228
+ * @throws SQLException if a database error occurs, or if the number of rows affected is larger
229
+ * than {@link Integer#MAX_VALUE}
230
+ */
231
+ int executeUpdate (com .google .cloud .spanner .Statement statement ) throws SQLException {
232
+ return checkedCast (executeLargeUpdate (statement ));
233
+ }
234
+
235
+ /**
236
+ * Do a checked cast from long to int. Throws a {@link SQLException} with code {@link
237
+ * Code#OUT_OF_RANGE} if the update count is too big to fit in an int.
238
+ */
239
+ int checkedCast (long updateCount ) throws SQLException {
240
+ if (updateCount > Integer .MAX_VALUE ) {
241
+ throw JdbcSqlExceptionFactory .of (
242
+ "update count too large for executeUpdate: " + updateCount , Code .OUT_OF_RANGE );
200
243
}
244
+ return (int ) updateCount ;
245
+ }
246
+
247
+ /**
248
+ * Executes a SQL statement on the connection of this {@link Statement} as an update (DML)
249
+ * statement.
250
+ *
251
+ * @param statement The SQL statement to execute
252
+ * @return the number of rows that was inserted/updated/deleted
253
+ * @throws SQLException if a database error occurs
254
+ */
255
+ long executeLargeUpdate (com .google .cloud .spanner .Statement statement ) throws SQLException {
256
+ return doWithStatementTimeout (() -> connection .getSpannerConnection ().executeUpdate (statement ));
201
257
}
202
258
203
259
/**
@@ -210,24 +266,16 @@ private ResultSet executeQuery(
210
266
* @throws SQLException if a database error occurs.
211
267
*/
212
268
StatementResult execute (com .google .cloud .spanner .Statement statement ) throws SQLException {
213
- StatementTimeout originalTimeout = setTemporaryStatementTimeout ();
214
- boolean mustResetTimeout = false ;
215
- try {
216
- StatementResult result = connection .getSpannerConnection ().execute (statement );
217
- mustResetTimeout = !resultIsSetStatementTimeout (result );
218
- if (mustResetTimeout && resultIsShowStatementTimeout (result )) {
219
- // it was a 'SHOW STATEMENT_TIMEOUT statement, we need to re-run to get the correct value
220
- mustResetTimeout = false ;
221
- result = rerunShowStatementTimeout (statement , originalTimeout );
222
- }
223
- return result ;
224
- } catch (SpannerException e ) {
225
- throw JdbcSqlExceptionFactory .of (e );
226
- } finally {
227
- if (mustResetTimeout ) {
228
- resetStatementTimeout (originalTimeout );
229
- }
269
+ StatementResult statementResult =
270
+ doWithStatementTimeout (
271
+ () -> connection .getSpannerConnection ().execute (statement ),
272
+ result -> !resultIsSetStatementTimeout (result ));
273
+ if (resultIsShowStatementTimeout (statementResult )) {
274
+ // We can safely re-run it without first resetting the timeout to the original value, as that
275
+ // has already been done by the 'doWithStatementTimeout' function.
276
+ return rerunShowStatementTimeout (statement );
230
277
}
278
+ return statementResult ;
231
279
}
232
280
233
281
/**
@@ -250,18 +298,22 @@ StatementResult execute(com.google.cloud.spanner.Statement statement) throws SQL
250
298
* executed was a SET STATEMENT_TIMEOUT statement.
251
299
*/
252
300
private boolean resultIsSetStatementTimeout (StatementResult result ) {
253
- return result .getClientSideStatementType () == ClientSideStatementType .SET_STATEMENT_TIMEOUT ;
301
+ return result != null
302
+ && result .getClientSideStatementType () == ClientSideStatementType .SET_STATEMENT_TIMEOUT ;
254
303
}
255
304
256
305
private boolean resultIsShowStatementTimeout (StatementResult result ) {
257
- return result .getClientSideStatementType () == ClientSideStatementType .SHOW_STATEMENT_TIMEOUT ;
306
+ return result != null
307
+ && result .getClientSideStatementType () == ClientSideStatementType .SHOW_STATEMENT_TIMEOUT ;
258
308
}
259
309
260
- private StatementResult rerunShowStatementTimeout (
261
- com .google .cloud .spanner .Statement statement , StatementTimeout originalTimeout )
310
+ private StatementResult rerunShowStatementTimeout (com .google .cloud .spanner .Statement statement )
262
311
throws SQLException {
263
- resetStatementTimeout (originalTimeout );
264
- return connection .getSpannerConnection ().execute (statement );
312
+ try {
313
+ return connection .getSpannerConnection ().execute (statement );
314
+ } catch (SpannerException spannerException ) {
315
+ throw JdbcSqlExceptionFactory .of (spannerException );
316
+ }
265
317
}
266
318
267
319
@ Override
0 commit comments