diff --git a/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParser.java b/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParser.java index 45a41a608..837d80339 100644 --- a/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParser.java +++ b/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParser.java @@ -2,6 +2,8 @@ import java.net.URI; import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -14,16 +16,17 @@ public class ClickhouseJdbcUrlParser { private static final Logger log = LoggerFactory.getLogger(ClickhouseJdbcUrlParser.class); + + protected static final String DEFAULT_DATABASE = "default"; + public static final String JDBC_PREFIX = "jdbc:"; public static final String JDBC_CLICKHOUSE_PREFIX = JDBC_PREFIX + "clickhouse:"; public static final Pattern DB_PATH_PATTERN = Pattern.compile("/([a-zA-Z0-9_*\\-]+)"); - protected final static String DEFAULT_DATABASE = "default"; - private ClickhouseJdbcUrlParser(){ + private ClickhouseJdbcUrlParser() { } - public static ClickHouseProperties parse(String jdbcUrl, Properties defaults) throws URISyntaxException - { + public static ClickHouseProperties parse(String jdbcUrl, Properties defaults) throws URISyntaxException { if (!jdbcUrl.startsWith(JDBC_CLICKHOUSE_PREFIX)) { throw new URISyntaxException(jdbcUrl, "'" + JDBC_CLICKHOUSE_PREFIX + "' prefix is mandatory"); } @@ -31,17 +34,31 @@ public static ClickHouseProperties parse(String jdbcUrl, Properties defaults) th } private static ClickHouseProperties parseClickhouseUrl(String uriString, Properties defaults) - throws URISyntaxException - { + throws URISyntaxException { URI uri = new URI(uriString); Properties urlProperties = parseUriQueryPart(uri.getQuery(), defaults); ClickHouseProperties props = new ClickHouseProperties(urlProperties); props.setHost(uri.getHost()); int port = uri.getPort(); if (port == -1) { - throw new IllegalArgumentException("port is missed or wrong"); + port = props.getProtocol().getDefaultPort(); } props.setPort(port); + String credentials = uri.getRawUserInfo(); + if (credentials != null && !credentials.isEmpty()) { + int index = credentials.indexOf(':'); + String userName = index == 0 ? "" + : URLDecoder.decode(index > 0 ? credentials.substring(0, index) : credentials, + StandardCharsets.UTF_8); + if (!userName.isEmpty()) { + props.setUser(userName); + } + String password = index < 0 ? "" + : URLDecoder.decode(credentials.substring(index + 1), StandardCharsets.UTF_8); + if (!password.isEmpty()) { + props.setPassword(password); + } + } String path = uri.getPath(); String database; if (props.isUsePathAsDb()) { @@ -75,9 +92,9 @@ static Properties parseUriQueryPart(String query, Properties defaults) { return defaults; } Properties urlProps = new Properties(defaults); - String queryKeyValues[] = query.split("&"); + String[] queryKeyValues = query.split("&"); for (String keyValue : queryKeyValues) { - String keyValueTokens[] = keyValue.split("="); + String[] keyValueTokens = keyValue.split("="); if (keyValueTokens.length == 2) { urlProps.put(keyValueTokens[0], keyValueTokens[1]); } else { diff --git a/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseConnectionSettings.java b/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseConnectionSettings.java index 2f9b53568..8b8ef6bde 100644 --- a/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseConnectionSettings.java +++ b/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseConnectionSettings.java @@ -16,6 +16,7 @@ public enum ClickHouseConnectionSettings implements DriverPropertyCreator { SSL_MODE("sslmode", "strict", "verify or not certificate: none (don't verify), strict (verify)"), USE_PATH_AS_DB("use_path_as_db", true, "whether URL path should be treated as database name"), PATH("path", "/", "URL path"), + PROTOCOL("protocol", "http", "protocol used to connect to server, http or grpc"), CHECK_FOR_REDIRECTS("check_for_redirects", false, "whether we should check for 307 redirect using GET before sending POST to given URL"), MAX_REDIRECTS("max_redirects", 5, "number of redirect checks before using last URL"), diff --git a/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseProperties.java b/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseProperties.java index 47b775c1e..2b144ce03 100644 --- a/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseProperties.java +++ b/clickhouse-jdbc/src/main/java/ru/yandex/clickhouse/settings/ClickHouseProperties.java @@ -5,6 +5,7 @@ import java.util.Properties; import com.clickhouse.client.ClickHouseChecker; +import com.clickhouse.client.ClickHouseProtocol; public class ClickHouseProperties { @@ -21,6 +22,7 @@ public class ClickHouseProperties { private int maxRetries; private String host; private int port; + private ClickHouseProtocol protocol; private boolean usePathAsDb; private String path; private boolean ssl; @@ -119,6 +121,7 @@ public ClickHouseProperties(Properties info) { this.sslMode = (String) getSetting(info, ClickHouseConnectionSettings.SSL_MODE); this.usePathAsDb = (Boolean) getSetting(info, ClickHouseConnectionSettings.USE_PATH_AS_DB); this.path = (String) getSetting(info, ClickHouseConnectionSettings.PATH); + this.protocol = ClickHouseProtocol.valueOf(((String) getSetting(info, ClickHouseConnectionSettings.PROTOCOL)).toUpperCase()); this.maxRedirects = (Integer) getSetting(info, ClickHouseConnectionSettings.MAX_REDIRECTS); this.checkForRedirects = (Boolean) getSetting(info, ClickHouseConnectionSettings.CHECK_FOR_REDIRECTS); this.useServerTimeZone = (Boolean)getSetting(info, ClickHouseConnectionSettings.USE_SERVER_TIME_ZONE); @@ -134,6 +137,7 @@ public ClickHouseProperties(Properties info) { this.quotaKey = getSetting(info, ClickHouseQueryParam.QUOTA_KEY); this.priority = getSetting(info, ClickHouseQueryParam.PRIORITY); this.database = getSetting(info, ClickHouseQueryParam.DATABASE); + this.protocol = ClickHouseProtocol.valueOf(((String)getSetting(info, ClickHouseConnectionSettings.PROTOCOL)).toUpperCase()); this.compress = (Boolean)getSetting(info, ClickHouseQueryParam.COMPRESS); this.decompress = (Boolean)getSetting(info, ClickHouseQueryParam.DECOMPRESS); this.extremes = (Boolean)getSetting(info, ClickHouseQueryParam.EXTREMES); @@ -187,6 +191,7 @@ public Properties asProperties() { ret.put(ClickHouseConnectionSettings.SSL_MODE.getKey(), String.valueOf(sslMode)); ret.put(ClickHouseConnectionSettings.USE_PATH_AS_DB.getKey(), String.valueOf(usePathAsDb)); ret.put(ClickHouseConnectionSettings.PATH.getKey(), String.valueOf(path)); + ret.put(ClickHouseConnectionSettings.PROTOCOL.getKey(), String.valueOf(protocol.name().toLowerCase())); ret.put(ClickHouseConnectionSettings.MAX_REDIRECTS.getKey(), String.valueOf(maxRedirects)); ret.put(ClickHouseConnectionSettings.CHECK_FOR_REDIRECTS.getKey(), String.valueOf(checkForRedirects)); ret.put(ClickHouseConnectionSettings.USE_SERVER_TIME_ZONE.getKey(), String.valueOf(useServerTimeZone)); @@ -258,6 +263,7 @@ public ClickHouseProperties(ClickHouseProperties properties) { setSslMode(properties.sslMode); setUsePathAsDb(properties.usePathAsDb); setPath(properties.path); + setProtocol(properties.protocol); setMaxRedirects(properties.maxRedirects); setCheckForRedirects(properties.checkForRedirects); setUseServerTimeZone(properties.useServerTimeZone); @@ -799,6 +805,14 @@ public void setPort(int port) { this.port = port; } + public ClickHouseProtocol getProtocol() { + return this.protocol; + } + + public void setProtocol(ClickHouseProtocol protocol) { + this.protocol = protocol; + } + public boolean isUsePathAsDb() { return usePathAsDb; } diff --git a/clickhouse-jdbc/src/test/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParserTest.java b/clickhouse-jdbc/src/test/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParserTest.java index 7a6aa99c1..8c9b50811 100644 --- a/clickhouse-jdbc/src/test/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParserTest.java +++ b/clickhouse-jdbc/src/test/java/ru/yandex/clickhouse/ClickhouseJdbcUrlParserTest.java @@ -2,6 +2,9 @@ import java.util.Properties; +import com.clickhouse.client.ClickHouseProtocol; + +import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2.Tag.Protocol; import org.testng.Assert; import org.testng.annotations.Test; @@ -12,16 +15,16 @@ public class ClickhouseJdbcUrlParserTest { @Test(groups = "unit") public void testParseDashes() throws Exception { Properties props = new Properties(); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337/db-name-with-dash", new Properties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser + .parse("jdbc:clickhouse://foo.yandex:1337/db-name-with-dash", new Properties()); Assert.assertEquals(chProps.getDatabase(), "db-name-with-dash"); } @Test(groups = "unit") public void testParseTrailingSlash() throws Exception { Properties props = new Properties(); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337/", new Properties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://foo.yandex:1337/", + new Properties()); Assert.assertEquals(chProps.getDatabase(), "default"); } @@ -29,8 +32,8 @@ public void testParseTrailingSlash() throws Exception { public void testParseDbInPathAndProps() throws Exception { ClickHouseProperties props = new ClickHouseProperties(); props.setDatabase("database-name"); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337/database-name", props.asProperties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://foo.yandex:1337/database-name", + props.asProperties()); Assert.assertEquals(chProps.getDatabase(), "database-name"); Assert.assertEquals(chProps.getPath(), "/"); } @@ -40,8 +43,8 @@ public void testParseDbInPathAndProps2() throws Exception { ClickHouseProperties props = new ClickHouseProperties(); props.setDatabase("database-name"); props.setUsePathAsDb(false); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337/database-name", props.asProperties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://foo.yandex:1337/database-name", + props.asProperties()); Assert.assertEquals(chProps.getDatabase(), "database-name"); Assert.assertEquals(chProps.getPath(), "/database-name"); } @@ -50,8 +53,8 @@ public void testParseDbInPathAndProps2() throws Exception { public void testParsePathDefaultDb() throws Exception { ClickHouseProperties props = new ClickHouseProperties(); props.setPath("/path"); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337/", props.asProperties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://foo.yandex:1337/", + props.asProperties()); Assert.assertEquals(chProps.getDatabase(), "default"); Assert.assertEquals(chProps.getPath(), "/path"); } @@ -61,17 +64,17 @@ public void testParsePathDefaultDb2() throws Exception { ClickHouseProperties props = new ClickHouseProperties(); props.setPath("/path"); props.setUsePathAsDb(false); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337", props.asProperties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://foo.yandex:1337", + props.asProperties()); Assert.assertEquals(chProps.getDatabase(), "default"); - Assert.assertEquals(chProps.getPath(), "/"); //uri takes priority + Assert.assertEquals(chProps.getPath(), "/"); // uri takes priority } @Test(groups = "unit") public void testParsePathAndDb() throws Exception { ClickHouseProperties props = new ClickHouseProperties(); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337/db?database=dbname", props.asProperties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser + .parse("jdbc:clickhouse://foo.yandex:1337/db?database=dbname", props.asProperties()); Assert.assertEquals(chProps.getDatabase(), "db"); Assert.assertEquals(chProps.getPath(), "/"); } @@ -80,10 +83,33 @@ public void testParsePathAndDb() throws Exception { public void testParsePathAndDb2() throws Exception { ClickHouseProperties props = new ClickHouseProperties(); props.setUsePathAsDb(false); - ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse( - "jdbc:clickhouse://foo.yandex:1337/db?database=dbname", props.asProperties()); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser + .parse("jdbc:clickhouse://foo.yandex:1337/db?database=dbname", props.asProperties()); Assert.assertEquals(chProps.getDatabase(), "dbname"); Assert.assertEquals(chProps.getPath(), "/db"); } + @Test(groups = "unit") + public void testParseCredentials() throws Exception { + ClickHouseProperties props = new ClickHouseProperties(); + props.setUser("default1"); + props.setPassword("password1"); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://user:a:passwd@foo.ch/test", + props.asProperties()); + Assert.assertEquals(chProps.getUser(), "user"); + Assert.assertEquals(chProps.getPassword(), "a:passwd"); + } + + @Test(groups = "unit") + public void testParseProtocol() throws Exception { + ClickHouseProperties props = new ClickHouseProperties(); + ClickHouseProperties chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://foo.ch/test", + props.asProperties()); + Assert.assertEquals(chProps.getProtocol(), ClickHouseProtocol.HTTP); + Assert.assertEquals(chProps.getPort(), ClickHouseProtocol.HTTP.getDefaultPort()); + + chProps = ClickhouseJdbcUrlParser.parse("jdbc:clickhouse://foo.ch/test?protocol=grpc", props.asProperties()); + Assert.assertEquals(chProps.getProtocol(), ClickHouseProtocol.GRPC); + Assert.assertEquals(chProps.getPort(), ClickHouseProtocol.GRPC.getDefaultPort()); + } }