From 94deb9790543cd2d098170832348571fa1025046 Mon Sep 17 00:00:00 2001 From: Zihan Dai <99155080+PDGGK@users.noreply.github.com> Date: Sat, 18 Jul 2026 19:33:02 +1000 Subject: [PATCH] Fix NullPointerException in findColumn for unknown column names findColumn returned columnOrdinalMap.get(columnName) directly, so an unknown column name unboxed a null Integer into a NullPointerException instead of the SQLException the JDBC contract (ResultSet.findColumn) requires. Guard the lookup and throw StatementExecutionException (reusing the existing UNKNOWN_COLUMN_NAME message), mirroring the sibling findColumnNameByIndex; the JDBC boundary IoTDBJDBCResultSet.findColumn converts it to SQLException like the existing getBigDecimal path, and SessionDataSet.DataIterator.findColumn propagates the checked exception, consistent with its sibling accessors. Also fixes the identical NPE in IoTDBJDBCDataSet.findColumn. Adds a test asserting an unknown column throws SQLException rather than NPE. Signed-off-by: Zihan Dai <99155080+PDGGK@users.noreply.github.com> --- .../apache/iotdb/isession/SessionDataSet.java | 2 +- .../apache/iotdb/jdbc/IoTDBJDBCResultSet.java | 8 +++- .../iotdb/jdbc/IoTDBJDBCResultSetTest.java | 38 +++++++++++++++++++ .../apache/iotdb/rpc/IoTDBJDBCDataSet.java | 8 +++- .../org/apache/iotdb/rpc/IoTDBRpcDataSet.java | 8 +++- 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java index a926103222a20..66c699f738d26 100644 --- a/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java +++ b/iotdb-client/isession/src/main/java/org/apache/iotdb/isession/SessionDataSet.java @@ -349,7 +349,7 @@ public Binary getBlob(String columnName) throws StatementExecutionException { return ioTDBRpcDataSet.getBinary(columnName); } - public int findColumn(String columnName) { + public int findColumn(String columnName) throws StatementExecutionException { return ioTDBRpcDataSet.findColumn(columnName); } 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 3ea3c52320d1a..8e83478f53740 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 @@ -232,8 +232,12 @@ public void deleteRow() throws SQLException { } @Override - public int findColumn(String columnName) { - return ioTDBRpcDataSet.findColumn(columnName); + public int findColumn(String columnName) throws SQLException { + try { + return ioTDBRpcDataSet.findColumn(columnName); + } catch (StatementExecutionException e) { + throw new SQLException(e.getMessage()); + } } @Override 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 3fb60b8760da8..025c94d738acc 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 @@ -43,6 +43,7 @@ import java.nio.ByteBuffer; import java.sql.ResultSet; import java.sql.ResultSetMetaData; +import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.sql.Types; @@ -363,4 +364,41 @@ private List FakedFirstFetchTsBlockResult() { return Collections.singletonList(tsBlock); } + + @Test + public void findColumnUnknownColumnThrowsSQLExceptionNotNpe() throws Exception { + List columns = new ArrayList<>(); + columns.add("root.vehicle.d0.s0"); + List dataTypeList = new ArrayList<>(); + dataTypeList.add("INT32"); + 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); + List columnIndex2TsBlockColumnIndexList = new ArrayList<>(); + columnIndex2TsBlockColumnIndexList.add(0); + when(execResp.getColumnIndex2TsBlockColumnIndexList()) + .thenReturn(columnIndex2TsBlockColumnIndexList); + + Assert.assertTrue(statement.execute("select s0 from root.vehicle.d0")); + try (ResultSet resultSet = statement.getResultSet()) { + // a known column resolves to its 1-based index + Assert.assertEquals(1, resultSet.findColumn("Time")); + // an unknown column must throw a clear SQLException, not a NullPointerException + try { + resultSet.findColumn("no_such_column"); + Assert.fail("findColumn on an unknown column must throw SQLException"); + } catch (SQLException e) { + Assert.assertTrue( + "message should name the missing column, got: " + e.getMessage(), + e.getMessage() != null && e.getMessage().contains("no_such_column")); + } + } + } } diff --git a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java index 75ff09a882e43..49d32bf4faaca 100644 --- a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java +++ b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/IoTDBJDBCDataSet.java @@ -579,8 +579,12 @@ public Timestamp getTimestamp(String columnName) throws StatementExecutionExcept return getTimestamp(findColumn(columnName)); } - public int findColumn(String columnName) { - return columnOrdinalMap.get(columnName); + public int findColumn(String columnName) throws StatementExecutionException { + Integer ordinal = columnOrdinalMap.get(columnName); + if (ordinal == null) { + throw new StatementExecutionException(RpcMessages.UNKNOWN_COLUMN_NAME + columnName); + } + return ordinal; } public String getValueByName(String columnName) throws StatementExecutionException { 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 7619be16734d6..b2376027b3619 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 @@ -614,8 +614,12 @@ private TSDataType getDataTypeByTsBlockColumnIndex(int tsBlockColumnIndex) { : dataTypeForTsBlockColumn.get(tsBlockColumnIndex); } - public int findColumn(String columnName) { - return columnOrdinalMap.get(columnName); + public int findColumn(String columnName) throws StatementExecutionException { + Integer ordinal = columnOrdinalMap.get(columnName); + if (ordinal == null) { + throw new StatementExecutionException(RpcMessages.UNKNOWN_COLUMN_NAME + columnName); + } + return ordinal; } public String findColumnNameByIndex(int columnIndex) throws StatementExecutionException {