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)

Reply via email to