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 66e88f2dfc8 Fix JDBC URL and DataSource edge cases
66e88f2dfc8 is described below

commit 66e88f2dfc83eeae523eb49f73b8b1d92da6bdc1
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jun 9 10:18:21 2026 +0800

    Fix JDBC URL and DataSource edge cases
---
 .../org/apache/iotdb/jdbc/IoTDBConnection.java     |  27 ++---
 .../apache/iotdb/jdbc/IoTDBConnectionParams.java   |   9 ++
 .../org/apache/iotdb/jdbc/IoTDBDataSource.java     |  86 ++++++++++++++--
 .../apache/iotdb/jdbc/IoTDBDataSourceFactory.java  |  80 ++++++++++++++-
 .../java/org/apache/iotdb/jdbc/IoTDBDriver.java    |  13 ++-
 .../org/apache/iotdb/jdbc/IoTDBJDBCResultSet.java  |   2 +-
 .../java/org/apache/iotdb/jdbc/IoTDBStatement.java |   6 +-
 .../src/main/java/org/apache/iotdb/jdbc/Utils.java | 109 +++++++++++++++------
 .../org/apache/iotdb/jdbc/IoTDBConnectionTest.java |  12 +++
 .../iotdb/jdbc/IoTDBDataSourceFactoryTest.java     |  63 +++++++++++-
 .../org/apache/iotdb/jdbc/IoTDBDriverTest.java     |  40 +++++++-
 .../apache/iotdb/jdbc/IoTDBJDBCResultSetTest.java  |   1 +
 .../org/apache/iotdb/jdbc/IoTDBStatementTest.java  |  14 +++
 .../test/java/org/apache/iotdb/jdbc/UtilsTest.java |  63 +++++++++++-
 14 files changed, 462 insertions(+), 63 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 66d17d43d79..ee94386fe54 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
@@ -135,11 +135,7 @@ public class IoTDBConnection implements Connection {
     this.zoneId = ZoneId.of(params.getTimeZone());
     this.charset = params.getCharset();
     openTransport();
-    if (Config.rpcThriftCompressionEnable) {
-      setClient(new IClientRPCService.Client(new TCompactProtocol(transport)));
-    } else {
-      setClient(new IClientRPCService.Client(new TBinaryProtocol(transport)));
-    }
+    openClient();
     // open client session
     openSession();
     // Wrap the client with a thread-safe proxy to serialize the RPC calls
@@ -311,7 +307,7 @@ public class IoTDBConnection implements Connection {
 
   @Override
   public int getHoldability() {
-    return 0;
+    return ResultSet.HOLD_CURSORS_OVER_COMMIT;
   }
 
   @Override
@@ -408,7 +404,10 @@ public class IoTDBConnection implements Connection {
   }
 
   @Override
-  public boolean isValid(int arg0) {
+  public boolean isValid(int arg0) throws SQLException {
+    if (arg0 < 0) {
+      throw new SQLException("timeout must be >= 0");
+    }
     return !isClosed;
   }
 
@@ -561,6 +560,14 @@ public class IoTDBConnection implements Connection {
     }
   }
 
+  private void openClient() {
+    if (params.isRpcThriftCompressionEnabled()) {
+      setClient(new IClientRPCService.Client(new TCompactProtocol(transport)));
+    } else {
+      setClient(new IClientRPCService.Client(new TBinaryProtocol(transport)));
+    }
+  }
+
   private void openSession() throws SQLException {
     TSOpenSessionReq openReq = new TSOpenSessionReq();
 
@@ -636,11 +643,7 @@ public class IoTDBConnection implements Connection {
         if (transport != null) {
           transport.close();
           openTransport();
-          if (Config.rpcThriftCompressionEnable) {
-            setClient(new IClientRPCService.Client(new 
TCompactProtocol(transport)));
-          } else {
-            setClient(new IClientRPCService.Client(new 
TBinaryProtocol(transport)));
-          }
+          openClient();
           openSession();
           setClient(RpcUtils.newSynchronizedClient(getClient()));
           flag = true;
diff --git 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java
 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java
index 1112caabd4e..1a8c4866bb7 100644
--- 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java
+++ 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBConnectionParams.java
@@ -51,6 +51,7 @@ public class IoTDBConnectionParams {
   private boolean useSSL = false;
   private String trustStore;
   private String trustStorePwd;
+  private boolean rpcThriftCompressionEnabled = 
Config.rpcThriftCompressionEnable;
 
   private String sqlDialect = TREE;
 
@@ -184,6 +185,14 @@ public class IoTDBConnectionParams {
     this.trustStorePwd = trustStorePwd;
   }
 
+  public boolean isRpcThriftCompressionEnabled() {
+    return rpcThriftCompressionEnabled;
+  }
+
+  public void setRpcThriftCompressionEnabled(boolean 
rpcThriftCompressionEnabled) {
+    this.rpcThriftCompressionEnabled = rpcThriftCompressionEnabled;
+  }
+
   public String getSqlDialect() {
     return sqlDialect;
   }
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 34645c78c49..689b08a45dd 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
@@ -38,8 +38,14 @@ public class IoTDBDataSource implements DataSource {
   private String url;
   private String user;
   private String password;
+  private String serverName = Config.IOTDB_DEFAULT_HOST;
+  private String databaseName;
+  private String dataSourceName;
+  private String description;
+  private String networkProtocol;
+  private String roleName;
   private Properties properties;
-  private Integer port = 6667;
+  private Integer port = Config.IOTDB_DEFAULT_PORT;
   private PrintWriter logWriter;
   private int loginTimeout;
 
@@ -52,9 +58,7 @@ public class IoTDBDataSource implements DataSource {
     this.properties = new Properties();
     setUser(user);
     setPassword(password);
-    if (port != null && port != 0) {
-      this.port = port;
-    }
+    setPort(port);
   }
 
   public String getUser() {
@@ -80,7 +84,63 @@ public class IoTDBDataSource implements DataSource {
   }
 
   public void setPort(Integer port) {
-    this.port = port;
+    this.port = port == null || port == 0 ? Config.IOTDB_DEFAULT_PORT : port;
+  }
+
+  public Integer getPortNumber() {
+    return getPort();
+  }
+
+  public void setPortNumber(Integer portNumber) {
+    setPort(portNumber);
+  }
+
+  public String getServerName() {
+    return serverName;
+  }
+
+  public void setServerName(String serverName) {
+    this.serverName = serverName;
+  }
+
+  public String getDatabaseName() {
+    return databaseName;
+  }
+
+  public void setDatabaseName(String databaseName) {
+    this.databaseName = databaseName;
+  }
+
+  public String getDataSourceName() {
+    return dataSourceName;
+  }
+
+  public void setDataSourceName(String dataSourceName) {
+    this.dataSourceName = dataSourceName;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+
+  public String getNetworkProtocol() {
+    return networkProtocol;
+  }
+
+  public void setNetworkProtocol(String networkProtocol) {
+    this.networkProtocol = networkProtocol;
+  }
+
+  public String getRoleName() {
+    return roleName;
+  }
+
+  public void setRoleName(String roleName) {
+    this.roleName = roleName;
   }
 
   public String getUrl() {
@@ -145,7 +205,7 @@ public class IoTDBDataSource implements DataSource {
   private Connection createConnection(Properties connectionProperties) throws 
SQLException {
     applyLoginTimeout(connectionProperties);
     try {
-      return new IoTDBConnection(url, connectionProperties);
+      return new IoTDBConnection(getConnectionUrl(), connectionProperties);
     } catch (TTransportException e) {
       LOGGER.error(JdbcMessages.GET_CONNECTION_ERROR, e);
       throw new SQLException(
@@ -155,6 +215,20 @@ public class IoTDBDataSource implements DataSource {
     }
   }
 
+  String getConnectionUrl() {
+    if (url != null) {
+      return url;
+    }
+    String host =
+        serverName == null || serverName.trim().isEmpty() ? 
Config.IOTDB_DEFAULT_HOST : serverName;
+    StringBuilder builder = new StringBuilder(Config.IOTDB_URL_PREFIX);
+    builder.append(host).append(':').append(port);
+    if (databaseName != null && !databaseName.isEmpty()) {
+      builder.append('/').append(databaseName);
+    }
+    return builder.toString();
+  }
+
   private void applyLoginTimeout(Properties connectionProperties) {
     if (loginTimeout <= 0 || 
connectionProperties.containsKey(Config.NETWORK_TIMEOUT)) {
       return;
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 228ad925af5..98d6bfb777c 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
@@ -38,29 +38,68 @@ public class IoTDBDataSourceFactory implements 
DataSourceFactory {
   private final Logger logger = 
LoggerFactory.getLogger(IoTDBDataSourceFactory.class);
 
   @Override
-  public DataSource createDataSource(Properties properties) {
+  public DataSource createDataSource(Properties properties) throws 
SQLException {
     IoTDBDataSource ds = new IoTDBDataSource();
     setProperties(ds, properties);
     return ds;
   }
 
-  public void setProperties(IoTDBDataSource ds, Properties prop) {
+  public void setProperties(IoTDBDataSource ds, Properties prop) throws 
SQLException {
     Properties properties = prop == null ? new Properties() : (Properties) 
prop.clone();
-    String url = (String) properties.remove(DataSourceFactory.JDBC_URL);
+    String url = removeStringProperty(properties, DataSourceFactory.JDBC_URL);
     if (url != null) {
       ds.setUrl(url);
     }
 
-    String user = (String) properties.remove(DataSourceFactory.JDBC_USER);
+    String user = removeStringProperty(properties, 
DataSourceFactory.JDBC_USER);
     if (user != null) {
       ds.setUser(user);
     }
 
-    String password = (String) 
properties.remove(DataSourceFactory.JDBC_PASSWORD);
+    String password = removeStringProperty(properties, 
DataSourceFactory.JDBC_PASSWORD);
     if (password != null) {
       ds.setPassword(password);
     }
 
+    String serverName = removeStringProperty(properties, 
DataSourceFactory.JDBC_SERVER_NAME);
+    if (serverName != null) {
+      ds.setServerName(serverName);
+    }
+
+    Integer portNumber = removeIntegerProperty(properties, 
DataSourceFactory.JDBC_PORT_NUMBER);
+    if (portNumber != null) {
+      ds.setPortNumber(portNumber);
+    }
+
+    String databaseName = removeStringProperty(properties, 
DataSourceFactory.JDBC_DATABASE_NAME);
+    if (databaseName != null) {
+      ds.setDatabaseName(databaseName);
+    }
+
+    String dataSourceName =
+        removeStringProperty(properties, 
DataSourceFactory.JDBC_DATASOURCE_NAME);
+    if (dataSourceName != null) {
+      ds.setDataSourceName(dataSourceName);
+    }
+
+    String description = removeStringProperty(properties, 
DataSourceFactory.JDBC_DESCRIPTION);
+    if (description != null) {
+      ds.setDescription(description);
+    }
+
+    String networkProtocol =
+        removeStringProperty(properties, 
DataSourceFactory.JDBC_NETWORK_PROTOCOL);
+    if (networkProtocol != null) {
+      ds.setNetworkProtocol(networkProtocol);
+    }
+
+    String roleName = removeStringProperty(properties, 
DataSourceFactory.JDBC_ROLE_NAME);
+    if (roleName != null) {
+      ds.setRoleName(roleName);
+    }
+
+    removeUnsupportedPoolProperties(properties);
+
     logger.info(JdbcMessages.REMAINING_PROPERTIES, properties.size());
 
     if (!properties.isEmpty()) {
@@ -83,4 +122,35 @@ public class IoTDBDataSourceFactory implements 
DataSourceFactory {
   public Driver createDriver(Properties properties) {
     return new IoTDBDriver();
   }
+
+  private static String removeStringProperty(Properties properties, String 
key) {
+    Object value = properties.remove(key);
+    return value == null ? null : value.toString();
+  }
+
+  private static Integer removeIntegerProperty(Properties properties, String 
key)
+      throws SQLException {
+    Object value = properties.remove(key);
+    if (value == null) {
+      return null;
+    }
+    try {
+      int integerValue = Integer.parseInt(value.toString());
+      if (integerValue < 0 || integerValue > 65535) {
+        throw new NumberFormatException(value.toString());
+      }
+      return integerValue;
+    } catch (NumberFormatException e) {
+      throw new SQLException("Invalid JDBC property " + key + ": " + value, e);
+    }
+  }
+
+  private static void removeUnsupportedPoolProperties(Properties properties) {
+    properties.remove(DataSourceFactory.JDBC_INITIAL_POOL_SIZE);
+    properties.remove(DataSourceFactory.JDBC_MAX_IDLE_TIME);
+    properties.remove(DataSourceFactory.JDBC_MAX_POOL_SIZE);
+    properties.remove(DataSourceFactory.JDBC_MAX_STATEMENTS);
+    properties.remove(DataSourceFactory.JDBC_MIN_POOL_SIZE);
+    properties.remove(DataSourceFactory.JDBC_PROPERTY_CYCLE);
+  }
 }
diff --git 
a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDriver.java 
b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDriver.java
index 71d69d0eb3c..3dcbd4fecb8 100644
--- a/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDriver.java
+++ b/iotdb-client/jdbc/src/main/java/org/apache/iotdb/jdbc/IoTDBDriver.java
@@ -98,8 +98,11 @@ public class IoTDBDriver implements Driver {
   }
 
   @Override
-  public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
-    Properties properties = info == null ? new Properties() : info;
+  public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) 
throws SQLException {
+    Properties properties = info == null ? new Properties() : (Properties) 
info.clone();
+    if (url != null && acceptsURL(url)) {
+      Utils.parseUrl(url, properties);
+    }
     return new DriverPropertyInfo[] {
       createProperty(
           Config.AUTH_USER, Config.DEFAULT_USER, "User name for 
authentication.", properties),
@@ -131,6 +134,12 @@ public class IoTDBDriver implements Driver {
           Config.CHARSET, TSFileConfig.STRING_CHARSET.name(), "Connection 
charset.", properties),
       createProperty(
           Config.USE_SSL, "false", BOOLEAN_CHOICES, "Whether to enable SSL.", 
properties),
+      createProperty(
+          Utils.RPC_COMPRESS,
+          String.valueOf(Config.rpcThriftCompressionEnable),
+          BOOLEAN_CHOICES,
+          "Whether to enable RPC thrift compression.",
+          properties),
       createProperty(Config.TRUST_STORE, null, "SSL trust store path.", 
properties),
       createSensitiveProperty(Config.TRUST_STORE_PWD, "SSL trust store 
password."),
       createProperty(
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 9ed67182ce8..fa615a2f2ab 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
@@ -530,7 +530,7 @@ public class IoTDBJDBCResultSet implements ResultSet {
 
   @Override
   public int getHoldability() throws SQLException {
-    throw new SQLException(Constant.METHOD_NOT_SUPPORTED);
+    return ResultSet.HOLD_CURSORS_OVER_COMMIT;
   }
 
   @Override
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 49818558b59..5952bb56e9b 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
@@ -669,6 +669,10 @@ public class IoTDBStatement implements Statement {
   @Override
   public void setQueryTimeout(int seconds) throws SQLException {
     checkConnection("setQueryTimeout");
+    if (seconds < 0) {
+      throw new SQLException(
+          String.format(JdbcMessages.QUERY_TIMEOUT_MUST_BE_NON_NEGATIVE, 
seconds));
+    }
     this.queryTimeout = seconds;
   }
 
@@ -685,7 +689,7 @@ public class IoTDBStatement implements Statement {
 
   @Override
   public int getResultSetHoldability() throws SQLException {
-    throw new 
SQLException(JdbcMessages.NOT_SUPPORT_GET_RESULT_SET_HOLDABILITY);
+    return ResultSet.HOLD_CURSORS_OVER_COMMIT;
   }
 
   @Override
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 873e01e7d21..d5eb1d720a4 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
@@ -48,49 +48,45 @@ public class Utils {
   static IoTDBConnectionParams parseUrl(String url, Properties info) throws 
IoTDBURLException {
     Properties properties = info == null ? new Properties() : info;
     IoTDBConnectionParams params = new IoTDBConnectionParams(url);
-    if (url.trim().equalsIgnoreCase(Config.IOTDB_URL_PREFIX)) {
-      return params;
-    }
 
-    boolean isUrlLegal = false;
+    boolean isUrlLegal = url.trim().equalsIgnoreCase(Config.IOTDB_URL_PREFIX);
     Matcher matcher = null;
     String host = null;
     String suffixURL = null;
-    if (url.startsWith(Config.IOTDB_URL_PREFIX)) {
+    if (!isUrlLegal && url.startsWith(Config.IOTDB_URL_PREFIX)) {
       String subURL = url.substring(Config.IOTDB_URL_PREFIX.length());
-      int i = subURL.lastIndexOf(COLON);
-      if (i <= 0) {
+      int authorityEnd = findAuthorityEnd(subURL);
+      String authority = subURL.substring(0, authorityEnd);
+      int portSeparatorIndex = authority.lastIndexOf(COLON);
+      if (portSeparatorIndex <= 0 || portSeparatorIndex == authority.length() 
- 1) {
         throw new IoTDBURLException(
             "Error url format, url should be 
jdbc:iotdb://anything:port/[database] or 
jdbc:iotdb://anything:port[/database]?property1=value1&property2=value2, 
current url is "
                 + url);
       }
-      host = subURL.substring(0, i);
+      host = authority.substring(0, portSeparatorIndex);
       params.setHost(host);
-      i++;
+      String portText = authority.substring(portSeparatorIndex + 1);
       // parse port
-      int port = 0;
-      for (; i < subURL.length() && Character.isDigit(subURL.charAt(i)); i++) {
-        port = port * 10 + (subURL.charAt(i) - '0');
-      }
-      suffixURL = i < subURL.length() ? subURL.substring(i) : "";
+      int port = parsePort(portText);
+      suffixURL = subURL.substring(authorityEnd);
       // legal port
       if (port >= 1 && port <= 65535) {
         params.setPort(port);
 
         // parse database
-        if (i < subURL.length() && subURL.charAt(i) == SLASH) {
-          int endIndex = subURL.indexOf(PARAMETER_SEPARATOR, i + 1);
+        if (!suffixURL.isEmpty() && suffixURL.charAt(0) == SLASH) {
+          int endIndex = suffixURL.indexOf(PARAMETER_SEPARATOR, 1);
           String database;
-          if (endIndex <= i + 1) {
-            if (i + 1 == subURL.length()) {
+          if (endIndex < 0) {
+            if (suffixURL.length() == 1) {
               database = null;
             } else {
-              database = subURL.substring(i + 1);
+              database = suffixURL.substring(1);
             }
             suffixURL = "";
           } else {
-            database = subURL.substring(i + 1, endIndex);
-            suffixURL = subURL.substring(endIndex);
+            database = endIndex == 1 ? null : suffixURL.substring(1, endIndex);
+            suffixURL = suffixURL.substring(endIndex);
           }
           params.setDb(database);
         }
@@ -115,16 +111,17 @@ public class Utils {
     }
     if (properties.containsKey(Config.DEFAULT_BUFFER_CAPACITY)) {
       params.setThriftDefaultBufferSize(
-          parseIntegerProperty(properties, Config.DEFAULT_BUFFER_CAPACITY));
+          parsePositiveIntegerProperty(properties, 
Config.DEFAULT_BUFFER_CAPACITY));
     }
     if (properties.containsKey(Config.THRIFT_FRAME_MAX_SIZE)) {
-      params.setThriftMaxFrameSize(parseIntegerProperty(properties, 
Config.THRIFT_FRAME_MAX_SIZE));
+      params.setThriftMaxFrameSize(
+          parsePositiveIntegerProperty(properties, 
Config.THRIFT_FRAME_MAX_SIZE));
     }
     if (properties.containsKey(Config.VERSION)) {
       params.setVersion(parseVersionProperty(properties));
     }
     if (properties.containsKey(Config.NETWORK_TIMEOUT)) {
-      params.setNetworkTimeout(parseIntegerProperty(properties, 
Config.NETWORK_TIMEOUT));
+      params.setNetworkTimeout(parseNonNegativeIntegerProperty(properties, 
Config.NETWORK_TIMEOUT));
     }
     if (properties.containsKey(Config.TIME_ZONE)) {
       params.setTimeZone(validateTimeZoneProperty(properties));
@@ -133,7 +130,7 @@ public class Utils {
       params.setCharset(validateCharsetProperty(properties));
     }
     if (properties.containsKey(Config.USE_SSL)) {
-      params.setUseSSL(parseBooleanProperty(properties));
+      params.setUseSSL(parseBooleanProperty(properties, Config.USE_SSL));
     }
     if (properties.containsKey(Config.TRUST_STORE)) {
       params.setTrustStore(properties.getProperty(Config.TRUST_STORE));
@@ -141,6 +138,9 @@ public class Utils {
     if (properties.containsKey(Config.TRUST_STORE_PWD)) {
       params.setTrustStorePwd(properties.getProperty(Config.TRUST_STORE_PWD));
     }
+    if (properties.containsKey(RPC_COMPRESS)) {
+      params.setRpcThriftCompressionEnabled(parseBooleanProperty(properties, 
RPC_COMPRESS));
+    }
     if (properties.containsKey(Config.SQL_DIALECT)) {
       params.setSqlDialect(validateSqlDialectProperty(properties));
     }
@@ -172,7 +172,7 @@ public class Utils {
       switch (key) {
         case RPC_COMPRESS:
           if (isBoolean(value)) {
-            Config.rpcThriftCompressionEnable = Boolean.parseBoolean(value);
+            info.put(key, value);
           } else {
             return false;
           }
@@ -197,7 +197,9 @@ public class Utils {
           break;
         case Config.NETWORK_TIMEOUT:
           try {
-            Integer.parseInt(value);
+            if (Integer.parseInt(value) < 0) {
+              return false;
+            }
           } catch (NumberFormatException e) {
             return false;
           }
@@ -237,6 +239,34 @@ public class Utils {
     return "true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value);
   }
 
+  private static int findAuthorityEnd(String subURL) {
+    int slashIndex = subURL.indexOf(SLASH);
+    int parameterIndex = subURL.indexOf(PARAMETER_SEPARATOR);
+    if (slashIndex < 0) {
+      return parameterIndex < 0 ? subURL.length() : parameterIndex;
+    }
+    if (parameterIndex < 0) {
+      return slashIndex;
+    }
+    return Math.min(slashIndex, parameterIndex);
+  }
+
+  private static int parsePort(String portText) {
+    if (portText.isEmpty()) {
+      return -1;
+    }
+    for (int i = 0; i < portText.length(); i++) {
+      if (!Character.isDigit(portText.charAt(i))) {
+        return -1;
+      }
+    }
+    try {
+      return Integer.parseInt(portText);
+    } catch (NumberFormatException e) {
+      return -1;
+    }
+  }
+
   private static int parseIntegerProperty(Properties properties, String key)
       throws IoTDBURLException {
     String value = getPropertyValue(properties, key);
@@ -247,6 +277,24 @@ public class Utils {
     }
   }
 
+  private static int parsePositiveIntegerProperty(Properties properties, 
String key)
+      throws IoTDBURLException {
+    int value = parseIntegerProperty(properties, key);
+    if (value <= 0) {
+      throw invalidPropertyValue(key, String.valueOf(value), null);
+    }
+    return value;
+  }
+
+  private static int parseNonNegativeIntegerProperty(Properties properties, 
String key)
+      throws IoTDBURLException {
+    int value = parseIntegerProperty(properties, key);
+    if (value < 0) {
+      throw invalidPropertyValue(key, String.valueOf(value), null);
+    }
+    return value;
+  }
+
   private static Constant.Version parseVersionProperty(Properties properties)
       throws IoTDBURLException {
     String value = getPropertyValue(properties, Config.VERSION);
@@ -257,10 +305,11 @@ public class Utils {
     }
   }
 
-  private static boolean parseBooleanProperty(Properties properties) throws 
IoTDBURLException {
-    String value = getPropertyValue(properties, Config.USE_SSL);
+  private static boolean parseBooleanProperty(Properties properties, String 
key)
+      throws IoTDBURLException {
+    String value = getPropertyValue(properties, key);
     if (!isBoolean(value)) {
-      throw invalidPropertyValue(Config.USE_SSL, value, null);
+      throw invalidPropertyValue(key, value, null);
     }
     return Boolean.parseBoolean(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 b1668dda5b2..230cb2c9b50 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
@@ -35,6 +35,7 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.sql.Connection;
+import java.sql.ResultSet;
 import java.sql.SQLClientInfoException;
 import java.sql.SQLException;
 import java.time.ZoneId;
@@ -124,6 +125,17 @@ public class IoTDBConnectionTest {
     Assert.assertEquals(60, connection.getQueryTimeout());
   }
 
+  @Test
+  public void testStandardConnectionStateMethods() throws SQLException {
+    assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, 
connection.getHoldability());
+    assertFalse(connection.isValid(0));
+  }
+
+  @Test(expected = SQLException.class)
+  public void testIsValidRejectsNegativeTimeout() throws SQLException {
+    connection.isValid(-1);
+  }
+
   @Test
   public void testWrapperMethods() throws SQLException {
     assertTrue(connection.isWrapperFor(IoTDBConnection.class));
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 b2f3ad4926f..e9e9a336058 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
@@ -39,7 +39,7 @@ import static org.junit.Assert.assertTrue;
 public class IoTDBDataSourceFactoryTest {
 
   @Test
-  public void testCreateDataSourceAllowsNullProperties() {
+  public void testCreateDataSourceAllowsNullProperties() throws SQLException {
     DataSource dataSource = new 
IoTDBDataSourceFactory().createDataSource(null);
 
     assertTrue(dataSource instanceof IoTDBDataSource);
@@ -50,7 +50,7 @@ public class IoTDBDataSourceFactoryTest {
   }
 
   @Test
-  public void testCreateDataSourceAllowsUrlOnlyProperties() {
+  public void testCreateDataSourceAllowsUrlOnlyProperties() throws 
SQLException {
     String url = "jdbc:iotdb://localhost:6667";
     Properties properties = new Properties();
     properties.setProperty(DataSourceFactory.JDBC_URL, url);
@@ -64,6 +64,65 @@ public class IoTDBDataSourceFactoryTest {
     assertEquals(url, properties.getProperty(DataSourceFactory.JDBC_URL));
   }
 
+  @Test
+  public void 
testCreateDataSourceSupportsStandardServerPortAndDatabaseProperties()
+      throws SQLException {
+    Properties properties = new Properties();
+    properties.setProperty(DataSourceFactory.JDBC_SERVER_NAME, "127.0.0.1");
+    properties.put(DataSourceFactory.JDBC_PORT_NUMBER, 6688);
+    properties.setProperty(DataSourceFactory.JDBC_DATABASE_NAME, "root.sg");
+
+    IoTDBDataSource dataSource =
+        (IoTDBDataSource) new 
IoTDBDataSourceFactory().createDataSource(properties);
+
+    assertEquals("127.0.0.1", dataSource.getServerName());
+    assertEquals(Integer.valueOf(6688), dataSource.getPortNumber());
+    assertEquals("root.sg", dataSource.getDatabaseName());
+    assertNull(dataSource.getUrl());
+    assertEquals("jdbc:iotdb://127.0.0.1:6688/root.sg", 
dataSource.getConnectionUrl());
+    assertEquals(Integer.valueOf(6688), 
properties.get(DataSourceFactory.JDBC_PORT_NUMBER));
+  }
+
+  @Test
+  public void 
testCreateDataSourceAcceptsStandardInformationalAndPoolProperties()
+      throws SQLException {
+    Properties properties = new Properties();
+    properties.setProperty(DataSourceFactory.JDBC_DATASOURCE_NAME, "iotdb-ds");
+    properties.setProperty(DataSourceFactory.JDBC_DESCRIPTION, "IoTDB test 
data source");
+    properties.setProperty(DataSourceFactory.JDBC_NETWORK_PROTOCOL, "tcp");
+    properties.setProperty(DataSourceFactory.JDBC_ROLE_NAME, "reader");
+    properties.put(DataSourceFactory.JDBC_INITIAL_POOL_SIZE, 1);
+    properties.put(DataSourceFactory.JDBC_MAX_POOL_SIZE, 2);
+
+    IoTDBDataSource dataSource =
+        (IoTDBDataSource) new 
IoTDBDataSourceFactory().createDataSource(properties);
+
+    assertEquals("iotdb-ds", dataSource.getDataSourceName());
+    assertEquals("IoTDB test data source", dataSource.getDescription());
+    assertEquals("tcp", dataSource.getNetworkProtocol());
+    assertEquals("reader", dataSource.getRoleName());
+  }
+
+  @Test(expected = SQLException.class)
+  public void testCreateDataSourceRejectsInvalidStandardPortProperty() throws 
SQLException {
+    Properties properties = new Properties();
+    properties.setProperty(DataSourceFactory.JDBC_PORT_NUMBER, "bad");
+
+    new IoTDBDataSourceFactory().createDataSource(properties);
+  }
+
+  @Test
+  public void testDataSourceExplicitUrlTakesPrecedenceOverStandardProperties() 
{
+    IoTDBDataSource dataSource = new IoTDBDataSource();
+
+    dataSource.setUrl("jdbc:iotdb://explicit:6667/root.explicit");
+    dataSource.setServerName("127.0.0.1");
+    dataSource.setPortNumber(6688);
+    dataSource.setDatabaseName("root.sg");
+
+    assertEquals("jdbc:iotdb://explicit:6667/root.explicit", 
dataSource.getConnectionUrl());
+  }
+
   @Test
   public void testDataSourceAllowsClearingUserAndPassword() {
     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 2dcd873dcd9..fb44d3664ec 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
@@ -22,6 +22,7 @@ package org.apache.iotdb.jdbc;
 import org.junit.Test;
 
 import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
 import java.util.Arrays;
 import java.util.Properties;
 
@@ -52,7 +53,7 @@ public class IoTDBDriverTest {
   }
 
   @Test
-  public void testGetPropertyInfo() {
+  public void testGetPropertyInfo() throws SQLException {
     IoTDBDriver driver = new IoTDBDriver();
     Properties properties = new Properties();
     properties.setProperty(Config.AUTH_USER, "root");
@@ -78,7 +79,7 @@ public class IoTDBDriverTest {
   }
 
   @Test
-  public void testGetPropertyInfoAllowsNullProperties() {
+  public void testGetPropertyInfoAllowsNullProperties() throws SQLException {
     IoTDBDriver driver = new IoTDBDriver();
 
     DriverPropertyInfo[] propertyInfos =
@@ -88,6 +89,41 @@ public class IoTDBDriverTest {
     assertEquals(Config.DEFAULT_USER, findProperty(propertyInfos, 
Config.AUTH_USER).value);
   }
 
+  @Test
+  public void testGetPropertyInfoMergesUrlPropertiesWithoutMutatingInput() 
throws SQLException {
+    IoTDBDriver driver = new IoTDBDriver();
+    Properties properties = new Properties();
+    properties.setProperty(Config.USE_SSL, "false");
+    boolean originalRpcCompression = Config.rpcThriftCompressionEnable;
+    Config.rpcThriftCompressionEnable = false;
+    try {
+      DriverPropertyInfo[] propertyInfos =
+          driver.getPropertyInfo(
+              
"jdbc:iotdb://localhost:6667?use_ssl=true&sql_dialect=table&network_timeout=123&rpc_compress=true",
+              properties);
+
+      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);
+      assertEquals("true", findProperty(propertyInfos, 
Utils.RPC_COMPRESS).value);
+      assertEquals("false", properties.getProperty(Config.USE_SSL));
+      assertFalse(Config.rpcThriftCompressionEnable);
+    } finally {
+      Config.rpcThriftCompressionEnable = originalRpcCompression;
+    }
+  }
+
+  @Test(expected = SQLException.class)
+  public void testGetPropertyInfoRejectsInvalidIoTDBUrl() throws SQLException {
+    new IoTDBDriver().getPropertyInfo("jdbc:iotdb://localhost", new 
Properties());
+  }
+
+  @Test(expected = SQLException.class)
+  public void testGetPropertyInfoRejectsInvalidUrlProperties() throws 
SQLException {
+    new IoTDBDriver()
+        .getPropertyInfo("jdbc:iotdb://localhost:6667?network_timeout=-1", new 
Properties());
+  }
+
   private static DriverPropertyInfo findProperty(DriverPropertyInfo[] 
propertyInfos, String name) {
     for (DriverPropertyInfo propertyInfo : propertyInfos) {
       if (name.equals(propertyInfo.name)) {
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 854709fddbb..208c77295db 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
@@ -207,6 +207,7 @@ public class IoTDBJDBCResultSetTest {
       Assert.assertFalse(resultSet.isWrapperFor(null));
       Assert.assertSame(resultSet, resultSet.unwrap(IoTDBJDBCResultSet.class));
       Assert.assertSame(resultSet, resultSet.unwrap(ResultSet.class));
+      Assert.assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, 
resultSet.getHoldability());
 
       // check columnInfoMap
       Assert.assertEquals(1, resultSet.findColumn("Time"));
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 95973222498..d54ccf15fa3 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
@@ -32,6 +32,7 @@ import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.time.ZoneId;
@@ -111,6 +112,12 @@ public class IoTDBStatementTest {
     Assert.assertEquals(100, statement.getQueryTimeout());
   }
 
+  @Test(expected = SQLException.class)
+  public void testSetQueryTimeoutRejectsNegativeValue() throws SQLException {
+    IoTDBStatement statement = new IoTDBStatement(connection, client, 
sessionId, zoneID);
+    statement.setQueryTimeout(-1);
+  }
+
   @Test
   public void testWrapperMethods() throws SQLException {
     IoTDBStatement statement = new IoTDBStatement(connection, client, 
sessionId, zoneID);
@@ -123,6 +130,13 @@ public class IoTDBStatementTest {
     assertSame(statement, statement.unwrap(Statement.class));
   }
 
+  @Test
+  public void testStandardResultSetHoldability() throws SQLException {
+    IoTDBStatement statement = new IoTDBStatement(connection, client, 
sessionId, zoneID);
+
+    assertEquals(ResultSet.HOLD_CURSORS_OVER_COMMIT, 
statement.getResultSetHoldability());
+  }
+
   @Test(expected = SQLException.class)
   public void testUnwrapRejectsUnsupportedClass() throws SQLException {
     new IoTDBStatement(connection, client, sessionId, 
zoneID).unwrap(String.class);
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 9927d050cb9..e5b1de15da8 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
@@ -30,6 +30,7 @@ import org.junit.Test;
 import java.util.Properties;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -115,6 +116,40 @@ public class UtilsTest {
     assertEquals(Config.DEFAULT_PASSWORD, params.getPassword());
   }
 
+  @Test
+  public void testParseDefaultUrlAppliesConnectionProperties() throws 
IoTDBURLException {
+    Properties properties = new Properties();
+    properties.setProperty(Config.AUTH_USER, "root");
+    properties.setProperty(Config.NETWORK_TIMEOUT, "123");
+
+    IoTDBConnectionParams params = Utils.parseUrl(Config.IOTDB_URL_PREFIX, 
properties);
+
+    assertEquals(Config.IOTDB_DEFAULT_HOST, params.getHost());
+    assertEquals(Config.IOTDB_DEFAULT_PORT, params.getPort());
+    assertEquals("root", params.getUsername());
+    assertEquals(123, params.getNetworkTimeout());
+  }
+
+  @Test
+  public void testParseUrlAllowsEmptyDatabaseBeforeQuery() throws 
IoTDBURLException {
+    IoTDBConnectionParams params =
+        Utils.parseUrl("jdbc:iotdb://test:6667/?use_ssl=true", new 
Properties());
+
+    assertEquals("test", params.getHost());
+    assertEquals(6667, params.getPort());
+    assertFalse(params.getDb().isPresent());
+    assertTrue(params.isUseSSL());
+  }
+
+  @Test
+  public void testParseUrlAllowsColonAfterAuthority() throws IoTDBURLException 
{
+    Properties properties = new Properties();
+
+    Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?trust_store=scheme:path", 
properties);
+
+    assertEquals("scheme:path", properties.getProperty(Config.TRUST_STORE));
+  }
+
   @Test(expected = IoTDBURLException.class)
   public void testParseWrongUrl2() throws IoTDBURLException {
     Properties properties = new Properties();
@@ -170,9 +205,21 @@ public class UtilsTest {
 
   @Test
   public void testRpcCompress() throws IoTDBURLException {
+    boolean originalRpcCompression = Config.rpcThriftCompressionEnable;
+    Config.rpcThriftCompressionEnable = false;
     Properties properties = new Properties();
-    Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?rpc_compress=true", 
properties);
-    assertTrue(Config.rpcThriftCompressionEnable);
+    try {
+      IoTDBConnectionParams params =
+          Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?rpc_compress=true", 
properties);
+
+      assertTrue(params.isRpcThriftCompressionEnabled());
+      assertFalse(Config.rpcThriftCompressionEnable);
+      assertFalse(
+          Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667", new Properties())
+              .isRpcThriftCompressionEnabled());
+    } finally {
+      Config.rpcThriftCompressionEnable = originalRpcCompression;
+    }
   }
 
   @Test
@@ -209,6 +256,11 @@ public class UtilsTest {
     Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?network_timeout=bad", new 
Properties());
   }
 
+  @Test(expected = IoTDBURLException.class)
+  public void testParseUrlParamRejectsNegativeNetworkTimeoutValue() throws 
IoTDBURLException {
+    Utils.parseUrl("jdbc:iotdb://127.0.0.1:6667?network_timeout=-1", 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());
@@ -226,6 +278,13 @@ public class UtilsTest {
     assertInvalidProperty(Config.SQL_DIALECT, "bad");
   }
 
+  @Test
+  public void testParseUrlRejectsInvalidIntegerRanges() {
+    assertInvalidProperty(Config.DEFAULT_BUFFER_CAPACITY, "0");
+    assertInvalidProperty(Config.THRIFT_FRAME_MAX_SIZE, "-1");
+    assertInvalidProperty(Config.NETWORK_TIMEOUT, "-1");
+  }
+
   private static void assertInvalidProperty(String key, String value) {
     Properties properties = new Properties();
     properties.setProperty(key, value);


Reply via email to