Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -929,9 +929,9 @@ public String encodeArray(Object[] elements, int levels, ClickHouseDataType elem
} else if (cursor.arrayAsTuple) {
arraySb.append(encodeTuple((Object[]) element)).append(',');
cursor.pos++;
} else if (cursor.level == 1 && elementType == ClickHouseDataType.Tuple && element instanceof Array ) {
} else if (cursor.level == 1 && isTupleType(elementType) && element instanceof Array ) {
cursor.arrayObjAsTuple = true;
} else if (cursor.level == 1 && elementType == ClickHouseDataType.Tuple && element instanceof Object[] ) {
} else if (cursor.level == 1 && isTupleType(elementType) && element instanceof Object[] ) {
cursor.arrayAsTuple = true;
} else if (cursor.level == 1) {
arraySb.append(encodeObject(element)).append(',');
Expand All @@ -947,6 +947,10 @@ public String encodeArray(Object[] elements, int levels, ClickHouseDataType elem
return arraySb.toString();
}

private static boolean isTupleType(ClickHouseDataType type ) {
return type == ClickHouseDataType.Tuple || type == ClickHouseDataType.Point;
}

private static final class ArrayProcessingCursor {
Object[] array; // current array
int pos; // processing position
Expand Down
95 changes: 54 additions & 41 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/ResultSetImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,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.data.ClickHouseDataType;
import com.clickhouse.data.ClickHouseColumn;
import com.clickhouse.jdbc.internal.ExceptionUtils;
import com.clickhouse.jdbc.internal.FeatureManager;
import com.clickhouse.jdbc.internal.JdbcUtils;
Expand Down Expand Up @@ -35,6 +35,7 @@
import java.sql.Timestamp;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;

Expand Down Expand Up @@ -481,16 +482,6 @@ 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()));
}

@Override
public Object getObject(String columnLabel) throws SQLException {
return getObject(columnLabel, JdbcUtils.convertToJavaClass(getSchema().getColumnByName(columnLabel).getDataType()));
}

@Override
public int findColumn(String columnLabel) throws SQLException {
checkClosed();
Expand Down Expand Up @@ -962,12 +953,6 @@ public Statement getStatement() throws SQLException {
return this.parentStatement;
}

@Override
public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
ClickHouseDataType type = getSchema().getColumnByIndex(columnIndex).getDataType();
return getObject(columnIndex, map.get(JdbcUtils.convertToSqlType(type).getName()));
}

@Override
public Ref getRef(int columnIndex) throws SQLException {
return getRef(columnIndexToName(columnIndex));
Expand All @@ -988,12 +973,6 @@ public java.sql.Array getArray(int columnIndex) throws SQLException {
return getObject(columnIndex, java.sql.Array.class);
}

@Override
public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
checkClosed();
return getObject(columnLabel, map.get(JdbcUtils.convertToSqlType(getSchema().getColumnByName(columnLabel).getDataType()).getName()));
}

@Override
public Ref getRef(String columnLabel) throws SQLException {
checkClosed();
Expand Down Expand Up @@ -1437,38 +1416,72 @@ public void updateNClob(String columnLabel, Reader reader) throws SQLException {
}

@Override
public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
public Object getObject(int columnIndex) throws SQLException {
return getObject(columnIndexToName(columnIndex));
}

@Override
public Object getObject(String columnLabel) throws SQLException {
return getObjectImpl(columnLabel, null, Collections.emptyMap());
}

@Override
public Object getObject(int columnIndex, Map<String, Class<?>> map) throws SQLException {
return getObject(columnIndexToName(columnIndex), map);
}

@Override
public Object getObject(String columnLabel, Map<String, Class<?>> map) throws SQLException {
checkClosed();
try {
if (reader.hasValue(columnIndex)) {
wasNull = false;
if (type == null) {//As a fallback, try to get the value as is
return reader.readValue(columnIndex);
}
return getObjectImpl(columnLabel, null, map);
}

return (T) JdbcUtils.convert(reader.readValue(columnIndex), type, type == java.sql.Array.class ? getSchema().getColumnByIndex(columnIndex) : null);
} else {
wasNull = true;
return null;
}
} catch (Exception e) {
throw ExceptionUtils.toSqlState(String.format("Method: getObject(\"%s\", %s) encountered an exception.",
reader.getSchema().columnIndexToName(columnIndex), type),
String.format("SQL: [%s]", parentStatement.getLastStatementSql()), e);
}
@Override
public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
checkClosed();
return getObject(columnIndexToName(columnIndex), type);
}

@Override
public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
checkClosed();
return getObjectImpl(columnLabel, type, Collections.emptyMap());
}

@SuppressWarnings("unchecked")
public <T> T getObjectImpl(String columnLabel, Class<?> type, Map<String, Class<?>> typeMap) throws SQLException {
try {
ClickHouseColumn column = getSchema().getColumnByName(columnLabel);
if (column == null) {
throw new SQLException("Column \"" + columnLabel + "\" does not exist.");
}

if (reader.hasValue(columnLabel)) {
wasNull = false;

if (type == null) {
switch (column.getDataType()) {
case Point:
case Ring:
case LineString:
case Polygon:
case MultiPolygon:
case MultiLineString:
break; // read as is
default:
if (typeMap == null || typeMap.isEmpty()) {
type = JdbcUtils.convertToJavaClass(column.getDataType());
} else {
type = typeMap.get(JdbcUtils.convertToSqlType(column.getDataType()).getName());
}
}
}

if (type == null) {//As a fallback, try to get the value as is
return reader.readValue(columnLabel);
}

return (T) JdbcUtils.convert(reader.readValue(columnLabel), type, type == java.sql.Array.class ? getSchema().getColumnByName(columnLabel) : null);
return (T) JdbcUtils.convert(reader.readValue(columnLabel), type, column);
} else {
wasNull = true;
return null;
Expand Down
86 changes: 80 additions & 6 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.time.chrono.ChronoZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
Expand Down Expand Up @@ -78,12 +79,12 @@ private static Map<ClickHouseDataType, SQLType> generateTypeMap() {
map.put(ClickHouseDataType.Array, JDBCType.ARRAY);
map.put(ClickHouseDataType.Nested, JDBCType.ARRAY);
map.put(ClickHouseDataType.Map, JDBCType.JAVA_OBJECT);
map.put(ClickHouseDataType.Point, JDBCType.OTHER);
map.put(ClickHouseDataType.Ring, JDBCType.OTHER);
map.put(ClickHouseDataType.Polygon, JDBCType.OTHER);
map.put(ClickHouseDataType.LineString, JDBCType.OTHER);
map.put(ClickHouseDataType.MultiPolygon, JDBCType.OTHER);
map.put(ClickHouseDataType.MultiLineString, JDBCType.OTHER);
map.put(ClickHouseDataType.Point, JDBCType.ARRAY);
map.put(ClickHouseDataType.Ring, JDBCType.ARRAY);
map.put(ClickHouseDataType.Polygon, JDBCType.ARRAY);
map.put(ClickHouseDataType.LineString, JDBCType.ARRAY);
map.put(ClickHouseDataType.MultiPolygon, JDBCType.ARRAY);
map.put(ClickHouseDataType.MultiLineString, JDBCType.ARRAY);
return ImmutableMap.copyOf(map);
}

Expand Down Expand Up @@ -281,6 +282,8 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
}
// base type is unknown. all objects should be converted
return new Array(column, ((List<?>) value).toArray());
} else if (type == java.sql.Array.class && value.getClass().isArray()) {
return new Array(column, arrayToObjectArray(value));
} else if (type == Inet4Address.class && value instanceof Inet6Address) {
// Convert Inet6Address to Inet4Address
return InetAddressConverter.convertToIpv4((InetAddress) value);
Expand Down Expand Up @@ -322,4 +325,75 @@ public static Object[] convertArray(Object[] values, Class<?> type) throws SQLEx
}
return convertedValues;
}

private static Object[] arrayToObjectArray(Object array) {
if (array == null) {
return null;
}
if (array instanceof Object[]) {
return (Object[]) array;
}
if (!array.getClass().isArray()) {
throw new IllegalArgumentException("Not an array: " + array.getClass().getName());
}

if (array instanceof byte[]) {
byte[] src = (byte[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof short[]) {
short[] src = (short[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof int[]) {
int[] src = (int[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof long[]) {
long[] src = (long[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof float[]) {
float[] src = (float[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof double[]) {
double[] src = (double[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof char[]) {
char[] src = (char[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
} else if (array instanceof boolean[]) {
boolean[] src = (boolean[]) array;
Object[] dst = new Object[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[i];
}
return dst;
}
throw new IllegalArgumentException("Cannot convert " + array.getClass().getName() + " to an Object[]");
}
}
12 changes: 12 additions & 0 deletions jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,16 @@ private void ensureValid() throws SQLException {
throw ExceptionUtils.toSqlState(new SQLFeatureNotSupportedException("Array is not valid. Possible free() was called."));
}
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Array other = (Array) obj;
return type == other.type && java.util.Arrays.equals(array, other.array);
}
Comment on lines +123 to +132
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've implemented equals() but not hashCode(). According to the Java contract, when overriding equals(), you should also override hashCode() to ensure that equal objects have the same hash code. This is important if instances of this class will be used in hash-based collections like HashMap or HashSet.

Consider adding a matching hashCode() implementation like:

Suggested change
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Array other = (Array) obj;
return type == other.type && java.util.Arrays.equals(array, other.array);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Array other = (Array) obj;
return type == other.type && java.util.Arrays.equals(array, other.array);
}
@Override
public int hashCode() {
int result = type;
result = 31 * result + java.util.Arrays.hashCode(array);
return result;
}

}
Loading
Loading