Skip to content

Commit 5f92ec9

Browse files
committed
added more test for array data types
1 parent 444adde commit 5f92ec9

File tree

8 files changed

+116
-25
lines changed

8 files changed

+116
-25
lines changed

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReader.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.LinkedHashMap;
3333
import java.util.List;
3434
import java.util.Map;
35+
import java.util.Objects;
3536
import java.util.Set;
3637
import java.util.TimeZone;
3738
import java.util.UUID;

client-v2/src/main/java/com/clickhouse/client/api/data_formats/internal/SerializerUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,4 +1189,20 @@ public static void writeDateTime64(OutputStream output, Object value, int scale,
11891189

11901190
BinaryStreamUtils.writeInt64(output, ts);
11911191
}
1192+
1193+
public static final Map<Class<?>, Class<?>> PRIMITIVE_2_WRAPPER_CLASS = getPrimitive2WrapperClassMap();
1194+
1195+
private static Map<Class<?>, Class<?>> getPrimitive2WrapperClassMap() {
1196+
Map<Class<?>, Class<?>> map = new HashMap<>();
1197+
map.put(boolean.class, Boolean.class);
1198+
map.put(byte.class, Byte.class);
1199+
map.put(char.class, Character.class);
1200+
map.put(short.class, Short.class);
1201+
map.put(int.class, Integer.class);
1202+
map.put(long.class, Long.class);
1203+
map.put(float.class, Float.class);
1204+
map.put(double.class, Double.class);
1205+
1206+
return map;
1207+
}
11921208
}

client-v2/src/test/java/com/clickhouse/client/api/data_formats/internal/BinaryStreamReaderTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import java.io.ByteArrayInputStream;
44
import java.io.ByteArrayOutputStream;
55
import java.io.IOException;
6+
import java.lang.reflect.Array;
67
import java.time.ZoneId;
78
import java.time.ZonedDateTime;
89
import java.time.temporal.ChronoUnit;
10+
import java.util.Arrays;
911
import java.util.TimeZone;
1012

1113
import org.testng.Assert;
@@ -176,4 +178,16 @@ private Object[][] provideDateTimeTestData() {
176178
};
177179
}
178180

181+
@Test
182+
public void testArrayValue() throws Exception {
183+
BinaryStreamReader.ArrayValue array = new BinaryStreamReader.ArrayValue(int.class, 10);
184+
185+
for (int i = 0; i < array.length(); i++) {
186+
array.set(i, i);
187+
}
188+
189+
int[] array1 = (int[]) array.getArray();
190+
Object[] array2 = array.getArrayOfObjects();
191+
Assert.assertEquals(array1.length, array2.length);
192+
}
179193
}

jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,15 +581,17 @@ public Array createArrayOf(String typeName, Object[] elements) throws SQLExcepti
581581
throw new SQLFeatureNotSupportedException("typeName cannot be null");
582582
}
583583

584+
584585
int parentPos = typeName.indexOf('(');
585-
String clickhouseDataTypeName = (typeName.substring(0, parentPos == -1 ? typeName.length() : parentPos)).trim();
586+
int endPos = parentPos == -1 ? typeName.length() : parentPos;
587+
String clickhouseDataTypeName = (typeName.substring(0, endPos)).trim();
586588
ClickHouseDataType dataType = ClickHouseDataType.valueOf(clickhouseDataTypeName);
587589
if (dataType.equals(ClickHouseDataType.Array)) {
588590
throw new SQLFeatureNotSupportedException("Array cannot be a base type. In case of nested array provide most deep element type name.");
589591
}
590592
try {
591-
return new com.clickhouse.jdbc.types.Array(clickhouseDataTypeName,
592-
JdbcUtils.CLICKHOUSE_TO_SQL_TYPE_MAP.getOrDefault(dataType, JDBCType.OTHER).getVendorTypeNumber(), elements);
593+
return new com.clickhouse.jdbc.types.Array(elements, typeName,
594+
JdbcUtils.CLICKHOUSE_TO_SQL_TYPE_MAP.getOrDefault(dataType, JDBCType.OTHER).getVendorTypeNumber());
593595
} catch (Exception e) {
594596
throw new SQLException("Failed to create array", ExceptionUtils.SQL_STATE_CLIENT_ERROR, e);
595597
}

jdbc-v2/src/main/java/com/clickhouse/jdbc/internal/JdbcUtils.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,16 +266,18 @@ public static Object convert(Object value, Class<?> type, ClickHouseColumn colum
266266
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
267267
Object[] convertedValues = convertArray(arrayValue.getArrayOfObjects(),
268268
JdbcUtils.convertToJavaClass(column.getArrayBaseColumn().getDataType()));
269-
return new Array(baseType.getName(), baseType.getVendorTypeNumber(), convertedValues);
269+
return new Array(convertedValues, baseType.getName(), baseType.getVendorTypeNumber());
270270
}
271-
return new Array("Object", JDBCType.JAVA_OBJECT.getVendorTypeNumber(), arrayValue.getArrayOfObjects());
271+
return new Array(arrayValue.getArrayOfObjects(), "Unknown", JDBCType.OTHER.getVendorTypeNumber());
272272
} else if (type == java.sql.Array.class && value instanceof List<?>) {
273+
273274
if (column != null && column.getArrayBaseColumn() != null) {
274275
ClickHouseDataType baseType = column.getArrayBaseColumn().getDataType();
275-
return new Array(baseType.getName(), JdbcUtils.CLICKHOUSE_TO_SQL_TYPE_MAP.getOrDefault(baseType, JDBCType.OTHER).getVendorTypeNumber(), convertList((List<?>) value, JdbcUtils.convertToJavaClass(column.getArrayBaseColumn().getDataType())) );
276+
return new Array(convertList((List<?>) value, JdbcUtils.convertToJavaClass(column.getArrayBaseColumn().getDataType())),
277+
baseType.getName(), JdbcUtils.CLICKHOUSE_TO_SQL_TYPE_MAP.getOrDefault(baseType, JDBCType.OTHER).getVendorTypeNumber());
276278
}
277279
// base type is unknown. all objects should be converted
278-
return new Array("Object", JDBCType.JAVA_OBJECT.getVendorTypeNumber(), ((List<?>) value).toArray());
280+
return new Array(((List<?>) value).toArray(), "Unknown", JDBCType.OTHER.getVendorTypeNumber());
279281
} else if (type == Inet4Address.class && value instanceof Inet6Address) {
280282
// Convert Inet6Address to Inet4Address
281283
return Inet4Address.getByName(value.toString());

jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Array.java

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@
1212

1313
public class Array implements java.sql.Array {
1414
private static final Logger log = LoggerFactory.getLogger(Array.class);
15-
Object[] array;
16-
int type; //java.sql.Types
17-
String elementTypeName;
15+
16+
private Object[] array;
17+
private final int type; //java.sql.Types
18+
private final String elementTypeName;
19+
private boolean valid;
1820

1921
/**
20-
* @deprecated
22+
* @deprecated this constructor should not be used. Elements array should be constructed externally.
2123
*/
2224
public Array(List<Object> list, String elementTypeName, int itemType) throws SQLException {
23-
this(elementTypeName, itemType, list.toArray());
25+
this(list.toArray(), elementTypeName, itemType);
2426
}
2527

26-
public Array(String elementTypeName, int itemType, Object[] elements) throws SQLException {
28+
public Array(Object[] elements, String elementTypeName, int itemType) throws SQLException {
2729
if (elements == null) {
2830
throw ExceptionUtils.toSqlState(new IllegalArgumentException("Array cannot be null"));
2931
}
@@ -33,20 +35,24 @@ public Array(String elementTypeName, int itemType, Object[] elements) throws SQ
3335
this.array = elements;
3436
this.type = itemType;
3537
this.elementTypeName = elementTypeName;
38+
this.valid = true;
3639
}
3740

3841
@Override
3942
public String getBaseTypeName() throws SQLException {
43+
ensureValid();
4044
return elementTypeName;
4145
}
4246

4347
@Override
4448
public int getBaseType() throws SQLException {
49+
ensureValid();
4550
return type;
4651
}
4752

4853
@Override
4954
public Object getArray() throws SQLException {
55+
ensureValid();
5056
return array;
5157
}
5258

@@ -57,14 +63,20 @@ public Object getArray(Map<String, Class<?>> map) throws SQLException {
5763

5864
@Override
5965
public Object getArray(long index, int count) throws SQLException {
60-
try {
61-
Object[] smallerArray = new Object[count];
62-
System.arraycopy(array, (int) index, smallerArray, 0, count);
63-
return smallerArray;
64-
} catch (Exception e) {
65-
log.error("Failed to get array", e);
66-
throw new SQLException(e.getMessage(), ExceptionUtils.SQL_STATE_CLIENT_ERROR, e);
66+
ensureValid();
67+
if (index < 0) {
68+
throw new SQLException("Index cannot be negative");
69+
}
70+
if (count < 0) {
71+
throw new SQLException("Count cannot be negative");
6772
}
73+
if (count > (array.length - index)) {
74+
throw new SQLException("Not enough elements after index " + index);
75+
}
76+
77+
Object[] smallerArray = new Object[count];
78+
System.arraycopy(array, (int) index, smallerArray, 0, count);
79+
return smallerArray;
6880
}
6981

7082
@Override
@@ -94,6 +106,13 @@ public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map)
94106

95107
@Override
96108
public void free() throws SQLException {
109+
valid = false;
97110
array = null;
98111
}
112+
113+
private void ensureValid() throws SQLException {
114+
if (!valid) {
115+
throw ExceptionUtils.toSqlState(new SQLFeatureNotSupportedException("Array is not valid. Possible free() was called."));
116+
}
117+
}
99118
}

jdbc-v2/src/main/java/com/clickhouse/jdbc/types/Struct.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.clickhouse.jdbc.types;
22

33
import com.clickhouse.data.ClickHouseColumn;
4+
import com.clickhouse.jdbc.internal.ExceptionUtils;
45

56
import java.sql.SQLException;
7+
import java.sql.SQLFeatureNotSupportedException;
68
import java.util.Map;
79

810
public class Struct implements java.sql.Struct {
@@ -28,7 +30,7 @@ public Object[] getAttributes() throws SQLException {
2830

2931
@Override
3032
public Object[] getAttributes(Map<String, Class<?>> map) throws SQLException {
31-
return attributes;
33+
throw new SQLFeatureNotSupportedException("getAttributes(Map<String, Class<?>>) is not supported", ExceptionUtils.SQL_STATE_FEATURE_NOT_SUPPORTED);
3234
}
3335

3436
public ClickHouseColumn getColumn() {

jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionTest.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.sql.Array;
2121
import java.sql.Connection;
2222
import java.sql.DatabaseMetaData;
23+
import java.sql.JDBCType;
2324
import java.sql.PreparedStatement;
2425
import java.sql.ResultSet;
2526
import java.sql.SQLException;
@@ -38,6 +39,7 @@
3839

3940
import static org.testng.Assert.assertEquals;
4041
import static org.testng.Assert.assertFalse;
42+
import static org.testng.Assert.assertNotNull;
4143
import static org.testng.Assert.assertNull;
4244
import static org.testng.Assert.assertThrows;
4345
import static org.testng.Assert.fail;
@@ -365,8 +367,12 @@ public static Object[][] setAndGetClientInfoTestDataProvider() {
365367
}
366368

367369
@Test(groups = { "integration" })
368-
public void createArrayOfTest() throws SQLException {
370+
public void testCreateArray() throws SQLException {
369371
try (Connection conn = getJdbcConnection()) {
372+
373+
Assert.expectThrows(SQLException.class, () -> conn.createArrayOf("Array()", new Integer[] {1}));
374+
375+
370376
final String baseType = "Tuple(String, Int8)";
371377
final String tableName = "array_create_test";
372378
final String arrayType = "Array(Array(" + baseType + "))";
@@ -383,21 +389,46 @@ public void createArrayOfTest() throws SQLException {
383389
};
384390

385391
Array arrayValue = conn.createArrayOf("Tuple(String, Int8)", srcArray );
392+
assertEquals(arrayValue.getBaseTypeName(), baseType);
393+
assertEquals(arrayValue.getBaseType(), JDBCType.OTHER.getVendorTypeNumber());
394+
assertThrows(SQLFeatureNotSupportedException.class, () -> arrayValue.getArray(null));
395+
assertThrows(SQLFeatureNotSupportedException.class, () -> arrayValue.getArray(0, 1, null));
396+
assertThrows(SQLFeatureNotSupportedException.class, arrayValue::getResultSet);
397+
assertThrows(SQLFeatureNotSupportedException.class, () -> arrayValue.getResultSet(0, 1));
398+
assertThrows(SQLFeatureNotSupportedException.class, () -> arrayValue.getResultSet(null));
399+
assertThrows(SQLFeatureNotSupportedException.class, () -> arrayValue.getResultSet(0, 1, null));
400+
401+
Assert.expectThrows(SQLException.class, () -> arrayValue.getArray(-1, 1));
402+
Assert.expectThrows(SQLException.class, () -> arrayValue.getArray(0, -1));
403+
Assert.expectThrows(SQLException.class, () -> arrayValue.getArray(0, 3));
404+
Assert.expectThrows(SQLException.class, () -> arrayValue.getArray(1, 2));
405+
406+
Object[] subArray = (Object[]) arrayValue.getArray(1, 1);
407+
Assert.assertEquals(subArray.length, 1);
408+
386409
try (PreparedStatement pStmt = conn.prepareStatement("INSERT INTO " + tableName + " (v1) VALUES (?)")) {
387410
pStmt.setArray(1, arrayValue);
388411
pStmt.executeUpdate();
389412
pStmt.setObject(1, arrayValue);
390413
pStmt.executeUpdate();
414+
} finally {
415+
arrayValue.free();
416+
arrayValue.free(); // just to check that operation idempotent
417+
assertThrows(SQLException.class, () -> arrayValue.getArray(1, 1));
418+
assertThrows(SQLException.class, arrayValue::getArray);
391419
}
392420

393421
try (ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName)) {
394422
Assert.assertTrue(rs.next());
395423
Array array1 = rs.getArray(1);
396424
Object[] elements = (Object[]) array1.getArray();
397-
Object[] storedTuple1 = (Object[]) ((List)elements[0]).get(0);
398-
Object[] storedTuple2 = (Object[]) ((List)elements[1]).get(1);
425+
Object[] storedTuple1 = (Object[]) ((List<?>)elements[0]).get(0);
426+
Object[] storedTuple2 = (Object[]) ((List<?>)elements[1]).get(1);
399427
Assert.assertEquals(storedTuple1, tuple1.getAttributes());
400428
Assert.assertEquals(storedTuple2, tuple2.getAttributes());
429+
430+
Array array2 = (Array) rs.getObject(1);
431+
Assert.assertEquals(array2.getArray(), elements);
401432
}
402433
}
403434
}
@@ -415,8 +446,12 @@ public void testCreateStruct() throws SQLException {
415446
timePart.setNanos(333000000);
416447

417448
Struct tupleValue = conn.createStruct(tupleType, new Object[] {120, "test tuple value", timePart});
449+
assertEquals(tupleValue.getSQLTypeName(), tupleType);
450+
assertThrows(SQLFeatureNotSupportedException.class, () -> tupleValue.getAttributes(null));
451+
assertNotNull(((com.clickhouse.jdbc.types.Struct) tupleValue).getColumn());
452+
418453

419-
try (PreparedStatement pStmt = conn.prepareStatement("INSERT INTO " +tableName + " VALUES (?)")) {
454+
try (PreparedStatement pStmt = conn.prepareStatement("INSERT INTO " + tableName + " VALUES (?)")) {
420455
pStmt.setObject(1, tupleValue);
421456
pStmt.executeUpdate();
422457
}

0 commit comments

Comments
 (0)