2
2
3
3
import static java .nio .charset .StandardCharsets .US_ASCII ;
4
4
import static java .nio .charset .StandardCharsets .UTF_8 ;
5
+ import static java .util .concurrent .TimeUnit .SECONDS ;
5
6
import static org .duckdb .StatementReturnType .*;
6
7
import static org .duckdb .io .IOUtils .*;
7
8
37
38
import java .util .ArrayList ;
38
39
import java .util .Calendar ;
39
40
import java .util .List ;
41
+ import java .util .concurrent .ScheduledFuture ;
40
42
import java .util .concurrent .locks .Lock ;
41
43
import java .util .concurrent .locks .ReentrantLock ;
42
44
@@ -59,6 +61,8 @@ public class DuckDBPreparedStatement implements PreparedStatement {
59
61
private final List <String > batchedStatements = new ArrayList <>();
60
62
private Boolean isBatch = false ;
61
63
private Boolean isPreparedStatement = false ;
64
+ private int queryTimeoutSeconds = 0 ;
65
+ private ScheduledFuture <?> cancelQueryFuture = null ;
62
66
63
67
public DuckDBPreparedStatement (DuckDBConnection conn ) throws SQLException {
64
68
if (conn == null ) {
@@ -180,7 +184,14 @@ private boolean execute(boolean startTransaction) throws SQLException {
180
184
startTransaction ();
181
185
}
182
186
187
+ if (queryTimeoutSeconds > 0 ) {
188
+ cleanupCancelQueryTask ();
189
+ cancelQueryFuture =
190
+ DuckDBDriver .scheduler .schedule (new CancelQueryTask (), queryTimeoutSeconds , SECONDS );
191
+ }
192
+
183
193
resultRef = DuckDBNative .duckdb_jdbc_execute (stmtRef , params );
194
+ cleanupCancelQueryTask ();
184
195
DuckDBResultSetMetaData resultMeta = DuckDBNative .duckdb_jdbc_query_result_meta (resultRef );
185
196
selectResult = new DuckDBResultSet (conn , this , resultMeta , resultRef );
186
197
returnsResultSet = resultMeta .return_type .equals (QUERY_RESULT );
@@ -356,6 +367,7 @@ public void close() throws SQLException {
356
367
if (isClosed ()) {
357
368
return ;
358
369
}
370
+ cleanupCancelQueryTask ();
359
371
if (selectResult != null ) {
360
372
selectResult .close ();
361
373
selectResult = null ;
@@ -436,12 +448,16 @@ public void setEscapeProcessing(boolean enable) throws SQLException {
436
448
@ Override
437
449
public int getQueryTimeout () throws SQLException {
438
450
checkOpen ();
439
- return 0 ;
451
+ return queryTimeoutSeconds ;
440
452
}
441
453
442
454
@ Override
443
455
public void setQueryTimeout (int seconds ) throws SQLException {
444
456
checkOpen ();
457
+ if (seconds < 0 ) {
458
+ throw new SQLException ("Invalid negative timeout value: " + seconds );
459
+ }
460
+ this .queryTimeoutSeconds = seconds ;
445
461
}
446
462
447
463
@ Override
@@ -1244,4 +1260,25 @@ private Lock getConnRefLock() throws SQLException {
1244
1260
throw new SQLException (e );
1245
1261
}
1246
1262
}
1263
+
1264
+ private void cleanupCancelQueryTask () {
1265
+ if (cancelQueryFuture != null ) {
1266
+ cancelQueryFuture .cancel (false );
1267
+ cancelQueryFuture = null ;
1268
+ }
1269
+ }
1270
+
1271
+ private class CancelQueryTask implements Runnable {
1272
+ @ Override
1273
+ public void run () {
1274
+ try {
1275
+ if (DuckDBPreparedStatement .this .isClosed ()) {
1276
+ return ;
1277
+ }
1278
+ DuckDBPreparedStatement .this .cancel ();
1279
+ } catch (SQLException e ) {
1280
+ // suppress
1281
+ }
1282
+ }
1283
+ }
1247
1284
}
0 commit comments