clickhouse-java icon indicating copy to clipboard operation
clickhouse-java copied to clipboard

"getResultSet not implemented" error when mapping Array(Nullable(UInt32)) after updating from 0.3.2-patch11

Open gaabs opened this issue 1 year ago • 0 comments

Describe the bug

Upon trying to update from 0.3.2-patch11 to 0.6.0-patch1:http, some behavior changes were noticed regarding the mapping of Array(Nullable(UInt32)) data type when using JDBI. The previous code used to work correctly, but it started to throw a getResultSet not implemented after updating the clickhouse dependency. It seems to have started at the 0.4.0 version.

Upon further investigation, it was observed that Array(Nullable(UInt32)) is now being mapped to an array of UnsignedInteger, instead of to Long. That can be seen inspecting JDBI's buildArray method. That ends up trying to use the resultSet, which is not implemented in the clickhouse dependency.

It was also noticed thatNullable(UInt32) is still being mapped to Long, the same as before.

Is that behavior change expected?

Steps to reproduce

  1. Insert Array(Nullable(UInt32)) data into some clickhouse table
  2. Retrieve Array(Nullable(UInt32)) data using JDBI

Expected behavior

Array(Nullable(UInt32)) should be mapped to Long[].

Code example

build.gradle with used dependencies

plugins {
    id("io.micronaut.application") version "4.2.1"
}

version = "0.1"
group = "com.example"

repositories {
    mavenCentral()
}

ext {
    micronautVersion = '4.2.3'
}

dependencies {
    annotationProcessor "org.projectlombok:lombok:1.18.30"
    compileOnly "org.projectlombok:lombok:1.18.30"

    implementation "org.jdbi:jdbi3-sqlobject:3.44.0"
    implementation "com.clickhouse:clickhouse-jdbc:0.6.0-patch1:http"
    implementation 'org.apache.httpcomponents.client5:httpclient5:5.3.1'

    runtimeOnly "io.micronaut.sql:micronaut-jdbc-hikari"
    runtimeOnly "org.yaml:snakeyaml"
}

java {
    sourceCompatibility = JavaVersion.toVersion("21")
    targetCompatibility = JavaVersion.toVersion("21")
}

micronaut {
    testRuntime("junit5")
    processing {
        incremental(true)
        annotations("com.example.*")
    }
}

Sample Bean

package com.example.dao.dto;

import lombok.Value;

@Value
public class SampleArrayDto {
    String id;
    Long[] uint32Array;
}

Sample DAO

package com.example.dao.clickhouse;

import com.example.dao.dto.SampleArrayDto;
import org.jdbi.v3.sqlobject.config.RegisterConstructorMapper;
import org.jdbi.v3.sqlobject.customizer.BindBean;
import org.jdbi.v3.sqlobject.statement.SqlQuery;
import org.jdbi.v3.sqlobject.statement.SqlUpdate;

import java.util.List;

public interface SampleArrayDao {
    @SqlUpdate("INSERT INTO sample_array (id, uint32_array) VALUES (:id, :uint32Array::Array(Nullable(UInt32)))")
    void insert(@BindBean SampleArrayDto sampleArrayDto);

    @SqlQuery("SELECT * FROM sample_array")
    @RegisterConstructorMapper(SampleArrayDto.class)
    List<SampleArrayDto> getAll();

    @SqlUpdate("TRUNCATE TABLE sample_array")
    void truncate();
}

Jdbi Factory

package com.example.factory;

import io.micronaut.context.annotation.Factory;
import jakarta.inject.Singleton;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.sqlobject.SqlObjectPlugin;

import javax.sql.DataSource;

@Factory
public class JdbiFactory {
    @Singleton
    Jdbi newClickhouseJdbi(DataSource dataSource) {
        Jdbi jdbi = Jdbi.create(dataSource);
        jdbi.installPlugin(new SqlObjectPlugin());
        return jdbi;
    }
}

DAO Factory

package com.example.factory;

import com.example.dao.clickhouse.SampleArrayDao;
import io.micronaut.context.annotation.Factory;
import jakarta.inject.Singleton;
import org.jdbi.v3.core.Jdbi;

@Factory
public class ClickhouseDaoFactory {
    @Singleton
    SampleArrayDao getSampleArrayDao(Jdbi jdbi) {
        return jdbi.onDemand(SampleArrayDao.class);
    }
}

Test that reproduces the issue

package com.example.dao.clickhouse;

import com.example.dao.dto.SampleArrayDto;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

@MicronautTest
class SampleArrayDaoTest {
    @Inject private SampleArrayDao sampleArrayDao;

    @BeforeEach
    @AfterEach
    void clearTable() {
        sampleArrayDao.truncate();
    }

    @Test
    void getAll() {
        var sampleArrayDto = new SampleArrayDto("1", new Long[]{10L});
        sampleArrayDao.insert(sampleArrayDto);

        sampleArrayDao.getAll(); // In this line the issue happens
    }
}

application.yml with datasource configuration

datasources:
  clickhouse:
    url: "jdbc:ch://localhost:8123/default"
    username: default
    driverClassName: com.clickhouse.jdbc.ClickHouseDriver

(Whole sample project can be uploaded if that helps)

Error log

Exception thrown mapping result set into return type [statement:"SELECT * FROM sample_array", arguments:{positional:{}, named:{}, finder:[]}]
org.jdbi.v3.core.result.ResultSetException: Exception thrown mapping result set into return type [statement:"SELECT * FROM sample_array", arguments:{positional:{}, named:{}, finder:[]}]
	at org.jdbi.v3.core.result.internal.ResultSetResultIterator.next(ResultSetResultIterator.java:102)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at org.jdbi.v3.core.result.ResultIterator.forEachRemaining(ResultIterator.java:39)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.jdbi.v3.core.result.ResultIterable.collect(ResultIterable.java:341)
	at org.jdbi.v3.sqlobject.statement.internal.ResultReturner$CollectedResultReturner.mappedResult(ResultReturner.java:315)
	at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.lambda$configureReturner$0(SqlQueryHandler.java:65)
	at org.jdbi.v3.sqlobject.statement.internal.CustomizingStatementHandler.invoke(CustomizingStatementHandler.java:197)
	at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.invoke(SqlQueryHandler.java:27)
	at org.jdbi.v3.core.extension.ExtensionMetadata$ExtensionHandlerInvoker.lambda$invoke$0(ExtensionMetadata.java:345)
	at org.jdbi.v3.core.AbstractHandleSupplier.invokeInContext(AbstractHandleSupplier.java:36)
	at org.jdbi.v3.core.extension.ExtensionMetadata$ExtensionHandlerInvoker.call(ExtensionMetadata.java:363)
	at org.jdbi.v3.core.extension.ExtensionMetadata$ExtensionHandlerInvoker.invoke(ExtensionMetadata.java:346)
	at org.jdbi.v3.core.extension.ExtensionFactoryDelegate.lambda$attach$0(ExtensionFactoryDelegate.java:118)
	at org.jdbi.v3.core.internal.OnDemandExtensions.invoke(OnDemandExtensions.java:85)
	at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$2(OnDemandExtensions.java:71)
	at org.jdbi.v3.core.Jdbi.callWithExtension(Jdbi.java:560)
	at org.jdbi.v3.core.Jdbi.withExtension(Jdbi.java:547)
	at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$3(OnDemandExtensions.java:71)
	at jdk.proxy3/jdk.proxy3.$Proxy27.getAll(Unknown Source)
	at com.example.dao.clickhouse.SampleArrayDaoTest.getAll(SampleArrayDaoTest.java:25)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:142)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:157)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:114)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:129)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.sql.SQLFeatureNotSupportedException: getResultSet not implemented
	at com.clickhouse.jdbc.SqlExceptionUtils.unsupportedError(SqlExceptionUtils.java:152)
	at com.clickhouse.jdbc.ClickHouseArray.getResultSet(ClickHouseArray.java:73)
	at org.jdbi.v3.core.array.ArrayColumnMapper.buildFromResultSet(ArrayColumnMapper.java:74)
	at org.jdbi.v3.core.array.ArrayColumnMapper.buildArray(ArrayColumnMapper.java:69)
	at org.jdbi.v3.core.array.ArrayColumnMapper.map(ArrayColumnMapper.java:47)
	at org.jdbi.v3.core.mapper.SingleColumnMapper.lambda$new$0(SingleColumnMapper.java:41)
	at org.jdbi.v3.core.mapper.SingleColumnMapper.map(SingleColumnMapper.java:55)
	at org.jdbi.v3.core.mapper.reflect.ConstructorMapper$BoundConstructorMapper.map(ConstructorMapper.java:381)
	at org.jdbi.v3.core.result.internal.ResultSetResultIterator.next(ResultSetResultIterator.java:100)
	... 32 more


getResultSet not implemented
java.sql.SQLFeatureNotSupportedException: getResultSet not implemented
	at com.clickhouse.jdbc.SqlExceptionUtils.unsupportedError(SqlExceptionUtils.java:152)
	at com.clickhouse.jdbc.ClickHouseArray.getResultSet(ClickHouseArray.java:73)
	at org.jdbi.v3.core.array.ArrayColumnMapper.buildFromResultSet(ArrayColumnMapper.java:74)
	at org.jdbi.v3.core.array.ArrayColumnMapper.buildArray(ArrayColumnMapper.java:69)
	at org.jdbi.v3.core.array.ArrayColumnMapper.map(ArrayColumnMapper.java:47)
	at org.jdbi.v3.core.mapper.SingleColumnMapper.lambda$new$0(SingleColumnMapper.java:41)
	at org.jdbi.v3.core.mapper.SingleColumnMapper.map(SingleColumnMapper.java:55)
	at org.jdbi.v3.core.mapper.reflect.ConstructorMapper$BoundConstructorMapper.map(ConstructorMapper.java:381)
	at org.jdbi.v3.core.result.internal.ResultSetResultIterator.next(ResultSetResultIterator.java:100)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at org.jdbi.v3.core.result.ResultIterator.forEachRemaining(ResultIterator.java:39)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.jdbi.v3.core.result.ResultIterable.collect(ResultIterable.java:341)
	at org.jdbi.v3.sqlobject.statement.internal.ResultReturner$CollectedResultReturner.mappedResult(ResultReturner.java:315)
	at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.lambda$configureReturner$0(SqlQueryHandler.java:65)
	at org.jdbi.v3.sqlobject.statement.internal.CustomizingStatementHandler.invoke(CustomizingStatementHandler.java:197)
	at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.invoke(SqlQueryHandler.java:27)
	at org.jdbi.v3.core.extension.ExtensionMetadata$ExtensionHandlerInvoker.lambda$invoke$0(ExtensionMetadata.java:345)
	at org.jdbi.v3.core.AbstractHandleSupplier.invokeInContext(AbstractHandleSupplier.java:36)
	at org.jdbi.v3.core.extension.ExtensionMetadata$ExtensionHandlerInvoker.call(ExtensionMetadata.java:363)
	at org.jdbi.v3.core.extension.ExtensionMetadata$ExtensionHandlerInvoker.invoke(ExtensionMetadata.java:346)
	at org.jdbi.v3.core.extension.ExtensionFactoryDelegate.lambda$attach$0(ExtensionFactoryDelegate.java:118)
	at org.jdbi.v3.core.internal.OnDemandExtensions.invoke(OnDemandExtensions.java:85)
	at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$2(OnDemandExtensions.java:71)
	at org.jdbi.v3.core.Jdbi.callWithExtension(Jdbi.java:560)
	at org.jdbi.v3.core.Jdbi.withExtension(Jdbi.java:547)
	at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$3(OnDemandExtensions.java:71)
	at jdk.proxy3/jdk.proxy3.$Proxy27.getAll(Unknown Source)
	at com.example.dao.clickhouse.SampleArrayDaoTest.getAll(SampleArrayDaoTest.java:25)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension$2.proceed(MicronautJunit5Extension.java:142)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptEach(AbstractMicronautExtension.java:157)
	at io.micronaut.test.extensions.AbstractMicronautExtension.interceptTest(AbstractMicronautExtension.java:114)
	at io.micronaut.test.extensions.junit5.MicronautJunit5Extension.interceptTestMethod(MicronautJunit5Extension.java:129)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Configuration

Environment

  • Client version: 0.6.0-patch1
  • Language version: 21.0.2
  • OS: Ubuntu 22.04.2 LTS

ClickHouse server

  • ClickHouse Server version: altinity/clickhouse-server:23.3.8.22.altinitystable
  • CREATE TABLE statements for tables involved:
CREATE TABLE IF NOT EXISTS sample_array
(
    id String,
    uint32_array Array(Nullable(UInt32))
)
ENGINE = ReplacingMergeTree()
ORDER BY (id)

gaabs avatar Feb 17 '24 23:02 gaabs