h2database icon indicating copy to clipboard operation
h2database copied to clipboard

ARRAY data type and non-Object[] arrays in JDBC

Open katzyn opened this issue 6 years ago • 6 comments

H2 historically had an array data type without base data type. With recent changes H2 has optional base data type.

On the JDBC level ARRAY data type is mapped to Object[], that violates the JDBC specification. Table B-1 JDBC Types Mapped to Java Types in JDBC 4.3 requires the java.sql.Array.

Released versions of H2 was able to return custom arrays, such as Integer[] if this array was returned by user-defined function, for example. This logic was buggy, because H2 didn't preserve the data type and because H2 didn't generate the elements of the same data type in all cases (conversion to Value and back to Object may change the class), the ArrayStoreException was possible. This logic was removed. Note that JdbcArray.get() methods didn't have it, they returned an Object[] unconditionally and legally, they may return Object[] or custom array, it's implementation-defined.

H2 still able to accept such custom arrays, this issue is only about ResultSet.getObject() and Array.get() methods. Should we return custom arrays when data type is known?

Note that ARRAY literals don't have a base data type in the SQL Standard, only ARRAY data type has (columns, casts, etc.) Anyway, the literals must evaluate their base data type by combination of data types of all elements and cast all of them to it, H2 doesn't do that currently. H2 only can evaluate it when all elements are really have the same type (NULL values are possible too). For example, array literal with INT and BIGINT values is currently considered as array of mixed type.

katzyn avatar Oct 20 '19 02:10 katzyn

For example, an empty array or array with only NULL values currently doesn't know its base data type.

It could be recovered from column's data type in the same way as enum data types are recovered, but it requires some additional code.

katzyn avatar Oct 20 '19 04:10 katzyn

If we used to return an Object[], then I'm happy that we continue to do so

grandinj avatar Oct 20 '19 10:10 grandinj

Yes, the Object[] was always returned when value was read from a persisted table, it was returned from built-in functions, but with direct invocation of user-defined Java function the data type was either the Object[] or a some other depending on the way to read the value from the result set.

katzyn avatar Oct 20 '19 10:10 katzyn

When comparing 1.4.200 with 2.1.214 it seems that the JdbcArray#get once delegated to ValueArray#getObject which used

Object[] list = (Object[]) Array.newInstance(componentType, len);

But now JdbcArray#get uses ValueToObjectConverter#valueToDefaultArray which sadly does

Object[] list = new Object[len];

to construct the result. This breaks my MyBatis-Mapper.

paper-tiger avatar Aug 16 '22 14:08 paper-tiger

java.sql.Array.getArray() implementations may return Object[] according to specification.

Actually you don't need to use java.sql.Array with H2, you can get regular Java array directly:

Connection connection = DriverManager.getConnection("jdbc:h2:mem:1");
Statement s = connection.createStatement();
s.execute("CREATE TABLE A(A INTEGER ARRAY) AS VALUES ARRAY[1, 2, 3]");
ResultSet rs = s.executeQuery("TABLE A");
rs.next();
Integer[] a = rs.getObject(1, Integer[].class);
System.out.println(Arrays.toString(a));

katzyn avatar Aug 16 '22 15:08 katzyn

java.sql.Array.getArray() implementations may return Object[] according to specification.

That is correct, i just wanted to point out that h2 used to do it "better". I just thought it is weird that there is a typed array now but in the implementation the typing which was there with the untyped array is lost...

paper-tiger avatar Aug 17 '22 06:08 paper-tiger