This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch chore-clickhouse-empty-list in repository https://gitbox.apache.org/repos/asf/camel.git
commit b0b8f56b8d8b33cbc8969c18b2efd6b56ffd4006 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Nov 2 21:53:33 2026 +0100 chore: camel-clickhouse - treat an empty List body as a 0-row insert An empty List body was passed to Client.insert, and client-v2 fails it with "Data cannot be empty". The producer now skips the client call for an empty List and sets CamelClickHouseWrittenRows to 0. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/catalog/docs/clickhouse-component.adoc | 1 + .../src/main/docs/clickhouse-component.adoc | 1 + .../component/clickhouse/ClickHouseProducer.java | 8 ++++++-- .../component/clickhouse/ClickHouseProducerTest.java | 20 +++++++++++--------- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/clickhouse-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/clickhouse-component.adoc index a2a03d957ed4..e052b5bf5a62 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/clickhouse-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/clickhouse-component.adoc @@ -63,6 +63,7 @@ types are `InputStream`, `byte[]`, `String`, `java.io.File` (matched to the `for whose class has been registered on the shared `Client` with `Client.register(Class, TableSchema)`. A `List` of `Map` is not supported: marshal the rows to the `format` (such as `JSONEachRow`) first. When the body is a `List` and `batchSize` is greater than 0, the list is split into batches of that size, each sent as a separate insert. +An empty `List` is a 0-row insert: nothing is sent to ClickHouse. The number of written rows is set on the `CamelClickHouseWrittenRows` header. | `query` | Runs the SQL statement contained in the message body and returns the result set in the configured `format`: a `String` body for text formats (such as `JSONEachRow`, `CSV` or `TSV`), and a `byte[]` body for binary diff --git a/components/camel-clickhouse/src/main/docs/clickhouse-component.adoc b/components/camel-clickhouse/src/main/docs/clickhouse-component.adoc index a2a03d957ed4..e052b5bf5a62 100644 --- a/components/camel-clickhouse/src/main/docs/clickhouse-component.adoc +++ b/components/camel-clickhouse/src/main/docs/clickhouse-component.adoc @@ -63,6 +63,7 @@ types are `InputStream`, `byte[]`, `String`, `java.io.File` (matched to the `for whose class has been registered on the shared `Client` with `Client.register(Class, TableSchema)`. A `List` of `Map` is not supported: marshal the rows to the `format` (such as `JSONEachRow`) first. When the body is a `List` and `batchSize` is greater than 0, the list is split into batches of that size, each sent as a separate insert. +An empty `List` is a 0-row insert: nothing is sent to ClickHouse. The number of written rows is set on the `CamelClickHouseWrittenRows` header. | `query` | Runs the SQL statement contained in the message body and returns the result set in the configured `format`: a `String` body for text formats (such as `JSONEachRow`, `CSV` or `TSV`), and a `byte[]` body for binary diff --git a/components/camel-clickhouse/src/main/java/org/apache/camel/component/clickhouse/ClickHouseProducer.java b/components/camel-clickhouse/src/main/java/org/apache/camel/component/clickhouse/ClickHouseProducer.java index 1dea7d1c7ac3..62816152b236 100644 --- a/components/camel-clickhouse/src/main/java/org/apache/camel/component/clickhouse/ClickHouseProducer.java +++ b/components/camel-clickhouse/src/main/java/org/apache/camel/component/clickhouse/ClickHouseProducer.java @@ -124,7 +124,11 @@ public class ClickHouseProducer extends DefaultProducer { } private long insertList(Client client, String target, List<?> data, InsertSettings settings) throws Exception { - if (!data.isEmpty() && data.get(0) instanceof Map) { + if (data.isEmpty()) { + // client-v2 fails on an empty list (Data cannot be empty), so an empty list is a 0-row insert + return 0; + } + if (data.get(0) instanceof Map) { throw new ClickHouseException( "Cannot insert a List of Maps into " + target + ": a List body must hold objects whose class is" + " registered on the Client with Client.register(Class, TableSchema)." @@ -150,7 +154,7 @@ public class ClickHouseProducer extends DefaultProducer { future = client.insert(target, batch, settings); } catch (NullPointerException e) { // client-v2 throws a NullPointerException that does not say why when no class is registered for the table - Object first = batch.isEmpty() ? null : batch.get(0); + Object first = batch.get(0); String what = first != null ? "a List of " + first.getClass().getName() : "the List"; throw new ClickHouseException( "Cannot insert " + what + " into " + target diff --git a/components/camel-clickhouse/src/test/java/org/apache/camel/component/clickhouse/ClickHouseProducerTest.java b/components/camel-clickhouse/src/test/java/org/apache/camel/component/clickhouse/ClickHouseProducerTest.java index e25747a17896..9c28d79cabe2 100644 --- a/components/camel-clickhouse/src/test/java/org/apache/camel/component/clickhouse/ClickHouseProducerTest.java +++ b/components/camel-clickhouse/src/test/java/org/apache/camel/component/clickhouse/ClickHouseProducerTest.java @@ -182,17 +182,19 @@ class ClickHouseProducerTest extends CamelTestSupport { } @Test - void insertEmptyListKeepsTheClientErrorAsCause() { - // the message must not fail on the empty batch and hide the NullPointerException of the client - when(client.insert(anyString(), anyList(), any(InsertSettings.class))).thenThrow(new NullPointerException()); + void insertEmptyListIsZeroRowInsert() throws Exception { + // client-v2 fails an empty list with "Data cannot be empty", so the client must not be called + when(client.insert(anyString(), anyList(), any(InsertSettings.class))) + .thenThrow(new IllegalArgumentException("Data cannot be empty")); - Exchange out = template.send("direct:insert", exchange -> exchange.getIn().setBody(List.of())); + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(1); + mock.expectedHeaderReceived(ClickHouseConstants.WRITTEN_ROWS, 0L); - assertThat(out.getException()) - .isInstanceOf(ClickHouseException.class) - .hasMessageContaining("Cannot insert the List") - .hasMessageContaining("Client.register") - .hasCauseInstanceOf(NullPointerException.class); + template.sendBody("direct:insert", List.of()); + + MockEndpoint.assertIsSatisfied(context); + verify(client, never()).insert(anyString(), anyList(), any(InsertSettings.class)); } @Test
