Skip to content

Commit

Permalink
Merge pull request #1960 from ClickHouse/updated-testing-and-url-parsing
Browse files Browse the repository at this point in the history
Testing and parsing and enhancements
  • Loading branch information
Paultagoras authored Nov 21, 2024
2 parents ce65071 + a30f19d commit b1027ba
Show file tree
Hide file tree
Showing 10 changed files with 988 additions and 103 deletions.
18 changes: 18 additions & 0 deletions client-v2/src/main/java/com/clickhouse/client/api/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,24 @@ public Builder() {
.compressClientRequest(false);
}

/**
* Builds a client object with the provided configuration through URL parameters.
*
* @param url - URL formatted string with protocol, host, port, and client configuration settings.
* @return Client - a client object
*/
public Builder fromUrl(String url) {
try {
Map<String, String> urlProperties = HttpAPIClientHelper.parseUrlParameters(new java.net.URL(url));
this.addEndpoint(url);
this.configuration.putAll(urlProperties);//TODO: Mostly just a placeholder for now
} catch (java.net.MalformedURLException e) {
throw new IllegalArgumentException("Configuration via URL should be done with a valid URL string", e);
}

return this;
}

/**
* Server address to which client may connect. If there are multiple endpoints then client will
* connect to one of them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public <T> T readValue(ClickHouseColumn column, Class<?> typeHint) throws IOExce
} catch (EOFException e) {
throw e;
} catch (Exception e) {
log.debug("Failed to read value for column {}, {}", column.getColumnName(), e.getLocalizedMessage());
throw new ClientException("Failed to read value for column " + column.getColumnName(), e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,14 @@
import java.net.NoRouteToHostException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -580,4 +582,30 @@ public ClientException wrapException(String message, Exception cause) {

return new ClientException(message, cause);
}


/**
* Parses URL parameters.
* @param url
* @return Map of parameters
*/
public static Map<String, String> parseUrlParameters(URL url) {
Map<String, String> params = new HashMap<>();

try {
String query = url.getQuery();
if (query != null) {
for (String pair : query.split("&")) {
int idx = pair.indexOf("=");
if (idx > 0) {
params.put(pair.substring(0, idx), pair.substring(idx + 1));
}
}
}
} catch (Exception e) {
LOG.error("Failed to parse URL parameters", e);
}

return params;
}
}
11 changes: 4 additions & 7 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@ public class ConnectionImpl implements Connection, JdbcV2Wrapper {
public ConnectionImpl(String url, Properties info) {
log.debug("Creating connection to {}", url);

this.url = url;
this.url = url;//Raw URL
this.config = new JdbcConfiguration(url, info);
this.client = new Client.Builder()
.addEndpoint(config.getProtocol() + "://" + config.getHost() + ":" + config.getPort())
this.client = new Client.Builder()
.fromUrl(this.config.getUrl())//URL without prefix
.setUsername(config.getUser())
.setPassword(config.getPassword())
.compressServerResponse(true)
.setDefaultDatabase(config.getDatabase())
.build();
.setPassword(config.getPassword()).build();
}

public String getUser() {
Expand Down
8 changes: 4 additions & 4 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/Driver.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ public boolean acceptsURL(String url) throws SQLException {

@Override
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
if (!JdbcConfiguration.acceptsURL(url)) {
return new DriverPropertyInfo[0];
}
return new JdbcConfiguration(url, info).getPropertyInfo();
// if (!JdbcConfiguration.acceptsURL(url)) {
// return new DriverPropertyInfo[0];
// }
return new DriverPropertyInfo[0];
}

public static int getDriverMajorVersion() {
Expand Down
13 changes: 11 additions & 2 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.clickhouse.client.api.data_formats.ClickHouseBinaryFormatReader;
import com.clickhouse.client.api.metadata.TableSchema;
import com.clickhouse.client.api.query.QueryResponse;
import com.clickhouse.jdbc.internal.SimpleArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -913,7 +914,11 @@ public Clob getClob(int columnIndex) throws SQLException {
@Override
public Array getArray(int columnIndex) throws SQLException {
checkClosed();
throw new SQLFeatureNotSupportedException("Array is not supported.");
try {
return new SimpleArray(reader.getList(columnIndex));
} catch (Exception e) {
throw new SQLException(e);
}
}

@Override
Expand Down Expand Up @@ -943,7 +948,11 @@ public Clob getClob(String columnLabel) throws SQLException {
@Override
public Array getArray(String columnLabel) throws SQLException {
checkClosed();
throw new SQLFeatureNotSupportedException("Array is not supported.");
try {
return new SimpleArray(reader.getList(columnLabel));
} catch (Exception e) {
throw new SQLException(e);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,24 @@ public class JdbcConfiguration {
public static final String PREFIX_CLICKHOUSE = "jdbc:clickhouse:";
public static final String PREFIX_CLICKHOUSE_SHORT = "jdbc:ch:";

final String host;
final int port;
final String protocol;
final String database;
final String user;
final String password;
final Map<String, String> queryParams;

public String getDatabase() {
return database;
}

public String getHost() {
return host;
}
final String url;

public String getPassword() {
return password;
}

public int getPort() {
return port;
}

public String getProtocol() {
return protocol;
}

public Map<String, String> getQueryParams() {
return queryParams;
}

public String getUser() {
return user;
}

public JdbcConfiguration(String url, Properties info) {
Map<String, String> urlProperties = parseUrl(url);
this.host = urlProperties.get("host");
this.port = Integer.parseInt(urlProperties.get("port"));
this.protocol = urlProperties.get("protocol");
this.database = urlProperties.get("database") == null ? "default" : urlProperties.get("database");
this.queryParams = urlProperties.get("queryParams") == null ? new HashMap<>() : parseQueryParams(urlProperties.get("queryParams"));

public String getUrl() {
return url;
}

public JdbcConfiguration(String url, Properties info) {
this.url = stripUrlPrefix(url);
this.user = info.getProperty("user", "default");
this.password = info.getProperty("password", "");
}
Expand All @@ -70,51 +43,7 @@ public static boolean acceptsURL(String url) {
return url.startsWith(PREFIX_CLICKHOUSE) || url.startsWith(PREFIX_CLICKHOUSE_SHORT);
}

public DriverPropertyInfo[] getPropertyInfo() {
List<DriverPropertyInfo> properties = new ArrayList<>();
properties.add(new DriverPropertyInfo("host", host));
properties.add(new DriverPropertyInfo("port", String.valueOf(port)));
properties.add(new DriverPropertyInfo("protocol", protocol));
properties.add(new DriverPropertyInfo("database", database));
properties.add(new DriverPropertyInfo("user", user));
properties.add(new DriverPropertyInfo("password", "*REDACTED*"));
properties.add(new DriverPropertyInfo("queryParams", queryParams.toString()));
return properties.toArray(new DriverPropertyInfo[0]);
}

private Map<String, String> parseUrl(String urlString) {
log.debug("Parsing URL: {}", urlString);
URL url;
try {
String urlStripped = stripUrlPrefix(urlString);
int index = urlStripped.indexOf("//");
if (index == 0) {//Add in the HTTP protocol if it is missing
urlStripped = "http:" + urlStripped;
}

url = new URL(urlStripped);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("URL is malformed.");
}

Map<String, String> urlProperties = new HashMap<>();
urlProperties.put("host", url.getHost());
urlProperties.put("protocol", url.getProtocol());
urlProperties.put("port", String.valueOf(url.getPort() == -1 ?
url.getProtocol().equalsIgnoreCase("HTTP") ? 8123 : 8443
: url.getPort()));

try {
urlProperties.put("database", url.getPath().substring(1));
} catch (StringIndexOutOfBoundsException e) {
urlProperties.put("database", "default");
}

urlProperties.put("queryParams", url.getQuery());

return urlProperties;
}
private String stripUrlPrefix(String url) {
public String stripUrlPrefix(String url) {
if (url.startsWith(PREFIX_CLICKHOUSE)) {
return url.substring(PREFIX_CLICKHOUSE.length());
} else if (url.startsWith(PREFIX_CLICKHOUSE_SHORT)) {
Expand All @@ -123,15 +52,4 @@ private String stripUrlPrefix(String url) {
throw new IllegalArgumentException("URL is not supported.");
}
}
private Map<String, String> parseQueryParams(String queryParams) {
if (queryParams == null || queryParams.isEmpty()) {
return new HashMap<>(0);
}

return Arrays.stream(queryParams.split("&"))
.map(s -> {
String[] parts = s.split("=");
return new AbstractMap.SimpleImmutableEntry<>(parts[0], parts[1]);
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.clickhouse.jdbc.internal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
import java.util.List;
import java.util.Map;

public class SimpleArray implements java.sql.Array {
private static final Logger log = LoggerFactory.getLogger(SimpleArray.class);
Object[] array;
int type; //java.sql.Types

public SimpleArray(List<Object> list) {
if (list == null) {
throw new IllegalArgumentException("List cannot be null");
}

this.array = list.toArray();
this.type = Types.OTHER;
}

@Override
public String getBaseTypeName() throws SQLException {
return Object.class.getName();
}

@Override
public int getBaseType() throws SQLException {
return type;
}

@Override
public Object getArray() throws SQLException {
return array;
}

@Override
public Object getArray(Map<String, Class<?>> map) throws SQLException {
throw new SQLFeatureNotSupportedException("getArray(Map<String, Class<?>>) is not supported");
}

@Override
public Object getArray(long index, int count) throws SQLException {
try {
Object[] smallerArray = new Object[count];
System.arraycopy(array, (int) index, smallerArray, 0, count);
return smallerArray;
} catch (Exception e) {
log.error("Failed to get array", e);
throw new SQLException(e);
}
}

@Override
public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException {
throw new SQLFeatureNotSupportedException("getArray(long, int, Map<String, Class<?>>) is not supported");
}

@Override
public ResultSet getResultSet() throws SQLException {
throw new SQLFeatureNotSupportedException("getResultSet() is not supported");
}

@Override
public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException {
throw new SQLFeatureNotSupportedException("getResultSet(Map<String, Class<?>>) is not supported");
}

@Override
public ResultSet getResultSet(long index, int count) throws SQLException {
throw new SQLFeatureNotSupportedException("getResultSet(long, int) is not supported");
}

@Override
public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map) throws SQLException {
throw new SQLFeatureNotSupportedException("getResultSet(long, int, Map<String, Class<?>>) is not supported");
}

@Override
public void free() throws SQLException {
array = null;
}
}
Loading

0 comments on commit b1027ba

Please sign in to comment.