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 ebe1117b0c2 Fix JDBC metadata result set close handling
ebe1117b0c2 is described below

commit ebe1117b0c2a82433962213130691f969bc4f9c5
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jun 9 18:41:04 2026 +0800

    Fix JDBC metadata result set close handling
---
 .../org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java  |   4 +-
 .../java/org/apache/iotdb/jdbc/IoTDBStatement.java |   4 +
 .../IoTDBRelationalDatabaseMetadata.java           | 263 ++++++++++-----------
 .../iotdb/jdbc/IoTDBDatabaseMetadataTest.java      |  10 +
 .../jdbc/IoTDBRelationalDatabaseMetadataTest.java  | 144 +++++++++++
 5 files changed, 289 insertions(+), 136 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 d41366b677f..8ec783e05f9 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
@@ -118,7 +118,7 @@ public class IoTDBJDBCResultSet implements ResultSet {
             client,
             sessionId,
             dataSet,
-            statement.getFetchSize(),
+            statement.getFetchSizeInternal(),
             timeout,
             zoneId,
             timeFormat,
@@ -164,7 +164,7 @@ public class IoTDBJDBCResultSet implements ResultSet {
             client,
             sessionId,
             dataSet,
-            statement.getFetchSize(),
+            ((IoTDBStatement) statement).getFetchSizeInternal(),
             timeout,
             zoneId,
             timeFormat,
diff --git 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
index 297c19497a9..e2fa67aa2d8 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBStatement.java
@@ -620,6 +620,10 @@ public class IoTDBStatement implements Statement {
     return fetchSize;
   }
 
+  int getFetchSizeInternal() {
+    return fetchSize;
+  }
+
   @Override
   public void setFetchSize(int fetchSize) throws SQLException {
     checkConnection("setFetchSize");
diff --git 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/relational/IoTDBRelationalDatabaseMetadata.java
 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/relational/IoTDBRelationalDatabaseMetadata.java
index 7ddfca1c01a..eec5d1d29bd 100644
--- 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/relational/IoTDBRelationalDatabaseMetadata.java
+++ 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/relational/IoTDBRelationalDatabaseMetadata.java
@@ -225,10 +225,9 @@ public class IoTDBRelationalDatabaseMetadata extends 
IoTDBAbstractDatabaseMetada
         legacyMode = false;
       } catch (SQLException e1) {
         LOGGER.error(SHOW_TABLES_ERROR_MSG, e.getMessage());
+        close(null, stmt);
         throw e;
       }
-    } finally {
-      stmt.close();
     }
 
     // Setup Fields
@@ -257,43 +256,43 @@ public class IoTDBRelationalDatabaseMetadata extends 
IoTDBAbstractDatabaseMetada
       columnNameIndex.put(fields[i].getName(), i);
     }
 
-    // Extract Values
     boolean hasResultSet = false;
-    while (rs.next()) {
-      hasResultSet = true;
-      List<Object> valueInRow = new ArrayList<>();
-      for (int i = 0; i < fields.length; i++) {
-        if (i == 0) {
-          valueInRow.add(schemaPattern);
-        } else if (i == 1) {
-          // valueInRow.add(rs.getString(2));
-          valueInRow.add(legacyMode ? rs.getString("table_name") : 
rs.getString("TableName"));
-        } else if (i == 2) {
-          valueInRow.add("TABLE");
-        } else if (i == 3) {
-          // String tgtString = "";
-          // String ttl = rs.getString("ttl(ms)");
-          // tgtString += "TTL(ms): " + ttl;
-          String comment = legacyMode ? rs.getString("comment") : 
rs.getString("Comment");
-          if (comment != null && !comment.isEmpty()) {
-            valueInRow.add(comment);
+    ByteBuffer tsBlock = null;
+    try {
+      // Extract Values
+      while (rs.next()) {
+        hasResultSet = true;
+        List<Object> valueInRow = new ArrayList<>();
+        for (int i = 0; i < fields.length; i++) {
+          if (i == 0) {
+            valueInRow.add(schemaPattern);
+          } else if (i == 1) {
+            // valueInRow.add(rs.getString(2));
+            valueInRow.add(legacyMode ? rs.getString("table_name") : 
rs.getString("TableName"));
+          } else if (i == 2) {
+            valueInRow.add("TABLE");
+          } else if (i == 3) {
+            // String tgtString = "";
+            // String ttl = rs.getString("ttl(ms)");
+            // tgtString += "TTL(ms): " + ttl;
+            String comment = legacyMode ? rs.getString("comment") : 
rs.getString("Comment");
+            if (comment != null && !comment.isEmpty()) {
+              valueInRow.add(comment);
+            } else {
+              valueInRow.add("");
+            }
+          } else if (i == 4) {
+            valueInRow.add(getTypePrecision(fields[i].getSqlType()));
+          } else if (i == 5) {
+            valueInRow.add(getTypeScale(fields[i].getSqlType()));
           } else {
-            valueInRow.add("");
+            valueInRow.add("TABLE");
           }
-        } else if (i == 4) {
-          valueInRow.add(getTypePrecision(fields[i].getSqlType()));
-        } else if (i == 5) {
-          valueInRow.add(getTypeScale(fields[i].getSqlType()));
-        } else {
-          valueInRow.add("TABLE");
         }
+        valuesList.add(valueInRow);
       }
-      valuesList.add(valueInRow);
-    }
 
-    // Convert Values to ByteBuffer
-    ByteBuffer tsBlock = null;
-    try {
+      // Convert Values to ByteBuffer
       tsBlock = convertTsBlock(valuesList, tsDataTypeList);
     } catch (IOException e) {
       LOGGER.error(CONVERT_ERROR_MSG, e.getMessage());
@@ -358,11 +357,9 @@ public class IoTDBRelationalDatabaseMetadata extends 
IoTDBAbstractDatabaseMetada
         legacyMode = false;
       } catch (SQLException e1) {
         LOGGER.error(SHOW_TABLES_ERROR_MSG, e.getMessage());
+        close(null, stmt);
         throw e;
       }
-
-    } finally {
-      stmt.close();
     }
 
     // Setup Fields
@@ -428,85 +425,85 @@ public class IoTDBRelationalDatabaseMetadata extends 
IoTDBAbstractDatabaseMetada
       columnNameIndex.put(fields[i].getName(), i);
     }
 
-    // Extract Metadata
     int count = 1;
-    while (rs.next()) {
-      String columnName =
-          legacyMode ? rs.getString("column_name") : 
rs.getString("ColumnName"); // 3
-      String type = legacyMode ? rs.getString("datatype") : 
rs.getString("DataType"); // 4
-      List<Object> valueInRow = new ArrayList<>();
-      for (int i = 0; i < fields.length; i++) {
-        if (i == 0) {
-          valueInRow.add("");
-        } else if (i == 1) {
-          valueInRow.add(schemaPattern);
-        } else if (i == 2) {
-          valueInRow.add(tableNamePattern);
-        } else if (i == 3) {
-          valueInRow.add(columnName);
-        } else if (i == 4) {
-          valueInRow.add(getSQLType(type));
-        } else if (i == 5) {
-          valueInRow.add(type);
-        } else if (i == 6) {
-          valueInRow.add(0);
-        } else if (i == 7) {
-          valueInRow.add(65535);
-        } else if (i == 8) {
-          valueInRow.add(getTypeScale(fields[i].getSqlType()));
-        } else if (i == 9) {
-          valueInRow.add(0);
-        } else if (i == 10) {
-          if (!columnName.equals("time")) {
-            valueInRow.add(ResultSetMetaData.columnNullableUnknown);
-          } else {
-            valueInRow.add(ResultSetMetaData.columnNoNulls);
-          }
-        } else if (i == 11) {
-          String comment = legacyMode ? rs.getString("comment") : 
rs.getString("Comment");
-          if (comment != null && !comment.isEmpty()) {
-            valueInRow.add(comment);
-          } else {
+    ByteBuffer tsBlock = null;
+    try {
+      // Extract Metadata
+      while (rs.next()) {
+        String columnName =
+            legacyMode ? rs.getString("column_name") : 
rs.getString("ColumnName"); // 3
+        String type = legacyMode ? rs.getString("datatype") : 
rs.getString("DataType"); // 4
+        List<Object> valueInRow = new ArrayList<>();
+        for (int i = 0; i < fields.length; i++) {
+          if (i == 0) {
+            valueInRow.add("");
+          } else if (i == 1) {
+            valueInRow.add(schemaPattern);
+          } else if (i == 2) {
+            valueInRow.add(tableNamePattern);
+          } else if (i == 3) {
+            valueInRow.add(columnName);
+          } else if (i == 4) {
+            valueInRow.add(getSQLType(type));
+          } else if (i == 5) {
+            valueInRow.add(type);
+          } else if (i == 6) {
+            valueInRow.add(0);
+          } else if (i == 7) {
+            valueInRow.add(65535);
+          } else if (i == 8) {
+            valueInRow.add(getTypeScale(fields[i].getSqlType()));
+          } else if (i == 9) {
+            valueInRow.add(0);
+          } else if (i == 10) {
+            if (!columnName.equals("time")) {
+              valueInRow.add(ResultSetMetaData.columnNullableUnknown);
+            } else {
+              valueInRow.add(ResultSetMetaData.columnNoNulls);
+            }
+          } else if (i == 11) {
+            String comment = legacyMode ? rs.getString("comment") : 
rs.getString("Comment");
+            if (comment != null && !comment.isEmpty()) {
+              valueInRow.add(comment);
+            } else {
+              valueInRow.add("");
+            }
+          } else if (i == 12) {
+            valueInRow.add("");
+          } else if (i == 13) {
+            valueInRow.add(0);
+          } else if (i == 14) {
+            valueInRow.add(0);
+          } else if (i == 15) {
+            valueInRow.add(65535);
+          } else if (i == 16) {
+            valueInRow.add(count++);
+          } else if (i == 17) {
+            if (!columnName.equals("time")) {
+              valueInRow.add("YES");
+            } else {
+              valueInRow.add("NO");
+            }
+          } else if (i == 18) {
+            valueInRow.add("");
+          } else if (i == 19) {
+            valueInRow.add("");
+          } else if (i == 20) {
+            valueInRow.add("");
+          } else if (i == 21) {
+            valueInRow.add(0);
+          } else if (i == 22) {
+            valueInRow.add("");
+          } else if (i == 23) {
             valueInRow.add("");
-          }
-        } else if (i == 12) {
-          valueInRow.add("");
-        } else if (i == 13) {
-          valueInRow.add(0);
-        } else if (i == 14) {
-          valueInRow.add(0);
-        } else if (i == 15) {
-          valueInRow.add(65535);
-        } else if (i == 16) {
-          valueInRow.add(count++);
-        } else if (i == 17) {
-          if (!columnName.equals("time")) {
-            valueInRow.add("YES");
           } else {
-            valueInRow.add("NO");
+            valueInRow.add("");
           }
-        } else if (i == 18) {
-          valueInRow.add("");
-        } else if (i == 19) {
-          valueInRow.add("");
-        } else if (i == 20) {
-          valueInRow.add("");
-        } else if (i == 21) {
-          valueInRow.add(0);
-        } else if (i == 22) {
-          valueInRow.add("");
-        } else if (i == 23) {
-          valueInRow.add("");
-        } else {
-          valueInRow.add("");
         }
+        valuesList.add(valueInRow);
       }
-      valuesList.add(valueInRow);
-    }
 
-    // Convert Values to ByteBuffer
-    ByteBuffer tsBlock = null;
-    try {
+      // Convert Values to ByteBuffer
       tsBlock = convertTsBlock(valuesList, tsDataTypeList);
     } catch (IOException e) {
       LOGGER.error(CONVERT_ERROR_MSG, e.getMessage());
@@ -556,11 +553,9 @@ public class IoTDBRelationalDatabaseMetadata extends 
IoTDBAbstractDatabaseMetada
         legacyMode = false;
       } catch (SQLException e1) {
         LOGGER.error(SHOW_TABLES_ERROR_MSG, e.getMessage());
+        close(null, stmt);
         throw e;
       }
-
-    } finally {
-      stmt.close();
     }
 
     Field[] fields = new Field[6];
@@ -589,37 +584,37 @@ public class IoTDBRelationalDatabaseMetadata extends 
IoTDBAbstractDatabaseMetada
     }
 
     int count = 1;
-    while (rs.next()) {
-      String columnName = legacyMode ? rs.getString("column_name") : 
rs.getString("ColumnName");
-      String category = legacyMode ? rs.getString("category") : 
rs.getString("Category");
-      if (category.equals("TAG") || category.equals("TIME")) {
-        List<Object> valueInRow = new ArrayList<>();
-        for (int i = 0; i < fields.length; ++i) {
-          if (i == 0) {
-            valueInRow.add(schemaPattern);
-          } else if (i == 1) {
-            valueInRow.add(schemaPattern);
-          } else if (i == 2) {
-            valueInRow.add(tableNamePattern);
-          } else if (i == 3) {
-            valueInRow.add(columnName);
-          } else if (i == 4) {
-            valueInRow.add(count++);
-          } else {
-            valueInRow.add(PRIMARY);
+    ByteBuffer tsBlock = null;
+    try {
+      while (rs.next()) {
+        String columnName = legacyMode ? rs.getString("column_name") : 
rs.getString("ColumnName");
+        String category = legacyMode ? rs.getString("category") : 
rs.getString("Category");
+        if (category.equals("TAG") || category.equals("TIME")) {
+          List<Object> valueInRow = new ArrayList<>();
+          for (int i = 0; i < fields.length; ++i) {
+            if (i == 0) {
+              valueInRow.add(schemaPattern);
+            } else if (i == 1) {
+              valueInRow.add(schemaPattern);
+            } else if (i == 2) {
+              valueInRow.add(tableNamePattern);
+            } else if (i == 3) {
+              valueInRow.add(columnName);
+            } else if (i == 4) {
+              valueInRow.add(count++);
+            } else {
+              valueInRow.add(PRIMARY);
+            }
           }
+          valuesList.add(valueInRow);
         }
-        valuesList.add(valueInRow);
       }
-    }
 
-    ByteBuffer tsBlock = null;
-    try {
       tsBlock = convertTsBlock(valuesList, tsDataTypeList);
     } catch (IOException e) {
       LOGGER.error(JdbcMessages.RELATIONAL_GET_PRIMARY_KEYS_ERROR, 
e.getMessage());
     } finally {
-      close(null, stmt);
+      close(rs, stmt);
     }
 
     return new IoTDBJDBCResultSet(
diff --git 
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java
 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java
index f4dc8564a20..1bce1264d29 100644
--- 
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java
+++ 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDatabaseMetadataTest.java
@@ -203,6 +203,16 @@ public class IoTDBDatabaseMetadataTest {
     Assert.assertEquals("PKTABLE_SCHEM", 
resultSet.getMetaData().getColumnName(3));
   }
 
+  @Test
+  public void 
testGetPrimaryKeysBuildsMetadataResultSetAfterClosingInternalStatement()
+      throws SQLException {
+    ResultSet resultSet = databaseMetaData.getPrimaryKeys(null, null, 
"root.sg.d1");
+
+    Assert.assertEquals("TABLE_CAT", resultSet.getMetaData().getColumnName(1));
+    Assert.assertEquals("TABLE_SCHEM", 
resultSet.getMetaData().getColumnName(2));
+    Assert.assertEquals("TABLE_NAME", 
resultSet.getMetaData().getColumnName(3));
+  }
+
   @Test
   public void testGetIndexInfo() throws SQLException {
     ResultSet resultSet = databaseMetaData.getIndexInfo(null, null, null, 
false, false);
diff --git 
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBRelationalDatabaseMetadataTest.java
 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBRelationalDatabaseMetadataTest.java
new file mode 100644
index 00000000000..c4c9547ca48
--- /dev/null
+++ 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBRelationalDatabaseMetadataTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.jdbc;
+
+import org.apache.iotdb.jdbc.relational.IoTDBRelationalDatabaseMetadata;
+import org.apache.iotdb.service.rpc.thrift.IClientRPCService.Iface;
+
+import org.junit.Test;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.ZoneId;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class IoTDBRelationalDatabaseMetadataTest {
+
+  @Test
+  public void testReadsSourceResultSetsBeforeClosingInternalStatements() 
throws SQLException {
+    IoTDBConnection connection = mock(IoTDBConnection.class);
+    Iface client = mock(Iface.class);
+    long sessionId = 1L;
+    ZoneId zoneId = ZoneId.systemDefault();
+    when(connection.getTimeFactor()).thenReturn(1_000);
+
+    CloseAwareStatement tablesStatement =
+        new CloseAwareStatement(connection, client, sessionId, zoneId);
+    ResultSet tablesSource =
+        sourceResultSet(tablesStatement, values("table_name", "table1", 
"comment", ""));
+    tablesStatement.setResultSet(tablesSource);
+
+    CloseAwareStatement columnsStatement =
+        new CloseAwareStatement(connection, client, sessionId, zoneId);
+    ResultSet columnsSource =
+        sourceResultSet(
+            columnsStatement, values("column_name", "tag1", "datatype", 
"INT32", "comment", ""));
+    columnsStatement.setResultSet(columnsSource);
+
+    CloseAwareStatement primaryKeysStatement =
+        new CloseAwareStatement(connection, client, sessionId, zoneId);
+    ResultSet primaryKeysSource =
+        sourceResultSet(primaryKeysStatement, values("column_name", "tag1", 
"category", "TAG"));
+    primaryKeysStatement.setResultSet(primaryKeysSource);
+
+    when(connection.createStatement())
+        .thenReturn(tablesStatement, columnsStatement, primaryKeysStatement);
+
+    IoTDBRelationalDatabaseMetadata metadata =
+        new IoTDBRelationalDatabaseMetadata(connection, client, sessionId, 
zoneId);
+
+    ResultSet tables = metadata.getTables(null, "rootdb", null, null);
+    assertNotNull(tables);
+    assertEquals("TABLE_SCHEM", tables.getMetaData().getColumnName(1));
+
+    ResultSet columns = metadata.getColumns(null, "rootdb", "table1", null);
+    assertEquals("COLUMN_NAME", columns.getMetaData().getColumnName(4));
+
+    ResultSet primaryKeys = metadata.getPrimaryKeys(null, "rootdb", "table1");
+    assertEquals("PK_NAME", primaryKeys.getMetaData().getColumnName(6));
+
+    verify(tablesSource).close();
+    verify(columnsSource).close();
+    verify(primaryKeysSource).close();
+  }
+
+  private static ResultSet sourceResultSet(
+      CloseAwareStatement statement, Map<String, String> values) throws 
SQLException {
+    ResultSet resultSet = mock(ResultSet.class);
+    AtomicInteger nextCalls = new AtomicInteger();
+    when(resultSet.next())
+        .thenAnswer(
+            invocation -> {
+              if (statement.wasClosed()) {
+                throw new SQLException("source result set was closed with its 
statement");
+              }
+              return nextCalls.getAndIncrement() == 0;
+            });
+    when(resultSet.getString(anyString()))
+        .thenAnswer(invocation -> values.get(invocation.getArgument(0)));
+    return resultSet;
+  }
+
+  private static Map<String, String> values(String... entries) {
+    Map<String, String> values = new HashMap<>();
+    for (int i = 0; i < entries.length; i += 2) {
+      values.put(entries[i], entries[i + 1]);
+    }
+    return values;
+  }
+
+  private static class CloseAwareStatement extends IoTDBStatement {
+    private ResultSet resultSet;
+    private boolean closed;
+
+    private CloseAwareStatement(
+        IoTDBConnection connection, Iface client, long sessionId, ZoneId 
zoneId) {
+      super(connection, client, sessionId, zoneId, 0, -1L);
+    }
+
+    private void setResultSet(ResultSet resultSet) {
+      this.resultSet = resultSet;
+    }
+
+    private boolean wasClosed() {
+      return closed;
+    }
+
+    @Override
+    public ResultSet executeQuery(String sql) throws SQLException {
+      return resultSet;
+    }
+
+    @Override
+    public void close() throws SQLException {
+      closed = true;
+      super.close();
+    }
+  }
+}

Reply via email to