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();
+ }
+ }
+}