Vladimir Sitnikov created CALCITE-7849:
------------------------------------------
Summary: Remote JDBC driver returns [[[a], [b]]] for
MULTISET[MULTISET['a', 'b']] and fails for INTEGER elements
Key: CALCITE-7849
URL: https://issues.apache.org/jira/browse/CALCITE-7849
Project: Calcite
Issue Type: Bug
Components: core
Affects Versions: 1.42.0
Reporter: Vladimir Sitnikov
Reading a MULTISET nested in a MULTISET through the remote JDBC driver returns
a wrong value or fails, depending on the serialization. The same queries work
in-process (jdbc:calcite:) and through LocalService without serialization.
Reproduced with calcite-core 1.42.0 (avatica-core 1.28.0, JDK 21.0.9). Each
line is ResultSet.getString(1) for the query on the left; the remote
connections use jdbc:avatica:remote:factory=<a Service.Factory that wraps new
LocalService(CalciteMetaImpl.create(calciteConnection))> in LocalJsonService or
LocalProtobufService:
{noformat}
jdbc:calcite:
select multiset[multiset['a', 'b']] => [[a, b]]
select multiset[multiset[1, 2]] => [[1, 2]]
LocalService (no serialization)
select multiset[multiset['a', 'b']] => [[a, b]]
select multiset[multiset[1, 2]] => [[1, 2]]
LocalJsonService
select multiset[multiset['a', 'b']] => [[[a], [b]]]
select multiset[multiset[1, 2]] => THROWS java.lang.ClassCastException: class
java.util.ArrayList cannot be cast to class java.lang.Integer
(java.util.ArrayList and java.lang.Integer are in module java.base of loader
'bootstrap')
LocalProtobufService
select multiset[multiset['a', 'b']] => THROWS java.lang.RuntimeException:
Unhandled type in Frame: class [Ljava.lang.Object;
select multiset[multiset[1, 2]] => THROWS java.lang.RuntimeException:
Unhandled type in Frame: class [Ljava.lang.Object;
{noformat}
With JSON, the ClassCastException comes from:
{noformat}
at
org.apache.calcite.avatica.util.AbstractCursor$IntAccessor.getInt(AbstractCursor.java:610)
at
org.apache.calcite.avatica.util.AbstractCursor$ArrayAccessor.convertValue(AbstractCursor.java:1553)
at
org.apache.calcite.avatica.util.AbstractCursor$ArrayAccessor.getObject(AbstractCursor.java:1534)
at
org.apache.calcite.avatica.util.AbstractCursor$ArrayAccessor.getArray(AbstractCursor.java:1589)
{noformat}
The other nestings work through all three paths: multiset['a', 'b'],
array[multiset['a', 'b']], multiset[array['a', 'b']] and array[array['a', 'b']]
each return the expected value.
The "rows" array of the JSON frame the server sends shows the difference. Each
element of the inner MULTISET arrives wrapped in a one-element array:
{noformat}
select multiset['a', 'b'] [[["a","b"]]]
select array[multiset['a', 'b']] [[[["a","b"]]]]
select multiset[multiset['a', 'b']] [[[[["a"],["b"]]]]]
{noformat}
h3. Expected
select multiset[multiset['a', 'b']] returns [[a, b]] and select
multiset[multiset[1, 2]] returns [[1, 2]] through every connection type, as
they do in-process. The inner value is sent as ["a","b"], the same as a
top-level VARCHAR MULTISET or one nested in an ARRAY.
h3. Analysis (a guess; the report above stands without it)
The plan for the nested query has no $SLICE between the two collects:
{noformat}
EnumerableCalc(expr#0=[{inputs}], expr#1=[$SLICE($t0)], EXPR$0=[$t1])
EnumerableCollect(field=[EXPR$0])
EnumerableCollect(field=[EXPR$0])
EnumerableValues(tuples=[[{ 'a' }, { 'b' }]])
{noformat}
StandardConvertletTable.convertMultiset adds $SLICE, which turns a multiset of
one-field records into a multiset of scalars, only over the outermost Collect.
The inner MULTISET value constructor is converted by
SqlToRelConverter.convertMultisets into a Collect that feeds the outer Collect
directly, so its elements stay one-field records (Object[]). In-process, the
CALCITE-3557 change in Avatica (commit 810acf8) hides this: ArrayAccessor
unwraps an element that is an Object[]. After JSON serialization the record is
a List, which it does not unwrap, and the protobuf Frame serializer does not
accept an Object[] element at all.
Producing scalars for the inner MULTISET in Calcite would fix every
serialization at once; changing Avatica to also unwrap a List would fix only
JSON.
h3. Related
CALCITE-3557: the Avatica change that unwraps Object[] elements in-process.
CALCITE-7848: getObject().getArray() on nested collections of INTEGER; its fix
does not change this behavior. CALCITE-4999: the same one-column-to-scalar rule
for ARRAY and MULTISET sub-queries.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)