diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 5a3909c77..cdd11fdf4 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -96,5 +96,5 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | mvn --batch-mode -DclickhouseVersion=$PREFERRED_LTS_VERSION \ - -Panalysis verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar + -Panalysis org.sonarsource.scanner.maven:sonar-maven-plugin:sonar verify continue-on-error: true diff --git a/clickhouse-jdbc/pom.xml b/clickhouse-jdbc/pom.xml index a2abcced9..f0fe220de 100644 --- a/clickhouse-jdbc/pom.xml +++ b/clickhouse-jdbc/pom.xml @@ -20,6 +20,7 @@ 4.1.4 JDBC 4.2 + ${project.basedir}/target/site/jacoco-aggregate/jacoco.xml clickhouse-jdbc-bin com.clickhouse.jdbc.Main diff --git a/client-v2/pom.xml b/client-v2/pom.xml index ee94812f9..fe8e10cc9 100644 --- a/client-v2/pom.xml +++ b/client-v2/pom.xml @@ -45,6 +45,7 @@ slf4j-api ${slf4j.version} + org.apache.httpcomponents.client5 httpclient5 diff --git a/client-v2/src/main/java/com/clickhouse/client/api/metadata/TableSchema.java b/client-v2/src/main/java/com/clickhouse/client/api/metadata/TableSchema.java index f902ef521..8a1589532 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/metadata/TableSchema.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/metadata/TableSchema.java @@ -4,10 +4,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/jdbc-v2/pom.xml b/jdbc-v2/pom.xml index 2adaded36..307612d6a 100644 --- a/jdbc-v2/pom.xml +++ b/jdbc-v2/pom.xml @@ -20,7 +20,6 @@ 4.1.4 JDBC 4.2 - ${project.groupId}.shaded 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 a534b5556..18ec3006c 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/PreparedStatementImpl.java @@ -1,9 +1,11 @@ package com.clickhouse.jdbc; import com.clickhouse.client.api.metadata.TableSchema; -import com.clickhouse.data.ClickHouseColumn; import com.clickhouse.data.Tuple; import com.clickhouse.jdbc.internal.ExceptionUtils; +import com.clickhouse.jdbc.internal.JdbcUtils; +import com.clickhouse.jdbc.metadata.ParameterMetaDataImpl; +import com.clickhouse.jdbc.metadata.ResultSetMetaDataImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,7 +30,6 @@ import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLType; import java.sql.SQLXML; -import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; @@ -45,6 +46,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -64,9 +66,12 @@ public class PreparedStatementImpl extends StatementImpl implements PreparedStat String [] valueSegments; Object [] parameters; String insertIntoSQL; - StatementType statementType; + private final ParameterMetaData parameterMetaData; + + private ResultSetMetaData resultSetMetaData = null; + public PreparedStatementImpl(ConnectionImpl connection, String sql) throws SQLException { super(connection); this.originalSql = sql.trim(); @@ -74,7 +79,7 @@ public PreparedStatementImpl(ConnectionImpl connection, String sql) throws SQLEx this.sqlSegments = splitStatement(originalSql); this.statementType = parseStatementType(originalSql); - if (statementType == StatementType.INSERT) { + if (this.statementType == StatementType.INSERT) { insertIntoSQL = originalSql.substring(0, originalSql.indexOf("VALUES") + 6); valueSegments = originalSql.substring(originalSql.indexOf("VALUES") + 6).split("\\?"); } @@ -82,6 +87,7 @@ public PreparedStatementImpl(ConnectionImpl connection, String sql) throws SQLEx //Create an array of objects to store the parameters this.parameters = new Object[sqlSegments.length - 1]; this.defaultCalendar = connection.defaultCalendar; + this.parameterMetaData = new ParameterMetaDataImpl(this.parameters.length); } private String compileSql(String [] segments) { @@ -303,7 +309,32 @@ public void setArray(int parameterIndex, Array x) throws SQLException { @Override public ResultSetMetaData getMetaData() throws SQLException { checkClosed(); - return null; + + if (resultSetMetaData == null && currentResultSet == null) { + // before execution + if (statementType == StatementType.SELECT) { + try { + // Replace '?' with NULL to make SQL valid for DESCRIBE + String sql = JdbcUtils.replaceQuestionMarks(originalSql, JdbcUtils.NULL); + TableSchema tSchema = connection.getClient().getTableSchemaFromQuery(sql); + resultSetMetaData = new ResultSetMetaDataImpl(tSchema.getColumns(), + connection.getSchema(), connection.getCatalog(), + tSchema.getTableName(), JdbcUtils.DATA_TYPE_CLASS_MAP); + } catch (Exception e) { + LOG.warn("Failed to get schema for statement '{}'", originalSql); + } + } + + if (resultSetMetaData == null) { + resultSetMetaData = new ResultSetMetaDataImpl(Collections.emptyList(), + connection.getSchema(), connection.getCatalog(), + "", JdbcUtils.DATA_TYPE_CLASS_MAP); + } + } else if (currentResultSet != null) { + resultSetMetaData = currentResultSet.getMetaData(); + } + + return resultSetMetaData; } @Override @@ -350,10 +381,18 @@ public void setURL(int parameterIndex, URL x) throws SQLException { parameters[parameterIndex - 1] = encodeObject(x); } + /** + * Returned metadata has only minimal information about parameters. Currently only their count. + * Current implementation do not parse SQL to detect type of each parameter. + * + * @see ParameterMetaDataImpl + * @return {@link ParameterMetaDataImpl} + * @throws SQLException if the statement is close + */ @Override public ParameterMetaData getParameterMetaData() throws SQLException { checkClosed(); - return null; + return parameterMetaData; } @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 41aa8c1fb..535f9a495 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java @@ -1,5 +1,17 @@ package com.clickhouse.jdbc; +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.data.ClickHouseColumn; +import com.clickhouse.data.ClickHouseDataType; +import com.clickhouse.jdbc.internal.ExceptionUtils; +import com.clickhouse.jdbc.internal.JdbcUtils; +import com.clickhouse.jdbc.metadata.ResultSetMetaDataImpl; +import com.clickhouse.jdbc.types.Array; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.Reader; @@ -7,27 +19,31 @@ import java.math.BigDecimal; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.sql.*; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +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.SQLWarning; +import java.sql.SQLXML; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; import java.time.ZonedDateTime; import java.util.Calendar; -import java.util.GregorianCalendar; +import java.util.Collections; import java.util.List; import java.util.Map; -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.data.ClickHouseColumn; -import com.clickhouse.data.ClickHouseDataType; -import com.clickhouse.jdbc.internal.ExceptionUtils; -import com.clickhouse.jdbc.internal.JdbcUtils; -import com.clickhouse.jdbc.types.Array; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ResultSetImpl implements ResultSet, JdbcV2Wrapper { private static final Logger log = LoggerFactory.getLogger(ResultSetImpl.class); - private final ResultSetMetaData metaData; + private ResultSetMetaData metaData; protected ClickHouseBinaryFormatReader reader; private QueryResponse response; private boolean closed; @@ -39,7 +55,12 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic this.parentStatement = parentStatement; this.response = response; this.reader = reader; - this.metaData = new com.clickhouse.jdbc.metadata.ResultSetMetaData(this); + TableSchema tableMetadata = reader.getSchema(); + + // Result set contains columns from one database (there is a special table engine 'Merge' to do cross DB queries) + this.metaData = new ResultSetMetaDataImpl(tableMetadata + .getColumns(), response.getSettings().getDatabase(), "", tableMetadata.getTableName(), + JdbcUtils.DATA_TYPE_CLASS_MAP); this.closed = false; this.wasNull = false; this.defaultCalendar = parentStatement.connection.defaultCalendar; @@ -49,7 +70,7 @@ protected ResultSetImpl(ResultSetImpl resultSet) { this.parentStatement = resultSet.parentStatement; this.response = resultSet.response; this.reader = resultSet.reader; - this.metaData = new com.clickhouse.jdbc.metadata.ResultSetMetaData(this); + this.metaData = resultSet.metaData; this.closed = false; this.wasNull = false; this.defaultCalendar = parentStatement.connection.defaultCalendar; @@ -431,6 +452,10 @@ public ResultSetMetaData getMetaData() throws SQLException { return metaData; } + protected void setMetaData(ResultSetMetaDataImpl metaData) { + this.metaData = metaData; + } + @Override public Object getObject(int columnIndex) throws SQLException { return getObject(columnIndex, JdbcUtils.convertToJavaClass(getSchema().getColumnByIndex(columnIndex).getDataType())); 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 fe565514d..6fb43b69f 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/StatementImpl.java @@ -19,7 +19,6 @@ import java.sql.SQLWarning; import java.sql.Statement; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; @@ -60,7 +59,7 @@ protected enum StatementType { SELECT, INSERT, DELETE, UPDATE, CREATE, DROP, ALTER, TRUNCATE, USE, SHOW, DESCRIBE, EXPLAIN, SET, KILL, OTHER, INSERT_INTO_SELECT } - protected static StatementType parseStatementType(String sql) { + public static StatementType parseStatementType(String sql) { if (sql == null) { return StatementType.OTHER; } @@ -184,6 +183,7 @@ public ResultSetImpl executeQuery(String sql, QuerySettings settings) throws SQL mergedSettings.setQueryId(lastQueryId); } LOG.debug("Query ID: {}", lastQueryId); + mergedSettings.setDatabase(connection.getSchema()); try { lastSql = parseJdbcEscapeSyntax(sql); diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ClientInfoProperties.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ClientInfoProperties.java index 3c6913b64..ada5139d6 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ClientInfoProperties.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/ClientInfoProperties.java @@ -1,37 +1,37 @@ -package com.clickhouse.jdbc.internal; - -public enum ClientInfoProperties { - - APPLICATION_NAME("ApplicationName", 255, "", "Client application name."), - ; - - private String key; - private int maxValue; - - private String defaultValue; - - private String description; - - ClientInfoProperties(String key, int maxValue, String defaultValue, String description) { - this.key = key; - this.maxValue = maxValue; - this.defaultValue = defaultValue; - this.description = description; - } - - public String getKey() { - return key; - } - - public int getMaxValue() { - return maxValue; - } - - public String getDefaultValue() { - return defaultValue; - } - - public String getDescription() { - return description; - } -} +package com.clickhouse.jdbc.internal; + +public enum ClientInfoProperties { + + APPLICATION_NAME("ApplicationName", 255, "", "Client application name."), + ; + + private String key; + private int maxValue; + + private String defaultValue; + + private String description; + + ClientInfoProperties(String key, int maxValue, String defaultValue, String description) { + this.key = key; + this.maxValue = maxValue; + this.defaultValue = defaultValue; + this.description = description; + } + + public String getKey() { + return key; + } + + public int getMaxValue() { + return maxValue; + } + + public String getDefaultValue() { + return defaultValue; + } + + public String getDescription() { + return description; + } +} diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/DriverProperties.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/DriverProperties.java index cf7721eeb..c2d82f246 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/DriverProperties.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/DriverProperties.java @@ -1,53 +1,53 @@ -package com.clickhouse.jdbc.internal; - -import java.util.Collections; -import java.util.List; - -/** - * JDBC driver specific properties. Should not include any of ClientConfigProperties. - * Processing logic should be the follows - * 1. If property is among DriverProperties then Driver handles it specially and will not pass to a client - * 2. If property is not among DriverProperties then it is passed to a client - */ -public enum DriverProperties { - - IGNORE_UNSUPPORTED_VALUES("jdbc_ignore_unsupported_values", ""), - SCHEMA_TERM("jdbc_schema_term", ""), - /** - * Indicates if driver should create a secure connection over SSL/TLS - */ - SECURE_CONNECTION("ssl", "false"), - - /** - * query settings to be passed along with query operation. - * {@see com.clickhouse.client.api.query.QuerySettings} - */ - DEFAULT_QUERY_SETTINGS("default_query_settings", null); - private final String key; - - private final String defaultValue; - - private final List choices; - - DriverProperties(String key, String defaultValue) { - this(key, defaultValue, Collections.emptyList()); - } - - DriverProperties(String key, String defaultValue, List choices) { - this.key = key; - this.defaultValue = defaultValue; - this.choices = choices; - } - - public String getKey() { - return key; - } - - public String getDefaultValue() { - return defaultValue; - } - - public List getChoices() { - return choices; - } -} +package com.clickhouse.jdbc.internal; + +import java.util.Collections; +import java.util.List; + +/** + * JDBC driver specific properties. Should not include any of ClientConfigProperties. + * Processing logic should be the follows + * 1. If property is among DriverProperties then Driver handles it specially and will not pass to a client + * 2. If property is not among DriverProperties then it is passed to a client + */ +public enum DriverProperties { + + IGNORE_UNSUPPORTED_VALUES("jdbc_ignore_unsupported_values", ""), + SCHEMA_TERM("jdbc_schema_term", ""), + /** + * Indicates if driver should create a secure connection over SSL/TLS + */ + SECURE_CONNECTION("ssl", "false"), + + /** + * query settings to be passed along with query operation. + * {@see com.clickhouse.client.api.query.QuerySettings} + */ + DEFAULT_QUERY_SETTINGS("default_query_settings", null); + private final String key; + + private final String defaultValue; + + private final List choices; + + DriverProperties(String key, String defaultValue) { + this(key, defaultValue, Collections.emptyList()); + } + + DriverProperties(String key, String defaultValue, List choices) { + this.key = key; + this.defaultValue = defaultValue; + this.choices = choices; + } + + public String getKey() { + return key; + } + + public String getDefaultValue() { + return defaultValue; + } + + public List getChoices() { + return choices; + } +} diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java index 01662a8fd..754803fc1 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java @@ -3,6 +3,7 @@ import com.clickhouse.client.api.data_formats.internal.BinaryStreamReader; import com.clickhouse.data.ClickHouseDataType; import com.clickhouse.jdbc.types.Array; +import com.google.common.collect.ImmutableMap; import java.net.Inet4Address; import java.net.Inet6Address; @@ -22,10 +23,12 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class JdbcUtils { //Define a map to store the mapping between ClickHouse data types and SQL data types - private static final Map CLICKHOUSE_TO_SQL_TYPE_MAP = generateTypeMap(); + public static final Map CLICKHOUSE_TO_SQL_TYPE_MAP = generateTypeMap(); public static final Map CLICKHOUSE_TYPE_NAME_TO_SQL_TYPE_MAP = Collections.unmodifiableMap(generateTypeMap().entrySet() .stream().collect( @@ -70,10 +73,10 @@ private static Map generateTypeMap() { map.put(ClickHouseDataType.LineString, JDBCType.OTHER); map.put(ClickHouseDataType.MultiPolygon, JDBCType.OTHER); map.put(ClickHouseDataType.MultiLineString, JDBCType.OTHER); - return map; + return ImmutableMap.copyOf(map); } - private static final Map> SQL_TYPE_TO_CLASS_MAP = generateClassMap(); + public static final Map> SQL_TYPE_TO_CLASS_MAP = generateClassMap(); private static Map> generateClassMap() { Map> map = new HashMap<>(); map.put(JDBCType.CHAR, String.class); @@ -110,6 +113,16 @@ private static Map> generateClassMap() { map.put(JDBCType.LONGNVARCHAR, String.class); map.put(JDBCType.NCLOB, java.sql.NClob.class); map.put(JDBCType.SQLXML, java.sql.SQLXML.class); + return ImmutableMap.copyOf(map); + } + + public static final Map> DATA_TYPE_CLASS_MAP = getDataTypeClassMap(); + private static Map> getDataTypeClassMap() { + Map> map = new HashMap<>(); + for (Map.Entry e : CLICKHOUSE_TO_SQL_TYPE_MAP.entrySet()) { + map.put(e.getKey(), SQL_TYPE_TO_CLASS_MAP.get(e.getValue())); + } + return map; } @@ -122,7 +135,7 @@ public static SQLType convertToSqlType(ClickHouseDataType clickhouseType) { } public static Class convertToJavaClass(ClickHouseDataType clickhouseType) { - return SQL_TYPE_TO_CLASS_MAP.get(convertToSqlType(clickhouseType)); + return DATA_TYPE_CLASS_MAP.get(clickhouseType); } public static List tokenizeSQL(String sql) { @@ -271,4 +284,26 @@ public static String escapeQuotes(String str) { .replace("'", "\\'") .replace("\"", "\\\""); } + + public static final String NULL = "NULL"; + + private static final Pattern REPLACE_Q_MARK_PATTERN = Pattern.compile("(\"[^\"]*\"|`[^`]*`|'[^']*')|(\\?)"); + + public static String replaceQuestionMarks(String sql, String replacement) { + Matcher matcher = REPLACE_Q_MARK_PATTERN.matcher(sql); + + StringBuilder result = new StringBuilder(); + + while (matcher.find()) { + if (matcher.group(1) != null) { + // Quoted string — keep as-is + matcher.appendReplacement(result, Matcher.quoteReplacement(matcher.group(1))); + } else if (matcher.group(2) != null) { + // Question mark outside quotes — replace it + matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); + } + } + matcher.appendTail(result); + return result.toString(); + } } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/MetadataResultSet.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/MetadataResultSet.java index f3403c6a8..77ee23a4e 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/MetadataResultSet.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/MetadataResultSet.java @@ -3,6 +3,7 @@ import com.clickhouse.client.api.metadata.TableSchema; import com.clickhouse.data.ClickHouseColumn; import com.clickhouse.jdbc.ResultSetImpl; +import com.clickhouse.jdbc.metadata.ResultSetMetaDataImpl; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -22,6 +23,9 @@ public class MetadataResultSet extends ResultSetImpl { public MetadataResultSet(ResultSetImpl resultSet) throws SQLException { super(resultSet); this.overridingSchemaAdaptor = new OverridingSchemaAdaptor(resultSet.getSchema()); + this.setMetaData(new ResultSetMetaDataImpl(overridingSchemaAdaptor.getColumns(), + overridingSchemaAdaptor.getDatabaseName(), "", + overridingSchemaAdaptor.getTableName(), JdbcUtils.DATA_TYPE_CLASS_MAP)); ResultSetMetaData metaData = getMetaData(); int count = metaData.getColumnCount(); cachedColumnLabels = new String[count]; diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaData.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaData.java deleted file mode 100644 index 912aaf501..000000000 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaData.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.clickhouse.jdbc.metadata; - -import com.clickhouse.data.ClickHouseColumn; -import com.clickhouse.jdbc.JdbcV2Wrapper; -import com.clickhouse.jdbc.internal.JdbcUtils; -import com.clickhouse.jdbc.internal.ExceptionUtils; - -import java.sql.SQLException; -import java.util.List; - -public class ParameterMetaData implements java.sql.ParameterMetaData, JdbcV2Wrapper { - private final List params; - - protected ParameterMetaData(List params) throws SQLException { - if (params == null) { - throw ExceptionUtils.toSqlState(new IllegalArgumentException("Parameters array cannot be null.")); - } - - this.params = params; - } - - protected ClickHouseColumn getParam(int param) throws SQLException { - if (param < 1 || param > params.size()) { - throw new SQLException("Parameter index out of range: " + param, ExceptionUtils.SQL_STATE_CLIENT_ERROR); - } - - return params.get(param - 1); - } - - @Override - public int getParameterCount() throws SQLException { - try { - return params.size(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public int isNullable(int param) throws SQLException { - try { - return getParam(param).isNullable() ? parameterNullable : parameterNoNulls; - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public boolean isSigned(int param) throws SQLException { - try{ - return getParam(param).getDataType().isSigned(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public int getPrecision(int param) throws SQLException { - try { - return getParam(param).getPrecision(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public int getScale(int param) throws SQLException { - try { - return getParam(param).getScale(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public int getParameterType(int param) throws SQLException { - //TODO: Should we implement .getSQLType()? - try { - return JdbcUtils.convertToSqlType(getParam(param).getDataType()).getVendorTypeNumber(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public String getParameterTypeName(int param) throws SQLException { - try { - return getParam(param).getDataType().name(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public String getParameterClassName(int param) throws SQLException { - //TODO: Should we implement .getClassName()? - try { - return getParam(param).getDataType().getObjectClass().getName(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } - } - - @Override - public int getParameterMode(int param) throws SQLException { - return parameterModeIn; - } -} diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaDataImpl.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaDataImpl.java new file mode 100644 index 000000000..bca28e3ba --- /dev/null +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ParameterMetaDataImpl.java @@ -0,0 +1,132 @@ +package com.clickhouse.jdbc.metadata; + +import com.clickhouse.jdbc.JdbcV2Wrapper; + +import java.sql.ParameterMetaData; +import java.sql.SQLException; +import java.sql.Types; + +/** + * Implement ParameterMetaData interface and provides minimal information about parameters. + * This class will return only actual number of parameters. + * This class cannot be used to determine exact datatype for a parameter. + */ +public class ParameterMetaDataImpl implements ParameterMetaData, JdbcV2Wrapper { + + private static final int FAIL_SAFE_PRECISION = 0; + + private static final int FAIL_SAFE_SCALE = 0; + + private final int paramCount; + + public ParameterMetaDataImpl(int paramCount) { + this.paramCount = paramCount; + } + + @Override + public int getParameterCount() { + return paramCount; + } + + private void checkParamIndex(int index) throws SQLException { + if (index > paramCount || index < 1) { + throw new SQLException("Parameter index out of range. " + (paramCount == 0 ? "There are no parameters" : "[1," + paramCount + "]")); + } + } + + /** + * Always returns {@code ParameterMetaData.parameterNullableUnknown}. + * + * @param param parameter index starting from 1 + * @return ParameterMetaData.parameterNullableUnknown + */ + @Override + public int isNullable(int param) throws SQLException { + checkParamIndex(param); + return ParameterMetaData.parameterNullableUnknown; + } + + /** + * Always returns {@code false}. + * + * @param param parameter index starting from 1 + * @return false + */ + @Override + public boolean isSigned(int param) throws SQLException { + checkParamIndex(param); + return false; + } + + /** + * Always returns 0. + * + * @param param parameter index starting from 1 + * @return 0 + */ + @Override + public int getPrecision(int param) throws SQLException { + checkParamIndex(param); + return FAIL_SAFE_PRECISION; + } + + /** + * Always returns 0. + * + * @param param parameter index starting from 1 + * @return 0 + */ + @Override + public int getScale(int param) throws SQLException { + checkParamIndex(param); + return FAIL_SAFE_SCALE; + } + + /** + * Always returns {@code Types.OTHER}. + * + * @param param parameter index starting from 1 + * @return {@code Types.OTHER} + */ + @Override + public int getParameterType(int param) throws SQLException { + checkParamIndex(param); + return Types.OTHER; + } + + /** + * Always returns "UNKNOWN". + * + * @param param parameter index starting from 1 + * @return String {@code "UNKNOWN"} + */ + @Override + public String getParameterTypeName(int param) throws SQLException { + checkParamIndex(param); + return "UNKNOWN"; + } + + /** + * Always returns {@code Object.class.getName()}. + * + * @param param the first parameter is 1, the second is 2, ... + * @return String {@code Object.class.getName()} + */ + @Override + public String getParameterClassName(int param) throws SQLException { + checkParamIndex(param); + return Object.class.getName(); + } + + /** + * Always return {@code java.sql.ParameterMetaData#parameterModeIn}. + * + * @param param parameter index starting from 1 + * @return {@code java.sql.ParameterMetaData#parameterModeIn} + */ + @Override + public int getParameterMode(int param) throws SQLException { + checkParamIndex(param); + return parameterModeIn; + } +} diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaData.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataImpl.java similarity index 74% rename from jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaData.java rename to jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataImpl.java index ab759c685..0de57b4b8 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaData.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataImpl.java @@ -1,50 +1,67 @@ package com.clickhouse.jdbc.metadata; -import java.sql.SQLException; - -import com.clickhouse.client.api.metadata.TableSchema; import com.clickhouse.data.ClickHouseColumn; +import com.clickhouse.data.ClickHouseDataType; import com.clickhouse.jdbc.JdbcV2Wrapper; -import com.clickhouse.jdbc.ResultSetImpl; -import com.clickhouse.jdbc.internal.JdbcUtils; import com.clickhouse.jdbc.internal.ExceptionUtils; +import com.clickhouse.jdbc.internal.JdbcUtils; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +public class ResultSetMetaDataImpl implements java.sql.ResultSetMetaData, JdbcV2Wrapper { + + private final List columns; -public class ResultSetMetaData implements java.sql.ResultSetMetaData, JdbcV2Wrapper { - private final ResultSetImpl resultSet; - public ResultSetMetaData(ResultSetImpl resultSet) { - this.resultSet = resultSet; + private final String schema; + + private final String catalog; + + private final String tableName; + + private final Map> typeClassMap; + + public ResultSetMetaDataImpl(List columns, String schema, String catalog, String tableName, + Map> typeClassMap) { + this.columns = columns; + this.schema = schema; + this.catalog = catalog; + this.tableName = tableName; + this.typeClassMap = typeClassMap; } private ClickHouseColumn getColumn(int column) throws SQLException { - if (column < 1 || column > getColumnCount()) { + try { + return columns.get(column - 1); + } catch (IndexOutOfBoundsException e) { throw new SQLException("Column index out of range: " + column, ExceptionUtils.SQL_STATE_CLIENT_ERROR); } - return resultSet.getSchema().getColumns().get(column - 1); } @Override public int getColumnCount() throws SQLException { - try { - TableSchema schema = resultSet.getSchema(); - return schema.getColumns().size(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } + return columns.size(); } @Override public boolean isAutoIncrement(int column) throws SQLException { - return false; + return false; // no auto-incremental types } @Override public boolean isCaseSensitive(int column) throws SQLException { - return true; + try { + // TODO: should be in sync with DatabaseMetadata + return getColumn(column).getDataType().isCaseSensitive(); + } catch (Exception e) { + throw ExceptionUtils.toSqlState(e); + } } @Override public boolean isSearchable(int column) throws SQLException { - return true; + return true; // all columns are considered as searchable } @Override @@ -95,7 +112,7 @@ public String getColumnName(int column) throws SQLException { @Override public String getSchemaName(int column) throws SQLException { - return ""; + return schema; } @Override @@ -118,16 +135,12 @@ public int getScale(int column) throws SQLException { @Override public String getTableName(int column) throws SQLException { - try { - return resultSet.getSchema().getTableName(); - } catch (Exception e) { - throw ExceptionUtils.toSqlState(e); - } + return tableName; } @Override public String getCatalogName(int column) throws SQLException { - return ""; + return catalog; } @Override @@ -165,6 +178,10 @@ public boolean isDefinitelyWritable(int column) throws SQLException { @Override public String getColumnClassName(int column) throws SQLException { - return null; + try { + return typeClassMap.getOrDefault(getColumn(column).getDataType(), Object.class).getName(); + } catch (Exception e) { + throw ExceptionUtils.toSqlState(e); + } } } diff --git a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java index d2e06b2b6..e463a8fb5 100644 --- a/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java +++ b/jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java @@ -7,7 +7,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.sql.Types; import java.util.List; import java.util.Map; 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 9346edcd6..d8901dd22 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java @@ -1,15 +1,12 @@ package com.clickhouse.jdbc; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.sql.*; import java.util.Arrays; import java.util.Base64; -import java.util.List; import java.util.Properties; import java.util.Properties; -import java.util.concurrent.TimeUnit; import java.util.UUID; import com.clickhouse.client.ClickHouseNode; @@ -18,23 +15,17 @@ import com.clickhouse.client.api.Client; import com.clickhouse.client.api.ClientConfigProperties; import com.clickhouse.client.api.ServerException; -import com.clickhouse.client.api.enums.Protocol; import com.clickhouse.client.api.internal.ServerSettings; -import com.clickhouse.client.api.query.GenericRecord; -import com.clickhouse.client.api.query.QueryResponse; import com.clickhouse.jdbc.internal.ClientInfoProperties; import com.clickhouse.jdbc.internal.DriverProperties; import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.common.ConsoleNotifier; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import org.apache.hc.core5.http.HttpStatus; -import com.clickhouse.jdbc.internal.JdbcUtils; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; import static org.testng.Assert.fail; 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 af42aada7..6b80b9d55 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcIntegrationTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/JdbcIntegrationTest.java @@ -5,7 +5,6 @@ import com.clickhouse.client.BaseIntegrationTest; import com.clickhouse.client.ClickHouseProtocol; import com.clickhouse.client.api.ClientConfigProperties; -import com.clickhouse.client.api.internal.ServerSettings; import com.clickhouse.client.api.query.GenericRecord; import com.clickhouse.logging.Logger; import com.clickhouse.logging.LoggerFactory; diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java index 7f3f61ce5..59e6bb084 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/PreparedStatementTest.java @@ -1,12 +1,13 @@ package com.clickhouse.jdbc; import org.apache.commons.lang3.RandomStringUtils; +import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Ignore; import org.testng.annotations.Test; import java.sql.Array; import java.sql.Connection; -import java.sql.JDBCType; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -23,6 +24,8 @@ import static org.testng.Assert.assertTrue; + + public class PreparedStatementTest extends JdbcIntegrationTest { @Test(groups = { "integration" }) @@ -321,6 +324,48 @@ void testInsert() throws Exception { } } + @Test(dataProvider = "testGetMetadataDataProvider") + void testGetMetadata(String sql, int colCountBeforeExecution, Object[] values, + int colCountAfterExecution) throws Exception { + String tableName = "test_get_metadata"; + runQuery("CREATE TABLE IF NOT EXISTS " + tableName + " ( a1 String, b2 Float, b3 Float ) Engine=MergeTree ORDER BY ()"); + + try (Connection conn = getJdbcConnection(); + PreparedStatement stmt = conn.prepareStatement(String.format(sql, tableName))) { + ResultSetMetaData metadataRs = stmt.getMetaData(); + assertNotNull(metadataRs); + Assert.assertEquals(metadataRs.getColumnCount(), colCountBeforeExecution); + + for (int i = 1; i <= metadataRs.getColumnCount(); i++) { + assertEquals(metadataRs.getSchemaName(i), stmt.getConnection().getSchema()); + } + + if (values != null) { + for (int i = 0; i < values.length; i++) { + stmt.setObject(i + 1, values[i]); + } + } + + stmt.execute(); + metadataRs = stmt.getMetaData(); + + assertNotNull(metadataRs); + assertEquals(metadataRs.getColumnCount(), colCountAfterExecution); + for (int i = 1; i <= metadataRs.getColumnCount(); i++) { + assertEquals(metadataRs.getSchemaName(i), stmt.getConnection().getSchema()); + } + } + } + + @DataProvider(name = "testGetMetadataDataProvider") + static Object[][] testGetMetadataDataProvider() { + return new Object[][] { + {"INSERT INTO `%s` VALUES (?, ?, ?)", 0, new Object[]{"test", 0.3, 0.4}, 0}, + {"SELECT * FROM `%s`", 3, null, 3}, + {"SHOW TABLES", 0, null, 1} + }; + } + @Test(groups = { "integration" }) void testMetabaseBug01() throws Exception { try (Connection conn = getJdbcConnection()) { @@ -456,7 +501,10 @@ void testMetabaseBug01() throws Exception { void testStatementSplit() throws Exception { try (Connection conn = getJdbcConnection()) { try (Statement stmt = conn.createStatement()) { - stmt.execute("CREATE TABLE `with_complex_id` (`v?``1` Int32, \"v?\"\"2\" Int32,`v?\\`3` Int32, \"v?\\\"4\" Int32) ENGINE Memory;"); + stmt.execute("CREATE TABLE IF NOT EXISTS `with_complex_id` (`v?``1` Int32, " + + "\"v?\"\"2\" Int32,`v?\\`3` Int32, \"v?\\\"4\" Int32) ENGINE MergeTree ORDER BY ();"); + stmt.execute("CREATE TABLE IF NOT EXISTS `test_stmt_split2` (v1 Int32, v2 String) ENGINE MergeTree ORDER BY (); "); + stmt.execute("INSERT INTO `test_stmt_split2` VALUES (1, 'abc'), (2, '?'), (3, '?')"); } String insertQuery = "-- line comment1 ?\n" + "# line comment2 ?\n" @@ -489,6 +537,20 @@ void testStatementSplit() throws Exception { assertEquals(rs.getString(7), "test string3 ?\\"); } } + + try (PreparedStatement stmt = conn.prepareStatement("SELECT v1 FROM `test_stmt_split2` WHERE v1 > ? AND v2 = '?'")) { + stmt.setInt(1, 2); + try (ResultSet rs = stmt.executeQuery()) { + int count = 0; + while (rs.next()) { + count++; + assertEquals(rs.getInt(1), 3); + } + + Assert.assertEquals(count, 1); + } + } + } } } 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 8b5cca1f9..7396f037c 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/StatementTest.java @@ -1,9 +1,7 @@ package com.clickhouse.jdbc; import com.clickhouse.client.api.ClientConfigProperties; -import com.clickhouse.client.api.ClientException; import com.clickhouse.client.api.query.GenericRecord; -import com.clickhouse.client.api.query.QuerySettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; @@ -21,13 +19,9 @@ import java.sql.Statement; import java.time.LocalDate; import java.util.Arrays; -import java.util.GregorianCalendar; import java.util.List; import java.util.Properties; -import java.util.TimeZone; -import java.util.UUID; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -35,7 +29,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; public class StatementTest extends JdbcIntegrationTest { diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcConfigurationTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcConfigurationTest.java index 125ce0f85..771fed914 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcConfigurationTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcConfigurationTest.java @@ -7,11 +7,8 @@ import java.sql.DriverPropertyInfo; import java.util.Arrays; -import java.util.Collections; import java.util.Map; import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.testng.Assert.assertEquals; diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcUtilsTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcUtilsTest.java index e83e3f30c..009bff49d 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcUtilsTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/internal/JdbcUtilsTest.java @@ -1,5 +1,6 @@ package com.clickhouse.jdbc.internal; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.List; @@ -57,4 +58,20 @@ public void testEscapeQuotes() { assertEquals(JdbcUtils.escapeQuotes(inStr[i]), outStr[i]); } } + + @Test(dataProvider = "testReplaceQuestionMark_dataProvider") + public void testReplaceQuestionMark(String sql, String result) { + assertEquals(JdbcUtils.replaceQuestionMarks(sql, "NULL"), result); + } + + @DataProvider(name = "testReplaceQuestionMark_dataProvider") + public static Object[][] testReplaceQuestionMark_dataProvider() { + return new Object[][] { + {"", ""}, + {" ", " "}, + {"SELECT * FROM t WHERE a = '?'", "SELECT * FROM t WHERE a = '?'"}, + {"SELECT `v2?` FROM t WHERE `v1?` = ?", "SELECT `v2?` FROM t WHERE `v1?` = NULL"}, + {"INSERT INTO \"t2?\" VALUES (?, ?, 'some_?', ?)", "INSERT INTO \"t2?\" VALUES (NULL, NULL, 'some_?', NULL)"} + }; + } } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/DatabaseMetaDataTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/DatabaseMetaDataTest.java index fcd955c39..066792cd8 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/DatabaseMetaDataTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/DatabaseMetaDataTest.java @@ -1,7 +1,6 @@ package com.clickhouse.jdbc.metadata; import com.clickhouse.client.ClickHouseServerForTest; -import com.clickhouse.client.api.command.CommandResponse; import com.clickhouse.data.ClickHouseDataType; import com.clickhouse.data.ClickHouseVersion; import com.clickhouse.jdbc.JdbcIntegrationTest; @@ -9,7 +8,6 @@ import com.clickhouse.jdbc.internal.DriverProperties; import com.clickhouse.jdbc.internal.JdbcUtils; import org.testng.Assert; -import org.testng.annotations.Ignore; import org.testng.annotations.Test; import java.sql.Connection; @@ -19,10 +17,8 @@ import java.sql.DatabaseMetaData; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Properties; -import java.util.Set; import static org.testng.Assert.*; @@ -173,7 +169,7 @@ public void testGetCatalogs() throws Exception { DatabaseMetaData dbmd = conn.getMetaData(); ResultSet rs = dbmd.getCatalogs(); assertFalse(rs.next()); - ResultSetMetaDataTest.assertColumnNames(rs, "TABLE_CAT"); + ResultSetMetaDataImplTest.assertColumnNames(rs, "TABLE_CAT"); } } diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ParameterMetaDataImplTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ParameterMetaDataImplTest.java new file mode 100644 index 000000000..d039c8b76 --- /dev/null +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ParameterMetaDataImplTest.java @@ -0,0 +1,84 @@ +package com.clickhouse.jdbc.metadata; + +import org.testng.annotations.Test; + +import java.sql.SQLException; +import java.sql.Types; + +import static org.testng.Assert.*; + + +public class ParameterMetaDataImplTest { + @Test(groups = {"integration"}) + public void testGetParameterCount() { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(0); + assertEquals(metaData.getParameterCount(), 0); + + metaData = new ParameterMetaDataImpl(1); + assertEquals(metaData.getParameterCount(), 1); + } + + @Test(groups = {"integration"}) + public void testIsNullable() throws SQLException { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(1); + assertEquals(metaData.isNullable(1), ParameterMetaDataImpl.parameterNullableUnknown); + + assertThrows(() -> metaData.isNullable(0)); + assertThrows(() -> metaData.isNullable(2)); + } + + @Test(groups = {"integration"}) + public void testIsSigned() throws SQLException { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(1); + assertFalse(metaData.isSigned(1)); + + assertThrows(() -> metaData.isSigned(0)); + assertThrows(() -> metaData.isSigned(2)); + } + + @Test(groups = {"integration"}) + public void testGetPrecisionAndScale() throws SQLException { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(1); + assertEquals(metaData.getPrecision(1), 0); + assertEquals(metaData.getScale(1), 0); + + assertThrows(() -> metaData.getPrecision(0)); + assertThrows(() -> metaData.getPrecision(2)); + } + + @Test(groups = {"integration"}) + public void testGetParameterType() throws SQLException { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(1); + assertEquals(metaData.getParameterType(1), Types.OTHER); + + assertThrows(() -> metaData.getParameterType(0)); + assertThrows(() -> metaData.getParameterType(2)); + } + + @Test(groups = {"integration"}) + public void testGetParameterTypeName() throws SQLException { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(1); + assertEquals(metaData.getParameterTypeName(1), "UNKNOWN"); + + assertThrows(() -> metaData.getParameterTypeName(0)); + assertThrows(() -> metaData.getParameterTypeName(2)); + } + + @Test(groups = {"integration"}) + public void testGetParameterClassName() throws SQLException { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(1); + assertEquals(metaData.getParameterClassName(1), Object.class.getName()); + + assertThrows(() -> metaData.getParameterClassName(0)); + assertThrows(() -> metaData.getParameterClassName(2)); + } + + @Test(groups = {"integration"}) + public void testGetParameterMode() throws SQLException { + ParameterMetaDataImpl metaData = new ParameterMetaDataImpl(1); + assertEquals(metaData.getParameterMode(1), ParameterMetaDataImpl.parameterModeIn); + + assertThrows(() -> metaData.getParameterMode(0)); + assertThrows(() -> metaData.getParameterMode(2)); + } +} diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ParameterMetaDataTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ParameterMetaDataTest.java deleted file mode 100644 index 0420650b6..000000000 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ParameterMetaDataTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.clickhouse.jdbc.metadata; - -import com.clickhouse.data.ClickHouseColumn; -import com.clickhouse.data.ClickHouseDataType; -import com.clickhouse.jdbc.JdbcIntegrationTest; -import org.testng.annotations.Test; - -import java.sql.SQLException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - - -public class ParameterMetaDataTest extends JdbcIntegrationTest { - @Test(groups = { "integration" }) - public void testGetParameterCount() throws SQLException { - ParameterMetaData metaData = new ParameterMetaData(Collections.emptyList()); - assertEquals(metaData.getParameterCount(), 0); - - metaData = new ParameterMetaData(List.of(ClickHouseColumn.of("param1", ClickHouseDataType.Int32, false))); - assertEquals(metaData.getParameterCount(), 1); - } - - @Test(groups = { "integration" }) - public void testIsNullable() throws SQLException { - ClickHouseColumn column = ClickHouseColumn.of("param1", ClickHouseDataType.Int32, true); - ParameterMetaData metaData = new ParameterMetaData(Collections.singletonList(column)); - assertEquals(metaData.isNullable(1), ParameterMetaData.parameterNullable); - } - - @Test(groups = { "integration" }) - public void testIsSigned() throws SQLException { - ClickHouseColumn column = ClickHouseColumn.of("param1", ClickHouseDataType.Int32, false); - ParameterMetaData metaData = new ParameterMetaData(Collections.singletonList(column)); - assertTrue(metaData.isSigned(1)); - - column = ClickHouseColumn.of("param2", ClickHouseDataType.UInt32, false); - metaData = new ParameterMetaData(Collections.singletonList(column)); - assertFalse(metaData.isSigned(1)); - } - - @Test(groups = { "integration" }) - public void testGetPrecisionAndScale() throws SQLException { - ClickHouseColumn column = ClickHouseColumn.of("param1", ClickHouseDataType.Int32, false, 10, 5); - ParameterMetaData metaData = new ParameterMetaData(Collections.singletonList(column)); - assertEquals(metaData.getPrecision(1), 10); - assertEquals(metaData.getScale(1), 5); - } - - @Test(groups = { "integration" }) - public void testGetParameterType() throws SQLException { - ClickHouseColumn column = ClickHouseColumn.of("param1", ClickHouseDataType.Int32, false); - ParameterMetaData metaData = new ParameterMetaData(Collections.singletonList(column)); - assertEquals(metaData.getParameterType(1), java.sql.Types.INTEGER); - } - - @Test(groups = { "integration" }) - public void testGetParameterTypeName() throws SQLException { - ClickHouseColumn column = ClickHouseColumn.of("param1", ClickHouseDataType.Int32, false); - ParameterMetaData metaData = new ParameterMetaData(Collections.singletonList(column)); - assertEquals(metaData.getParameterTypeName(1), "Int32"); - } - - @Test(groups = { "integration" }) - public void testGetParameterClassName() throws SQLException { - ClickHouseColumn column = ClickHouseColumn.of("param1", ClickHouseDataType.Int32, false); - ParameterMetaData metaData = new ParameterMetaData(Collections.singletonList(column)); - assertEquals(metaData.getParameterClassName(1), "java.lang.Integer"); - } - - @Test(groups = { "integration" }) - public void testGetParameterMode() throws SQLException { - ClickHouseColumn column = ClickHouseColumn.of("param1", ClickHouseDataType.Int32, false); - ParameterMetaData metaData = new ParameterMetaData(Collections.singletonList(column)); - assertEquals(metaData.getParameterMode(1), ParameterMetaData.parameterModeIn); - } -} diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataImplTest.java similarity index 85% rename from jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataTest.java rename to jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataImplTest.java index 0b6c989a6..c722e85d3 100644 --- a/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataTest.java +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/metadata/ResultSetMetaDataImplTest.java @@ -3,24 +3,28 @@ import com.clickhouse.jdbc.JdbcIntegrationTest; import org.testng.annotations.Test; +import java.math.BigInteger; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Statement; import java.sql.Types; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertTrue; -public class ResultSetMetaDataTest extends JdbcIntegrationTest { +public class ResultSetMetaDataImplTest extends JdbcIntegrationTest { @Test(groups = { "integration" }) public void testGetColumnCount() throws Exception { try (Connection conn = getJdbcConnection()) { try (Statement stmt = conn.createStatement()) { ResultSet rs = stmt.executeQuery("SELECT 1 AS a, 2 AS b, 3 AS c"); ResultSetMetaData rsmd = rs.getMetaData(); - assertEquals(3, rsmd.getColumnCount()); + assertEquals(rsmd.getColumnCount(), 3); } } } @@ -31,7 +35,7 @@ public void testGetColumnLabel() throws Exception { try (Statement stmt = conn.createStatement()) { ResultSet rs = stmt.executeQuery("SELECT 1 AS a"); ResultSetMetaData rsmd = rs.getMetaData(); - assertEquals("a", rsmd.getColumnLabel(1)); + assertEquals(rsmd.getColumnLabel(1), "a"); } } } @@ -42,7 +46,7 @@ public void testGetColumnName() throws Exception { try (Statement stmt = conn.createStatement()) { ResultSet rs = stmt.executeQuery("SELECT 1 AS a"); ResultSetMetaData rsmd = rs.getMetaData(); - assertEquals("a", rsmd.getColumnName(1)); + assertEquals(rsmd.getColumnName(1), "a"); } } } @@ -54,9 +58,21 @@ public void testGetColumnTypeIntegers() throws Exception { ResultSet rs = stmt.executeQuery("SELECT toInt8(1), toInt16(1), toInt32(1), toInt64(1) AS a"); ResultSetMetaData rsmd = rs.getMetaData(); assertEquals(rsmd.getColumnType(1), Types.TINYINT); + assertEquals(rsmd.getColumnClassName(1), Integer.class.getName()); assertEquals(rsmd.getColumnType(2), Types.SMALLINT); + assertEquals(rsmd.getColumnClassName(2), Integer.class.getName()); assertEquals(rsmd.getColumnType(3), Types.INTEGER); + assertEquals(rsmd.getColumnClassName(3), Integer.class.getName()); assertEquals(rsmd.getColumnType(4), Types.BIGINT); + assertEquals(rsmd.getColumnClassName(4), Long.class.getName()); + + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + assertTrue(rsmd.isCaseSensitive(i)); + assertFalse(rsmd.isCurrency(i)); + assertEquals(rsmd.isNullable(i), ResultSetMetaData.columnNoNulls); + assertTrue(rsmd.isSearchable(i)); + assertTrue(rsmd.isSigned(i)); + } } } } diff --git a/pom.xml b/pom.xml index 7fae19284..8bb776012 100644 --- a/pom.xml +++ b/pom.xml @@ -159,7 +159,7 @@ **/*0*.java,**/data/*Value.java,**/data/array/*Value.java,**/stream/*Stream.java jacoco reuseReports - target/site/jacoco-aggregate/jacoco.xml + ${project.basedir}/**/target/site/jacoco-aggregate/jacoco.xml java ${maven.build.timestamp} @@ -393,6 +393,16 @@ + + com.diffplug.spotless + spotless-maven-plugin + 2.43.0 + + + + + + kr.motd.maven os-maven-plugin