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 bc18c420c55 Fix JDBC edge case handling
bc18c420c55 is described below
commit bc18c420c55f696a95c41e99a28c1ad06721bcd2
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jun 9 11:30:17 2026 +0800
Fix JDBC edge case handling
---
.../org/apache/iotdb/jdbc/IoTDBConnection.java | 23 +++++-
.../org/apache/iotdb/jdbc/IoTDBDataSource.java | 11 +++
.../apache/iotdb/jdbc/IoTDBDataSourceFactory.java | 44 ++++++++++-
.../apache/iotdb/jdbc/IoTDBPreparedStatement.java | 85 ++++++++++++++++++----
.../org/apache/iotdb/jdbc/IoTDBSQLException.java | 4 +
.../iotdb/jdbc/IoTDBTablePreparedStatement.java | 57 ++++++++++++---
.../src/main/java/org/apache/iotdb/jdbc/Utils.java | 15 ++++
.../org/apache/iotdb/jdbc/IoTDBConnectionTest.java | 40 ++++++++++
.../iotdb/jdbc/IoTDBDataSourceFactoryTest.java | 38 ++++++++++
.../org/apache/iotdb/jdbc/IoTDBDriverTest.java | 5 +-
.../iotdb/jdbc/IoTDBPreparedStatementTest.java | 56 ++++++++++++++
.../jdbc/IoTDBTablePreparedStatementTest.java | 84 +++++++++++++++++++++
.../test/java/org/apache/iotdb/jdbc/UtilsTest.java | 30 ++++++++
13 files changed, 463 insertions(+), 29 deletions(-)
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java
index ee94386fe54..2a9cd3816df 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnection.java
@@ -60,6 +60,7 @@ import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
+import java.time.DateTimeException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@@ -269,6 +270,9 @@ public class IoTDBConnection implements Connection {
@Override
public void setCatalog(String arg0) throws SQLException {
if (getSqlDialect().equals(Constant.TABLE_DIALECT)) {
+ if (arg0 == null) {
+ throw new SQLException("catalog cannot be null");
+ }
if (APACHE_IOTDB.equals(arg0)) {
return;
}
@@ -343,6 +347,9 @@ public class IoTDBConnection implements Connection {
public void setSchema(String arg0) throws SQLException {
// changeDefaultDatabase(arg0);
if (getSqlDialect().equals(Constant.TABLE_DIALECT)) {
+ if (arg0 == null) {
+ throw new SQLException("schema cannot be null");
+ }
for (String str :
IoTDBRelationalDatabaseMetadata.allIotdbTableSQLKeywords) {
if (arg0.equalsIgnoreCase(str)) {
arg0 = "\"" + arg0 + "\"";
@@ -486,7 +493,7 @@ public class IoTDBConnection implements Connection {
@Override
public void setClientInfo(String name, String value) throws
SQLClientInfoException {
- if (name.equalsIgnoreCase("time_zone")) {
+ if ("time_zone".equalsIgnoreCase(name)) {
try {
setTimeZone(value);
} catch (TException | IoTDBSQLException e) {
@@ -669,6 +676,7 @@ public class IoTDBConnection implements Connection {
}
public void setTimeZone(String timeZone) throws TException,
IoTDBSQLException {
+ ZoneId newZoneId = parseTimeZone(timeZone);
TSSetTimeZoneReq req = new TSSetTimeZoneReq(sessionId, timeZone);
TSStatus resp = getClient().setTimeZone(req);
try {
@@ -676,7 +684,18 @@ public class IoTDBConnection implements Connection {
} catch (StatementExecutionException e) {
throw new IoTDBSQLException(e.getMessage(), resp);
}
- this.zoneId = ZoneId.of(timeZone);
+ this.zoneId = newZoneId;
+ }
+
+ private static ZoneId parseTimeZone(String timeZone) throws
IoTDBSQLException {
+ if (timeZone == null) {
+ throw new IoTDBSQLException("Invalid time_zone: null");
+ }
+ try {
+ return ZoneId.of(timeZone);
+ } catch (DateTimeException e) {
+ throw new IoTDBSQLException("Invalid time_zone: " + timeZone, e);
+ }
}
public ServerProperties getServerProperties() throws TException {
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSource.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSource.java
index 689b08a45dd..23ccf32fcd0 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSource.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSource.java
@@ -84,6 +84,9 @@ public class IoTDBDataSource implements DataSource {
}
public void setPort(Integer port) {
+ if (port != null && (port < 0 || port > 65535)) {
+ throw new IllegalArgumentException("port must be between 0 and 65535");
+ }
this.port = port == null || port == 0 ? Config.IOTDB_DEFAULT_PORT : port;
}
@@ -151,6 +154,14 @@ public class IoTDBDataSource implements DataSource {
this.url = url;
}
+ String getConnectionProperty(String key) {
+ return properties.getProperty(key);
+ }
+
+ void setConnectionProperty(String key, String value) {
+ setProperty(key, value);
+ }
+
@Override
public Connection getConnection() throws SQLException {
return createConnection((Properties) properties.clone());
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactory.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactory.java
index 98d6bfb777c..bf2ec57cc9e 100644
---
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactory.java
+++
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactory.java
@@ -99,11 +99,16 @@ public class IoTDBDataSourceFactory implements
DataSourceFactory {
}
removeUnsupportedPoolProperties(properties);
+ applyConnectionProperties(ds, properties);
logger.info(JdbcMessages.REMAINING_PROPERTIES, properties.size());
if (!properties.isEmpty()) {
- BeanConfig.configure(ds, properties);
+ try {
+ BeanConfig.configure(ds, properties);
+ } catch (IllegalArgumentException e) {
+ throw new SQLException("Invalid JDBC DataSource property", e);
+ }
}
}
@@ -128,6 +133,43 @@ public class IoTDBDataSourceFactory implements
DataSourceFactory {
return value == null ? null : value.toString();
}
+ private static void applyConnectionProperties(IoTDBDataSource ds, Properties
properties) {
+ applyCredentialProperty(ds, properties, Config.AUTH_USER);
+ applyCredentialProperty(ds, properties, Config.AUTH_PASSWORD);
+ applyConnectionProperty(ds, properties, Config.DEFAULT_BUFFER_CAPACITY);
+ applyConnectionProperty(ds, properties, Config.THRIFT_FRAME_MAX_SIZE);
+ applyConnectionProperty(ds, properties, Config.VERSION);
+ applyConnectionProperty(ds, properties, Config.NETWORK_TIMEOUT);
+ applyConnectionProperty(ds, properties, Config.TIME_ZONE);
+ applyConnectionProperty(ds, properties, Config.CHARSET);
+ applyConnectionProperty(ds, properties, Config.USE_SSL);
+ applyConnectionProperty(ds, properties, Config.TRUST_STORE);
+ applyConnectionProperty(ds, properties, Config.TRUST_STORE_PWD);
+ applyConnectionProperty(ds, properties, Utils.RPC_COMPRESS);
+ applyConnectionProperty(ds, properties, Config.SQL_DIALECT);
+ }
+
+ private static void applyCredentialProperty(
+ IoTDBDataSource ds, Properties properties, String key) {
+ String value = removeStringProperty(properties, key);
+ if (value == null) {
+ return;
+ }
+ if (Config.AUTH_USER.equals(key)) {
+ ds.setUser(value);
+ } else {
+ ds.setPassword(value);
+ }
+ }
+
+ private static void applyConnectionProperty(
+ IoTDBDataSource ds, Properties properties, String key) {
+ String value = removeStringProperty(properties, key);
+ if (value != null) {
+ ds.setConnectionProperty(key, value);
+ }
+ }
+
private static Integer removeIntegerProperty(Properties properties, String
key)
throws SQLException {
Object value = properties.remove(key);
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 647b74a9e1a..384a85e228e 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
@@ -134,28 +134,32 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
}
@Override
- public int isNullable(int param) {
+ public int isNullable(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return ParameterMetaData.parameterNullableUnknown;
}
@Override
- public boolean isSigned(int param) {
+ public boolean isSigned(int param) throws SQLException {
+ String value = getParameterMetadataValue(param);
try {
- return Integer.parseInt(parameters.get(param)) < 0;
+ return Integer.parseInt(value) < 0;
} catch (Exception e) {
return false;
}
}
@Override
- public int getPrecision(int param) {
- return parameters.get(param).length();
+ public int getPrecision(int param) throws SQLException {
+ String value = getParameterMetadataValue(param);
+ return value == null ? 0 : value.length();
}
@Override
- public int getScale(int param) {
+ public int getScale(int param) throws SQLException {
+ String value = getParameterMetadataValue(param);
try {
- double d = Double.parseDouble(parameters.get(param));
+ double d = Double.parseDouble(value);
if (d >= 1) { // we only need the fraction digits
d = d - (long) d;
}
@@ -175,23 +179,27 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
}
@Override
- public int getParameterType(int param) {
- return 0;
+ public int getParameterType(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
+ return Types.NULL;
}
@Override
- public String getParameterTypeName(int param) {
+ public String getParameterTypeName(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return null;
}
@Override
- public String getParameterClassName(int param) {
+ public String getParameterClassName(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return null;
}
@Override
- public int getParameterMode(int param) {
- return 0;
+ public int getParameterMode(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
+ return ParameterMetaData.parameterModeUnknown;
}
@Override
@@ -206,6 +214,17 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
};
}
+ private void checkParameterMetadataIndex(int param) throws SQLException {
+ if (param < 1 || !parameters.containsKey(param)) {
+ throw new SQLException("Parameter index is not set: " + param);
+ }
+ }
+
+ private String getParameterMetadataValue(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
+ return parameters.get(param);
+ }
+
@Override
public void setArray(int parameterIndex, Array x) throws SQLException {
throw new SQLException(Constant.PARAMETER_SUPPORTED);
@@ -238,6 +257,13 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
+ if (length < 0) {
+ throw new SQLException("length must be >= 0");
+ }
+ if (x == null) {
+ setNull(parameterIndex, Types.BINARY);
+ return;
+ }
byte[] bytes = null;
try {
bytes = ReadWriteIOUtils.readBytes(x, length);
@@ -284,6 +310,10 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
@Override
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.BINARY);
+ return;
+ }
Binary binary = new Binary(x);
this.parameters.put(parameterIndex,
binary.getStringValue(TSFileConfig.STRING_CHARSET));
}
@@ -322,6 +352,10 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
@Override
public void setDate(int parameterIndex, Date x) throws SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.DATE);
+ return;
+ }
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
this.parameters.put(parameterIndex, "'" + dateFormat.format(x) + "'");
}
@@ -394,7 +428,9 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
@Override
public void setObject(int parameterIndex, Object x) throws SQLException {
- if (x instanceof String) {
+ if (x == null) {
+ setNull(parameterIndex, Types.NULL);
+ } else if (x instanceof String) {
setString(parameterIndex, (String) x);
} else if (x instanceof Integer) {
setInt(parameterIndex, (Integer) x);
@@ -924,6 +960,10 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
@Override
public void setTime(int parameterIndex, Time x) throws SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.TIME);
+ return;
+ }
try {
long time = x.getTime();
String timeprecision = client.getProperties().getTimestampPrecision();
@@ -948,6 +988,10 @@ public class IoTDBPreparedStatement extends IoTDBStatement
implements PreparedSt
@Override
public void setTime(int parameterIndex, Time x, Calendar cal) throws
SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.TIME);
+ return;
+ }
try {
ZonedDateTime zonedDateTime = null;
long time = x.getTime();
@@ -980,7 +1024,11 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
}
@Override
- public void setTimestamp(int parameterIndex, Timestamp x) {
+ public void setTimestamp(int parameterIndex, Timestamp x) throws
SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.TIMESTAMP);
+ return;
+ }
ZonedDateTime zonedDateTime =
ZonedDateTime.ofInstant(Instant.ofEpochMilli(x.getTime()),
super.zoneId);
this.parameters.put(
@@ -989,6 +1037,10 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
@Override
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
throws SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.TIMESTAMP);
+ return;
+ }
ZonedDateTime zonedDateTime = null;
if (cal != null) {
zonedDateTime =
@@ -1024,7 +1076,8 @@ public class IoTDBPreparedStatement extends
IoTDBStatement implements PreparedSt
if (!parameters.containsKey(i)) {
throw new SQLException(String.format(JdbcMessages.PARAMETER_UNSET, i));
}
- newSql.append(parameters.get(i));
+ String parameter = parameters.get(i);
+ newSql.append(parameter == null ? "NULL" : parameter);
newSql.append(parts.get(i));
}
return newSql.toString();
diff --git
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBSQLException.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBSQLException.java
index 540ae847b54..a75a54862fa 100644
---
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBSQLException.java
+++
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBSQLException.java
@@ -31,6 +31,10 @@ public class IoTDBSQLException extends SQLException {
super(reason);
}
+ public IoTDBSQLException(String reason, Throwable cause) {
+ super(reason, cause);
+ }
+
public IoTDBSQLException(String reason, TSStatus status) {
super(reason, status.message, status.code);
}
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 79312bc5f84..998d053fb06 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
@@ -272,12 +272,14 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
}
@Override
- public int isNullable(int param) {
+ public int isNullable(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return ParameterMetaData.parameterNullableUnknown;
}
@Override
- public boolean isSigned(int param) {
+ public boolean isSigned(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
if (!serverSidePrepared) {
return false;
}
@@ -289,17 +291,20 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
}
@Override
- public int getPrecision(int param) {
+ public int getPrecision(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return 0;
}
@Override
- public int getScale(int param) {
+ public int getScale(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return 0;
}
@Override
- public int getParameterType(int param) {
+ public int getParameterType(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
if (!serverSidePrepared) {
return Types.NULL;
}
@@ -307,17 +312,20 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
}
@Override
- public String getParameterTypeName(int param) {
+ public String getParameterTypeName(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return null;
}
@Override
- public String getParameterClassName(int param) {
+ public String getParameterClassName(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return null;
}
@Override
- public int getParameterMode(int param) {
+ public int getParameterMode(int param) throws SQLException {
+ checkParameterMetadataIndex(param);
return ParameterMetaData.parameterModeIn;
}
@@ -393,6 +401,10 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
@Override
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.BINARY);
+ return;
+ }
checkParameterIndex(parameterIndex);
setPreparedParameterValue(parameterIndex, x, Types.BINARY);
// Format as hexadecimal string literal for SQL: X'0A0B0C'
@@ -401,6 +413,10 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
@Override
public void setDate(int parameterIndex, Date x) throws SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.DATE);
+ return;
+ }
checkParameterIndex(parameterIndex);
// Use ISO-8601 format YYYY-MM-DD for DATE columns
String dateStr = x.toLocalDate().toString();
@@ -415,6 +431,10 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
@Override
public void setTime(int parameterIndex, Time x) throws SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.TIME);
+ return;
+ }
checkParameterIndex(parameterIndex);
try {
long time = x.getTime();
@@ -445,6 +465,10 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
@Override
public void setTimestamp(int parameterIndex, Timestamp x) throws
SQLException {
+ if (x == null) {
+ setNull(parameterIndex, Types.TIMESTAMP);
+ return;
+ }
checkParameterIndex(parameterIndex);
// Use millisecond value for SQL compatibility with TIMESTAMP columns
long timestampMs = x.getTime();
@@ -510,6 +534,13 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
}
}
+ private void checkParameterMetadataIndex(int index) throws SQLException {
+ if (index < 1 || index > parameterCount) {
+ throw new SQLException(
+ "Parameter index out of range: " + index + " (expected 1-" +
parameterCount + ")");
+ }
+ }
+
private void setPreparedParameterValue(int parameterIndex, Object value, int
sqlType) {
if (!serverSidePrepared) {
return;
@@ -554,6 +585,13 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
+ if (length < 0) {
+ throw new SQLException("length must be >= 0");
+ }
+ if (x == null) {
+ setNull(parameterIndex, Types.BINARY);
+ return;
+ }
try {
byte[] bytes = ReadWriteIOUtils.readBytes(x, length);
setBytes(parameterIndex, bytes);
@@ -692,7 +730,8 @@ public class IoTDBTablePreparedStatement extends
IoTDBStatement implements Prepa
if (!parameters.containsKey(i)) {
throw new SQLException(String.format(JdbcMessages.PARAMETER_UNSET, i));
}
- newSql.append(parameters.get(i));
+ String parameter = parameters.get(i);
+ newSql.append(parameter == null ? "NULL" : parameter);
newSql.append(parts.get(i));
}
return newSql.toString();
diff --git a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java
index d5eb1d720a4..5957421d949 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/Utils.java
@@ -170,6 +170,21 @@ public class Utils {
String key = tmpParam.substring(0, separatorIndex);
String value = tmpParam.substring(separatorIndex + 1);
switch (key) {
+ case Config.AUTH_USER:
+ case Config.AUTH_PASSWORD:
+ info.put(key, value);
+ break;
+ case Config.DEFAULT_BUFFER_CAPACITY:
+ case Config.THRIFT_FRAME_MAX_SIZE:
+ try {
+ if (Integer.parseInt(value) <= 0) {
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ info.put(key, value);
+ break;
case RPC_COMPRESS:
if (isBoolean(value)) {
info.put(key, value);
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java
index 230cb2c9b50..2064a4027f6 100644
---
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java
+++
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBConnectionTest.java
@@ -46,8 +46,11 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
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.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class IoTDBConnectionTest {
@@ -94,6 +97,43 @@ public class IoTDBConnectionTest {
assertEquals(connection.getTimeZone(), timeZone);
}
+ @Test
+ public void testSetTimeZoneRejectsInvalidZoneBeforeRpc() throws TException {
+ connection.setClient(client);
+
+ assertThrows(IoTDBSQLException.class, () ->
connection.setTimeZone("invalid-zone"));
+ verify(client, never()).setTimeZone(any(TSSetTimeZoneReq.class));
+ }
+
+ @Test
+ public void testSetClientInfoWrapsInvalidTimeZone() {
+ SQLClientInfoException exception =
+ assertThrows(
+ SQLClientInfoException.class,
+ () -> connection.setClientInfo("time_zone", "invalid-zone"));
+
+ assertTrue(exception.getCause() instanceof IoTDBSQLException);
+ }
+
+ @Test
+ public void testSetClientInfoRejectsNullNameWithoutNpe() {
+ assertThrows(SQLClientInfoException.class, () ->
connection.setClientInfo(null, "value"));
+ }
+
+ @Test
+ public void testTableCatalogAndSchemaRejectNull() {
+ IoTDBConnection tableConnection =
+ new IoTDBConnection() {
+ @Override
+ public String getSqlDialect() {
+ return Constant.TABLE_DIALECT;
+ }
+ };
+
+ assertThrows(SQLException.class, () -> tableConnection.setCatalog(null));
+ assertThrows(SQLException.class, () -> tableConnection.setSchema(null));
+ }
+
@Test
public void testGetServerProperties() throws TException {
final String version = "v0.1";
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactoryTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactoryTest.java
index e9e9a336058..724917d9081 100644
---
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactoryTest.java
+++
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDataSourceFactoryTest.java
@@ -103,6 +103,31 @@ public class IoTDBDataSourceFactoryTest {
assertEquals("reader", dataSource.getRoleName());
}
+ @Test
+ public void testCreateDataSourceAcceptsIoTDBConnectionProperties() throws
SQLException {
+ Properties properties = new Properties();
+ properties.setProperty(Config.NETWORK_TIMEOUT, "1234");
+ properties.setProperty(Config.TIME_ZONE, "Asia/Shanghai");
+ properties.setProperty(Config.CHARSET, "UTF-8");
+ properties.setProperty(Config.USE_SSL, "true");
+ properties.setProperty(Config.TRUST_STORE, "trust-store");
+ properties.setProperty(Config.TRUST_STORE_PWD, "trust-store-password");
+ properties.setProperty(Config.SQL_DIALECT, Constant.TABLE);
+ properties.setProperty(Utils.RPC_COMPRESS, "true");
+
+ IoTDBDataSource dataSource =
+ (IoTDBDataSource) new
IoTDBDataSourceFactory().createDataSource(properties);
+
+ assertEquals("1234",
dataSource.getConnectionProperty(Config.NETWORK_TIMEOUT));
+ assertEquals("Asia/Shanghai",
dataSource.getConnectionProperty(Config.TIME_ZONE));
+ assertEquals("UTF-8", dataSource.getConnectionProperty(Config.CHARSET));
+ assertEquals("true", dataSource.getConnectionProperty(Config.USE_SSL));
+ assertEquals("trust-store",
dataSource.getConnectionProperty(Config.TRUST_STORE));
+ assertEquals("trust-store-password",
dataSource.getConnectionProperty(Config.TRUST_STORE_PWD));
+ assertEquals(Constant.TABLE,
dataSource.getConnectionProperty(Config.SQL_DIALECT));
+ assertEquals("true", dataSource.getConnectionProperty(Utils.RPC_COMPRESS));
+ }
+
@Test(expected = SQLException.class)
public void testCreateDataSourceRejectsInvalidStandardPortProperty() throws
SQLException {
Properties properties = new Properties();
@@ -111,6 +136,14 @@ public class IoTDBDataSourceFactoryTest {
new IoTDBDataSourceFactory().createDataSource(properties);
}
+ @Test(expected = SQLException.class)
+ public void testCreateDataSourceRejectsUnknownBeanProperty() throws
SQLException {
+ Properties properties = new Properties();
+ properties.setProperty("unknownProperty", "value");
+
+ new IoTDBDataSourceFactory().createDataSource(properties);
+ }
+
@Test
public void testDataSourceExplicitUrlTakesPrecedenceOverStandardProperties()
{
IoTDBDataSource dataSource = new IoTDBDataSource();
@@ -144,6 +177,11 @@ public class IoTDBDataSourceFactoryTest {
assertEquals(Integer.valueOf(6667), dataSource.getPort());
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testDataSourceRejectsInvalidDirectPort() {
+ new IoTDBDataSource().setPortNumber(65536);
+ }
+
@Test
public void testDataSourceWrapperMethods() throws SQLException {
IoTDBDataSource dataSource = new IoTDBDataSource();
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDriverTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDriverTest.java
index fb44d3664ec..7a9173186c1 100644
--- a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDriverTest.java
+++ b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/IoTDBDriverTest.java
@@ -99,9 +99,12 @@ public class IoTDBDriverTest {
try {
DriverPropertyInfo[] propertyInfos =
driver.getPropertyInfo(
-
"jdbc:iotdb://localhost:6667?use_ssl=true&sql_dialect=table&network_timeout=123&rpc_compress=true",
+
"jdbc:iotdb://localhost:6667?user=url-user&thrift_default_buffer_capacity=1024&thrift_max_frame_size=2048&use_ssl=true&sql_dialect=table&network_timeout=123&rpc_compress=true",
properties);
+ assertEquals("url-user", findProperty(propertyInfos,
Config.AUTH_USER).value);
+ assertEquals("1024", findProperty(propertyInfos,
Config.DEFAULT_BUFFER_CAPACITY).value);
+ assertEquals("2048", findProperty(propertyInfos,
Config.THRIFT_FRAME_MAX_SIZE).value);
assertEquals("true", findProperty(propertyInfos, Config.USE_SSL).value);
assertEquals(Constant.TABLE, findProperty(propertyInfos,
Config.SQL_DIALECT).value);
assertEquals("123", findProperty(propertyInfos,
Config.NETWORK_TIMEOUT).value);
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 acc6b523b5e..3d9e8154e8b 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
@@ -31,8 +31,12 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.SQLException;
+import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.ZoneId;
@@ -98,6 +102,24 @@ public class IoTDBPreparedStatementTest {
assertThrows(SQLException.class, () -> metadata.unwrap(String.class));
}
+ @SuppressWarnings("resource")
+ @Test
+ public void testParameterMetadataRejectsUnsetIndexAndHandlesNullValue()
throws Exception {
+ IoTDBPreparedStatement ps =
+ new IoTDBPreparedStatement(connection, client, sessionId, "SELECT ?",
zoneId);
+ ParameterMetaData metadata = ps.getParameterMetaData();
+
+ assertEquals(0, metadata.getParameterCount());
+ assertThrows(SQLException.class, () -> metadata.getPrecision(1));
+
+ ps.setString(1, null);
+
+ assertEquals(1, metadata.getParameterCount());
+ assertEquals(0, metadata.getPrecision(1));
+ assertEquals(Types.NULL, metadata.getParameterType(1));
+ assertEquals(ParameterMetaData.parameterModeUnknown,
metadata.getParameterMode(1));
+ }
+
@SuppressWarnings("resource")
@Test
public void unusedArgument() throws SQLException {
@@ -253,6 +275,40 @@ public class IoTDBPreparedStatementTest {
"SELECT status, 'temperature' FROM root.ln.wf01.wt01",
argument.getValue().getStatement());
}
+ @SuppressWarnings("resource")
+ @Test
+ public void nullArgumentsUseSqlNullLiteral() throws Exception {
+ String sql = "INSERT INTO root.ln.wf01.wt01(time,a,b,c,d,e,f,g)
VALUES(1,?,?,?,?,?,?,?)";
+ IoTDBPreparedStatement ps =
+ new IoTDBPreparedStatement(connection, client, sessionId, sql, zoneId);
+
+ ps.setString(1, null);
+ ps.setObject(2, null);
+ ps.setDate(3, (Date) null);
+ ps.setTime(4, (Time) null);
+ ps.setTimestamp(5, (Timestamp) null);
+ ps.setBytes(6, null);
+ ps.setBinaryStream(7, (InputStream) null, 0);
+ ps.execute();
+
+ ArgumentCaptor<TSExecuteStatementReq> argument =
+ ArgumentCaptor.forClass(TSExecuteStatementReq.class);
+ verify(client).executeStatementV2(argument.capture());
+ assertEquals(
+ "INSERT INTO root.ln.wf01.wt01(time,a,b,c,d,e,f,g)
VALUES(1,NULL,NULL,NULL,NULL,NULL,NULL,NULL)",
+ argument.getValue().getStatement());
+ }
+
+ @SuppressWarnings("resource")
+ @Test
+ public void setBinaryStreamRejectsNegativeLength() throws Exception {
+ IoTDBPreparedStatement ps =
+ new IoTDBPreparedStatement(connection, client, sessionId, "SELECT ?",
zoneId);
+
+ assertThrows(
+ SQLException.class, () -> ps.setBinaryStream(1, new
ByteArrayInputStream(new byte[0]), -1));
+ }
+
@SuppressWarnings("resource")
@Test
public void oneTimeLongArgument() throws Exception {
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 80aa9fa32af..f3422a01638 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
@@ -21,8 +21,11 @@ package org.apache.iotdb.jdbc;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.rpc.TSStatusCode;
+import org.apache.iotdb.rpc.stmt.PreparedParameterSerde;
+import org.apache.iotdb.rpc.stmt.PreparedParameterSerde.DeserializedParam;
import org.apache.iotdb.service.rpc.thrift.IClientRPCService.Iface;
import org.apache.iotdb.service.rpc.thrift.TSExecutePreparedReq;
+import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementReq;
import org.apache.iotdb.service.rpc.thrift.TSExecuteStatementResp;
import org.apache.iotdb.service.rpc.thrift.TSPrepareReq;
import org.apache.iotdb.service.rpc.thrift.TSPrepareResp;
@@ -35,10 +38,19 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
import java.time.ZoneId;
+import java.util.List;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
@@ -86,6 +98,7 @@ public class IoTDBTablePreparedStatementTest {
// Mock for executePreparedStatement
when(client.executePreparedStatement(any(TSExecutePreparedReq.class)))
.thenReturn(execStatementResp);
+
when(client.executeStatementV2(any(TSExecuteStatementReq.class))).thenReturn(execStatementResp);
}
/** Count the number of '?' placeholders in a SQL string, ignoring those
inside quotes */
@@ -130,6 +143,23 @@ public class IoTDBTablePreparedStatementTest {
assertThrows(SQLException.class, () -> metadata.unwrap(String.class));
}
+ @SuppressWarnings("resource")
+ @Test
+ public void testParameterMetadataRejectsInvalidIndex() throws Exception {
+ IoTDBTablePreparedStatement ps =
+ new IoTDBTablePreparedStatement(connection, client, sessionId, "SELECT
?", zoneId);
+ ParameterMetaData metadata = ps.getParameterMetaData();
+
+ assertEquals(1, metadata.getParameterCount());
+ assertThrows(SQLException.class, () -> metadata.getParameterType(0));
+ assertThrows(SQLException.class, () -> metadata.isSigned(2));
+
+ ps.setInt(1, 1);
+
+ assertEquals(Types.INTEGER, metadata.getParameterType(1));
+ assertTrue(metadata.isSigned(1));
+ }
+
@SuppressWarnings("resource")
@Test
public void testTableModelLoginInjectionWithComment() throws Exception {
@@ -256,4 +286,58 @@ public class IoTDBTablePreparedStatementTest {
verify(client).executePreparedStatement(argument.capture());
assertTrue(argument.getValue().getParameters() != null);
}
+
+ @SuppressWarnings("resource")
+ @Test
+ public void testTableModelPreparedNullSettersSerializeNulls() throws
Exception {
+ String sql = "SELECT * FROM users WHERE a=? AND b=? AND c=? AND d=? AND
e=? AND f=?";
+ IoTDBTablePreparedStatement ps =
+ new IoTDBTablePreparedStatement(connection, client, sessionId, sql,
zoneId);
+
+ ps.setObject(1, null);
+ ps.setBytes(2, null);
+ ps.setDate(3, (Date) null);
+ ps.setTime(4, (Time) null);
+ ps.setTimestamp(5, (Timestamp) null);
+ ps.setBinaryStream(6, (InputStream) null, 0);
+ ps.execute();
+
+ ArgumentCaptor<TSExecutePreparedReq> argument =
+ ArgumentCaptor.forClass(TSExecutePreparedReq.class);
+ verify(client).executePreparedStatement(argument.capture());
+ List<DeserializedParam> parameters =
+
PreparedParameterSerde.deserialize(ByteBuffer.wrap(argument.getValue().getParameters()));
+
+ assertEquals(6, parameters.size());
+ for (DeserializedParam parameter : parameters) {
+ assertTrue(parameter.isNull());
+ }
+ }
+
+ @SuppressWarnings("resource")
+ @Test
+ public void testTableModelSetBinaryStreamRejectsNegativeLength() throws
Exception {
+ IoTDBTablePreparedStatement ps =
+ new IoTDBTablePreparedStatement(connection, client, sessionId, "SELECT
?", zoneId);
+
+ assertThrows(
+ SQLException.class, () -> ps.setBinaryStream(1, new
ByteArrayInputStream(new byte[0]), -1));
+ }
+
+ @SuppressWarnings("resource")
+ @Test
+ public void testTableModelClientSideNullStringUsesSqlNullLiteral() throws
Exception {
+ String sql = "INSERT INTO users(time,email) VALUES(1, ?)";
+ IoTDBTablePreparedStatement ps =
+ new IoTDBTablePreparedStatement(connection, client, sessionId, sql,
zoneId);
+
+ ps.setString(1, null);
+ ps.execute();
+
+ ArgumentCaptor<TSExecuteStatementReq> argument =
+ ArgumentCaptor.forClass(TSExecuteStatementReq.class);
+ verify(client).executeStatementV2(argument.capture());
+ assertEquals(
+ "INSERT INTO users(time,email) VALUES(1, NULL)",
argument.getValue().getStatement());
+ }
}
diff --git
a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java
b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java
index e5b1de15da8..fbc7fe4831a 100644
--- a/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java
+++ b/iotdb-client/jdbc/src/test/java/org/apache/iotdb/jdbc/UtilsTest.java
@@ -130,6 +130,25 @@ public class UtilsTest {
assertEquals(123, params.getNetworkTimeout());
}
+ @Test
+ public void testParseUrlAppliesCredentialAndBufferQueryProperties() throws
IoTDBURLException {
+ Properties properties = new Properties();
+ properties.setProperty(Config.AUTH_USER, "prop-user");
+ properties.setProperty(Config.DEFAULT_BUFFER_CAPACITY, "1");
+
+ IoTDBConnectionParams params =
+ Utils.parseUrl(
+
"jdbc:iotdb://test:6667?user=url-user&password=url-password&thrift_default_buffer_capacity=1024&thrift_max_frame_size=2048",
+ properties);
+
+ assertEquals("url-user", params.getUsername());
+ assertEquals("url-password", params.getPassword());
+ assertEquals(1024, params.getThriftDefaultBufferSize());
+ assertEquals(2048, params.getThriftMaxFrameSize());
+ assertEquals("url-user", properties.getProperty(Config.AUTH_USER));
+ assertEquals("1024",
properties.getProperty(Config.DEFAULT_BUFFER_CAPACITY));
+ }
+
@Test
public void testParseUrlAllowsEmptyDatabaseBeforeQuery() throws
IoTDBURLException {
IoTDBConnectionParams params =
@@ -261,6 +280,17 @@ public class UtilsTest {
Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?network_timeout=-1", new
Properties());
}
+ @Test(expected = IoTDBURLException.class)
+ public void testParseUrlParamRejectsInvalidThriftBufferCapacityValue()
throws IoTDBURLException {
+ Utils.parseUrl(
+ "jdbc:iotdb://127.0.0.1:6667?thrift_default_buffer_capacity=0", new
Properties());
+ }
+
+ @Test(expected = IoTDBURLException.class)
+ public void testParseUrlParamRejectsInvalidThriftFrameMaxSizeValue() throws
IoTDBURLException {
+ Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?thrift_max_frame_size=bad",
new Properties());
+ }
+
@Test(expected = IoTDBURLException.class)
public void testParseUrlParamRejectsInvalidSqlDialectValue() throws
IoTDBURLException {
Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?sql_dialect=bad", new
Properties());