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 c23ff58f160 Fix JDBC statement and metadata edge cases
c23ff58f160 is described below

commit c23ff58f1609cf1cb3291b85b22776d046b36fff
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jun 9 15:11:12 2026 +0800

    Fix JDBC statement and metadata edge cases
---
 .../org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java  | 18 +++--
 .../apache/iotdb/jdbc/IoTDBPreparedStatement.java  |  7 +-
 .../org/apache/iotdb/jdbc/IoTDBResultMetadata.java | 64 +++++++++++-----
 .../java/org/apache/iotdb/jdbc/IoTDBStatement.java | 41 +++++++----
 .../iotdb/jdbc/IoTDBTablePreparedStatement.java    |  4 +
 .../apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java  | 58 +++++++++++++++
 .../iotdb/jdbc/IoTDBPreparedStatementTest.java     | 10 +++
 .../apache/iotdb/jdbc/IoTDBResultMetadataTest.java | 86 ++++++++++++++++++++++
 .../org/apache/iotdb/jdbc/IoTDBStatementTest.java  | 20 +++++
 9 files changed, 269 insertions(+), 39 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 f30fcadba86..903f0bb8926 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
@@ -274,17 +274,25 @@ public class IoTDBJDBCResultSet implements ResultSet {
   @Override
   public BigDecimal getBigDecimal(String columnName) throws SQLException {
     String value = getValueByName(columnName);
-    if (value != null) {
-      return new BigDecimal(value);
-    } else {
+    if (value == null) {
       return null;
     }
+    try {
+      return new BigDecimal(value);
+    } catch (NumberFormatException e) {
+      throw new SQLException(e.getMessage(), e);
+    }
   }
 
   @Override
   public BigDecimal getBigDecimal(int columnIndex, int scale) throws 
SQLException {
-    MathContext mc = new MathContext(scale);
-    return getBigDecimal(columnIndex).round(mc);
+    try {
+      MathContext mc = new MathContext(scale);
+      BigDecimal value = getBigDecimal(columnIndex);
+      return value == null ? null : value.round(mc);
+    } catch (IllegalArgumentException e) {
+      throw new SQLException(e.getMessage(), e);
+    }
   }
 
   @Override
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 203aacdc073..60222d3e7da 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
@@ -100,6 +100,7 @@ public class IoTDBPreparedStatement extends IoTDBStatement 
implements PreparedSt
 
   @Override
   public void addBatch() throws SQLException {
+    checkConnection("addBatch");
     super.addBatch(createCompleteSql(sql, parameters));
   }
 
@@ -110,22 +111,26 @@ public class IoTDBPreparedStatement extends 
IoTDBStatement implements PreparedSt
 
   @Override
   public boolean execute() throws SQLException {
+    checkConnection("execute");
     return super.execute(createCompleteSql(sql, parameters));
   }
 
   @Override
   public ResultSet executeQuery() throws SQLException {
+    checkConnection("executeQuery");
     return super.executeQuery(createCompleteSql(sql, parameters));
   }
 
   @Override
   public int executeUpdate() throws SQLException {
+    checkConnection("executeUpdate");
     return super.executeUpdate(createCompleteSql(sql, parameters));
   }
 
   @Override
   public ResultSetMetaData getMetaData() throws SQLException {
-    return getResultSet().getMetaData();
+    ResultSet currentResultSet = getResultSet();
+    return currentResultSet == null ? null : currentResultSet.getMetaData();
   }
 
   @Override
diff --git 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java
 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java
index 4fba689c3e5..973f6a888a4 100644
--- 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java
+++ 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBResultMetadata.java
@@ -83,6 +83,7 @@ public class IoTDBResultMetadata implements ResultSetMetaData 
{
   }) // ignore Cognitive Complexity of methods should not be too high
   @Override
   public String getCatalogName(int column) throws SQLException {
+    checkColumnIndex(column);
     String systemSchmea = "_system_schmea";
     String system = "_system";
     String systemUser = "_system_user";
@@ -92,9 +93,6 @@ public class IoTDBResultMetadata implements ResultSetMetaData 
{
     String systemNull = "";
     String columnName = columnInfoList.get(column - 1);
     List<String> listColumns = columnInfoList;
-    if (column < 1 || column > columnInfoList.size()) {
-      throw new SQLException(Constant.METHOD_NOT_SUPPORTED);
-    }
     if ("SHOW".equals(operationType)) {
       if ("count".equals(listColumns.get(0))) {
         return systemDatabase;
@@ -135,16 +133,22 @@ public class IoTDBResultMetadata implements 
ResultSetMetaData {
     } else if (!"FILL".equals(operationType)) {
       return systemNull;
     }
-    if (nonAlign) {
-      return sgColumns.get(column - 1);
-    } else {
-      return sgColumns.get(column - 2);
+    if (sgColumns == null || sgColumns.isEmpty()) {
+      return systemNull;
     }
+    int sgColumnIndex = nonAlign ? column - 1 : column - 2;
+    if (sgColumnIndex < 0 || sgColumnIndex >= sgColumns.size()) {
+      return systemNull;
+    }
+    return sgColumns.get(sgColumnIndex);
   }
 
   @Override
   public String getColumnClassName(int column) throws SQLException {
     String columnTypeName = getColumnTypeName(column);
+    if (columnTypeName == null) {
+      return null;
+    }
     switch (columnTypeName) {
       case TIMESTAMP:
         return Timestamp.class.getName();
@@ -210,7 +214,7 @@ public class IoTDBResultMetadata implements 
ResultSetMetaData {
     if (column == 1 && !ignoreTimestamp) {
       return Types.TIMESTAMP;
     }
-    String columnType = columnTypeList.get(column - 1);
+    String columnType = getColumnTypeString(column);
 
     switch (columnType.toUpperCase()) {
       case BOOLEAN:
@@ -244,7 +248,7 @@ public class IoTDBResultMetadata implements 
ResultSetMetaData {
     if (column == 1 && !ignoreTimestamp) {
       return TIMESTAMP;
     }
-    String columnType = columnTypeList.get(column - 1);
+    String columnType = getColumnTypeString(column);
     String typeString = columnType.toUpperCase();
     if (BOOLEAN.equals(typeString)
         || INT32.equals(typeString)
@@ -267,7 +271,7 @@ public class IoTDBResultMetadata implements 
ResultSetMetaData {
     if (column == 1 && !ignoreTimestamp) {
       return 3;
     }
-    String columnType = columnTypeList.get(column - 1);
+    String columnType = getColumnTypeString(column);
     switch (columnType.toUpperCase()) {
       case BOOLEAN:
         return 1;
@@ -299,7 +303,7 @@ public class IoTDBResultMetadata implements 
ResultSetMetaData {
     if (column == 1 && !ignoreTimestamp) {
       return 0;
     }
-    String columnType = columnTypeList.get(column - 1);
+    String columnType = getColumnTypeString(column);
     switch (columnType.toUpperCase()) {
       case BOOLEAN:
       case INT32:
@@ -337,47 +341,67 @@ public class IoTDBResultMetadata implements 
ResultSetMetaData {
   }
 
   @Override
-  public boolean isAutoIncrement(int arg0) throws SQLException {
+  public boolean isAutoIncrement(int column) throws SQLException {
+    checkColumnIndex(column);
     return false;
   }
 
   @Override
-  public boolean isCaseSensitive(int arg0) throws SQLException {
+  public boolean isCaseSensitive(int column) throws SQLException {
+    checkColumnIndex(column);
     return true;
   }
 
   @Override
-  public boolean isCurrency(int arg0) throws SQLException {
+  public boolean isCurrency(int column) throws SQLException {
+    checkColumnIndex(column);
     return false;
   }
 
   @Override
-  public boolean isDefinitelyWritable(int arg0) throws SQLException {
+  public boolean isDefinitelyWritable(int column) throws SQLException {
+    checkColumnIndex(column);
     return false;
   }
 
   @Override
-  public int isNullable(int arg0) throws SQLException {
+  public int isNullable(int column) throws SQLException {
+    checkColumnIndex(column);
     return 1;
   }
 
   @Override
-  public boolean isReadOnly(int arg0) throws SQLException {
+  public boolean isReadOnly(int column) throws SQLException {
+    checkColumnIndex(column);
     return true;
   }
 
   @Override
-  public boolean isSearchable(int arg0) throws SQLException {
+  public boolean isSearchable(int column) throws SQLException {
+    checkColumnIndex(column);
     return true;
   }
 
   @Override
-  public boolean isSigned(int arg0) throws SQLException {
+  public boolean isSigned(int column) throws SQLException {
+    checkColumnIndex(column);
     return true;
   }
 
   @Override
-  public boolean isWritable(int arg0) throws SQLException {
+  public boolean isWritable(int column) throws SQLException {
+    checkColumnIndex(column);
     return false;
   }
+
+  private String getColumnTypeString(int column) throws SQLException {
+    if (columnTypeList == null || column > columnTypeList.size()) {
+      throw new SQLException(String.format(JdbcMessages.COLUMN_DOES_NOT_EXIST, 
column));
+    }
+    String columnType = columnTypeList.get(column - 1);
+    if (columnType == null) {
+      throw new SQLException(String.format(JdbcMessages.COLUMN_DOES_NOT_EXIST, 
column));
+    }
+    return columnType;
+  }
 }
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 5952bb56e9b..6bc1f03fbd2 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
@@ -73,6 +73,8 @@ public class IoTDBStatement implements Statement {
   private List<String> batchSQLList;
   private static final String NOT_SUPPORT_EXECUTE = "Not support execute";
   private static final String NOT_SUPPORT_EXECUTE_UPDATE = "Not support 
executeUpdate";
+  private static final String CANNOT_AFTER_STATEMENT_CLOSED =
+      "Cannot %s after statement has been closed!";
 
   /** Keep state so we can fail certain calls made after close(). */
   private boolean isClosed = false;
@@ -191,7 +193,8 @@ public class IoTDBStatement implements Statement {
   }
 
   @Override
-  public void addBatch(String sql) {
+  public void addBatch(String sql) throws SQLException {
+    checkConnection("addBatch");
     if (batchSQLList == null) {
       batchSQLList = new ArrayList<>();
     }
@@ -217,7 +220,12 @@ public class IoTDBStatement implements Statement {
   }
 
   @Override
-  public void clearBatch() {
+  public void clearBatch() throws SQLException {
+    checkConnection("clearBatch");
+    clearBatchInternal();
+  }
+
+  private void clearBatchInternal() {
     if (batchSQLList == null) {
       batchSQLList = new ArrayList<>();
     }
@@ -225,7 +233,8 @@ public class IoTDBStatement implements Statement {
   }
 
   @Override
-  public void clearWarnings() {
+  public void clearWarnings() throws SQLException {
+    checkConnection("clearWarnings");
     warningChain = null;
   }
 
@@ -273,7 +282,6 @@ public class IoTDBStatement implements Statement {
   @Override
   public boolean execute(String sql) throws SQLException {
     checkConnection("execute");
-    isClosed = false;
     try {
       return executeSQL(sql);
     } catch (TException e) {
@@ -415,14 +423,13 @@ public class IoTDBStatement implements Statement {
   @Override
   public int[] executeBatch() throws SQLException {
     checkConnection("executeBatch");
-    isClosed = false;
     try {
       return executeBatchSQL();
     } catch (TException e) {
       throw new SQLException(
           "Fail to reconnect to server when executing batch sqls. please check 
server status", e);
     } finally {
-      clearBatch();
+      clearBatchInternal();
     }
   }
 
@@ -478,7 +485,6 @@ public class IoTDBStatement implements Statement {
 
   public ResultSet executeQuery(String sql, long timeoutInMS) throws 
SQLException {
     checkConnection("execute query");
-    isClosed = false;
     try {
       return executeQuerySQL(sql, timeoutInMS);
     } catch (TException e) {
@@ -545,7 +551,6 @@ public class IoTDBStatement implements Statement {
   @Override
   public int executeUpdate(String sql) throws SQLException {
     checkConnection("execute update");
-    isClosed = false;
     try {
       return executeUpdateSQL(sql);
     } catch (TException e) {
@@ -588,7 +593,8 @@ public class IoTDBStatement implements Statement {
   }
 
   @Override
-  public Connection getConnection() {
+  public Connection getConnection() throws SQLException {
+    checkConnection("getConnection");
     return connection;
   }
 
@@ -639,6 +645,7 @@ public class IoTDBStatement implements Statement {
 
   @Override
   public int getMaxRows() throws SQLException {
+    checkConnection("getMaxRows");
     return this.maxRows;
   }
 
@@ -653,6 +660,7 @@ public class IoTDBStatement implements Statement {
 
   @Override
   public boolean getMoreResults() throws SQLException {
+    checkConnection("getMoreResults");
     return false;
   }
 
@@ -662,7 +670,8 @@ public class IoTDBStatement implements Statement {
   }
 
   @Override
-  public int getQueryTimeout() {
+  public int getQueryTimeout() throws SQLException {
+    checkConnection("getQueryTimeout");
     return this.queryTimeout;
   }
 
@@ -689,6 +698,7 @@ public class IoTDBStatement implements Statement {
 
   @Override
   public int getResultSetHoldability() throws SQLException {
+    checkConnection("getResultSetHoldability");
     return ResultSet.HOLD_CURSORS_OVER_COMMIT;
   }
 
@@ -699,12 +709,14 @@ public class IoTDBStatement implements Statement {
   }
 
   @Override
-  public int getUpdateCount() {
+  public int getUpdateCount() throws SQLException {
+    checkConnection("getUpdateCount");
     return -1;
   }
 
   @Override
-  public SQLWarning getWarnings() {
+  public SQLWarning getWarnings() throws SQLException {
+    checkConnection("getWarnings");
     return warningChain;
   }
 
@@ -738,10 +750,13 @@ public class IoTDBStatement implements Statement {
     throw new SQLException(JdbcMessages.NOT_SUPPORT_SET_ESCAPE_PROCESSING);
   }
 
-  private void checkConnection(String action) throws SQLException {
+  protected void checkConnection(String action) throws SQLException {
     if (connection == null || connection.isClosed()) {
       throw new 
SQLException(String.format(JdbcMessages.CANNOT_AFTER_CONNECTION_CLOSED, 
action));
     }
+    if (isClosed) {
+      throw new SQLException(String.format(CANNOT_AFTER_STATEMENT_CLOSED, 
action));
+    }
   }
 
   private boolean reInit() throws SQLException {
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 459a4fead14..25bfbdcadb2 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
@@ -140,6 +140,7 @@ public class IoTDBTablePreparedStatement extends 
IoTDBStatement implements Prepa
 
   @Override
   public void addBatch() throws SQLException {
+    checkConnection("addBatch");
     super.addBatch(createCompleteSql(sql, parameters));
   }
 
@@ -156,6 +157,7 @@ public class IoTDBTablePreparedStatement extends 
IoTDBStatement implements Prepa
 
   @Override
   public boolean execute() throws SQLException {
+    checkConnection("execute");
     if (isQueryStatement(sql)) {
       TSExecuteStatementResp resp = executeInternal();
       return resp.isSetQueryDataSet() || resp.isSetQueryResult();
@@ -174,12 +176,14 @@ public class IoTDBTablePreparedStatement extends 
IoTDBStatement implements Prepa
 
   @Override
   public ResultSet executeQuery() throws SQLException {
+    checkConnection("executeQuery");
     TSExecuteStatementResp resp = executeInternal();
     return processQueryResult(resp);
   }
 
   @Override
   public int executeUpdate() throws SQLException {
+    checkConnection("executeUpdate");
     return super.executeUpdate(createCompleteSql(sql, parameters));
   }
 
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 c323fb032bc..d06464112cd 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
@@ -30,9 +30,11 @@ import 
org.apache.iotdb.service.rpc.thrift.TSFetchMetadataResp;
 import org.apache.iotdb.service.rpc.thrift.TSFetchResultsReq;
 import org.apache.iotdb.service.rpc.thrift.TSFetchResultsResp;
 
+import org.apache.tsfile.common.conf.TSFileConfig;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.block.TsBlockBuilder;
 import org.apache.tsfile.read.common.block.column.TsBlockSerde;
+import org.apache.tsfile.utils.Binary;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -227,6 +229,11 @@ public class IoTDBJDBCResultSetTest {
           Assert.assertTrue(resultSet.wasNull());
           Assert.assertNull(resultSet.getTimestamp(4));
           Assert.assertTrue(resultSet.wasNull());
+          Assert.assertNull(resultSet.getBigDecimal(4, 2));
+          Assert.assertTrue(resultSet.wasNull());
+          Assert.assertNull(resultSet.getBigDecimal("root.vehicle.d0.s0", 2));
+          Assert.assertTrue(resultSet.wasNull());
+          Assert.assertThrows(SQLException.class, () -> 
resultSet.getBigDecimal(4, -1));
           firstRow = false;
         }
         for (int i = 1; i <= colCount; i++) {
@@ -274,6 +281,20 @@ public class IoTDBJDBCResultSetTest {
     }
   }
 
+  @SuppressWarnings("resource")
+  @Test
+  public void testInvalidBigDecimalConversionThrowsSQLException() throws 
Exception {
+    mockTextQueryResponse();
+
+    Assert.assertTrue(statement.execute("select s3 from root.vehicle.d0"));
+
+    try (ResultSet resultSet = statement.getResultSet()) {
+      Assert.assertTrue(resultSet.next());
+      Assert.assertThrows(SQLException.class, () -> 
resultSet.getBigDecimal(2));
+      Assert.assertThrows(SQLException.class, () -> 
resultSet.getBigDecimal("root.vehicle.d0.s3"));
+    }
+  }
+
   private void mockVehicleQueryResponse() {
     List<String> columns = new ArrayList<>();
     columns.add("root.vehicle.d0.s2");
@@ -313,6 +334,25 @@ public class IoTDBJDBCResultSetTest {
         .getDataType();
   }
 
+  private void mockTextQueryResponse() {
+    List<String> columns = new 
ArrayList<>(Collections.singletonList("root.vehicle.d0.s3"));
+    List<String> dataTypeList = new 
ArrayList<>(Collections.singletonList("TEXT"));
+
+    when(execResp.isSetColumns()).thenReturn(true);
+    when(execResp.getColumns()).thenReturn(columns);
+    when(execResp.isSetDataTypeList()).thenReturn(true);
+    when(execResp.getDataTypeList()).thenReturn(dataTypeList);
+    when(execResp.isSetOperationType()).thenReturn(true);
+    when(execResp.getOperationType()).thenReturn("QUERY");
+    when(execResp.isSetQueryId()).thenReturn(true);
+    when(execResp.getQueryId()).thenReturn(queryId);
+    when(execResp.isSetTableModel()).thenReturn(false);
+    when(execResp.isIgnoreTimeStamp()).thenReturn(false);
+    when(execResp.getColumnIndex2TsBlockColumnIndexList())
+        .thenReturn(new ArrayList<>(Collections.singletonList(0)));
+    execResp.queryResult = fakedTextFetchTsBlockResult();
+  }
+
   private void constructObjectList(List<Object> standardObject) {
     Object[][] input = {
       {
@@ -416,4 +456,22 @@ public class IoTDBJDBCResultSetTest {
 
     return Collections.singletonList(tsBlock);
   }
+
+  private List<ByteBuffer> fakedTextFetchTsBlockResult() {
+    TsBlockBuilder tsBlockBuilder = new 
TsBlockBuilder(Collections.singletonList(TSDataType.TEXT));
+    tsBlockBuilder.getTimeColumnBuilder().writeLong(1L);
+    tsBlockBuilder
+        .getColumnBuilder(0)
+        .writeBinary(new Binary("not-a-number", TSFileConfig.STRING_CHARSET));
+    tsBlockBuilder.declarePosition();
+
+    ByteBuffer tsBlock = null;
+    try {
+      tsBlock = new TsBlockSerde().serialize(tsBlockBuilder.build());
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    return Collections.singletonList(tsBlock);
+  }
 }
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 9375e032bb6..b043705cf72 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
@@ -43,6 +43,7 @@ import java.time.ZoneId;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
@@ -125,6 +126,15 @@ public class IoTDBPreparedStatementTest {
     assertEquals(ParameterMetaData.parameterModeUnknown, 
metadata.getParameterMode(1));
   }
 
+  @SuppressWarnings("resource")
+  @Test
+  public void getMetaDataReturnsNullWhenNoResultSetExists() throws Exception {
+    IoTDBPreparedStatement ps =
+        new IoTDBPreparedStatement(connection, client, sessionId, "SELECT ?", 
zoneId);
+
+    assertNull(ps.getMetaData());
+  }
+
   @SuppressWarnings("resource")
   @Test
   public void invalidParameterIndex() throws SQLException {
diff --git 
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java
 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java
index b9e5b3dfedb..69afa751093 100644
--- 
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java
+++ 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBResultMetadataTest.java
@@ -33,7 +33,9 @@ import java.util.List;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 public class IoTDBResultMetadataTest {
@@ -176,6 +178,90 @@ public class IoTDBResultMetadataTest {
     }
   }
 
+  @Test
+  public void testMetadataColumnMethodsRejectInvalidIndex() throws 
SQLException {
+    metadata =
+        new IoTDBResultMetadata(
+            false,
+            null,
+            "QUERY",
+            Arrays.asList("Time", "root.a.b.c1"),
+            Arrays.asList("TIMESTAMP", "INT32"),
+            false);
+
+    assertThrows(SQLException.class, () -> metadata.getCatalogName(0));
+    assertThrows(SQLException.class, () -> metadata.isAutoIncrement(0));
+    assertThrows(SQLException.class, () -> metadata.isNullable(3));
+    assertFalse(metadata.isAutoIncrement(1));
+    assertTrue(metadata.isCaseSensitive(1));
+    assertEquals(ResultSetMetaData.columnNullable, metadata.isNullable(1));
+  }
+
+  @Test
+  public void testMissingColumnTypeMetadataThrowsSQLException() {
+    metadata =
+        new IoTDBResultMetadata(
+            false,
+            null,
+            "QUERY",
+            Arrays.asList("Time", "root.a.b.c1"),
+            Collections.singletonList("TIMESTAMP"),
+            false);
+
+    assertThrows(SQLException.class, () -> metadata.getColumnType(2));
+    assertThrows(SQLException.class, () -> metadata.getColumnTypeName(2));
+    assertThrows(SQLException.class, () -> metadata.getPrecision(2));
+    assertThrows(SQLException.class, () -> metadata.getScale(2));
+  }
+
+  @Test
+  public void testNullColumnTypeMetadataThrowsSQLException() {
+    metadata =
+        new IoTDBResultMetadata(
+            false,
+            null,
+            "QUERY",
+            Arrays.asList("Time", "root.a.b.c1"),
+            Arrays.asList("TIMESTAMP", null),
+            false);
+
+    assertThrows(SQLException.class, () -> metadata.getColumnType(2));
+    assertThrows(SQLException.class, () -> metadata.getColumnTypeName(2));
+    assertThrows(SQLException.class, () -> metadata.getPrecision(2));
+    assertThrows(SQLException.class, () -> metadata.getScale(2));
+  }
+
+  @Test
+  public void testCatalogAndSchemaHandleMissingStorageGroupMetadata() throws 
SQLException {
+    metadata =
+        new IoTDBResultMetadata(
+            false,
+            null,
+            "QUERY",
+            Arrays.asList("Time", "root.a.b.c1"),
+            Arrays.asList("TIMESTAMP", "INT32"),
+            false);
+
+    assertEquals("", metadata.getCatalogName(2));
+    assertEquals("", metadata.getSchemaName(2));
+  }
+
+  @Test
+  public void testUnknownColumnTypeClassNameReturnsNull() throws SQLException {
+    metadata =
+        new IoTDBResultMetadata(
+            false,
+            null,
+            "QUERY",
+            Arrays.asList("Time", "root.a.b.c1"),
+            Arrays.asList("TIMESTAMP", "UNKNOWN"),
+            false);
+
+    assertEquals(0, metadata.getColumnType(2));
+    assertNull(metadata.getColumnTypeName(2));
+    assertNull(metadata.getColumnClassName(2));
+  }
+
   @Test
   public void testWrapperMethods() throws SQLException {
     metadata = new IoTDBResultMetadata(false, null, "QUERY", null, null, 
false);
diff --git 
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java
index d54ccf15fa3..cee793b773e 100644
--- 
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java
+++ 
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBStatementTest.java
@@ -40,6 +40,7 @@ import java.time.ZoneId;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
@@ -137,6 +138,25 @@ public class IoTDBStatementTest {
     assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, 
statement.getResultSetHoldability());
   }
 
+  @Test
+  public void testClosedStatementRejectsOperations() throws SQLException {
+    IoTDBStatement statement = new IoTDBStatement(connection, client, 
sessionId, zoneID, 0, -1L);
+
+    statement.close();
+
+    assertTrue(statement.isClosed());
+    assertThrows(SQLException.class, () -> statement.execute("select 1"));
+    assertThrows(SQLException.class, () -> statement.executeQuery("select 1"));
+    assertThrows(
+        SQLException.class,
+        () -> statement.executeUpdate("insert into root.sg.d(time,s) 
values(1,1)"));
+    assertThrows(SQLException.class, () -> statement.executeBatch());
+    assertThrows(SQLException.class, () -> statement.addBatch("select 1"));
+    assertThrows(SQLException.class, () -> statement.clearBatch());
+    assertThrows(SQLException.class, () -> statement.getFetchSize());
+    assertThrows(SQLException.class, () -> statement.getWarnings());
+  }
+
   @Test(expected = SQLException.class)
   public void testUnwrapRejectsUnsupportedClass() throws SQLException {
     new IoTDBStatement(connection, client, sessionId, 
zoneID).unwrap(String.class);

Reply via email to