Repository: clickhouse/clickhouse-java
Issue: ResultSet getObject with class return null values filled as it was not null

When selecting values from table with getObject(index, Class)
simple types (like float/int/boolean) return 0/false as result (when null is expected).
```
CREATE TABLE db.tbl
   (
   id int not null,
   double_col double,
   float_col float,
   varchar_col varchar(36),
   boolean_col boolean,
   int_col int,
   bigint_col bigint,
   char_col char(10),
   int32_col int32
   primary key (id)
   )
   distributed by (id)
```
`Expected: [1, null, null, null, null, null, null, null, null]`
`Actual: [1, 0.0, 0.0, null, false, 0, 0, null, 0]`

getObject(int columnIndex) returns null as expected.

Is that expected behaivor? (it breaks Vertx jdbc client as it always uses getObject with expected class)

Changed files:
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBitmapValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseNestedValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseLongArrayValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseDoubleArrayValue.java
clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHouseResultSetTest.java
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoMultiPolygonValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoRingValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseArrayValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPointValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPolygonValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseByteArrayValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseIntArrayValue.java
clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseFloatArrayValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseTupleValue.java
clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseShortArrayValue.java

diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java
index e13bceb1..b55fc5df 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseValue.java
@@ -88,6 +88,16 @@ public interface ClickHouseValue extends Serializable {
         return v != v;
     }
 
+    /**
+     * Checks whether the value is nullable. This always returns {@code false} for
+     * nested value type.
+     *
+     * @return true if the value is nullable; false otherwise
+     */
+    default boolean isNullable() {
+        return true;
+    }
+
     /**
      * Checks if the value is null, or empty for non-null types like Array, Tuple
      * and Map.
@@ -485,7 +495,7 @@ public interface ClickHouseValue extends Serializable {
      * @return a typed object representing the value, could be null
      */
     default <T, E extends Enum<E>> T asObject(Class<T> clazz) {
-        if (clazz == null) {
+        if (clazz == null || (isNullable() && isNullOrEmpty())) {
             return null;
         } else if (clazz == boolean.class || clazz == Boolean.class) {
             return clazz.cast(asBoolean());
@@ -531,6 +541,8 @@ public interface ClickHouseValue extends Serializable {
             return clazz.cast(asArray());
         } else if (List.class.isAssignableFrom(clazz)) {
             return clazz.cast(asTuple());
+        } else if (Map.class.isAssignableFrom(clazz)) {
+            return clazz.cast(asMap());
         } else if (Enum.class.isAssignableFrom(clazz)) {
             return clazz.cast(asEnum((Class<E>) clazz));
         } else {
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseArrayValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseArrayValue.java
index 9bbdaec6..08612456 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseArrayValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseArrayValue.java
@@ -128,6 +128,11 @@ public class ClickHouseArrayValue<T> extends ClickHouseObjectValue<T[]> {
         return new ClickHouseArrayValue<>(newValue);
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBitmapValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBitmapValue.java
index 5aa18326..31c16d6e 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBitmapValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseBitmapValue.java
@@ -78,6 +78,11 @@ public class ClickHouseBitmapValue extends ClickHouseObjectValue<ClickHouseBitma
         return new ClickHouseBitmapValue(getValue());
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().isEmpty();
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoMultiPolygonValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoMultiPolygonValue.java
index 80627407..5ec4c456 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoMultiPolygonValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoMultiPolygonValue.java
@@ -149,6 +149,11 @@ public class ClickHouseGeoMultiPolygonValue extends ClickHouseObjectValue<double
         return convert(getValue(), length);
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPointValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPointValue.java
index eec0ea3b..04bbe8bf 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPointValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPointValue.java
@@ -95,6 +95,11 @@ public class ClickHouseGeoPointValue extends ClickHouseObjectValue<double[]> {
         return convert(getValue(), length);
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return false;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPolygonValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPolygonValue.java
index e59793da..899f6d4e 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPolygonValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoPolygonValue.java
@@ -144,6 +144,11 @@ public class ClickHouseGeoPolygonValue extends ClickHouseObjectValue<double[][][
         return convert(getValue(), length);
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoRingValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoRingValue.java
index 5483b5ee..074f78b1 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoRingValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseGeoRingValue.java
@@ -140,6 +140,11 @@ public class ClickHouseGeoRingValue extends ClickHouseObjectValue<double[][]> {
         return convert(getValue(), length);
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         double[][] value = getValue();
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseNestedValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseNestedValue.java
index 12d95888..5b57517e 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseNestedValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseNestedValue.java
@@ -187,6 +187,11 @@ public class ClickHouseNestedValue extends ClickHouseObjectValue<Object[][]> {
         return str;
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         Object[][] value = getValue();
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseTupleValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseTupleValue.java
index def2aa66..ebf2722c 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseTupleValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/ClickHouseTupleValue.java
@@ -152,6 +152,11 @@ public class ClickHouseTupleValue extends ClickHouseObjectValue<List<Object>> {
         return getValue();
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().isEmpty();
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseByteArrayValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseByteArrayValue.java
index 22c851f5..c2e09d2c 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseByteArrayValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseByteArrayValue.java
@@ -133,6 +133,11 @@ public class ClickHouseByteArrayValue extends ClickHouseObjectValue<byte[]> {
         return new ClickHouseByteArrayValue(Arrays.copyOf(value, value.length));
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseDoubleArrayValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseDoubleArrayValue.java
index 47fd7853..1fde864a 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseDoubleArrayValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseDoubleArrayValue.java
@@ -133,6 +133,11 @@ public class ClickHouseDoubleArrayValue extends ClickHouseObjectValue<double[]>
         return new ClickHouseDoubleArrayValue(Arrays.copyOf(value, value.length));
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseFloatArrayValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseFloatArrayValue.java
index 5444253c..a9c0fd66 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseFloatArrayValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseFloatArrayValue.java
@@ -133,6 +133,11 @@ public class ClickHouseFloatArrayValue extends ClickHouseObjectValue<float[]> {
         return new ClickHouseFloatArrayValue(Arrays.copyOf(value, value.length));
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseIntArrayValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseIntArrayValue.java
index e91536e5..b2b28432 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseIntArrayValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseIntArrayValue.java
@@ -133,6 +133,11 @@ public class ClickHouseIntArrayValue extends ClickHouseObjectValue<int[]> {
         return new ClickHouseIntArrayValue(Arrays.copyOf(value, value.length));
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseLongArrayValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseLongArrayValue.java
index dd4392ff..e39fd682 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseLongArrayValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseLongArrayValue.java
@@ -133,6 +133,11 @@ public class ClickHouseLongArrayValue extends ClickHouseObjectValue<long[]> {
         return new ClickHouseLongArrayValue(Arrays.copyOf(value, value.length));
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseShortArrayValue.java b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseShortArrayValue.java
index 2ac35b15..b7db7083 100644
--- a/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseShortArrayValue.java
+++ b/clickhouse-client/src/main/java/com/clickhouse/client/data/array/ClickHouseShortArrayValue.java
@@ -133,6 +133,11 @@ public class ClickHouseShortArrayValue extends ClickHouseObjectValue<short[]> {
         return new ClickHouseShortArrayValue(Arrays.copyOf(value, value.length));
     }
 
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
     @Override
     public boolean isNullOrEmpty() {
         return getValue().length == 0;
diff --git a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHouseResultSetTest.java b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHouseResultSetTest.java
index 56b774a0..ef975e78 100644
--- a/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHouseResultSetTest.java
+++ b/clickhouse-jdbc/src/test/java/com/clickhouse/jdbc/ClickHouseResultSetTest.java
@@ -1,6 +1,7 @@
 package com.clickhouse.jdbc;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -90,28 +91,28 @@ public class ClickHouseResultSetTest extends JdbcIntegrationTest {
     @DataProvider(name = "nullableColumns")
     private Object[][] getNullableColumns() {
         return new Object[][] {
-                new Object[] { "Bool", "false" },
-                new Object[] { "Date", "1970-01-01" },
-                new Object[] { "Date32", "1970-01-01" },
-                new Object[] { "DateTime32('UTC')", "1970-01-01 00:00:00" },
-                new Object[] { "DateTime64(3, 'UTC')", "1970-01-01 00:00:00" },
-                new Object[] { "Decimal(10,4)", "0" },
-                new Object[] { "Enum8('x'=0,'y'=1)", "x" },
-                new Object[] { "Enum16('xx'=1,'yy'=0)", "yy" },
-                new Object[] { "Float32", "0.0" },
-                new Object[] { "Float64", "0.0" },
-                new Object[] { "Int8", "0" },
-                new Object[] { "UInt8", "0" },
-                new Object[] { "Int16", "0" },
-                new Object[] { "UInt16", "0" },
-                new Object[] { "Int32", "0" },
-                new Object[] { "UInt32", "0" },
-                new Object[] { "Int64", "0" },
-                new Object[] { "UInt64", "0" },
-                new Object[] { "Int128", "0" },
-                new Object[] { "UInt128", "0" },
-                new Object[] { "Int256", "0" },
-                new Object[] { "UInt256", "0" },
+                new Object[] { "Bool", "false", Boolean.class },
+                new Object[] { "Date", "1970-01-01", LocalDate.class },
+                new Object[] { "Date32", "1970-01-01", LocalDate.class },
+                new Object[] { "DateTime32('UTC')", "1970-01-01 00:00:00", LocalDateTime.class },
+                new Object[] { "DateTime64(3, 'UTC')", "1970-01-01 00:00:00", OffsetDateTime.class },
+                new Object[] { "Decimal(10,4)", "0", BigDecimal.class },
+                new Object[] { "Enum8('x'=0,'y'=1)", "x", Integer.class },
+                new Object[] { "Enum16('xx'=1,'yy'=0)", "yy", String.class },
+                new Object[] { "Float32", "0.0", Float.class },
+                new Object[] { "Float64", "0.0", Double.class },
+                new Object[] { "Int8", "0", Byte.class },
+                new Object[] { "UInt8", "0", Short.class },
+                new Object[] { "Int16", "0", Short.class },
+                new Object[] { "UInt16", "0", Integer.class },
+                new Object[] { "Int32", "0", Integer.class },
+                new Object[] { "UInt32", "0", Long.class },
+                new Object[] { "Int64", "0", Long.class },
+                new Object[] { "UInt64", "0", BigInteger.class },
+                new Object[] { "Int128", "0", BigInteger.class },
+                new Object[] { "UInt128", "0", BigInteger.class },
+                new Object[] { "Int256", "0", BigInteger.class },
+                new Object[] { "UInt256", "0", BigInteger.class },
         };
     }
 
@@ -296,7 +297,7 @@ public class ClickHouseResultSetTest extends JdbcIntegrationTest {
     }
 
     @Test(dataProvider = "nullableColumns", groups = "integration")
-    public void testNullValue(String columnType, String defaultValue) throws Exception {
+    public void testNullValue(String columnType, String defaultValue, Class<?> clazz) throws Exception {
         Properties props = new Properties();
         props.setProperty(JdbcConfig.PROP_NULL_AS_DEFAULT, "2");
         String tableName = "test_query_null_value_" + columnType.split("\\\\(")[0].trim().toLowerCase();
@@ -312,6 +313,8 @@ public class ClickHouseResultSetTest extends JdbcIntegrationTest {
                 Assert.assertTrue(rs.next(), "Should have at least one row");
                 Assert.assertEquals(rs.getInt(1), 1);
                 Assert.assertEquals(rs.getString(2), defaultValue);
+                Assert.assertNotNull(rs.getObject(2));
+                Assert.assertNotNull(rs.getObject(2, clazz));
                 Assert.assertFalse(rs.wasNull(), "Should not be null");
                 Assert.assertFalse(rs.next(), "Should have only one row");
             }
@@ -321,6 +324,7 @@ public class ClickHouseResultSetTest extends JdbcIntegrationTest {
                 Assert.assertTrue(rs.next(), "Should have at least one row");
                 Assert.assertEquals(rs.getInt(1), 1);
                 Assert.assertEquals(rs.getString(2), null);
+                Assert.assertEquals(rs.getObject(2, clazz), null);
                 Assert.assertTrue(rs.wasNull(), "Should be null");
                 Assert.assertFalse(rs.next(), "Should have only one row");
             }
@@ -330,6 +334,7 @@ public class ClickHouseResultSetTest extends JdbcIntegrationTest {
                 Assert.assertTrue(rs.next(), "Should have at least one row");
                 Assert.assertEquals(rs.getInt(1), 1);
                 Assert.assertEquals(rs.getString(2), null);
+                Assert.assertEquals(rs.getObject(2, clazz), null);
                 Assert.assertTrue(rs.wasNull(), "Should be null");
                 Assert.assertFalse(rs.next(), "Should have only one row");
             }