[
https://issues.apache.org/jira/browse/CAMEL-24255?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen resolved CAMEL-24255.
---------------------------------
Resolution: Fixed
Fixed via https://github.com/apache/camel/pull/25102
> camel-duckdb - A component for duckdb and test-infra
> ----------------------------------------------------
>
> Key: CAMEL-24255
> URL: https://issues.apache.org/jira/browse/CAMEL-24255
> Project: Camel
> Issue Type: New Feature
> Reporter: Claus Ibsen
> Assignee: Omar Atie
> Priority: Major
> Fix For: 4.22.0
>
> Attachments: camel-duckdb-demo.zip
>
>
> Would be nice with duckdb as a component and test infra so we can have
> in-process databases and integrate with this database
> following [~davsclaus] requirements above : I'd like to propose a new
> *camel-duckdb* component and a matching *camel-test-infra-duckdb* module for
> integrating with [DuckDB|https://duckdb.org/], the in-process analytical SQL
> database.
> Camel can reach DuckDB today through *camel-jdbc* or *camel-sql* with the
> official JDBC driver (\{{org.duckdb:duckdb_jdbc}}), but that path treats
> DuckDB like any other JDBC source: generic \{{PreparedStatement}} usage, no
> first-class support for DuckDB’s strengths (embedded file/memory databases,
> fast bulk load via \{{COPY}}, reading Parquet/CSV/JSON as tables,
> \{{Appender}}-style batch ingest, and \{{ATTACH}}/extensions). Teams building
> local analytics, CI-friendly pipelines, and “drop a file → query in SQL”
> flows end up wiring DataSources and SQL strings by hand.
> The idea is a dedicated component on the DuckDB JDBC driver (and optionally a
> shared \{{java.sql.Connection}} / \{{javax.sql.DataSource}} bean) that
> exposes embedded analytics workflows as idiomatic endpoint options, plus test
> infrastructure that runs *in-process* (memory or temp file) so component and
> IT tests do not require Docker by default.
> duckdb:analytics/events?operation=insert&table=events&batchSize=1000
> This follows the same product pattern as *camel-clickhouse* (vendor-focused
> component + \{{camel-test-infra-*}}) rather than stretching *camel-jdbc* with
> DuckDB-specific URI semantics.
> h2. *Why a dedicated component (vs camel-jdbc / camel-sql)*
> - *Embedded-first*: first-class \{{databasePath}} (file, \{{:memory:}}, or
> directory) instead of opaque JDBC URLs in every route.
> - *Bulk ingest*: \{{COPY ... FROM}} / read functions for CSV, Parquet, JSON
> — route bodies and \{{camel-file}} drops map naturally to DuckDB load
> patterns.
> - *Batch append*: use DuckDB’s JDBC \{{Appender}} (where applicable) for
> high-throughput inserts from \{{List<Map>}} / POJOs without row-by-row
> \{{INSERT}}.
> - *Analytics SQL*: \{{read_parquet()}}, \{{read_csv()}}, \{{read_json()}}
> in \{{operation=query}} for federated “query files in place” pipelines.
> - *Testability*: \{{camel-test-infra-duckdb}} provides a shared in-process
> database for unit and integration tests (no container required for the
> default profile), with optional remote JDBC URL for CI that prefers an
> external instance.
> h2. *Design*
> - *Producer-first* (initial scope): DuckDB is not a message broker; polling
> consumers remain better served by *camel-sql* if needed. Phase 1 focuses on
> producer operations; a poll-based consumer can be a follow-up if there is
> demand.
> - *Operations (initial):* \{{execute}} (DDL/DML, no result set required),
> \{{query}} (SQL → body as \{{List<Map>}} or JSON string), \{{insert}}
> (structured batch into a table), \{{copy}} (bulk load from file path or
> stream in body), \{{ping}} (connectivity / \{{SELECT 1}}).
> - *Connection model:* autowire a shared \{{Connection}} or \{{DataSource}}
> bean, *or* configure \{{databasePath}} / \{{jdbcUrl}} on the component or
> endpoint. One embedded database per Camel context should be documented to
> avoid accidental multi-writer issues on the same file.
> - *Body types:* for \{{insert}} — \{{List<Map<String,Object>>}},
> \{{List<POJO>}}, or JSON array string; for \{{copy}} — \{{java.io.File}},
> path \{{String}}, or \{{InputStream}} with \{{format}} (CSV, Parquet, JSON);
> for \{{query}} — SQL string in body or \{{query}} URI option.
> - *Tests:* \{{camel-test-infra-duckdb}} with
> \{{DuckDBServiceFactory.createService()}} (in-memory + temp-file
> implementations); component unit tests without infra; optional IT module
> using the infra service. AssertJ in new tests per project convention.
> - *Docs:* component page under \{{components/camel-duckdb}}, catalog JSON,
> upgrade-guide entry on \{{main}} for the target 4.x release.
> h2. *Use cases*
> {*}Use Case 1: In-process analytics in integration tests and JBang routes\{*}
> Use a file-backed or memory DuckDB for fast local SQL without external
> services.
> {code:java}
> from("timer:tick?period=5000")
> .setBody(constant("INSERT INTO metrics VALUES (current_timestamp, 42)"))
> .to("duckdb:metrics.db?operation=execute");
> {code}
> {*}Use Case 2: Ingest Parquet/CSV dropped by camel-file\{*}
> Load landed files with DuckDB native read/copy instead of parsing in Java.
> {code:java}
> from("file:landing/parquet?include=.*\\.parquet")
>
> .to("duckdb:warehouse.db?operation=copy&table=staging_events&format=parquet")
> .to("duckdb:warehouse.db?operation=query")
> .constant("INSERT INTO events SELECT * FROM staging_events")
> .log("Loaded ${header.CamelFileName}");
> {code}
> {*}Use Case 3: Kafka → batch insert into embedded DuckDB\{*}
> Buffer events and append batches for dashboarding or downstream export.
> {code:java}
> from("kafka:events?groupId=local-analytics")
> .unmarshal().json(JsonArray.class)
> .aggregate(constant(true), new GroupedBodyAggregationStrategy())
> .completionSize(500).completionTimeout(2000)
>
> .to("duckdb:analytics.db/events?operation=insert&table=events&batchSize=500");
> {code}
> {*}Use Case 4: Federated query over files (read_parquet / read_csv)\{*}
> Run analytics SQL over files without importing them into a permanent table
> first.
> {code:java}
> from("direct:report")
> .setBody(constant(
> "SELECT region, count(*) AS n FROM read_parquet('data/**/*.parquet')
> GROUP BY region"))
> .to("duckdb::memory:?operation=query&resultFormat=JSON")
> .to("platform-http:proxy/report");
> {code}
> {*}Use Case 5: ETL staging — land JSON, merge into curated tables\{*}
> Combine \{{execute}} and \{{query}} for lightweight ETL in a single embedded
> DB.
> {code:java}
> from("direct:stage")
> .to("duckdb:etl.db?operation=copy&table=raw_orders&format=JSON")
> .setBody(constant(
> "INSERT INTO orders SELECT * FROM raw_orders WHERE id IS NOT NULL"))
> .to("duckdb:etl.db?operation=execute");
> {code}
> {*}Use Case 6: Health / readiness for routes using embedded DuckDB\{*}
> Verify the database file is open and writable before starting heavy
> processing.
> {code:java}
> from("timer:health?period=30000")
> .to("duckdb:app.db?operation=ping")
> .choice()
> .when(header("CamelDuckDbPingOk").isEqualTo(true))
> .log("DuckDB OK")
> .otherwise()
> .to("direct:alert")
> .end();
> {code}
> {*}Use Case 7: Shared test-infra for other components\{*}
> Other modules’ ITs can depend on \{{camel-test-infra-duckdb}} for a
> consistent embedded database (similar to \{{camel-test-infra-clickhouse}} for
> ClickHouse).
> {code:java}
> @RegisterExtension
> static DuckDBService db = DuckDBServiceFactory.createService();
> {code}
> h2. *Proposed URI shape and options (initial)*
> - *Scheme:* \{{duckdb:databasePath}} — file path (e.g. \{{analytics.db}}),
> \{{:memory:}}, or empty for default in-memory; optional path segment for
> default schema/table naming
> - \{{jdbcUrl}} — optional full JDBC URL override (\{{jdbc:duckdb:...}})
> - \{{operation}} — execute | query | insert | copy | ping (default:
> \{{execute}} for body-as-SQL, or \{{insert}} when \{{table}} is set — exact
> default TBD in PR)
> - \{{table}} — target table for \{{insert}} / \{{copy}}
> - \{{query}} — static SQL for \{{query}} when body is empty
> - \{{batchSize}} — split list bodies for \{{insert}}
> - \{{format}} — csv | parquet | json | auto (for \{{copy}} and some
> \{{insert}} paths)
> - \{{readOnly}} — open embedded database read-only where supported
> - \{{resultFormat}} — for \{{query}}: \{{ListMap}} (default) | \{{JSON}} |
> \{{Stream}}
> - \{{username}} / \{{password}} — only when using remote DuckDB via
> extension/wire protocol (future; mark \{{secret}} on password)
> h2. *Proposed message headers*
> - \{{CamelDuckDbOperation}} — override endpoint operation
> - \{{CamelDuckDbDatabasePath}} — override database location for this message
> - \{{CamelDuckDbTable}} — override target table
> - \{{CamelDuckDbQuery}} — override SQL for \{{query}} / \{{execute}}
> - \{{CamelDuckDbRowsWritten}} — (out) rows affected / inserted where
> available
> - \{{CamelDuckDbPingOk}} — (out) boolean from \{{ping}}
> h2. *Test infrastructure (camel-test-infra-duckdb)*
> - \{{DuckDBService}} — lifecycle (start/stop), JDBC URL, optional temp
> directory cleanup
> - \{{DuckDBLocalEmbeddedService}} — \{{:memory:}} or temp \{{*.db}} file
> (default for local/CI)
> - \{{DuckDBServiceFactory}} — \{{SimpleTestServiceBuilder}} pattern aligned
> with \{{camel-test-infra-clickhouse}}
> - \{{RemoteDuckDBInfraService}} — optional JDBC URL from environment for
> contributors who run a external DuckDB instance (document in README; no
> Docker Hub requirement for default tests)
> - Architectures: embedded driver bundles native libs — document supported
> platforms and use Maven skip properties for arches without published DuckDB
> JDBC builds if CI requires it (same approach as other native-backed
> components)
> h2. *Out of scope (initial PR, can be follow-up tickets)*
> - Full parity with every DuckDB extension (httpfs, postgres scanner, etc.)
> — enable via \{{PRAGMA}}/session SQL or documented \{{initScript}} option
> instead
> - Replacing *camel-sql* polling consumers for DuckDB
> - DuckDB *server* mode as a hard dependency for default tests (embedded
> only first)
> h2. *Deliverables*
> - Maven module \{{components/camel-duckdb}} (Component, Endpoint, Producer,
> constants, JSON metadata)
> - Maven module \{{test-infra/camel-test-infra-duckdb}}
> - Component documentation + user-manual upgrade-guide entry for the target
> 4.x release
> - Unit tests + integration tests using test-infra
> I'm happy to implement this following the *camel-clickhouse* /
> *camel-influxdb2* layout. Feedback welcome on the initial operation set,
> default connection mode (embedded file vs memory), and whether \{{copy}}
> should be in v1 or deferred to a second PR.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)