-
Notifications
You must be signed in to change notification settings - Fork 542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nested array in tuple array is incorrectly deserialized #1324
Comments
Hi @GFriedrich, thanks for the reporting. It's indeed an issue being introduced in 0.4.0, but I thought it's been fixed in 0.4.1. Could you provide table schema and/or sample code for me to reproduce the issue? Judging from the value you provided, the column is something like @Test(groups = "integration")
public void testNestedArrayInTuple() throws SQLException {
Properties props = new Properties();
try (ClickHouseConnection conn = newConnection(props);
ClickHouseStatement stmt = conn.createStatement()) {
// nested values on same row
Assert.assertFalse(stmt.execute("drop table if exists test_nested_array_in_tuple; "
+ "create table test_nested_array_in_tuple(id UInt64, v1 Tuple(Array(Int32)), v2 Tuple(Array(Int32)))engine=Memory; "
+ "insert into test_nested_array_in_tuple values(1, ([1, 2]), ([2, 3]))"));
try (ResultSet rs = stmt.executeQuery("select * from test_nested_array_in_tuple order by id")) {
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getInt(1), 1);
Assert.assertEquals(((List<?>) rs.getObject(2)).size(), 1);
Assert.assertEquals(((List<?>) rs.getObject(2)).get(0), new int[] { 1, 2 });
Assert.assertEquals(((List<?>) rs.getObject(3)).size(), 1);
Assert.assertEquals(((List<?>) rs.getObject(3)).get(0), new int[] { 2, 3 });
Assert.assertFalse(rs.next());
}
// nested values on same column
Assert.assertFalse(stmt.execute("drop table if exists test_nested_array_in_tuple; "
+ "create table test_nested_array_in_tuple(id UInt64, val Tuple(Array(Int32)))engine=Memory; "
+ "insert into test_nested_array_in_tuple values(1, ([1, 2])), (2, ([2, 3]))"));
try (ResultSet rs = stmt.executeQuery("select * from test_nested_array_in_tuple order by id")) {
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getInt(1), 1);
Assert.assertEquals(((List<?>) rs.getObject(2)).size(), 1);
Assert.assertEquals(((List<?>) rs.getObject(2)).get(0), new int[] { 1, 2 });
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getInt(1), 2);
Assert.assertEquals(((List<?>) rs.getObject(2)).size(), 1);
Assert.assertEquals(((List<?>) rs.getObject(2)).get(0), new int[] { 2, 3 });
Assert.assertFalse(rs.next());
}
// deeper nested level and more elements
Assert.assertFalse(stmt.execute("drop table if exists test_nested_array_in_tuple; "
+ "create table test_nested_array_in_tuple(id UInt64, val Array(Tuple(UInt16,Array(UInt32))))engine=Memory; "
+ "insert into test_nested_array_in_tuple values(1, [(0, [1, 2]), (1, [2, 3])]), (2, [(2, [4, 5]), (3, [6, 7])])"));
try (ResultSet rs = stmt.executeQuery("select * from test_nested_array_in_tuple order by id")) {
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getInt(1), 1);
Assert.assertEquals(((Object[]) rs.getObject(2)).length, 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).get(0), UnsignedShort.ZERO);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).get(1), new int[] { 1, 2 });
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).get(0), UnsignedShort.ONE);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).get(1), new int[] { 2, 3 });
Assert.assertTrue(rs.next());
Assert.assertEquals(((Object[]) rs.getObject(2)).length, 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).get(0),
UnsignedShort.valueOf((short) 2));
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).get(1), new int[] { 4, 5 });
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).get(0),
UnsignedShort.valueOf((short) 3));
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).get(1), new int[] { 6, 7 });
Assert.assertFalse(rs.next());
}
}
} |
Hey @zhicwu, |
Cool, thanks @GFriedrich. I'll create a patch release to fix that, once we can reproduce the issue. |
Hey @zhicwu, Assert.assertFalse(stmt.execute("drop table if exists test_nested_array_in_tuple; "
+ "create table test_nested_array_in_tuple(id UInt64, val Array(Tuple(UInt16,Array(Decimal(10,0)))))engine=Memory; "
+ "insert into test_nested_array_in_tuple values(1, [(0, [1, 2]), (1, [2, 3])]), (2, [(2, [4, 5]), (3, [6, 7])])"));
try (ResultSet rs = stmt.executeQuery("select * from test_nested_array_in_tuple order by id")) {
Assert.assertTrue(rs.next());
Assert.assertEquals(rs.getInt(1), 1);
Assert.assertEquals(((Object[]) rs.getObject(2)).length, 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).get(0), UnsignedShort.ZERO);
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[0]).get(1))[0], BigDecimal.valueOf(1));
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[0]).get(1))[1], BigDecimal.valueOf(2));
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).get(0), UnsignedShort.ONE);
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[1]).get(1))[0], BigDecimal.valueOf(2));
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[1]).get(1))[1], BigDecimal.valueOf(3));
Assert.assertTrue(rs.next());
Assert.assertEquals(((Object[]) rs.getObject(2)).length, 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[0]).get(0),
UnsignedShort.valueOf((short) 2));
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[0]).get(1))[0], BigDecimal.valueOf(4));
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[0]).get(1))[1], BigDecimal.valueOf(5));
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).size(), 2);
Assert.assertEquals(((List<?>) ((Object[]) rs.getObject(2))[1]).get(0),
UnsignedShort.valueOf((short) 3));
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[1]).get(1))[0], BigDecimal.valueOf(6));
Assert.assertEquals(((BigDecimal[]) ((List<?>) ((Object[]) rs.getObject(2))[1]).get(1))[1], BigDecimal.valueOf(7));
Assert.assertFalse(rs.next());
} If you run this code, you'll see that it fails as the first inner array will be overwritten by the second inner array. |
Thanks @GFriedrich for helping me to reproduce the issue along with all the details and suggestions. It's caused by the tricky way trying to reduce object creation when the inner array length stays the same. I've fixed the issue in local, but I need to think it over to understand if there's better way without impacting performance and memory usage. Will definitely include the fix in v0.4.6. |
Can confirm that with version 0.4.6 things are now working as expected for me. |
Describe the bug
After upgrading from ClickHouse HTTP driver 0.3.2 to 0.4.4 I found that data from nested numeric arrays in tuples is not correctly deserialized. E.g. instead of getting
[(0, [1, 2]), (1, [2, 3])]
I got[(0, [2, 3]), (1, [2, 3])]
This is because the inner array gets recycled and overwrites the previous values instead of using a fresh array or copying the array after reading.
Steps to reproduce
Create a statement that returns in a column an array of tuples which in turn also contains a numeric array.
Expected behaviour
The values should be correctly deserialized.
Configuration
Environment
The text was updated successfully, but these errors were encountered: