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

Reply via email to