Skip to content

Commit 239fd4c

Browse files
committed
feat(connectors): add generic JDBC source connector
Adds a JDBC source connector that polls a configured SQL query against any JDBC-compliant database (PostgreSQL, MySQL, Oracle, SQL Server, H2) through an embedded JVM and produces each row as a JSON message. Supports bulk and incremental (offset-tracked) modes with persisted offset state. Also adds the shared connector integration-test harness and a small TCP listener change so that harness can read the server's written runtime config when bound to a fixed port. The matching JDBC sink connector follows in a separate PR.
1 parent d6059a1 commit 239fd4c

23 files changed

Lines changed: 3943 additions & 7 deletions

File tree

.config/nextest.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ max-threads = 1
4848
filter = 'package(integration) and test(/connectors::elasticsearch::/)'
4949
test-group = "elasticsearch"
5050

51+
# JDBC tests each start their own iggy-server plus a Postgres testcontainer and
52+
# an embedded JVM, and they share a JDBC driver JAR downloaded to a single path
53+
# on first run. nextest runs each test in its own process, so the in-source
54+
# `#[serial]` guard does not serialize them here; `max-threads = 1` does, which
55+
# avoids resource contention and a race to download the same driver JAR.
56+
[test-groups.jdbc]
57+
max-threads = 1
58+
59+
[[profile.default.overrides]]
60+
filter = 'package(integration) and test(/connectors::jdbc::/)'
61+
test-group = "jdbc"
62+
5163
[profile.default]
5264
slow-timeout = { period = "60s", terminate-after = 5 }
5365

.github/workflows/_build_rust_artifacts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ on:
4646
connector_plugins:
4747
type: string
4848
required: false
49-
default: "iggy_connector_elasticsearch_sink,iggy_connector_elasticsearch_source,iggy_connector_iceberg_sink,iggy_connector_postgres_sink,iggy_connector_postgres_source,iggy_connector_quickwit_sink,iggy_connector_random_source,iggy_connector_s3_sink,iggy_connector_stdout_sink"
49+
default: "iggy_connector_elasticsearch_sink,iggy_connector_elasticsearch_source,iggy_connector_iceberg_sink,iggy_connector_jdbc_source,iggy_connector_postgres_sink,iggy_connector_postgres_source,iggy_connector_quickwit_sink,iggy_connector_random_source,iggy_connector_s3_sink,iggy_connector_stdout_sink"
5050
description: "Comma-separated list of connector plugin crates to build as shared libraries"
5151
outputs:
5252
artifact_name:

.github/workflows/edge-release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ jobs:
104104
- `iggy_connector_elasticsearch_sink`
105105
- `iggy_connector_elasticsearch_source`
106106
- `iggy_connector_iceberg_sink`
107+
- `iggy_connector_jdbc_source`
107108
- `iggy_connector_postgres_sink`
108109
- `iggy_connector_postgres_source`
109110
- `iggy_connector_quickwit_sink`

Cargo.lock

Lines changed: 53 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ members = [
4646
"core/connectors/sinks/stdout_sink",
4747
"core/connectors/sources/elasticsearch_source",
4848
"core/connectors/sources/influxdb_source",
49+
"core/connectors/sources/jdbc_source",
4950
"core/connectors/sources/postgres_source",
5051
"core/connectors/sources/random_source",
5152
"core/consensus",

core/connectors/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ Please refer to the **[Source documentation](https://github.com/apache/iggy/tree
9797
### Available Sources
9898
9999
- **Elasticsearch Source** - polls documents from Elasticsearch indices
100+
- **JDBC Source** - reads rows from any JDBC-compliant database (PostgreSQL, MySQL, Oracle, SQL Server, H2) via an embedded JVM; bulk and incremental modes
100101
- **PostgreSQL Source** - reads rows from PostgreSQL tables with multiple consumption strategies (delete after read, mark as processed, timestamp tracking)
101102
- **Random Source** - generates random test messages (useful for testing/development)
102103
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# Example JDBC Source Connector Configuration - BULK MODE
19+
# Bulk mode works with ALL JDBC databases without any special requirements
20+
# No tracking column needed - just executes your query and fetches results
21+
22+
type = "source"
23+
key = "jdbc_bulk_example"
24+
enabled = true
25+
version = 0
26+
name = "JDBC Bulk Mode Source"
27+
path = "target/release/libiggy_connector_jdbc_source"
28+
plugin_config_format = "toml"
29+
30+
[plugin_config]
31+
# This example uses PostgreSQL, but bulk mode works identically with:
32+
# MySQL, Oracle, SQL Server, H2, Derby, DB2, etc.
33+
jdbc_url = "jdbc:postgresql://localhost:5432/warehouse"
34+
35+
# Database credentials can be in URL or separate
36+
# jdbc_url = "jdbc:postgresql://localhost:5432/warehouse?user=myuser&password=mypass"
37+
username = "warehouse_user"
38+
password = "secret"
39+
40+
driver_class = "org.postgresql.Driver"
41+
driver_jar_path = "/opt/jdbc-drivers/postgresql-42.6.0.jar"
42+
43+
# Bulk mode: Any valid SELECT query
44+
# Can include JOINs, aggregations, complex WHERE clauses, etc.
45+
query = """
46+
SELECT
47+
p.product_id,\
48+
p.product_name,
49+
p.category,
50+
p.price,
51+
COUNT(o.order_id) as total_orders,
52+
SUM(o.quantity) as total_quantity
53+
FROM products p
54+
LEFT JOIN orders o ON p.product_id = o.product_id
55+
GROUP BY p.product_id, p.product_name, p.category, p.price
56+
"""
57+
58+
# Poll once per hour for daily snapshots
59+
poll_interval = "1h"
60+
61+
# Large batch size for full table scans
62+
batch_size = 10000
63+
64+
# BULK MODE - no tracking column needed!
65+
mode = "bulk"
66+
67+
# Bulk mode benefits:
68+
# - No tracking column required
69+
# - Works with any SELECT query
70+
# - Supports complex queries (JOINs, aggregations, window functions)
71+
# - Perfect for periodic snapshots
72+
# - Universal compatibility with all databases
73+
74+
snake_case_columns = true
75+
include_metadata = false
76+
77+
[[streams]]
78+
stream = "warehouse"
79+
topic = "product_summary"
80+
partition_id = 1
81+
schema = "json"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# Example JDBC Source Connector Configuration for H2 Database
19+
# H2 is useful for testing and development as it's an embedded Java database
20+
21+
type = "source"
22+
key = "jdbc_h2_example"
23+
enabled = true
24+
version = 0
25+
name = "JDBC H2 Source"
26+
path = "target/release/libiggy_connector_jdbc_source"
27+
plugin_config_format = "toml"
28+
29+
[plugin_config]
30+
# H2 connection URL (in-memory database)
31+
jdbc_url = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1"
32+
33+
# H2 JDBC driver
34+
driver_class = "org.h2.Driver"
35+
36+
# Path to H2 driver JAR
37+
# Download from: https://repo1.maven.org/maven2/com/h2database/h2/2.2.224/h2-2.2.224.jar
38+
# Note: Update this path to match where you downloaded the JAR file
39+
driver_jar_path = "/tmp/jdbc-drivers/h2-2.2.224.jar"
40+
41+
# H2 credentials (default)
42+
username = "sa"
43+
password = ""
44+
45+
# Simple query for testing
46+
query = "SELECT * FROM users WHERE id > {last_offset} ORDER BY id"
47+
48+
poll_interval = "10s"
49+
batch_size = 100
50+
tracking_column = "id"
51+
initial_offset = "0"
52+
mode = "incremental"
53+
snake_case_columns = false
54+
include_metadata = true
55+
56+
[[streams]]
57+
stream = "test"
58+
topic = "users"
59+
partition_id = 1
60+
schema = "json"
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# Example JDBC Source Connector Configuration for MySQL
19+
# This file demonstrates how to configure the JDBC source connector
20+
# to read data from a MySQL database and publish to Iggy streams.
21+
22+
type = "source"
23+
key = "jdbc_mysql_example"
24+
enabled = true
25+
version = 0
26+
name = "JDBC MySQL Source"
27+
path = "target/release/libiggy_connector_jdbc_source"
28+
plugin_config_format = "toml"
29+
30+
[plugin_config]
31+
# JDBC connection URL
32+
# Option 1: Separate credentials (recommended)
33+
jdbc_url = "jdbc:mysql://localhost:3306/ecommerce?useSSL=false&serverTimezone=UTC"
34+
35+
# Option 2: Embedded credentials in URL (alternative)
36+
# jdbc_url = "jdbc:mysql://iggy_user:iggy_password@localhost:3306/ecommerce?useSSL=false&serverTimezone=UTC"
37+
38+
# JDBC driver class name
39+
driver_class = "com.mysql.cj.jdbc.Driver"
40+
41+
# Path to JDBC driver JAR file
42+
# Download from: https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar
43+
driver_jar_path = "/opt/jdbc-drivers/mysql-connector-j-8.0.33.jar"
44+
45+
# Database credentials (optional if included in jdbc_url)
46+
username = "iggy_user"
47+
password = "iggy_password"
48+
49+
# SQL query to execute
50+
# Use {last_offset} placeholder for incremental reads
51+
query = "SELECT * FROM orders WHERE updated_at > {last_offset} ORDER BY updated_at ASC"
52+
53+
# How often to poll the database
54+
poll_interval = "30s"
55+
56+
# Maximum number of rows to fetch per poll
57+
batch_size = 1000
58+
59+
# Column to track for incremental reads (must be in query result)
60+
tracking_column = "updated_at"
61+
62+
# Initial offset value for the first poll
63+
initial_offset = "2024-01-01 00:00:00"
64+
65+
# Source mode: "incremental" or "bulk"
66+
# Note: Both modes work with ALL JDBC databases (MySQL, Oracle, PostgreSQL, etc.)
67+
# - incremental: Tracks last offset, avoids duplicate reads
68+
# - bulk: Full table scan, no offset tracking
69+
mode = "incremental"
70+
71+
# Convert column names to snake_case (e.g., OrderDate -> order_date)
72+
snake_case_columns = true
73+
74+
# Include metadata wrapper in output messages
75+
include_metadata = true
76+
77+
# Custom JVM options (optional)
78+
jvm_options = ["-Xmx512m", "-Xms128m"]
79+
80+
# Target Iggy stream and topic
81+
[[streams]]
82+
stream = "ecommerce"
83+
topic = "orders"
84+
partition_id = 1
85+
schema = "json"

0 commit comments

Comments
 (0)