ClickHouse / clickhouse-java

ClickHouse Java Clients & JDBC Driver
https://clickhouse.com
Apache License 2.0
1.45k stars 535 forks source link

Can't use Array of Tuple in PreparedStatement #1862

Closed joceron closed 1 month ago

joceron commented 1 month ago

I'm trying to use an Array of Tuple in a PreparedStatement, but somehow while serializing, it seems the Tuple gets combined into a single value. I don't know if it's due to my misunderstanding of how to use the driver, or an actual bug. If it's the first case, I apologize.

I created an example table with this:

CREATE TABLE tmp (my_column Array(Tuple(String, String))) PRIMARY KEY my_column;

And this is my testing code:

val conn = DriverManager.getConnection(???, ???, ???)
val pstmt: PreparedStatement = conn.prepareStatement("insert into tmp (my_column) values (?)")
val arr  = conn.createArrayOf("Tuple(String, String)", List(("first", "second")).toArray[Object])
pstmt.setArray(1, arr)
pstmt.executeUpdate()

When executing that, it goes to BinaryDataProcessor.ArraySerializer.serialize, and then I see that at that point so far the serializing goes well: Screenshot 2024-10-10 at 15 08 58

But then when going into ClickHouseRowBinaryProcessor.TupleSerializer.serialize later during the execution, it seems that it has combined the whole tuple into a single value: Screenshot 2024-10-10 at 15 10 07

That throws anIndexOutOfBoundsException, because (I'm copy&pasting code from ClickHouseRowBinaryProcessor):

public void serialize(ClickHouseValue value, ClickHouseOutputStream output) throws IOException {
    List<Object> tupleValues = value.asTuple();
    int i = 0;

    for(int len = this.serializers.length; i < len; ++i) {
        this.serializers[i].serialize(this.values[i].update(tupleValues.get(i)), output);
    }

}

There, tupleValues, I suppose it's meant to have two values: each of the values of the Tuple. But since it was combined into one (see my second screenshot), in the for loop it throws an exception when trying to do tupleValues.get(1).

Amd I misunderstanding something?

7hong commented 1 month ago

You can use Array instead of Tuple, like this:

connection.createArrayOf(
        "Tuple(String, String)",
        List(Array("first", "second"), Array("aa", "bb")).toArray[Object]
      )

or

connection.createArrayOf(
        "Tuple(String, String)",
        Array(Array("first", "second"), Array("aa", "bb"))
      )

@joceron

joceron commented 1 month ago

Thank you very much @7hong ! That worked