From 5bf67df9b2b75dfc51f1dde96cd7b8d33ef43bad Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Fri, 22 Nov 2024 14:53:54 -0500 Subject: [PATCH 1/6] Adding types, adjusting constructors --- .../com/clickhouse/jdbc/DataSourceImpl.java | 18 +-- .../main/java/com/clickhouse/jdbc/Driver.java | 28 +++- .../jdbc/PreparedStatementImpl.java | 26 +++- .../com/clickhouse/jdbc/ResultSetImpl.java | 46 +++++-- .../com/clickhouse/jdbc/StatementImpl.java | 2 +- .../SimpleArray.java => types/Array.java} | 8 +- .../java/com/clickhouse/jdbc/types/Blob.java | 111 ++++++++++++++++ .../java/com/clickhouse/jdbc/types/Clob.java | 125 ++++++++++++++++++ .../com/clickhouse/jdbc/ConnectionTest.java | 2 +- 9 files changed, 327 insertions(+), 39 deletions(-) rename jdbc-v2/src/main/java/com/clickhouse/jdbc/{internal/SimpleArray.java => types/Array.java} (92%) create mode 100644 jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java create mode 100644 jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/DataSourceImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/DataSourceImpl.java index 024f91a0b..2389909e5 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/DataSourceImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/DataSourceImpl.java @@ -11,9 +11,11 @@ import java.util.logging.Logger; public class DataSourceImpl implements DataSource, JdbcV2Wrapper { + private static final Logger log = Logger.getLogger(DataSourceImpl.class.getName()); private String url; private Properties info; - private Driver driver; + private final Driver driver; + private PrintWriter logWriter; public void setUrl(String url) { this.url = url; @@ -24,22 +26,20 @@ private Properties getProperties() { copy.putAll(info); return copy; } + public void setProperties(Properties info) { this.info = info; } - public DataSourceImpl() { + + public DataSourceImpl() {//No-arg constructor required by the standard this(null, new Properties()); } - public DataSourceImpl(String url) { - this(url, new Properties()); - } - public DataSourceImpl(String url, Properties info) { this.url = url; this.info = info; - this.driver = new Driver(); + this.driver = new Driver(this); } @Override @@ -58,12 +58,12 @@ public Connection getConnection(String username, String password) throws SQLExce @Override public PrintWriter getLogWriter() throws SQLException { - throw new SQLFeatureNotSupportedException("Method not supported"); + return logWriter; } @Override public void setLogWriter(PrintWriter out) throws SQLException { - throw new SQLFeatureNotSupportedException("Method not supported"); + logWriter = out; } @Override diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/Driver.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/Driver.java index c9acc3b1e..aa1477358 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/Driver.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/Driver.java @@ -1,18 +1,28 @@ package com.clickhouse.jdbc; -import java.sql.*; -import java.util.*; import com.clickhouse.jdbc.internal.JdbcConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + /** * JDBC driver for ClickHouse. */ public class Driver implements java.sql.Driver { private static final Logger log = LoggerFactory.getLogger(Driver.class); public static final String driverVersion; + private final DataSourceImpl dataSource; public static String frameworksDetected = null; public static class FrameworksDetection { @@ -54,6 +64,14 @@ public static String getFrameworksDetected() { //load(); //Commented out to avoid loading the driver multiple times, because we're referenced in V1 } + public Driver() { + this.dataSource = null; + } + + public Driver(DataSourceImpl dataSourceImpl) { + this.dataSource = dataSourceImpl; + } + public static void load() { try { DriverManager.registerDriver(new Driver()); @@ -74,6 +92,9 @@ public static void unload() { @Override public Connection connect(String url, Properties info) throws SQLException { + if (!acceptsURL(url)) { + return null; + } return new ConnectionImpl(url, info); } @@ -84,9 +105,6 @@ 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 DriverPropertyInfo[0]; } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java index 5e92a31d4..a55dab1e8 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java @@ -16,7 +16,9 @@ import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoField; import java.util.Calendar; +import java.util.Collection; import java.util.GregorianCalendar; +import java.util.List; import java.util.Map; public class PreparedStatementImpl extends StatementImpl implements PreparedStatement, JdbcV2Wrapper { @@ -439,7 +441,7 @@ private static String encodeObject(Object x) throws SQLException { if (x == null) { return "NULL"; } else if (x instanceof String) { - return "'" + x + "'"; + return "'" + escapeString((String) x) + "'"; } else if (x instanceof Boolean) { return (Boolean) x ? "1" : "0"; } else if (x instanceof Date) { @@ -454,8 +456,18 @@ private static String encodeObject(Object x) throws SQLException { return "'" + DATETIME_FORMATTER.format(((Timestamp) x).toLocalDateTime()) + "'"; } else if (x instanceof LocalDateTime) { return "'" + DATETIME_FORMATTER.format((LocalDateTime) x) + "'"; + } else if (x instanceof Collection) { + StringBuilder listString = new StringBuilder(); + listString.append("["); + for (Object item : (Collection) x) { + listString.append(encodeObject(item)).append(", "); + } + listString.delete(listString.length() - 2, listString.length()); + listString.append("]"); + + return listString.toString(); } else if (x instanceof Map) { - Map tmpMap = (Map) x; + Map tmpMap = (Map) x; StringBuilder mapString = new StringBuilder(); mapString.append("{"); for (Object key : tmpMap.keySet()) { @@ -473,7 +485,7 @@ private static String encodeObject(Object x) throws SQLException { while ((len = reader.read(buffer)) != -1) { sb.append(buffer, 0, len); } - return "'" + sb + "'"; + return "'" + escapeString(sb.toString()) + "'"; } else if (x instanceof InputStream) { StringBuilder sb = new StringBuilder(); InputStream is = (InputStream) x; @@ -482,13 +494,17 @@ private static String encodeObject(Object x) throws SQLException { while ((len = is.read(buffer)) != -1) { sb.append(new String(buffer, 0, len)); } - return "'" + sb + "'"; + return "'" + escapeString(sb.toString()) + "'"; } - return x.toString(); + return escapeString(x.toString());//Escape single quotes } catch (Exception e) { LOG.error("Error encoding object", e); throw new SQLException("Error encoding object", e); } } + + private static String escapeString(String x) { + return x.replace("'", "''");//Escape single quotes + } } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java index 8bcf0bc5a..7445a8408 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -17,7 +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 com.clickhouse.jdbc.types.Array; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,8 +27,10 @@ public class ResultSetImpl implements ResultSet, JdbcV2Wrapper { protected ClickHouseBinaryFormatReader reader; private final ResultSetMetaData metaData; private boolean closed; + private Statement parentStatement; - public ResultSetImpl(QueryResponse response, ClickHouseBinaryFormatReader reader) { + public ResultSetImpl(Statement parentStatement, QueryResponse response, ClickHouseBinaryFormatReader reader) { + this.parentStatement = parentStatement; this.response = response; this.reader = reader; this.metaData = new com.clickhouse.jdbc.metadata.ResultSetMetaData(this); @@ -883,7 +885,7 @@ public void moveToCurrentRow() throws SQLException { @Override public Statement getStatement() throws SQLException { checkClosed(); - return null; + return this.parentStatement; } @Override @@ -902,20 +904,28 @@ public Ref getRef(int columnIndex) throws SQLException { @Override public Blob getBlob(int columnIndex) throws SQLException { checkClosed(); - throw new SQLFeatureNotSupportedException("Blob is not supported."); + try { + return new com.clickhouse.jdbc.types.Blob(reader.getString(columnIndex)); + } catch (Exception e) { + throw new SQLException(e); + } } @Override - public Clob getClob(int columnIndex) throws SQLException { + public java.sql.Clob getClob(int columnIndex) throws SQLException { checkClosed(); - throw new SQLFeatureNotSupportedException("Clob is not supported."); + try { + return new com.clickhouse.jdbc.types.Clob(reader.getString(columnIndex)); + } catch (Exception e) { + throw new SQLException(e); + } } @Override - public Array getArray(int columnIndex) throws SQLException { + public java.sql.Array getArray(int columnIndex) throws SQLException { checkClosed(); try { - return new SimpleArray(reader.getList(columnIndex)); + return new Array(reader.getList(columnIndex)); } catch (Exception e) { throw new SQLException(e); } @@ -936,20 +946,28 @@ public Ref getRef(String columnLabel) throws SQLException { @Override public Blob getBlob(String columnLabel) throws SQLException { checkClosed(); - throw new SQLFeatureNotSupportedException("Blob is not supported."); + try { + return new com.clickhouse.jdbc.types.Blob(reader.getString(columnLabel)); + } catch (Exception e) { + throw new SQLException(e); + } } @Override public Clob getClob(String columnLabel) throws SQLException { checkClosed(); - throw new SQLFeatureNotSupportedException("Clob is not supported."); + try { + return new com.clickhouse.jdbc.types.Clob(reader.getString(columnLabel)); + } catch (Exception e) { + throw new SQLException(e); + } } @Override - public Array getArray(String columnLabel) throws SQLException { + public java.sql.Array getArray(String columnLabel) throws SQLException { checkClosed(); try { - return new SimpleArray(reader.getList(columnLabel)); + return new Array(reader.getList(columnLabel)); } catch (Exception e) { throw new SQLException(e); } @@ -1048,13 +1066,13 @@ public void updateClob(String columnLabel, Clob x) throws SQLException { } @Override - public void updateArray(int columnIndex, Array x) throws SQLException { + public void updateArray(int columnIndex, java.sql.Array x) throws SQLException { checkClosed(); throw new SQLFeatureNotSupportedException("Writes are not supported."); } @Override - public void updateArray(String columnLabel, Array x) throws SQLException { + public void updateArray(String columnLabel, java.sql.Array x) throws SQLException { checkClosed(); throw new SQLFeatureNotSupportedException("Writes are not supported."); } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index 7a46209c0..8cd4a2cc6 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -114,7 +114,7 @@ public ResultSet executeQuery(String sql, QuerySettings settings) throws SQLExce sql = parseJdbcEscapeSyntax(sql); QueryResponse response = connection.client.query(sql, mergedSettings).get(queryTimeout, TimeUnit.SECONDS); ClickHouseBinaryFormatReader reader = connection.client.newBinaryFormatReader(response); - currentResultSet = new ResultSetImpl(response, reader); + currentResultSet = new ResultSetImpl(this, response, reader); metrics = response.getMetrics(); } catch (Exception e) { throw new SQLException(e); diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SimpleArray.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java similarity index 92% rename from jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SimpleArray.java rename to jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java index 608f037d3..89a13ceec 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/SimpleArray.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java @@ -1,4 +1,4 @@ -package com.clickhouse.jdbc.internal; +package com.clickhouse.jdbc.types; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,12 +10,12 @@ import java.util.List; import java.util.Map; -public class SimpleArray implements java.sql.Array { - private static final Logger log = LoggerFactory.getLogger(SimpleArray.class); +public class Array implements java.sql.Array { + private static final Logger log = LoggerFactory.getLogger(Array.class); Object[] array; int type; //java.sql.Types - public SimpleArray(List list) { + public Array(List list) { if (list == null) { throw new IllegalArgumentException("List cannot be null"); } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java new file mode 100644 index 000000000..5f1527f78 --- /dev/null +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java @@ -0,0 +1,111 @@ +package com.clickhouse.jdbc.types; + +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.SQLException; + +public class Blob implements java.sql.Blob { + String data; + + public Blob(String data) { + this.data = data; + } + + @Override + public long length() throws SQLException { + try { + return data.length(); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public byte[] getBytes(long pos, int length) throws SQLException { + try { + int adjustedPos = (int) pos - 1; + return data.substring(adjustedPos, adjustedPos + length).getBytes(); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public InputStream getBinaryStream() throws SQLException { + try { + return new java.io.ByteArrayInputStream(data.getBytes()); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public long position(byte[] pattern, long start) throws SQLException { + try { + int adjustedStart = (int) start - 1; + return data.indexOf(new String(pattern), adjustedStart); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public long position(java.sql.Blob pattern, long start) throws SQLException { + try { + return position(pattern.getBytes(1, (int) pattern.length()), start); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public int setBytes(long pos, byte[] bytes) throws SQLException { + try { + int adjustedPos = (int) pos - 1; + data = data.substring(0, adjustedPos) + new String(bytes) + data.substring(adjustedPos + bytes.length); + return bytes.length; + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { + try { + int adjustedPos = (int) pos - 1; + data = data.substring(0, adjustedPos) + new String(bytes, offset, len) + data.substring(adjustedPos + len); + return len; + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public OutputStream setBinaryStream(long pos) throws SQLException { + throw new SQLException("setBinaryStream is not supported."); + } + + @Override + public void truncate(long len) throws SQLException { + try { + data = data.substring(0, (int) len); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public void free() throws SQLException { + data = null; + } + + @Override + public InputStream getBinaryStream(long pos, long length) throws SQLException { + try { + int adjustedPos = (int) pos - 1; + return new java.io.ByteArrayInputStream(data.substring(adjustedPos, adjustedPos + (int) length).getBytes()); + } catch (Exception e) { + throw new SQLException(e); + } + } +} diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java new file mode 100644 index 000000000..afd8bb091 --- /dev/null +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java @@ -0,0 +1,125 @@ +package com.clickhouse.jdbc.types; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.sql.SQLException; + +public class Clob implements java.sql.Clob { + String data; + + public Clob(String data) { + this.data = data; + } + + @Override + public long length() throws SQLException { + try { + return data.length(); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public String getSubString(long pos, int length) throws SQLException { + try { + int adjustedPos = (int) pos - 1; + return data.substring(adjustedPos, adjustedPos + length); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public Reader getCharacterStream() throws SQLException { + try { + return new java.io.StringReader(data); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public InputStream getAsciiStream() throws SQLException { + try { + return new java.io.ByteArrayInputStream(data.getBytes()); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public long position(String searchstr, long start) throws SQLException { + try { + return data.indexOf(searchstr, (int) start); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public long position(java.sql.Clob searchstr, long start) throws SQLException { + try { + return data.indexOf(searchstr.getSubString(1, (int) searchstr.length()), (int) start); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public int setString(long pos, String str) throws SQLException { + try { + int adjustedPos = (int) pos - 1; + data = data.substring(0, adjustedPos) + str + data.substring(adjustedPos + str.length()); + return str.length(); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public int setString(long pos, String str, int offset, int len) throws SQLException { + try { + int adjustedPos = (int) pos - 1; + data = data.substring(0, adjustedPos) + str.substring(offset, offset + len) + data.substring(adjustedPos + len); + return len; + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public OutputStream setAsciiStream(long pos) throws SQLException { + throw new SQLException("setAsciiStream is not supported."); + } + + @Override + public Writer setCharacterStream(long pos) throws SQLException { + throw new SQLException("setCharacterStream is not supported."); + } + + @Override + public void truncate(long len) throws SQLException { + try { + data = data.substring(0, (int) len); + } catch (Exception e) { + throw new SQLException(e); + } + } + + @Override + public void free() throws SQLException { + data = null; + } + + @Override + public Reader getCharacterStream(long pos, long length) throws SQLException { + try { + return new java.io.StringReader(data.substring((int) pos - 1, (int) pos - 1 + (int) length)); + } catch (Exception e) { + throw new SQLException(e); + } + } +} diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java index d00d3b60b..835a381e5 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java @@ -49,7 +49,7 @@ public void nativeSQLTest() throws SQLException { public void setAutoCommitTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.setAutoCommit(false)); - Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.setAutoCommit(true)); + localConnection.setAutoCommit(true); } @Test From 260502d7883cf6e1818bde75558071895f6b9c52 Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Fri, 22 Nov 2024 15:32:18 -0500 Subject: [PATCH 2/6] Adding array to prepared statement, adjusting get and set client info --- .../com/clickhouse/jdbc/ConnectionImpl.java | 59 ++++++++++++++----- .../jdbc/PreparedStatementImpl.java | 31 +++++++++- .../java/com/clickhouse/jdbc/types/Blob.java | 4 ++ .../java/com/clickhouse/jdbc/types/Clob.java | 15 +++++ .../com/clickhouse/jdbc/DataTypeTests.java | 25 ++------ 5 files changed, 98 insertions(+), 36 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java index d0c4b3b88..597d62507 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java @@ -1,19 +1,19 @@ package com.clickhouse.jdbc; import com.clickhouse.client.api.Client; -import com.clickhouse.client.api.data_formats.ClickHouseBinaryFormatReader; import com.clickhouse.client.api.query.GenericRecord; -import com.clickhouse.client.api.query.QueryResponse; import com.clickhouse.client.api.query.QuerySettings; import com.clickhouse.jdbc.internal.JdbcConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; public class ConnectionImpl implements Connection, JdbcV2Wrapper { private static final Logger log = LoggerFactory.getLogger(ConnectionImpl.class); @@ -47,7 +47,6 @@ public ConnectionImpl(String url, Properties info) { .setPassword(config.getPassword()) .setClientName(clientName) .build(); - this.defaultQuerySettings = new QuerySettings(); } @@ -166,8 +165,8 @@ public DatabaseMetaData getMetaData() throws SQLException { @Override public void setReadOnly(boolean readOnly) throws SQLException { checkOpen(); - if (!readOnly) { - throw new SQLFeatureNotSupportedException("read-only=false unsupported"); + if (readOnly) { + throw new SQLFeatureNotSupportedException("read-only=true unsupported"); } } @@ -324,19 +323,31 @@ public PreparedStatement prepareStatement(String sql, String[] columnNames) thro @Override public Clob createClob() throws SQLException { checkOpen(); - throw new SQLFeatureNotSupportedException("Clob not supported"); + try { + return new com.clickhouse.jdbc.types.Clob(); + } catch (Exception e) { + throw new SQLException("Failed to create Clob", e); + } } @Override public Blob createBlob() throws SQLException { checkOpen(); - throw new SQLFeatureNotSupportedException("Blob not supported"); + try { + return new com.clickhouse.jdbc.types.Blob(); + } catch (Exception e) { + throw new SQLException("Failed to create Blob", e); + } } @Override public NClob createNClob() throws SQLException { checkOpen(); - throw new SQLFeatureNotSupportedException("NClob not supported"); + try { + return new com.clickhouse.jdbc.types.Clob.NClob(); + } catch (Exception e) { + throw new SQLException("Failed to create NClob", e); + } } @Override @@ -358,30 +369,48 @@ public boolean isValid(int timeout) throws SQLException { @Override public void setClientInfo(String name, String value) throws SQLClientInfoException { - throw new SQLClientInfoException("ClientInfo not supported", null); + try { + checkOpen(); + this.defaultQuerySettings.setOption(name, value); + } catch (Exception e) { + throw new SQLClientInfoException("Failed to set client info.", Collections.singletonMap(name, ClientInfoStatus.REASON_UNKNOWN), e); + } } @Override public void setClientInfo(Properties properties) throws SQLClientInfoException { - throw new SQLClientInfoException("ClientInfo not supported", null); + try { + checkOpen(); + } catch (SQLException e) { + throw new SQLClientInfoException("Failed to set client info.", new HashMap<>(), e); + } + + for (Map.Entry entry : properties.entrySet()) { + setClientInfo(entry.getKey().toString(), entry.getValue().toString()); + } } @Override public String getClientInfo(String name) throws SQLException { checkOpen(); - return null; + return String.valueOf(this.defaultQuerySettings.getAllSettings().get(name)); } @Override public Properties getClientInfo() throws SQLException { checkOpen(); - return new Properties(); + Properties clientInfo = new Properties(); + clientInfo.putAll(this.defaultQuerySettings.getAllSettings()); + return clientInfo; } @Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { - //TODO: Should this be supported? - return null; + try { + return new com.clickhouse.jdbc.types.Array(List.of(elements)); + } catch (Exception e) { + throw new SQLException("Failed to create array", e); + } } @Override diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java index a55dab1e8..904358943 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java @@ -7,7 +7,25 @@ import java.io.Reader; import java.math.BigDecimal; import java.net.URL; -import java.sql.*; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.JDBCType; +import java.sql.NClob; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.SQLType; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -18,7 +36,6 @@ import java.util.Calendar; import java.util.Collection; import java.util.GregorianCalendar; -import java.util.List; import java.util.Map; public class PreparedStatementImpl extends StatementImpl implements PreparedStatement, JdbcV2Wrapper { @@ -456,6 +473,16 @@ private static String encodeObject(Object x) throws SQLException { return "'" + DATETIME_FORMATTER.format(((Timestamp) x).toLocalDateTime()) + "'"; } else if (x instanceof LocalDateTime) { return "'" + DATETIME_FORMATTER.format((LocalDateTime) x) + "'"; + } else if (x instanceof Array) { + StringBuilder listString = new StringBuilder(); + listString.append("["); + for (Object item : (Object[])((Array) x).getArray()) { + listString.append(encodeObject(item)).append(", "); + } + listString.delete(listString.length() - 2, listString.length()); + listString.append("]"); + + return listString.toString(); } else if (x instanceof Collection) { StringBuilder listString = new StringBuilder(); listString.append("["); diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java index 5f1527f78..f680205bf 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java @@ -7,6 +7,10 @@ public class Blob implements java.sql.Blob { String data; + public Blob() { + this.data = ""; + } + public Blob(String data) { this.data = data; } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java index afd8bb091..68d5f6ec5 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java @@ -9,6 +9,10 @@ public class Clob implements java.sql.Clob { String data; + public Clob() { + this.data = ""; + } + public Clob(String data) { this.data = data; } @@ -122,4 +126,15 @@ public Reader getCharacterStream(long pos, long length) throws SQLException { throw new SQLException(e); } } + + + public static class NClob extends Clob implements java.sql.NClob { + public NClob() { + super(); + } + + public NClob(String data) { + super(data); + } + } } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index 07835ff0b..1a0380a27 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -453,7 +453,7 @@ public void testArrayTypesSimpleStatement () throws SQLException { Random rand = new Random(seed); log.info("Random seed was: {}", seed); - int[] array = new int[rand.nextInt(10)]; + Integer[] array = new Integer[rand.nextInt(10)]; for (int i = 0; i < array.length; i++) { array[i] = rand.nextInt(256) - 128; } @@ -464,26 +464,13 @@ public void testArrayTypesSimpleStatement () throws SQLException { } // Insert random (valid) values - StringBuilder sb = new StringBuilder(); - sb.append("INSERT INTO test_arrays VALUES ( 1, ["); - for (int i = 0; i < array.length; i++) { - if (i > 0) { - sb.append(", "); - } - sb.append(array[i]); - } - sb.append("], ["); - for (int i = 0; i < arraystr.length; i++) { - if (i > 0) { - sb.append(", "); + try (Connection conn = getConnection()) { + try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO test_arrays VALUES ( 1, ?, ? )")) { + stmt.setArray(1, conn.createArrayOf("Int8", array)); + stmt.setArray(2, conn.createArrayOf("String", arraystr)); + stmt.executeUpdate(); } - sb.append("'"); - sb.append(arraystr[i]); - sb.append("'"); } - sb.append("])"); - String sql = sb.toString(); - insertData(sql); // Check the results try (Connection conn = getConnection()) { From 48092d8ebc476efa600cc02e5dcd118e3435193e Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Fri, 22 Nov 2024 15:44:36 -0500 Subject: [PATCH 3/6] Fixing older (unimplemented) tests --- .../com/clickhouse/jdbc/ConnectionImpl.java | 3 +- .../com/clickhouse/jdbc/ConnectionTest.java | 29 +++++++++---------- .../com/clickhouse/jdbc/DataSourceTest.java | 9 +++--- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java index 597d62507..1844f33f2 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java @@ -393,7 +393,8 @@ public void setClientInfo(Properties properties) throws SQLClientInfoException { @Override public String getClientInfo(String name) throws SQLException { checkOpen(); - return String.valueOf(this.defaultQuerySettings.getAllSettings().get(name)); + Object value = this.defaultQuerySettings.getAllSettings().get(name); + return value == null ? null : String.valueOf(value); } @Override diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java index 835a381e5..04da854a7 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java @@ -90,8 +90,8 @@ public void getMetaDataTest() throws SQLException { @Test public void setReadOnlyTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - localConnection.setReadOnly(true); - Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.setReadOnly(false)); + localConnection.setReadOnly(false); + Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.setReadOnly(true)); } @Test @@ -177,25 +177,25 @@ public void releaseSavepointTest() throws SQLException { @Test public void createClobTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.createClob()); + Assert.assertNotNull(localConnection.createClob()); } @Test public void createBlobTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.createBlob()); + Assert.assertNotNull(localConnection.createBlob()); } @Test public void createNClobTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.createNClob()); + Assert.assertNotNull(localConnection.createNClob()); } @Test public void createSQLXMLTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.createSQLXML()); + Assert.assertThrows(SQLFeatureNotSupportedException.class, localConnection::createSQLXML); } @Test @@ -206,23 +206,20 @@ public void isValidTest() throws SQLException { } @Test - public void setClientInfoTest() throws SQLException { - Connection localConnection = this.getJdbcConnection(); - Assert.assertThrows(SQLClientInfoException.class, () -> localConnection.setClientInfo("key", "value")); - Assert.assertThrows(SQLClientInfoException.class, () -> localConnection.setClientInfo(new Properties())); - } - - @Test - public void getClientInfoTest() throws SQLException { + public void setAndGetClientInfoTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); Assert.assertNull(localConnection.getClientInfo("key")); - Assert.assertNotNull(localConnection.getClientInfo()); + localConnection.setClientInfo("key", "value"); + Assert.assertNotNull(localConnection.getClientInfo("key")); } + @Test public void createArrayOfTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertNull(localConnection.createArrayOf("type-name", new Object[] { 1, 2, 3 })); + Array array = localConnection.createArrayOf("type-name", new Object[] { 1, 2, 3 }); + Assert.assertNotNull(array); + Assert.assertEquals(array.getArray(), new Object[] { 1, 2, 3 }); } @Test diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataSourceTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataSourceTest.java index 49aece048..4e69b2ee5 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataSourceTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataSourceTest.java @@ -5,6 +5,7 @@ import java.sql.SQLFeatureNotSupportedException; import java.util.Properties; +import static org.testng.Assert.assertNull; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertNotNull; @@ -46,13 +47,13 @@ public void testGetConnectionWithUserAndPassword() throws SQLException { } @Test - public void testGetLogWriter() { - assertThrows(SQLFeatureNotSupportedException.class, () -> dataSource.getLogWriter()); + public void testGetLogWriter() throws SQLException { + assertNull(dataSource.getLogWriter()); } @Test - public void testSetLogWriter() { - assertThrows(SQLFeatureNotSupportedException.class, () -> dataSource.setLogWriter(null)); + public void testSetLogWriter() throws SQLException { + dataSource.setLogWriter(null); } @Test From de9fae52e84c11a01aed7d1f80f3a89789acf7f7 Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Fri, 22 Nov 2024 15:52:00 -0500 Subject: [PATCH 4/6] Update DataTypeTests.java --- .../com/clickhouse/jdbc/DataTypeTests.java | 53 +++++-------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index 1a0380a27..b3dffc324 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -506,50 +506,25 @@ public void testMapTypesSimpleStatement() throws SQLException { Random rand = new Random(seed); log.info("Random seed was: {}", seed); - int mapSize = rand.nextInt(10); - String[] keys = new String[mapSize]; - int[] values = new int[mapSize]; + int mapSize = rand.nextInt(100); + Map integerMap = new java.util.HashMap<>(mapSize); for (int i = 0; i < mapSize; i++) { - keys[i] = "key" + i; - values[i] = rand.nextInt(256) - 128; + integerMap.put("key" + i, rand.nextInt(256) - 128); } - String[] keysstr = new String[mapSize]; - String[] valuesstr = new String[mapSize]; + Map stringMap = new java.util.HashMap<>(mapSize); for (int i = 0; i < mapSize; i++) { - keysstr[i] = "key" + i; - valuesstr[i] = "string" + rand.nextInt(1000); + stringMap.put("key" + i, "string" + rand.nextInt(1000)); } // Insert random (valid) values - StringBuilder sb = new StringBuilder(); - sb.append("INSERT INTO test_maps VALUES ( 1, "); - sb.append("{"); - for (int i = 0; i < mapSize; i++) { - if (i > 0) { - sb.append(", "); - } - sb.append("'"); - sb.append(keys[i]); - sb.append("': "); - sb.append(values[i]); - } - sb.append("}, "); - sb.append("{"); - for (int i = 0; i < mapSize; i++) { - if (i > 0) { - sb.append(", "); + try (Connection conn = getConnection()) { + try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO test_maps VALUES ( 1, ?, ? )")) { + stmt.setObject(1, integerMap); + stmt.setObject(2, stringMap); + stmt.executeUpdate(); } - sb.append("'"); - sb.append(keysstr[i]); - sb.append("': '"); - sb.append(valuesstr[i]); - sb.append("'"); } - sb.append("}"); - sb.append(")"); - String sql = sb.toString(); - insertData(sql); // Check the results try (Connection conn = getConnection()) { @@ -558,14 +533,14 @@ public void testMapTypesSimpleStatement() throws SQLException { assertTrue(rs.next()); Map mapResult = (Map) rs.getObject("map"); assertEquals(mapResult.size(), mapSize); - for (int i = 0; i < mapSize; i++) { - assertEquals(String.valueOf(mapResult.get(keys[i])), String.valueOf(values[i])); + for (String key: integerMap.keySet()) { + assertEquals(String.valueOf(mapResult.get(key)), String.valueOf(integerMap.get(key))); } Map mapstrResult = (Map) rs.getObject("mapstr"); assertEquals(mapstrResult.size(), mapSize); - for (int i = 0; i < mapSize; i++) { - assertEquals(mapstrResult.get(keysstr[i]), valuesstr[i]); + for (String key: stringMap.keySet()) { + assertEquals(String.valueOf(mapstrResult.get(key)), String.valueOf(stringMap.get(key))); } } } From 904c68e920a178001e033d688e4ea91c9ee3234a Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Fri, 22 Nov 2024 15:52:31 -0500 Subject: [PATCH 5/6] Update DataTypeTests.java --- jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index b3dffc324..8970437f5 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -443,7 +443,7 @@ public void testBooleanTypes() throws SQLException { } @Test - public void testArrayTypesSimpleStatement () throws SQLException { + public void testArrayTypes() throws SQLException { runQuery("CREATE TABLE test_arrays (order Int8, " + "array Array(Int8), arraystr Array(String)" + ") ENGINE = Memory"); @@ -496,7 +496,7 @@ public void testArrayTypesSimpleStatement () throws SQLException { } @Test - public void testMapTypesSimpleStatement() throws SQLException { + public void testMapTypes() throws SQLException { runQuery("CREATE TABLE test_maps (order Int8, " + "map Map(String, Int8), mapstr Map(String, String)" + ") ENGINE = Memory"); From 094d238c22436a11874f2304d1f79e3e136f4191 Mon Sep 17 00:00:00 2001 From: Paultagoras Date: Fri, 22 Nov 2024 20:09:01 -0500 Subject: [PATCH 6/6] Removing some implementation to simply --- .../com/clickhouse/jdbc/ConnectionImpl.java | 62 ++++---- .../com/clickhouse/jdbc/ResultSetImpl.java | 27 +--- .../com/clickhouse/jdbc/StatementImpl.java | 14 +- .../java/com/clickhouse/jdbc/types/Blob.java | 115 -------------- .../java/com/clickhouse/jdbc/types/Clob.java | 140 ------------------ .../com/clickhouse/jdbc/ConnectionTest.java | 13 +- .../com/clickhouse/jdbc/DataTypeTests.java | 9 +- .../clickhouse/jdbc/JdbcIntegrationTest.java | 12 ++ .../com/clickhouse/jdbc/StatementTest.java | 1 - 9 files changed, 66 insertions(+), 327 deletions(-) delete mode 100644 jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java delete mode 100644 jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java index 1844f33f2..e844c6108 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java @@ -323,31 +323,19 @@ public PreparedStatement prepareStatement(String sql, String[] columnNames) thro @Override public Clob createClob() throws SQLException { checkOpen(); - try { - return new com.clickhouse.jdbc.types.Clob(); - } catch (Exception e) { - throw new SQLException("Failed to create Clob", e); - } + throw new SQLFeatureNotSupportedException("Clob not supported"); } @Override public Blob createBlob() throws SQLException { checkOpen(); - try { - return new com.clickhouse.jdbc.types.Blob(); - } catch (Exception e) { - throw new SQLException("Failed to create Blob", e); - } + throw new SQLFeatureNotSupportedException("Blob not supported"); } @Override public NClob createNClob() throws SQLException { checkOpen(); - try { - return new com.clickhouse.jdbc.types.Clob.NClob(); - } catch (Exception e) { - throw new SQLException("Failed to create NClob", e); - } + throw new SQLFeatureNotSupportedException("NClob not supported"); } @Override @@ -369,40 +357,44 @@ public boolean isValid(int timeout) throws SQLException { @Override public void setClientInfo(String name, String value) throws SQLClientInfoException { - try { - checkOpen(); - this.defaultQuerySettings.setOption(name, value); - } catch (Exception e) { - throw new SQLClientInfoException("Failed to set client info.", Collections.singletonMap(name, ClientInfoStatus.REASON_UNKNOWN), e); - } +// try { +// checkOpen(); +// this.defaultQuerySettings.setOption(name, value); +// } catch (Exception e) { +// throw new SQLClientInfoException("Failed to set client info.", Collections.singletonMap(name, ClientInfoStatus.REASON_UNKNOWN), e); +// } + throw new SQLClientInfoException("Failed to set client info.", new HashMap<>(), new SQLFeatureNotSupportedException("setClientInfo not supported")); } @Override public void setClientInfo(Properties properties) throws SQLClientInfoException { - try { - checkOpen(); - } catch (SQLException e) { - throw new SQLClientInfoException("Failed to set client info.", new HashMap<>(), e); - } - - for (Map.Entry entry : properties.entrySet()) { - setClientInfo(entry.getKey().toString(), entry.getValue().toString()); - } +// try { +// checkOpen(); +// } catch (SQLException e) { +// throw new SQLClientInfoException("Failed to set client info.", new HashMap<>(), e); +// } +// +// for (Map.Entry entry : properties.entrySet()) { +// setClientInfo(entry.getKey().toString(), entry.getValue().toString()); +// } + throw new SQLClientInfoException("Failed to set client info.", new HashMap<>(), new SQLFeatureNotSupportedException("setClientInfo not supported")); } @Override public String getClientInfo(String name) throws SQLException { checkOpen(); - Object value = this.defaultQuerySettings.getAllSettings().get(name); - return value == null ? null : String.valueOf(value); +// Object value = this.defaultQuerySettings.getAllSettings().get(name); +// return value == null ? null : String.valueOf(value); + throw new SQLFeatureNotSupportedException("getClientInfo not supported"); } @Override public Properties getClientInfo() throws SQLException { checkOpen(); - Properties clientInfo = new Properties(); - clientInfo.putAll(this.defaultQuerySettings.getAllSettings()); - return clientInfo; +// Properties clientInfo = new Properties(); +// clientInfo.putAll(this.defaultQuerySettings.getAllSettings()); +// return clientInfo; + throw new SQLFeatureNotSupportedException("getClientInfo not supported"); } @Override diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java index 7445a8408..8cef62531 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -891,8 +891,7 @@ public Statement getStatement() throws SQLException { @Override public Object getObject(int columnIndex, Map> map) throws SQLException { checkClosed(); - //TODO: Should we implement? - return null; + return getObject(columnIndex); } @Override @@ -904,21 +903,13 @@ public Ref getRef(int columnIndex) throws SQLException { @Override public Blob getBlob(int columnIndex) throws SQLException { checkClosed(); - try { - return new com.clickhouse.jdbc.types.Blob(reader.getString(columnIndex)); - } catch (Exception e) { - throw new SQLException(e); - } + throw new SQLFeatureNotSupportedException("Blob is not supported."); } @Override public java.sql.Clob getClob(int columnIndex) throws SQLException { checkClosed(); - try { - return new com.clickhouse.jdbc.types.Clob(reader.getString(columnIndex)); - } catch (Exception e) { - throw new SQLException(e); - } + throw new SQLFeatureNotSupportedException("Clob is not supported."); } @Override @@ -946,21 +937,13 @@ public Ref getRef(String columnLabel) throws SQLException { @Override public Blob getBlob(String columnLabel) throws SQLException { checkClosed(); - try { - return new com.clickhouse.jdbc.types.Blob(reader.getString(columnLabel)); - } catch (Exception e) { - throw new SQLException(e); - } + throw new SQLFeatureNotSupportedException("Blob is not supported."); } @Override public Clob getClob(String columnLabel) throws SQLException { checkClosed(); - try { - return new com.clickhouse.jdbc.types.Clob(reader.getString(columnLabel)); - } catch (Exception e) { - throw new SQLException(e); - } + throw new SQLFeatureNotSupportedException("Clob is not supported."); } @Override diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java index 8cd4a2cc6..6d66ff514 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -112,7 +112,12 @@ public ResultSet executeQuery(String sql, QuerySettings settings) throws SQLExce try { sql = parseJdbcEscapeSyntax(sql); - QueryResponse response = connection.client.query(sql, mergedSettings).get(queryTimeout, TimeUnit.SECONDS); + QueryResponse response; + if (queryTimeout == 0) { + response = connection.client.query(sql, mergedSettings).get(); + } else { + response = connection.client.query(sql, mergedSettings).get(queryTimeout, TimeUnit.SECONDS); + } ClickHouseBinaryFormatReader reader = connection.client.newBinaryFormatReader(response); currentResultSet = new ResultSetImpl(this, response, reader); metrics = response.getMetrics(); @@ -140,7 +145,12 @@ public int executeUpdate(String sql, QuerySettings settings) throws SQLException try { sql = parseJdbcEscapeSyntax(sql); - QueryResponse response = connection.client.query(sql, mergedSettings).get(queryTimeout, TimeUnit.SECONDS); + QueryResponse response; + if (queryTimeout == 0) { + response = connection.client.query(sql, mergedSettings).get(); + } else { + response = connection.client.query(sql, mergedSettings).get(queryTimeout, TimeUnit.SECONDS); + } currentResultSet = null; metrics = response.getMetrics(); response.close(); diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java deleted file mode 100644 index f680205bf..000000000 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Blob.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.clickhouse.jdbc.types; - -import java.io.InputStream; -import java.io.OutputStream; -import java.sql.SQLException; - -public class Blob implements java.sql.Blob { - String data; - - public Blob() { - this.data = ""; - } - - public Blob(String data) { - this.data = data; - } - - @Override - public long length() throws SQLException { - try { - return data.length(); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public byte[] getBytes(long pos, int length) throws SQLException { - try { - int adjustedPos = (int) pos - 1; - return data.substring(adjustedPos, adjustedPos + length).getBytes(); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public InputStream getBinaryStream() throws SQLException { - try { - return new java.io.ByteArrayInputStream(data.getBytes()); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public long position(byte[] pattern, long start) throws SQLException { - try { - int adjustedStart = (int) start - 1; - return data.indexOf(new String(pattern), adjustedStart); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public long position(java.sql.Blob pattern, long start) throws SQLException { - try { - return position(pattern.getBytes(1, (int) pattern.length()), start); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public int setBytes(long pos, byte[] bytes) throws SQLException { - try { - int adjustedPos = (int) pos - 1; - data = data.substring(0, adjustedPos) + new String(bytes) + data.substring(adjustedPos + bytes.length); - return bytes.length; - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException { - try { - int adjustedPos = (int) pos - 1; - data = data.substring(0, adjustedPos) + new String(bytes, offset, len) + data.substring(adjustedPos + len); - return len; - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public OutputStream setBinaryStream(long pos) throws SQLException { - throw new SQLException("setBinaryStream is not supported."); - } - - @Override - public void truncate(long len) throws SQLException { - try { - data = data.substring(0, (int) len); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public void free() throws SQLException { - data = null; - } - - @Override - public InputStream getBinaryStream(long pos, long length) throws SQLException { - try { - int adjustedPos = (int) pos - 1; - return new java.io.ByteArrayInputStream(data.substring(adjustedPos, adjustedPos + (int) length).getBytes()); - } catch (Exception e) { - throw new SQLException(e); - } - } -} diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java deleted file mode 100644 index 68d5f6ec5..000000000 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Clob.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.clickhouse.jdbc.types; - -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.sql.SQLException; - -public class Clob implements java.sql.Clob { - String data; - - public Clob() { - this.data = ""; - } - - public Clob(String data) { - this.data = data; - } - - @Override - public long length() throws SQLException { - try { - return data.length(); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public String getSubString(long pos, int length) throws SQLException { - try { - int adjustedPos = (int) pos - 1; - return data.substring(adjustedPos, adjustedPos + length); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public Reader getCharacterStream() throws SQLException { - try { - return new java.io.StringReader(data); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public InputStream getAsciiStream() throws SQLException { - try { - return new java.io.ByteArrayInputStream(data.getBytes()); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public long position(String searchstr, long start) throws SQLException { - try { - return data.indexOf(searchstr, (int) start); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public long position(java.sql.Clob searchstr, long start) throws SQLException { - try { - return data.indexOf(searchstr.getSubString(1, (int) searchstr.length()), (int) start); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public int setString(long pos, String str) throws SQLException { - try { - int adjustedPos = (int) pos - 1; - data = data.substring(0, adjustedPos) + str + data.substring(adjustedPos + str.length()); - return str.length(); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public int setString(long pos, String str, int offset, int len) throws SQLException { - try { - int adjustedPos = (int) pos - 1; - data = data.substring(0, adjustedPos) + str.substring(offset, offset + len) + data.substring(adjustedPos + len); - return len; - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public OutputStream setAsciiStream(long pos) throws SQLException { - throw new SQLException("setAsciiStream is not supported."); - } - - @Override - public Writer setCharacterStream(long pos) throws SQLException { - throw new SQLException("setCharacterStream is not supported."); - } - - @Override - public void truncate(long len) throws SQLException { - try { - data = data.substring(0, (int) len); - } catch (Exception e) { - throw new SQLException(e); - } - } - - @Override - public void free() throws SQLException { - data = null; - } - - @Override - public Reader getCharacterStream(long pos, long length) throws SQLException { - try { - return new java.io.StringReader(data.substring((int) pos - 1, (int) pos - 1 + (int) length)); - } catch (Exception e) { - throw new SQLException(e); - } - } - - - public static class NClob extends Clob implements java.sql.NClob { - public NClob() { - super(); - } - - public NClob(String data) { - super(data); - } - } -} diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java index 04da854a7..b8b599ace 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java @@ -177,19 +177,19 @@ public void releaseSavepointTest() throws SQLException { @Test public void createClobTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertNotNull(localConnection.createClob()); + Assert.assertThrows(SQLFeatureNotSupportedException.class, localConnection::createClob); } @Test public void createBlobTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertNotNull(localConnection.createBlob()); + Assert.assertThrows(SQLFeatureNotSupportedException.class, localConnection::createBlob); } @Test public void createNClobTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertNotNull(localConnection.createNClob()); + Assert.assertThrows(SQLFeatureNotSupportedException.class, localConnection::createNClob); } @Test @@ -208,9 +208,10 @@ public void isValidTest() throws SQLException { @Test public void setAndGetClientInfoTest() throws SQLException { Connection localConnection = this.getJdbcConnection(); - Assert.assertNull(localConnection.getClientInfo("key")); - localConnection.setClientInfo("key", "value"); - Assert.assertNotNull(localConnection.getClientInfo("key")); + Assert.assertThrows(SQLFeatureNotSupportedException.class, () -> localConnection.getClientInfo("name")); + Assert.assertThrows(SQLFeatureNotSupportedException.class, localConnection::getClientInfo); + Assert.assertThrows(SQLClientInfoException.class, () -> localConnection.setClientInfo("name", "value")); + Assert.assertThrows(SQLClientInfoException.class, () -> localConnection.setClientInfo(new Properties())); } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java index 8970437f5..e2829684f 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/DataTypeTests.java @@ -2,11 +2,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.testng.annotations.BeforeTest; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.math.BigDecimal; import java.math.BigInteger; +import java.sql.Blob; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; @@ -14,10 +15,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.sql.Time; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.util.Map; import java.util.Random; import java.util.UUID; @@ -29,7 +26,7 @@ public class DataTypeTests extends JdbcIntegrationTest { private static final Logger log = LoggerFactory.getLogger(DataTypeTests.class); - @BeforeTest + @BeforeClass public void setUp() throws SQLException { DriverManager.registerDriver(new Driver()); } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcIntegrationTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcIntegrationTest.java index b47a3da1b..ab0cfaa12 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcIntegrationTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcIntegrationTest.java @@ -9,6 +9,7 @@ import java.sql.Connection; import java.sql.SQLException; +import java.sql.Statement; import java.util.Properties; public abstract class JdbcIntegrationTest extends BaseIntegrationTest { @@ -35,4 +36,15 @@ public Connection getJdbcConnection() throws SQLException { protected static String getDatabase() { return ClickHouseServerForTest.isCloud() ? ClickHouseServerForTest.getDatabase() : "default"; } + + protected boolean runQuery(String query) { + try (Connection connection = getJdbcConnection()) { + try (Statement stmt = connection.createStatement()) { + return stmt.execute(query); + } + } catch (SQLException e) { + LOGGER.error("Failed to run query: {}", query, e); + return false; + } + } } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java index 55e761f99..cf20fb819 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -324,5 +324,4 @@ public void testExecuteQueryTimeout() throws Exception { } } } - }