This is an automated email from the ASF dual-hosted git repository.
Caideyipi pushed a commit to branch codex/jdbc-driver-info
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/codex/jdbc-driver-info by this
push:
new 02884fff460 Reject JDBC operations after close
02884fff460 is described below
commit 02884fff4604d32820a32af8471392793c588c03
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jun 9 15:52:11 2026 +0800
Reject JDBC operations after close
---
.../org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java | 15 ++++++++++++--
.../apache/iotdb/jdbc/IoTDBPreparedStatement.java | 22 ++++++++++++++++----
.../iotdb/jdbc/IoTDBTablePreparedStatement.java | 22 +++++++++++++-------
.../apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java | 21 +++++++++++++++++++
.../iotdb/jdbc/IoTDBPreparedStatementTest.java | 22 ++++++++++++++++++++
.../jdbc/IoTDBTablePreparedStatementTest.java | 24 ++++++++++++++++++++++
.../java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java | 17 +++++++++++++--
7 files changed, 128 insertions(+), 15 deletions(-)
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java
index 903f0bb8926..fa889371bdc 100644
---
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java
+++
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java
@@ -83,6 +83,7 @@ public class IoTDBJDBCResultSet implements ResultSet {
private List<String> sgColumns = null;
private Charset charset = TSFileConfig.STRING_CHARSET;
private String timeFormat = RpcUtils.DEFAULT_TIME_FORMAT;
+ private boolean explicitlyClosed = false;
@SuppressWarnings("squid:S107") // ignore Methods should not have too many
parameters
public IoTDBJDBCResultSet(
@@ -217,6 +218,7 @@ public class IoTDBJDBCResultSet implements ResultSet {
public void close() throws SQLException {
try {
ioTDBRpcDataSet.close();
+ explicitlyClosed = true;
} catch (StatementExecutionException e) {
throw new SQLException(JdbcMessages.CLOSE_SERVER_SIDE_ERROR, e);
} catch (TException e) {
@@ -231,6 +233,7 @@ public class IoTDBJDBCResultSet implements ResultSet {
@Override
public int findColumn(String columnName) throws SQLException {
+ checkOpen();
if (!ioTDBRpcDataSet.getColumnNameList().contains(columnName)) {
throw new SQLException("Unknown column name: " + columnName);
}
@@ -586,7 +589,8 @@ public class IoTDBJDBCResultSet implements ResultSet {
}
@Override
- public ResultSetMetaData getMetaData() {
+ public ResultSetMetaData getMetaData() throws SQLException {
+ checkOpen();
String operationTypeColumn = "";
boolean nonAlign = false;
try {
@@ -833,6 +837,12 @@ public class IoTDBJDBCResultSet implements ResultSet {
return ioTDBRpcDataSet.isClosed();
}
+ private void checkOpen() throws SQLException {
+ if (explicitlyClosed) {
+ throw new SQLException("ResultSet has been closed");
+ }
+ }
+
@Override
public boolean isFirst() throws SQLException {
throw new SQLException(Constant.METHOD_NOT_SUPPORTED);
@@ -1313,7 +1323,8 @@ public class IoTDBJDBCResultSet implements ResultSet {
}
@Override
- public boolean wasNull() {
+ public boolean wasNull() throws SQLException {
+ checkOpen();
return ioTDBRpcDataSet.isLastReadWasNull();
}
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java
index 60222d3e7da..0accd32c92b 100644
---
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java
+++
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBPreparedStatement.java
@@ -105,7 +105,8 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
}
@Override
- public void clearParameters() {
+ public void clearParameters() throws SQLException {
+ checkConnection("clearParameters");
this.parameters.clear();
}
@@ -134,10 +135,12 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
}
@Override
- public ParameterMetaData getParameterMetaData() {
+ public ParameterMetaData getParameterMetaData() throws SQLException {
+ checkConnection("getParameterMetaData");
return new ParameterMetaData() {
@Override
- public int getParameterCount() {
+ public int getParameterCount() throws SQLException {
+ checkConnection("getParameterMetaData");
return parameterCount;
}
@@ -223,10 +226,16 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
}
private void checkParameterMetadataIndex(int param) throws SQLException {
- checkParameterIndex(param);
+ checkConnection("getParameterMetaData");
+ checkParameterIndexRange(param);
}
private void checkParameterIndex(int param) throws SQLException {
+ checkConnection("set parameter");
+ checkParameterIndexRange(param);
+ }
+
+ private void checkParameterIndexRange(int param) throws SQLException {
if (param < 1 || param > parameterCount) {
throw new SQLException(
"Parameter index out of range: " + param + " (expected 1-" +
parameterCount + ")");
@@ -275,6 +284,7 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
+ checkParameterIndex(parameterIndex);
if (length < 0) {
throw new SQLException("length must be >= 0");
}
@@ -332,6 +342,7 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
setNull(parameterIndex, Types.BINARY);
return;
}
+ checkParameterIndex(parameterIndex);
Binary binary = new Binary(x);
setParameter(parameterIndex,
binary.getStringValue(TSFileConfig.STRING_CHARSET));
}
@@ -374,6 +385,7 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
setNull(parameterIndex, Types.DATE);
return;
}
+ checkParameterIndex(parameterIndex);
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
setParameter(parameterIndex, "'" + dateFormat.format(x) + "'");
}
@@ -1048,6 +1060,7 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
setNull(parameterIndex, Types.TIMESTAMP);
return;
}
+ checkParameterIndex(parameterIndex);
ZonedDateTime zonedDateTime =
ZonedDateTime.ofInstant(Instant.ofEpochMilli(x.getTime()),
super.zoneId);
setParameter(parameterIndex,
zonedDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
@@ -1059,6 +1072,7 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
setNull(parameterIndex, Types.TIMESTAMP);
return;
}
+ checkParameterIndex(parameterIndex);
ZonedDateTime zonedDateTime = null;
if (cal != null) {
zonedDateTime =
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatement.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatement.java
index 25bfbdcadb2..9760b8fd528 100644
---
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatement.java
+++
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatement.java
@@ -145,7 +145,8 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
}
@Override
- public void clearParameters() {
+ public void clearParameters() throws SQLException {
+ checkConnection("clearParameters");
this.parameters.clear();
if (serverSidePrepared) {
for (int i = 0; i < parameterCount; i++) {
@@ -261,6 +262,7 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
@Override
public ResultSetMetaData getMetaData() throws SQLException {
+ checkConnection("getMetaData");
if (resultSet != null) {
return resultSet.getMetaData();
}
@@ -268,10 +270,12 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
}
@Override
- public ParameterMetaData getParameterMetaData() {
+ public ParameterMetaData getParameterMetaData() throws SQLException {
+ checkConnection("getParameterMetaData");
return new ParameterMetaData() {
@Override
- public int getParameterCount() {
+ public int getParameterCount() throws SQLException {
+ checkConnection("getParameterMetaData");
return parameterCount;
}
@@ -529,13 +533,16 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
}
private void checkParameterIndex(int index) throws SQLException {
- if (index < 1 || index > parameterCount) {
- throw new SQLException(
- "Parameter index out of range: " + index + " (expected 1-" +
parameterCount + ")");
- }
+ checkConnection("set parameter");
+ checkParameterIndexRange(index);
}
private void checkParameterMetadataIndex(int index) throws SQLException {
+ checkConnection("getParameterMetaData");
+ checkParameterIndexRange(index);
+ }
+
+ private void checkParameterIndexRange(int index) throws SQLException {
if (index < 1 || index > parameterCount) {
throw new SQLException(
"Parameter index out of range: " + index + " (expected 1-" +
parameterCount + ")");
@@ -586,6 +593,7 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
+ checkParameterIndex(parameterIndex);
if (length < 0) {
throw new SQLException("length must be >= 0");
}
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java
index d06464112cd..b3b43356447 100644
---
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java
+++
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java
@@ -243,6 +243,7 @@ public class IoTDBJDBCResultSetTest {
resultStr.append("\n");
fetchResultsResp.hasResultSet = false; // at the second time to fetch
}
+ Assert.assertFalse(resultSet.next());
String standard =
"Time,root.vehicle.d0.s2,root.vehicle.d0.s1,root.vehicle.d0.s0,root.vehicle.d0.s2,\n"
+ "2,2.22,40000,null,2.22,\n"
@@ -281,6 +282,26 @@ public class IoTDBJDBCResultSetTest {
}
}
+ @SuppressWarnings("resource")
+ @Test
+ public void testClosedResultSetRejectsCachedReads() throws Exception {
+ mockVehicleQueryResponse();
+
+ Assert.assertTrue(statement.execute("select * from root.vehicle.d0"));
+
+ ResultSet resultSet = statement.getResultSet();
+ Assert.assertTrue(resultSet.next());
+
+ resultSet.close();
+
+ Assert.assertTrue(resultSet.isClosed());
+ Assert.assertThrows(SQLException.class, () -> resultSet.next());
+ Assert.assertThrows(SQLException.class, () -> resultSet.getString(1));
+ Assert.assertThrows(SQLException.class, () ->
resultSet.findColumn("Time"));
+ Assert.assertThrows(SQLException.class, () -> resultSet.getMetaData());
+ Assert.assertThrows(SQLException.class, () -> resultSet.wasNull());
+ }
+
@SuppressWarnings("resource")
@Test
public void testInvalidBigDecimalConversionThrowsSQLException() throws
Exception {
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBPreparedStatementTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBPreparedStatementTest.java
index b043705cf72..a147f6b8eb9 100644
---
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBPreparedStatementTest.java
+++
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBPreparedStatementTest.java
@@ -70,6 +70,7 @@ public class IoTDBPreparedStatementTest {
when(execStatementResp.getQueryId()).thenReturn(queryId);
when(client.executeStatementV2(any(TSExecuteStatementReq.class))).thenReturn(execStatementResp);
+ when(client.closeOperation(any())).thenReturn(Status_SUCCESS);
}
@SuppressWarnings("resource")
@@ -135,6 +136,27 @@ public class IoTDBPreparedStatementTest {
assertNull(ps.getMetaData());
}
+ @SuppressWarnings("resource")
+ @Test
+ public void testClosedPreparedStatementRejectsParameterOperations() throws
Exception {
+ IoTDBPreparedStatement ps =
+ new IoTDBPreparedStatement(connection, client, sessionId, "SELECT ?",
zoneId);
+ ParameterMetaData metadata = ps.getParameterMetaData();
+
+ ps.close();
+
+ assertTrue(ps.isClosed());
+ assertThrows(SQLException.class, () -> ps.clearParameters());
+ assertThrows(SQLException.class, () -> ps.setInt(1, 1));
+ assertThrows(SQLException.class, () -> ps.setString(1, "x"));
+ assertThrows(
+ SQLException.class,
+ () -> ps.setBinaryStream(1, new ByteArrayInputStream(new byte[] {1}),
1));
+ assertThrows(SQLException.class, () -> ps.getParameterMetaData());
+ assertThrows(SQLException.class, () -> metadata.getParameterCount());
+ assertThrows(SQLException.class, () -> metadata.getPrecision(1));
+ }
+
@SuppressWarnings("resource")
@Test
public void invalidParameterIndex() throws SQLException {
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatementTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatementTest.java
index 166587eab86..50334a25156 100644
---
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatementTest.java
+++
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBTablePreparedStatementTest.java
@@ -99,6 +99,8 @@ public class IoTDBTablePreparedStatementTest {
when(client.executePreparedStatement(any(TSExecutePreparedReq.class)))
.thenReturn(execStatementResp);
when(client.executeStatementV2(any(TSExecuteStatementReq.class))).thenReturn(execStatementResp);
+ when(client.deallocatePreparedStatement(any())).thenReturn(Status_SUCCESS);
+ when(client.closeOperation(any())).thenReturn(Status_SUCCESS);
}
/** Count the number of '?' placeholders in a SQL string, ignoring those
inside quotes */
@@ -162,6 +164,28 @@ public class IoTDBTablePreparedStatementTest {
assertTrue(metadata.isSigned(1));
}
+ @SuppressWarnings("resource")
+ @Test
+ public void testClosedTablePreparedStatementRejectsParameterOperations()
throws Exception {
+ IoTDBTablePreparedStatement ps =
+ new IoTDBTablePreparedStatement(connection, client, sessionId, "SELECT
?", zoneId);
+ ParameterMetaData metadata = ps.getParameterMetaData();
+
+ ps.close();
+
+ assertTrue(ps.isClosed());
+ assertThrows(SQLException.class, () -> ps.clearParameters());
+ assertThrows(SQLException.class, () -> ps.getMetaData());
+ assertThrows(SQLException.class, () -> ps.setInt(1, 1));
+ assertThrows(SQLException.class, () -> ps.setString(1, "x"));
+ assertThrows(
+ SQLException.class,
+ () -> ps.setBinaryStream(1, new ByteArrayInputStream(new byte[] {1}),
1));
+ assertThrows(SQLException.class, () -> ps.getParameterMetaData());
+ assertThrows(SQLException.class, () -> metadata.getParameterCount());
+ assertThrows(SQLException.class, () -> metadata.getParameterType(1));
+ }
+
@SuppressWarnings("resource")
@Test
public void testTableModelLoginInjectionWithComment() throws Exception {
diff --git
a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java
b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java
index 42bf974a3eb..75d4e0a4754 100644
---
a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java
+++
b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBRpcDataSet.java
@@ -54,6 +54,7 @@ public class IoTDBRpcDataSet {
private final String sql;
private boolean isClosed = false;
+ private boolean explicitlyClosed = false;
private IClientRPCService.Iface client;
private final List<String> columnNameList; // no deduplication
private final List<String> columnTypeList; // no deduplication
@@ -207,7 +208,12 @@ public class IoTDBRpcDataSet {
}
public void close() throws StatementExecutionException, TException {
+ close(true);
+ }
+
+ private void close(boolean explicitlyClosed) throws
StatementExecutionException, TException {
if (isClosed) {
+ this.explicitlyClosed = this.explicitlyClosed || explicitlyClosed;
return;
}
if (client != null) {
@@ -225,9 +231,13 @@ public class IoTDBRpcDataSet {
}
client = null;
isClosed = true;
+ this.explicitlyClosed = explicitlyClosed;
}
public boolean next() throws StatementExecutionException,
IoTDBConnectionException {
+ if (explicitlyClosed) {
+ throw new IoTDBConnectionException(RpcMessages.DATASET_ALREADY_CLOSED);
+ }
if (hasCachedBlock()) {
lastReadWasNull = false;
constructOneRow();
@@ -245,7 +255,7 @@ public class IoTDBRpcDataSet {
return true;
} else {
try {
- close();
+ close(false);
return false;
} catch (TException e) {
throw new IoTDBConnectionException(RpcMessages.CANNOT_CLOSE_DATASET,
e);
@@ -265,7 +275,7 @@ public class IoTDBRpcDataSet {
RpcUtils.verifySuccess(resp.getStatus());
moreData = resp.moreData;
if (!resp.hasResultSet) {
- close();
+ close(false);
} else {
queryResult = resp.getQueryResult();
queryResultIndex = 0;
@@ -640,6 +650,9 @@ public class IoTDBRpcDataSet {
}
public void checkRecord() throws StatementExecutionException {
+ if (explicitlyClosed) {
+ throw new
StatementExecutionException(RpcMessages.DATASET_ALREADY_CLOSED);
+ }
if (queryResultIndex > queryResultSize
|| tsBlockIndex >= tsBlockSize
|| queryResult == null