This is an automated email from the ASF dual-hosted git repository.
MartijnVisser pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-connector-jdbc.git
The following commit(s) were added to refs/heads/main by this push:
new 36caea9d [FLINK-40273][docs] Update JDBC documentation for the 4.x
module split and APIs
36caea9d is described below
commit 36caea9d91e3321ca1d2113be1133851d740a716
Author: Purushottam Sinha <[email protected]>
AuthorDate: Thu Jul 30 15:12:40 2026 +0530
[FLINK-40273][docs] Update JDBC documentation for the 4.x module split and
APIs
The docs still described the 3.x connector. FLINK-36659 removed the
flink-connector-jdbc module, but docs/data/jdbc.yml was never updated,
and several features added since have gone undocumented.
Dependencies and artifacts:
- Point docs/data/jdbc.yml at 5.0-SNAPSHOT with flink_compatibility,
and list one variant per published module instead of the removed
aggregate artifact.
- Document the module split in both the Table and DataStream
dependency sections, including that no self-contained SQL uber jar
is published, that lib/ also needs the OpenLineage jars, and which
dialects need other dialect artifacts.
DataStream API:
- Rewrite the sink section for the Sink V2 builder
(JdbcSink.builder() / buildAtLeastOnce / buildExactlyOnce with
sinkTo). The documented JdbcSink.sink(), JdbcSink.exactlyOnceSink()
and JdbcXaSinkFunction were removed together with the old module.
- Lead the source section with setSplitter and document
PreparedSplitterEnumerator and SlideTimingSplitterEnumerator
(FLINK-38733), keeping the deprecated setSql path (FLINK-40746) in
its own section with the migration.
- Fix JdbcSourceBuilder.builder() to JdbcSource.builder().
- Document the exactly-once requirements: TYPE_SCROLL_INSENSITIVE on
the source and maxRetries = 0 on the sink.
- Add a lineage section (FLINK-34467), noting that the namespace is
only set when the connector connects from a JDBC URL itself.
Table API:
- Correct scan.partition.lower-bound and upper-bound to Long.
- Document compatible-mode as OceanBase-only with its actual values.
- Rewrite the catalog base-url and default-database semantics for
FLINK-38851, including query-parameter passthrough and that a
base-url with a database but no query string pins the catalog.
- Add CrateDB and OceanBase to the upsert grammar table and describe
the insert-or-update fallback for Trino and Derby.
- Add PostgreSQL JSON/JSONB (FLINK-39224) and UUID (FLINK-38850) to
the data type mapping, including stringtype=unspecified for writes.
Applied to both docs/content and docs/content.zh.
Generated-by: Claude Code (Claude Opus 5.5)
---
docs/content.zh/docs/connectors/datastream/jdbc.md | 500 ++++++++++-----------
docs/content.zh/docs/connectors/table/jdbc.md | 103 ++++-
docs/content/docs/connectors/datastream/jdbc.md | 499 ++++++++++----------
docs/content/docs/connectors/table/jdbc.md | 110 ++++-
docs/data/jdbc.yml | 25 +-
5 files changed, 695 insertions(+), 542 deletions(-)
diff --git a/docs/content.zh/docs/connectors/datastream/jdbc.md
b/docs/content.zh/docs/connectors/datastream/jdbc.md
index bdc45ec6..0f7f72a0 100644
--- a/docs/content.zh/docs/connectors/datastream/jdbc.md
+++ b/docs/content.zh/docs/connectors/datastream/jdbc.md
@@ -29,9 +29,22 @@ under the License.
This connector provides a source that read data from a JDBC database and
provides a sink that writes data to a JDBC database.
-To use it, add the following dependency to your project (along with your JDBC
driver):
+Since version 4.0 the connector is no longer published as a single artifact.
It is split into
+`flink-connector-jdbc-core`, which contains the source and the sink, and one
artifact per supported
+database, which contains the dialect and the catalog for that database. Add
the artifact for the
+database you are connecting to (along with your JDBC driver); it pulls in
+`flink-connector-jdbc-core` transitively. For example, for PostgreSQL:
-{{< connector_artifact flink-connector-jdbc jdbc >}}
+{{< connector_artifact flink-connector-jdbc-postgres jdbc >}}
+
+The available database artifacts are `flink-connector-jdbc-mysql`,
`flink-connector-jdbc-oracle`,
+`flink-connector-jdbc-postgres`, `flink-connector-jdbc-sqlserver`,
`flink-connector-jdbc-cratedb`,
+`flink-connector-jdbc-db2`, `flink-connector-jdbc-trino`,
`flink-connector-jdbc-oceanbase` and
+`flink-connector-jdbc-spanner`. The Derby dialect ships in
`flink-connector-jdbc-core`.
+
+The `JdbcSource` and `JdbcSink` themselves live in `flink-connector-jdbc-core`
and work against any
+JDBC driver, so a database artifact is only required for the Table/SQL API,
where the dialect is used
+to generate statements.
Note that the streaming connectors are currently __NOT__ part of the binary
distribution.
See how to link with them for cluster execution [here]({{< ref
"docs/dev/configuration/overview" >}}).
@@ -42,28 +55,24 @@ Please consult your database documentation on how to add
the corresponding drive
### Usage
-{{< tabs "4ab65f13-607a-411a-8d24-e709f701cd41" >}}
-{{< tab "Java" >}}
```java
-JdbcSource source = JdbcSourceBuilder.builder()
+JdbcSource<OUT> source = JdbcSource.<OUT>builder()
// Required
- .setSql(...)
+ .setSplitter(...)
.setResultExtractor(...)
+ .setTypeInformation(...)
+ .setDBUrl(...)
+ .setDriverName(...)
.setUsername(...)
.setPassword(...)
- .setDriverName(...)
- .setDBUrl(...)
- .setTypeInformation(...)
-
- // Optional
- .setContinuousUnBoundingSettings(...)
- .setJdbcParameterValuesProvider(...)
+
+ // Optional
.setDeliveryGuarantee(...)
.setConnectionCheckTimeoutSeconds(...)
-
+
// The extended JDBC connection property passing
.setConnectionProperty("key", "value")
-
+
// other attributes
.setSplitReaderFetchBatchSize(...)
.setResultSetType(...)
@@ -72,15 +81,73 @@ JdbcSource source = JdbcSourceBuilder.builder()
.setResultSetFetchSize(...)
.setConnectionProvider(...)
.build();
+```
+
+`setSplitter` describes the query and how it is divided into splits, and is
the only required way to
+define what the source reads. The older `setSql` /
`setJdbcParameterValuesProvider` pair is
+deprecated in favour of it; see [Deprecated query API](#deprecated-query-api).
Setting both a
+splitter and a query fails at `build()` time.
+### SplitterEnumerator
+
+A `SplitterEnumerator` produces the `JdbcSourceSplit`s that the readers
execute, and decides whether
+the source is bounded or continuously unbounded.
+
+#### PreparedSplitterEnumerator
+
+`PreparedSplitterEnumerator` builds bounded splits from a query template. With
no parameters the
+query becomes a single split:
+
+```java
+PreparedSplitterEnumerator.of("select * from books");
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
+
+To read in parallel, provide a parameterized query template (i.e. a valid
+[JDBC prepared
statement](https://docs.oracle.com/en/java/javase/11/docs/api/java.sql/java/sql/PreparedStatement.html))
+together with the binding values. One split is generated per row of the
parameter array:
+
+```java
+String query = "select * from books where author = ?";
+Serializable[][] queryParameters = new String[2][1];
+queryParameters[0] = new String[]{"Kumar"};
+queryParameters[1] = new String[]{"Tan Ah Teck"};
+
+PreparedSplitterEnumerator.of(query, queryParameters);
```
-{{< /tab >}}
-{{< /tabs >}}
+
+For a numeric range there are convenience overloads that generate the
parameter pairs for you. The
+template must take a lower and an upper bound, and the range is divided either
into splits of a given
+size or into a given number of splits:
+
+```java
+// splits of at most 1000 values each
+PreparedSplitterEnumerator.of("select * from books where id between ? and ?",
1L, 10_000L, 1000L);
+
+// exactly 10 splits
+PreparedSplitterEnumerator.of("select * from books where id between ? and ?",
1L, 10_000L, 10);
+```
+
+Note that the two overloads differ only in whether the last argument is a
`long` (batch size) or an
+`int` (number of batches).
+
+#### SlideTimingSplitterEnumerator
+
+`SlideTimingSplitterEnumerator` generates splits continuously from a sliding
window over a timestamp
+column, which makes the source unbounded. The query template takes the window
start and end:
+
+```java
+SlideTimingSplitterEnumerator.builder()
+ .setSqlTemplate("select * from books where ts >= ? and ts < ?")
+ .setStartMillis(startMillis)
+ .setSlideSpanMillis(1000L)
+ .setSlideStepMillis(1000L)
+ .setSplitGenerateDelayMillis(100L)
+ .build();
+```
+
+`setStartMillis` is the epoch timestamp, in milliseconds, at which the first
window starts and must be
+greater than 0. `setSplitGenerateDelayMillis` holds a window back for the
given duration before it is
+emitted as a split, which gives late-arriving rows time to land.
### Delivery guarantee
@@ -93,12 +160,15 @@ remains unchanged in the whole lifecycle of
`JdbcSourceSplit` processing.
Unfortunately, this condition is not met in most databases and data scenarios.
See
[FLIP-239](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=217386271)
for more details.
+Using `DeliveryGuarantee.EXACTLY_ONCE` requires
`setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)`;
+with the default `ResultSet.TYPE_FORWARD_ONLY`, `build()` fails.
+
### ResultExtractor
An `Extractor` to extract a record from `ResultSet` executed by a sql.
```java
-import org.apache.flink.connector.jdbc.source.reader.extractor.ResultExtractor;
+import
org.apache.flink.connector.jdbc.core.datastream.source.reader.extractor.ResultExtractor;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -113,73 +183,18 @@ class Book {
final String title;
};
-ResultExtractor resultExtractor = new ResultExtractor() {
+ResultExtractor<Book> resultExtractor = new ResultExtractor<Book>() {
@Override
- public Object extract(ResultSet resultSet) throws SQLException {
- return new Book(resultSet.getLong("id"),
resultSet.getString("titile"));
+ public Book extract(ResultSet resultSet) throws SQLException {
+ return new Book(resultSet.getLong("id"), resultSet.getString("title"));
}
};
-
-```
-
-### JdbcParameterValuesProvider
-
-A provider to provide parameters in sql to fulfill actual value in the
corresponding placeholders, which is in the form of two-dimension array.
-
-```java
-
-class TestEntry {
- ...
-};
-
-ResultSetExtractor extractor = ...;
-
-StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
-
-JdbcSource<TestEntry> jdbcSource =
- JdbcSource.<TestEntry>builder()
- .setTypeInformation(TypeInformation.of(TestEntry.class))
- .setSql("select * from testing_table where id >= ? and id <=
?")
- .setJdbcParameterValuesProvider(
- new JdbcGenericParameterValuesProvider(
- new Serializable[][] {{1001, 1005}, {1006,
1010}}))
- ...
- .build();
-env.fromSource(jdbcSource, WatermarkStrategy.noWatermarks(), "TestSource")
- .addSink(new DiscardSink());
-env.execute();
-
-```
-
-### Minimalist Streaming Semantic and ContinuousUnBoundingSettings
-
-If you want to generate continuous milliseconds parameters based on
sliding-window,
-please have a try on setting the followed attributes of `JdbcSource`:
-
-```java
-
-jdbcSourceBuilder =
- JdbcSource.<TestEntry>builder()
- .setSql("select * from testing_table where ts >= ? and ts < ?")
-
- // Required for streaming related semantic.
- .setContinuousUnBoundingSettings(new
ContinuousUnBoundingSettings(Duration.ofMillis(10L), Duration.ofSeconds(1L)))
- .setJdbcParameterValuesProvider(new
JdbcSlideTimingParameterProvider(0L, 1000L, 1000L, 100L))
- .setDeliveryGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
- .setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
-
- // other attributes
- ...
-
-JdbcSource source = jdbcSourceBuilder.build();
```
### Full example
-{{< tabs "4ab65f13-608a-411a-8d24-e303f348ds81" >}}
-{{< tab "Java" >}}
-
```java
+import org.apache.flink.streaming.api.functions.sink.v2.DiscardingSink;
public class JdbcSourceExample {
@@ -198,11 +213,11 @@ public class JdbcSourceExample {
JdbcSource<Book> jdbcSource =
JdbcSource.<Book>builder()
.setTypeInformation(TypeInformation.of(Book.class))
- .setSql("select * from testing_table where id < ?")
- .setDBUrl(...)
- .setJdbcParameterValuesProvider(
- new JdbcGenericParameterValuesProvider(
+ .setSplitter(
+ PreparedSplitterEnumerator.of(
+ "select * from books where id < ?",
new Serializable[][] {{1001L}}))
+ .setDBUrl(...)
.setDriverName(...)
.setResultExtractor(resultSet ->
new Book(
@@ -210,47 +225,76 @@ public class JdbcSourceExample {
resultSet.getString("title")))
.build();
env.fromSource(jdbcSource, WatermarkStrategy.noWatermarks(),
"TestSource")
- .addSink(new DiscardingSink());
+ .sinkTo(new DiscardingSink<>());
env.execute();
}
}
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
+
+### Deprecated query API
+
+Before `SplitterEnumerator` was introduced, the query was defined with
`setSql` and the splits with a
+`JdbcParameterValuesProvider`. Both methods are deprecated, as are
`setContinuousUnBoundingSettings`
+and `setOptionalSqlSplitEnumeratorState`, which are only read on this path. A
bounded read maps
+directly to a `PreparedSplitterEnumerator`:
+
+```java
+// deprecated
+JdbcSource.<TestEntry>builder()
+ .setSql("select * from testing_table where id >= ? and id <= ?")
+ .setJdbcParameterValuesProvider(
+ new JdbcGenericParameterValuesProvider(
+ new Serializable[][] {{1001, 1005}, {1006, 1010}}))
+ ...
+
+// current
+JdbcSource.<TestEntry>builder()
+ .setSplitter(
+ PreparedSplitterEnumerator.of(
+ "select * from testing_table where id >= ? and id <=
?",
+ new Serializable[][] {{1001, 1005}, {1006, 1010}}))
+ ...
+```
+
+On this deprecated path, continuous unbounded reads are configured with
+`setContinuousUnBoundingSettings` and a `JdbcSlideTimingParameterProvider`,
which must be set
+together:
+
+```java
+// deprecated
+JdbcSource.<TestEntry>builder()
+ .setSql("select * from testing_table where ts >= ? and ts < ?")
+ .setContinuousUnBoundingSettings(
+ new ContinuousUnBoundingSettings(Duration.ofMillis(10L),
Duration.ofSeconds(1L)))
+ .setJdbcParameterValuesProvider(
+ new JdbcSlideTimingParameterProvider(startMillis, 1000L,
1000L, 100L))
+ .setDeliveryGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
+ .setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
```
-{{< /tab >}}
-{{< /tabs >}}
-## `JdbcSink.sink`
+The closest replacement is
[`SlideTimingSplitterEnumerator`](#slidetimingsplitterenumerator), but it
+is not like-for-like: it has no initial discovery delay and no discovery
interval. A window is emitted
+as a split as soon as it has closed and `splitGenerateDelayMillis` has passed.
+`setContinuousUnBoundingSettings` has no effect when a splitter is set.
-The JDBC sink provides at-least-once guarantee.
-Effectively though, exactly-once can be achieved by crafting upsert SQL
statements or idempotent SQL updates.
-Configuration goes as follows:
+## JDBC Sink
+
+The JDBC sink is built with `JdbcSink.builder()`. It provides an at-least-once
and an exactly-once
+variant, selected by which `build` method you call. Effectively though,
exactly-once can also be
+achieved on the at-least-once path by crafting upsert SQL statements or
idempotent SQL updates.
-{{< tabs "4ab65f13-607a-411a-8d24-e709f701cd4c" >}}
-{{< tab "Java" >}}
```java
-JdbcSink.sink(
- sqlDmlStatement, // mandatory
- jdbcStatementBuilder, // mandatory
- jdbcExecutionOptions, // optional
- jdbcConnectionOptions // mandatory
-);
+JdbcSink<IN> sink = JdbcSink.<IN>builder()
+ .withQueryStatement(sqlDmlStatement, jdbcStatementBuilder) //
mandatory
+ .withExecutionOptions(jdbcExecutionOptions) //
optional
+ .buildAtLeastOnce(jdbcConnectionOptions); //
mandatory
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-JdbcSink.sink(
- sql_dml_statement, # mandatory
- type_info, # mandatory
- jdbc_connection_options, # mandatory
- jdbc_execution_options # optional
-)
+
+The sink is attached to a stream with `sinkTo`:
+
+```java
+stream.sinkTo(sink);
```
-{{< /tab >}}
-{{< /tabs >}}
### SQL DML statement and JDBC statement builder
@@ -266,12 +310,14 @@ It then repeatedly calls a user-provided function to
update that prepared statem
(preparedStatement, someRecord) -> { ... update here the preparedStatement
with values from someRecord ... }
```
+The two are passed together to `withQueryStatement`. Alternatively, pass a
`JdbcQueryStatement`,
+which bundles both in one class. Either way the query is fixed: the writer
prepares it once, so it
+cannot vary per record.
+
### JDBC execution options
The SQL DML statements are executed in batches, which can optionally be
configured with the following instance:
-{{< tabs "4ab65f13-607a-411a-8d24-e709f512ed6k" >}}
-{{< tab "Java" >}}
```java
JdbcExecutionOptions.builder()
.withBatchIntervalMs(200) // optional: default = 0,
meaning no time-based execution is done
@@ -279,17 +325,6 @@ JdbcExecutionOptions.builder()
.withMaxRetries(5) // optional: default = 3
.build();
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-JdbcExecutionOptions.builder() \
- .with_batch_interval_ms(2000) \
- .with_batch_size(100) \
- .with_max_retries(5) \
- .build()
-```
-{{< /tab >}}
-{{< /tabs >}}
A JDBC batch is executed as soon as one of the following conditions is true:
@@ -299,12 +334,12 @@ A JDBC batch is executed as soon as one of the following
conditions is true:
### JDBC connection parameters
-The connection to the database is configured with a `JdbcConnectionOptions`
instance.
+The connection to the database is configured with a `JdbcConnectionOptions`
instance, which is passed
+to `buildAtLeastOnce`. A `JdbcConnectionProvider` can be passed instead to
reuse an existing
+connection strategy.
### Full example
-{{< tabs "4ab65f13-608a-411a-8d24-e303f348ds8d" >}}
-{{< tab "Java" >}}
```java
public class JdbcSinkExample {
@@ -324,138 +359,94 @@ public class JdbcSinkExample {
public static void main(String[] args) throws Exception {
var env = StreamExecutionEnvironment.getExecutionEnvironment();
- env.fromElements(
+ env.fromData(
new Book(101L, "Stream Processing with Apache Flink", "Fabian
Hueske, Vasiliki Kalavri", 2019),
new Book(102L, "Streaming Systems", "Tyler Akidau, Slava
Chernyak, Reuven Lax", 2018),
new Book(103L, "Designing Data-Intensive Applications",
"Martin Kleppmann", 2017),
new Book(104L, "Kafka: The Definitive Guide", "Gwen Shapira,
Neha Narkhede, Todd Palino", 2017)
- ).addSink(
- JdbcSink.sink(
- "insert into books (id, title, authors, year) values
(?, ?, ?, ?)",
- (statement, book) -> {
- statement.setLong(1, book.id);
- statement.setString(2, book.title);
- statement.setString(3, book.authors);
- statement.setInt(4, book.year);
- },
- JdbcExecutionOptions.builder()
- .withBatchSize(1000)
- .withBatchIntervalMs(200)
- .withMaxRetries(5)
- .build(),
- new
JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
-
.withUrl("jdbc:postgresql://dbhost:5432/postgresdb")
- .withDriverName("org.postgresql.Driver")
- .withUsername("someUser")
- .withPassword("somePassword")
- .build()
- ));
-
+ ).sinkTo(
+ JdbcSink.<Book>builder()
+ .withQueryStatement(
+ "insert into books (id, title, authors, year)
values (?, ?, ?, ?)",
+ (statement, book) -> {
+ statement.setLong(1, book.id);
+ statement.setString(2, book.title);
+ statement.setString(3, book.authors);
+ statement.setInt(4, book.year);
+ })
+ .withExecutionOptions(
+ JdbcExecutionOptions.builder()
+ .withBatchSize(1000)
+ .withBatchIntervalMs(200)
+ .withMaxRetries(5)
+ .build())
+ .buildAtLeastOnce(
+ new
JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
+
.withUrl("jdbc:postgresql://dbhost:5432/postgresdb")
+
.withDriverName("org.postgresql.Driver")
+ .withUsername("someUser")
+ .withPassword("somePassword")
+ .build()));
+
env.execute();
}
}
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-env = StreamExecutionEnvironment.get_execution_environment()
-type_info = Types.ROW([Types.INT(), Types.STRING(), Types.STRING(),
Types.INT()])
-env.from_collection(
- [(101, "Stream Processing with Apache Flink", "Fabian Hueske, Vasiliki
Kalavri", 2019),
- (102, "Streaming Systems", "Tyler Akidau, Slava Chernyak, Reuven Lax",
2018),
- (103, "Designing Data-Intensive Applications", "Martin Kleppmann", 2017),
- (104, "Kafka: The Definitive Guide", "Gwen Shapira, Neha Narkhede, Todd
Palino", 2017)
- ], type_info=type_info) \
- .add_sink(
- JdbcSink.sink(
- "insert into books (id, title, authors, year) values (?, ?, ?, ?)",
- type_info,
- JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
- .with_url('jdbc:postgresql://dbhost:5432/postgresdb')
- .with_driver_name('org.postgresql.Driver')
- .with_user_name('someUser')
- .with_password('somePassword')
- .build(),
- JdbcExecutionOptions.builder()
- .with_batch_interval_ms(1000)
- .with_batch_size(200)
- .with_max_retries(5)
- .build()
- ))
-
-env.execute()
-```
-{{< /tab >}}
-{{< /tabs >}}
-## `JdbcSink.exactlyOnceSink`
+### Exactly-once sink
-Since 1.13, Flink JDBC sink supports exactly-once mode.
-The implementation relies on the JDBC driver support of XA
+The exactly-once implementation relies on the JDBC driver support of XA
[standard](https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf).
Most drivers support XA if the database also supports XA (so the driver is
usually the same).
-To use it, create a sink using `exactlyOnceSink()` method as above and
additionally provide:
+To use it, call `buildExactlyOnce()` instead of `buildAtLeastOnce()` and
provide:
- `JdbcExactlyOnceOptions`
-- `JdbcExecutionOptions`
-- [XA
DataSource](https://docs.oracle.com/javase/8/docs/api/javax/sql/XADataSource.html)
Supplier
+- an [XA
DataSource](https://docs.oracle.com/javase/8/docs/api/javax/sql/XADataSource.html)
Supplier
For example:
-{{< tabs "4ab65f13-608a-411a-8d24-e304f627ac8f" >}}
-{{< tab "Java" >}}
```java
StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
env
- .fromElements(...)
- .addSink(
- JdbcSink.exactlyOnceSink(
- "insert into books (id, title, author, price, qty) values
(?,?,?,?,?)",
- (ps, t) -> {
- ps.setInt(1, t.id);
- ps.setString(2, t.title);
- ps.setString(3, t.author);
- ps.setDouble(4, t.price);
- ps.setInt(5, t.qty);
- },
- JdbcExecutionOptions.builder()
- .withMaxRetries(0)
- .build(),
- JdbcExactlyOnceOptions.defaults(),
- () -> {
- // create a driver-specific XA DataSource
- // The following example is for derby
- EmbeddedXADataSource ds = new EmbeddedXADataSource();
- ds.setDatabaseName("my_db");
- return ds;
- }));
+ .fromData(...)
+ .sinkTo(JdbcSink.<Book>builder()
+ .withQueryStatement(
+ "insert into books (id, title, author, price, qty)
values (?,?,?,?,?)",
+ (ps, t) -> {
+ ps.setInt(1, t.id);
+ ps.setString(2, t.title);
+ ps.setString(3, t.author);
+ ps.setDouble(4, t.price);
+ ps.setInt(5, t.qty);
+ })
+ .withExecutionOptions(
+ JdbcExecutionOptions.builder()
+ .withMaxRetries(0)
+ .build())
+ .buildExactlyOnce(
+ JdbcExactlyOnceOptions.defaults(),
+ () -> {
+ // create a driver-specific XA DataSource
+ PGXADataSource ds = new PGXADataSource();
+
ds.setUrl("jdbc:postgresql://localhost:5432/postgres");
+ ds.setUser(username);
+ ds.setPassword(password);
+ return ds;
+ }));
env.execute();
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
+
+An `XaConnectionProvider` can be passed instead of the supplier when the
connection strategy has to
+be controlled directly.
**NOTE:** Some databases only allow a single XA transaction per connection
(e.g. PostgreSQL, MySQL).
In such cases, please use the following API to construct
`JdbcExactlyOnceOptions`:
-{{< tabs "4ab65f13-608a-411a-8d24-e304f627cd4e" >}}
-{{< tab "Java" >}}
```java
JdbcExactlyOnceOptions.builder()
.withTransactionPerConnection(true)
.build();
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
This will make Flink use a separate connection for every XA transaction. This
may require adjusting connection limits.
For PostgreSQL and MySQL, this can be done by increasing `max_connections`.
@@ -464,63 +455,48 @@ Furthermore, XA needs to be enabled and/or configured in
some databases.
For PostgreSQL, you should set `max_prepared_transactions` to some value
greater than zero.
For MySQL v8+, you should grant `XA_RECOVER_ADMIN` to Flink DB user.
-**ATTENTION:** Currently, `JdbcSink.exactlyOnceSink` can ensure exactly once
semantics
-with `JdbcExecutionOptions.maxRetries == 0`; otherwise, duplicated results
maybe produced.
+**ATTENTION:** The exactly-once sink requires
`JdbcExecutionOptions.maxRetries` to be set to `0`.
+The default is `3`, so set it explicitly with
`JdbcExecutionOptions.builder().withMaxRetries(0)`;
+otherwise the sink fails at startup.
### `XADataSource` examples
PostgreSQL `XADataSource` example:
-{{< tabs "4ab65f13-608a-411a-8d24-e304f323ab3a" >}}
-{{< tab "Java" >}}
```java
PGXADataSource xaDataSource = new org.postgresql.xa.PGXADataSource();
xaDataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
xaDataSource.setUser(username);
xaDataSource.setPassword(password);
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
MySQL `XADataSource` example:
-{{< tabs "4ab65f13-608a-411a-8d24-b213f323ca3c" >}}
-{{< tab "Java" >}}
```java
MysqlXADataSource xaDataSource = new com.mysql.cj.jdbc.MysqlXADataSource();
xaDataSource.setUrl("jdbc:mysql://localhost:3306/");
xaDataSource.setUser(username);
xaDataSource.setPassword(password);
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
Oracle `XADataSource` example:
-{{< tabs "4ab65f13-608a-411a-8d24-b213f337ad5f" >}}
-{{< tab "Java" >}}
```java
OracleXADataSource xaDataSource = new oracle.jdbc.xa.OracleXADataSource();
xaDataSource.setURL("jdbc:oracle:oci8:@");
xaDataSource.setUser("scott");
xaDataSource.setPassword("tiger");
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
Please also take Oracle connection pooling into account.
-Please refer to the `JdbcXaSinkFunction` documentation for more details.
+## Lineage
+
+`JdbcSource` and `JdbcSink` expose lineage information as described in
+[FLIP-314](https://cwiki.apache.org/confluence/display/FLINK/FLIP-314%3A+Support+Customized+Job+Lineage+Listener),
+as do the Table API source and lookup function. No configuration is needed on
the connector side; the
+information is delivered to any `JobStatusChangedListener` registered in the
cluster.
+
+The dataset name is derived from the queries the job executes. The dataset
namespace is derived
+from the JDBC URL, but only when the connector opens connections from that URL
itself, i.e. a
+`JdbcSource` configured with `setDBUrl` or a `JdbcSink` built with
+`buildAtLeastOnce(JdbcConnectionOptions)`. With `buildExactlyOnce` or a custom
+`JdbcConnectionProvider` the namespace is empty.
{{< top >}}
diff --git a/docs/content.zh/docs/connectors/table/jdbc.md
b/docs/content.zh/docs/connectors/table/jdbc.md
index 61f0bfee..3faf564d 100644
--- a/docs/content.zh/docs/connectors/table/jdbc.md
+++ b/docs/content.zh/docs/connectors/table/jdbc.md
@@ -41,8 +41,32 @@ JDBC 连接器允许使用 JDBC 驱动向任意类型的关系型数据库读取
依赖
------------
+从 4.0 版本开始,JDBC 连接器不再作为单个构件发布。它被拆分为包含公共运行时的
+`flink-connector-jdbc-core`,以及每个支持的数据库各一个构件,后者包含该数据库的方言(dialect)和
+catalog。你需要引入所连接数据库对应的构件,它会以传递依赖的方式引入 `flink-connector-jdbc-core`。
+
+| 数据库 | 连接器构件 |
+|:-----------|:----------------------------------|
+| MySQL | `flink-connector-jdbc-mysql` |
+| Oracle | `flink-connector-jdbc-oracle` |
+| PostgreSQL | `flink-connector-jdbc-postgres` |
+| SQL Server | `flink-connector-jdbc-sqlserver` |
+| CrateDB | `flink-connector-jdbc-cratedb` |
+| Db2 | `flink-connector-jdbc-db2` |
+| Trino | `flink-connector-jdbc-trino` |
+| OceanBase | `flink-connector-jdbc-oceanbase` |
+| Spanner | `flink-connector-jdbc-spanner` |
+| Derby | `flink-connector-jdbc-core` |
+
{{< sql_connector_download_table "jdbc" >}}
+这些构件都不是自包含的 SQL uber jar。在使用 SQL Client 或 session 集群时,需要把
+`flink-connector-jdbc-core` 和所用数据库对应的构件同时放入 `lib/` 目录,并且还需要放入
+`flink-connector-jdbc-core` 所依赖的 `io.openlineage:openlineage-java` 和
+`io.openlineage:openlineage-sql-java` jar,否则构建作业时会抛出
`NoClassDefFoundError`。部分方言基于其他方言实现,
+因此还需要对应的构件:CrateDB 还需要 `flink-connector-jdbc-postgres`,OceanBase 还需要
+`flink-connector-jdbc-mysql` 和 `flink-connector-jdbc-oracle`。
+
JDBC 连接器不是二进制发行版的一部分,请查阅[这里]({{< ref "docs/dev/configuration/overview"
>}})了解如何在集群运行中引用 JDBC 连接器。
在连接到具体数据库时,也需要对应的驱动依赖,目前支持的驱动如下:
@@ -146,7 +170,9 @@ ON myTopic.key = MyUserTable.id;
<td>可选</td>
<td style="word-wrap: break-word;">(none)</td>
<td>String</td>
- <td>数据库的兼容模式。</td>
+ <td>数据库的兼容模式。只有 OceanBase 支持该配置项,取值为 <code>'mysql'</code>(未设置时的默认值)
+ 和 <code>'oracle'</code>,用于选择方言所针对的租户模式。为其他数据库设置该项会以
+ <code>Not supported option 'compatible-mode'</code> 失败。</td>
</tr>
<tr>
<td><h5>username</h5></td>
@@ -187,14 +213,14 @@ ON myTopic.key = MyUserTable.id;
<td><h5>scan.partition.lower-bound</h5></td>
<td>可选</td>
<td style="word-wrap: break-word;">(none)</td>
- <td>Integer</td>
+ <td>Long</td>
<td>第一个分区的最小值。</td>
</tr>
<tr>
<td><h5>scan.partition.upper-bound</h5></td>
<td>可选</td>
<td style="word-wrap: break-word;">(none)</td>
- <td>Integer</td>
+ <td>Long</td>
<td>最后一个分区的最大值。</td>
</tr>
<tr>
@@ -439,9 +465,22 @@ lookup cache 的主要目的是用于提高时态表关联 JDBC 连接器的性
<td>Spanner</td>
<td>INSERT OR UPDATE INTO .. (..) VALUES (..)</td>
</tr>
+ <tr>
+ <td>CrateDB</td>
+ <td>INSERT .. ON CONFLICT .. DO UPDATE SET ..</td>
+ </tr>
+ <tr>
+ <td>OceanBase</td>
+ <td>取决于 <code>'compatible-mode'</code>:<code>'mysql'</code> 模式下使用
MySQL 语法,
+ <code>'oracle'</code> 模式下使用 Oracle 语法。</td>
+ </tr>
</tbody>
</table>
+Trino 和 Derby 没有 upsert 语法。如果在 DDL 中定义了主键,sink 会先检查该主键对应的行是否存在,
+然后执行 `UPDATE` 或 `INSERT`,并对撤回消息执行 `DELETE`。对于 Trino,只有当表所在的 Trino
+connector 支持 `UPDATE` 和 `DELETE` 时才能正常工作。
+
<a name="jdbc-catalog"></a>
JDBC Catalog
@@ -449,7 +488,7 @@ JDBC Catalog
`JdbcCatalog` 允许用户通过 JDBC 协议将 Flink 连接到关系数据库。
-目前,JDBC Catalog 有以下实现:Postgres Catalog、MySQL Catalog、CrateDB Catalog 和
OceanBase Catalog。目前支持如下 catalog 方法。其他方法目前尚不支持。
+目前,JDBC Catalog 有以下实现:Postgres Catalog、MySQL Catalog、CrateDB Catalog、OceanBase
Catalog 和 Spanner Catalog。目前支持如下 catalog 方法。其他方法目前尚不支持。
```java
databaseExists(String databaseName);
@@ -471,15 +510,43 @@ tableExists(ObjectPath tablePath);
JDBC catalog 支持以下参数:
- `name`:必填,catalog 的名称。
-- `default-database`:必填,默认要连接的数据库。
- `username`:必填,数据库账户的用户名。
- `password`:必填,账户的密码。
-- `base-url`:必填,(不应该包含数据库名)
+- `base-url`:必填
- 对于 Postgres Catalog `base-url` 应为 `"jdbc:postgresql://<ip>:<port>"` 的格式。
- 对于 MySQL Catalog `base-url` 应为 `"jdbc:mysql://<ip>:<port>"` 的格式。
- 对于 OceanBase Catalog `base-url` 应为 `"jdbc:oceanbase://<ip>:<port>"` 的格式。
- 对于 Spanner Catalog `base-url` 应为
`"jdbc:cloudspanner:/projects/<project>/instances/<instance>/databases/"` 的格式。
-- `compatible-mode`: 选填,数据库的兼容模式。
+- `default-database`:选填,默认要连接的数据库。Spanner Catalog 必须设置该参数。
+- `compatible-mode`: 选填,数据库的兼容模式。只有 OceanBase 支持该配置项,
+ 参见 [连接器参数](#connector-options)。
+
+除 Spanner Catalog 外,catalog 所连接的数据库必须能够从 `base-url`、`default-database`
或两者中确定。下列任意一种方式都是合法的:
+
+- 设置了 `default-database` 且 `base-url` 中不含数据库名,例如
+ `'base-url' = 'jdbc:postgresql://localhost:5432'` 配合 `'default-database' =
'mydb'`
+- `base-url` 中含有数据库名且省略 `default-database`,例如
+ `'base-url' = 'jdbc:postgresql://localhost:5432/mydb'`。如果不带查询字符串,这种写法会把
catalog
+ 固定在 `mydb` 上:catalog 中的所有数据库都通过同一个 URL 访问。
+- 两者都设置且数据库名一致
+
+如果两者不一致,或者两者都没有给出数据库名,创建 catalog 会失败。
+
+`base-url` 还可以通过查询参数携带任意的驱动参数,它们会被传递给 catalog 打开的每一个连接。
+数据库名会被替换到查询字符串之前,因此这些参数对该 catalog 下的所有数据库都生效。当 `base-url`
+带有查询字符串时,`base-url` 本身必须包含数据库名,只设置 `default-database` 会被拒绝:
+
+```sql
+CREATE CATALOG my_catalog WITH(
+ 'type' = 'jdbc',
+ 'username' = '...',
+ 'password' = '...',
+ 'base-url' =
'jdbc:postgresql://localhost:5432/mydb?ssl=true&connectTimeout=10'
+);
+```
+
+Spanner Catalog 总是从 `default-database` 中获取数据库名,驱动参数以 `;key=value` 的形式追加在
+`base-url` 后面,而不是使用查询字符串。
{{< tabs "10bd8bfb-674c-46aa-8a36-385537df5791" >}}
{{< tab "SQL" >}}
@@ -983,7 +1050,9 @@ Flink 支持连接到多个使用方言(dialect)的数据库,如 MySQL、O
<code>CHARACTER(n)</code><br>
<code>VARCHAR(n)</code><br>
<code>CHARACTER VARYING(n)</code><br>
- <code>TEXT</code></td>
+ <code>TEXT</code><br>
+ <code>JSON</code><br>
+ <code>JSONB</code></td>
<td>
<code>CHAR(n)</code><br>
<code>CHARACTER(n)</code><br>
@@ -1044,6 +1113,19 @@ Flink 支持连接到多个使用方言(dialect)的数据库,如 MySQL、O
<td><code>BYTES(n)</code></td>
<td><code>BYTES</code></td>
</tr>
+ <tr>
+ <td></td>
+ <td></td>
+ <td><code>UUID</code></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td><code>VARCHAR(36)</code></td>
+ </tr>
<tr>
<td></td>
<td></td>
@@ -1060,4 +1142,9 @@ Flink 支持连接到多个使用方言(dialect)的数据库,如 MySQL、O
</tbody>
</table>
+PostgreSQL 的 `JSON` 和 `JSONB` 列以其文本表示形式读取。`UUID` 列映射为 `VARCHAR(36)`;
+在 DDL 中将这类列声明为 `STRING` 同样可以工作。sink 会以字符串形式绑定这三种类型的参数,而 PostgreSQL
+会拒绝向这些类型的列写入字符串,除非 JDBC URL 设置了 `stringtype=unspecified`,例如
+`'url' = 'jdbc:postgresql://localhost:5432/mydb?stringtype=unspecified'`。
+
{{< top >}}
diff --git a/docs/content/docs/connectors/datastream/jdbc.md
b/docs/content/docs/connectors/datastream/jdbc.md
index 4a6f4a37..0d1fa299 100644
--- a/docs/content/docs/connectors/datastream/jdbc.md
+++ b/docs/content/docs/connectors/datastream/jdbc.md
@@ -29,9 +29,22 @@ under the License.
This connector provides a source that read data from a JDBC database and
provides a sink that writes data to a JDBC database.
-To use it, add the following dependency to your project (along with your JDBC
driver):
+Since version 4.0 the connector is no longer published as a single artifact.
It is split into
+`flink-connector-jdbc-core`, which contains the source and the sink, and one
artifact per supported
+database, which contains the dialect and the catalog for that database. Add
the artifact for the
+database you are connecting to (along with your JDBC driver); it pulls in
+`flink-connector-jdbc-core` transitively. For example, for PostgreSQL:
-{{< connector_artifact flink-connector-jdbc jdbc >}}
+{{< connector_artifact flink-connector-jdbc-postgres jdbc >}}
+
+The available database artifacts are `flink-connector-jdbc-mysql`,
`flink-connector-jdbc-oracle`,
+`flink-connector-jdbc-postgres`, `flink-connector-jdbc-sqlserver`,
`flink-connector-jdbc-cratedb`,
+`flink-connector-jdbc-db2`, `flink-connector-jdbc-trino`,
`flink-connector-jdbc-oceanbase` and
+`flink-connector-jdbc-spanner`. The Derby dialect ships in
`flink-connector-jdbc-core`.
+
+The `JdbcSource` and `JdbcSink` themselves live in `flink-connector-jdbc-core`
and work against any
+JDBC driver, so a database artifact is only required for the Table/SQL API,
where the dialect is used
+to generate statements.
Note that the streaming connectors are currently __NOT__ part of the binary
distribution.
See how to link with them for cluster execution [here]({{< ref
"docs/dev/configuration/overview" >}}).
@@ -42,28 +55,24 @@ Please consult your database documentation on how to add
the corresponding drive
### Usage
-{{< tabs "4ab65f13-607a-411a-8d24-e709f701cd41" >}}
-{{< tab "Java" >}}
```java
-JdbcSource source = JdbcSourceBuilder.builder()
+JdbcSource<OUT> source = JdbcSource.<OUT>builder()
// Required
- .setSql(...)
+ .setSplitter(...)
.setResultExtractor(...)
+ .setTypeInformation(...)
+ .setDBUrl(...)
+ .setDriverName(...)
.setUsername(...)
.setPassword(...)
- .setDriverName(...)
- .setDBUrl(...)
- .setTypeInformation(...)
-
- // Optional
- .setContinuousUnBoundingSettings(...)
- .setJdbcParameterValuesProvider(...)
+
+ // Optional
.setDeliveryGuarantee(...)
.setConnectionCheckTimeoutSeconds(...)
-
+
// The extended JDBC connection property passing
.setConnectionProperty("key", "value")
-
+
// other attributes
.setSplitReaderFetchBatchSize(...)
.setResultSetType(...)
@@ -72,15 +81,73 @@ JdbcSource source = JdbcSourceBuilder.builder()
.setResultSetFetchSize(...)
.setConnectionProvider(...)
.build();
+```
+
+`setSplitter` describes the query and how it is divided into splits, and is
the only required way to
+define what the source reads. The older `setSql` /
`setJdbcParameterValuesProvider` pair is
+deprecated in favour of it; see [Deprecated query API](#deprecated-query-api).
Setting both a
+splitter and a query fails at `build()` time.
+### SplitterEnumerator
+
+A `SplitterEnumerator` produces the `JdbcSourceSplit`s that the readers
execute, and decides whether
+the source is bounded or continuously unbounded.
+
+#### PreparedSplitterEnumerator
+
+`PreparedSplitterEnumerator` builds bounded splits from a query template. With
no parameters the
+query becomes a single split:
+
+```java
+PreparedSplitterEnumerator.of("select * from books");
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
+
+To read in parallel, provide a parameterized query template (i.e. a valid
+[JDBC prepared
statement](https://docs.oracle.com/en/java/javase/11/docs/api/java.sql/java/sql/PreparedStatement.html))
+together with the binding values. One split is generated per row of the
parameter array:
+
+```java
+String query = "select * from books where author = ?";
+Serializable[][] queryParameters = new String[2][1];
+queryParameters[0] = new String[]{"Kumar"};
+queryParameters[1] = new String[]{"Tan Ah Teck"};
+
+PreparedSplitterEnumerator.of(query, queryParameters);
```
-{{< /tab >}}
-{{< /tabs >}}
+
+For a numeric range there are convenience overloads that generate the
parameter pairs for you. The
+template must take a lower and an upper bound, and the range is divided either
into splits of a given
+size or into a given number of splits:
+
+```java
+// splits of at most 1000 values each
+PreparedSplitterEnumerator.of("select * from books where id between ? and ?",
1L, 10_000L, 1000L);
+
+// exactly 10 splits
+PreparedSplitterEnumerator.of("select * from books where id between ? and ?",
1L, 10_000L, 10);
+```
+
+Note that the two overloads differ only in whether the last argument is a
`long` (batch size) or an
+`int` (number of batches).
+
+#### SlideTimingSplitterEnumerator
+
+`SlideTimingSplitterEnumerator` generates splits continuously from a sliding
window over a timestamp
+column, which makes the source unbounded. The query template takes the window
start and end:
+
+```java
+SlideTimingSplitterEnumerator.builder()
+ .setSqlTemplate("select * from books where ts >= ? and ts < ?")
+ .setStartMillis(startMillis)
+ .setSlideSpanMillis(1000L)
+ .setSlideStepMillis(1000L)
+ .setSplitGenerateDelayMillis(100L)
+ .build();
+```
+
+`setStartMillis` is the epoch timestamp, in milliseconds, at which the first
window starts and must be
+greater than 0. `setSplitGenerateDelayMillis` holds a window back for the
given duration before it is
+emitted as a split, which gives late-arriving rows time to land.
### Delivery guarantee
@@ -93,12 +160,15 @@ remains unchanged in the whole lifecycle of
`JdbcSourceSplit` processing.
Unfortunately, this condition is not met in most databases and data scenarios.
See
[FLIP-239](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=217386271)
for more details.
+Using `DeliveryGuarantee.EXACTLY_ONCE` requires
`setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)`;
+with the default `ResultSet.TYPE_FORWARD_ONLY`, `build()` fails.
+
### ResultExtractor
An `Extractor` to extract a record from `ResultSet` executed by a sql.
```java
-import org.apache.flink.connector.jdbc.source.reader.extractor.ResultExtractor;
+import
org.apache.flink.connector.jdbc.core.datastream.source.reader.extractor.ResultExtractor;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -113,73 +183,18 @@ class Book {
final String title;
};
-ResultExtractor resultExtractor = new ResultExtractor() {
+ResultExtractor<Book> resultExtractor = new ResultExtractor<Book>() {
@Override
- public Object extract(ResultSet resultSet) throws SQLException {
- return new Book(resultSet.getLong("id"),
resultSet.getString("titile"));
+ public Book extract(ResultSet resultSet) throws SQLException {
+ return new Book(resultSet.getLong("id"), resultSet.getString("title"));
}
};
-
-```
-
-### JdbcParameterValuesProvider
-
-A provider to provide parameters in sql to fulfill actual value in the
corresponding placeholders, which is in the form of two-dimension array.
-
-```java
-
-class TestEntry {
- ...
-};
-
-ResultSetExtractor extractor = ...;
-
-StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
-
-JdbcSource<TestEntry> jdbcSource =
- JdbcSource.<TestEntry>builder()
- .setTypeInformation(TypeInformation.of(TestEntry.class))
- .setSql("select * from testing_table where id >= ? and id <=
?")
- .setJdbcParameterValuesProvider(
- new JdbcGenericParameterValuesProvider(
- new Serializable[][] {{1001, 1005}, {1006,
1010}}))
- ...
- .build();
-env.fromSource(jdbcSource, WatermarkStrategy.noWatermarks(), "TestSource")
- .addSink(new DiscardSink());
-env.execute();
-
-```
-
-### Minimalist Streaming Semantic and ContinuousUnBoundingSettings
-
-If you want to generate continuous milliseconds parameters based on
sliding-window,
-please have a try on setting the followed attributes of `JdbcSource`:
-
-```java
-
-jdbcSourceBuilder =
- JdbcSource.<TestEntry>builder()
- .setSql("select * from testing_table where ts >= ? and ts < ?")
-
- // Required for streaming related semantic.
- .setContinuousUnBoundingSettings(new
ContinuousUnBoundingSettings(Duration.ofMillis(10L), Duration.ofSeconds(1L)))
- .setJdbcParameterValuesProvider(new
JdbcSlideTimingParameterProvider(0L, 1000L, 1000L, 100L))
- .setDeliveryGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
- .setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
-
- // other attributes
- ...
-
-JdbcSource source = jdbcSourceBuilder.build();
```
### Full example
-{{< tabs "4ab65f13-608a-411a-8d24-e303f348ds81" >}}
-{{< tab "Java" >}}
-
```java
+import org.apache.flink.streaming.api.functions.sink.v2.DiscardingSink;
public class JdbcSourceExample {
@@ -198,11 +213,11 @@ public class JdbcSourceExample {
JdbcSource<Book> jdbcSource =
JdbcSource.<Book>builder()
.setTypeInformation(TypeInformation.of(Book.class))
- .setSql("select * from testing_table where id < ?")
- .setDBUrl(...)
- .setJdbcParameterValuesProvider(
- new JdbcGenericParameterValuesProvider(
+ .setSplitter(
+ PreparedSplitterEnumerator.of(
+ "select * from books where id < ?",
new Serializable[][] {{1001L}}))
+ .setDBUrl(...)
.setDriverName(...)
.setResultExtractor(resultSet ->
new Book(
@@ -210,47 +225,76 @@ public class JdbcSourceExample {
resultSet.getString("title")))
.build();
env.fromSource(jdbcSource, WatermarkStrategy.noWatermarks(),
"TestSource")
- .addSink(new DiscardingSink());
+ .sinkTo(new DiscardingSink<>());
env.execute();
}
}
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
+
+### Deprecated query API
+
+Before `SplitterEnumerator` was introduced, the query was defined with
`setSql` and the splits with a
+`JdbcParameterValuesProvider`. Both methods are deprecated, as are
`setContinuousUnBoundingSettings`
+and `setOptionalSqlSplitEnumeratorState`, which are only read on this path. A
bounded read maps
+directly to a `PreparedSplitterEnumerator`:
+
+```java
+// deprecated
+JdbcSource.<TestEntry>builder()
+ .setSql("select * from testing_table where id >= ? and id <= ?")
+ .setJdbcParameterValuesProvider(
+ new JdbcGenericParameterValuesProvider(
+ new Serializable[][] {{1001, 1005}, {1006, 1010}}))
+ ...
+
+// current
+JdbcSource.<TestEntry>builder()
+ .setSplitter(
+ PreparedSplitterEnumerator.of(
+ "select * from testing_table where id >= ? and id <=
?",
+ new Serializable[][] {{1001, 1005}, {1006, 1010}}))
+ ...
+```
+
+On this deprecated path, continuous unbounded reads are configured with
+`setContinuousUnBoundingSettings` and a `JdbcSlideTimingParameterProvider`,
which must be set
+together:
+
+```java
+// deprecated
+JdbcSource.<TestEntry>builder()
+ .setSql("select * from testing_table where ts >= ? and ts < ?")
+ .setContinuousUnBoundingSettings(
+ new ContinuousUnBoundingSettings(Duration.ofMillis(10L),
Duration.ofSeconds(1L)))
+ .setJdbcParameterValuesProvider(
+ new JdbcSlideTimingParameterProvider(startMillis, 1000L,
1000L, 100L))
+ .setDeliveryGuarantee(DeliveryGuarantee.EXACTLY_ONCE)
+ .setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
```
-{{< /tab >}}
-{{< /tabs >}}
-## `JdbcSink.sink`
+The closest replacement is
[`SlideTimingSplitterEnumerator`](#slidetimingsplitterenumerator), but it
+is not like-for-like: it has no initial discovery delay and no discovery
interval. A window is emitted
+as a split as soon as it has closed and `splitGenerateDelayMillis` has passed.
+`setContinuousUnBoundingSettings` has no effect when a splitter is set.
-The JDBC sink provides at-least-once guarantee.
-Effectively though, exactly-once can be achieved by crafting upsert SQL
statements or idempotent SQL updates.
-Configuration goes as follows:
+## JDBC Sink
+
+The JDBC sink is built with `JdbcSink.builder()`. It provides an at-least-once
and an exactly-once
+variant, selected by which `build` method you call. Effectively though,
exactly-once can also be
+achieved on the at-least-once path by crafting upsert SQL statements or
idempotent SQL updates.
-{{< tabs "4ab65f13-607a-411a-8d24-e709f701cd4c" >}}
-{{< tab "Java" >}}
```java
-JdbcSink.sink(
- sqlDmlStatement, // mandatory
- jdbcStatementBuilder, // mandatory
- jdbcExecutionOptions, // optional
- jdbcConnectionOptions // mandatory
-);
+JdbcSink<IN> sink = JdbcSink.<IN>builder()
+ .withQueryStatement(sqlDmlStatement, jdbcStatementBuilder) //
mandatory
+ .withExecutionOptions(jdbcExecutionOptions) //
optional
+ .buildAtLeastOnce(jdbcConnectionOptions); //
mandatory
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-JdbcSink.sink(
- sql_dml_statement, # mandatory
- type_info, # mandatory
- jdbc_connection_options, # mandatory
- jdbc_execution_options # optional
-)
+
+The sink is attached to a stream with `sinkTo`:
+
+```java
+stream.sinkTo(sink);
```
-{{< /tab >}}
-{{< /tabs >}}
### SQL DML statement and JDBC statement builder
@@ -266,12 +310,14 @@ It then repeatedly calls a user-provided function to
update that prepared statem
(preparedStatement, someRecord) -> { ... update here the preparedStatement
with values from someRecord ... }
```
+The two are passed together to `withQueryStatement`. Alternatively, pass a
`JdbcQueryStatement`,
+which bundles both in one class. Either way the query is fixed: the writer
prepares it once, so it
+cannot vary per record.
+
### JDBC execution options
The SQL DML statements are executed in batches, which can optionally be
configured with the following instance:
-{{< tabs "4ab65f13-607a-411a-8d24-e709f512ed6k" >}}
-{{< tab "Java" >}}
```java
JdbcExecutionOptions.builder()
.withBatchIntervalMs(200) // optional: default = 0,
meaning no time-based execution is done
@@ -279,17 +325,6 @@ JdbcExecutionOptions.builder()
.withMaxRetries(5) // optional: default = 3
.build();
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-JdbcExecutionOptions.builder() \
- .with_batch_interval_ms(2000) \
- .with_batch_size(100) \
- .with_max_retries(5) \
- .build()
-```
-{{< /tab >}}
-{{< /tabs >}}
A JDBC batch is executed as soon as one of the following conditions is true:
@@ -299,12 +334,12 @@ A JDBC batch is executed as soon as one of the following
conditions is true:
### JDBC connection parameters
-The connection to the database is configured with a `JdbcConnectionOptions`
instance.
+The connection to the database is configured with a `JdbcConnectionOptions`
instance, which is passed
+to `buildAtLeastOnce`. A `JdbcConnectionProvider` can be passed instead to
reuse an existing
+connection strategy.
### Full example
-{{< tabs "4ab65f13-608a-411a-8d24-e303f348ds8d" >}}
-{{< tab "Java" >}}
```java
public class JdbcSinkExample {
@@ -324,137 +359,94 @@ public class JdbcSinkExample {
public static void main(String[] args) throws Exception {
var env = StreamExecutionEnvironment.getExecutionEnvironment();
- env.fromElements(
+ env.fromData(
new Book(101L, "Stream Processing with Apache Flink", "Fabian
Hueske, Vasiliki Kalavri", 2019),
new Book(102L, "Streaming Systems", "Tyler Akidau, Slava
Chernyak, Reuven Lax", 2018),
new Book(103L, "Designing Data-Intensive Applications",
"Martin Kleppmann", 2017),
new Book(104L, "Kafka: The Definitive Guide", "Gwen Shapira,
Neha Narkhede, Todd Palino", 2017)
- ).addSink(
- JdbcSink.sink(
- "insert into books (id, title, authors, year) values
(?, ?, ?, ?)",
- (statement, book) -> {
- statement.setLong(1, book.id);
- statement.setString(2, book.title);
- statement.setString(3, book.authors);
- statement.setInt(4, book.year);
- },
- JdbcExecutionOptions.builder()
- .withBatchSize(1000)
- .withBatchIntervalMs(200)
- .withMaxRetries(5)
- .build(),
- new
JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
-
.withUrl("jdbc:postgresql://dbhost:5432/postgresdb")
- .withDriverName("org.postgresql.Driver")
- .withUsername("someUser")
- .withPassword("somePassword")
- .build()
- ));
-
+ ).sinkTo(
+ JdbcSink.<Book>builder()
+ .withQueryStatement(
+ "insert into books (id, title, authors, year)
values (?, ?, ?, ?)",
+ (statement, book) -> {
+ statement.setLong(1, book.id);
+ statement.setString(2, book.title);
+ statement.setString(3, book.authors);
+ statement.setInt(4, book.year);
+ })
+ .withExecutionOptions(
+ JdbcExecutionOptions.builder()
+ .withBatchSize(1000)
+ .withBatchIntervalMs(200)
+ .withMaxRetries(5)
+ .build())
+ .buildAtLeastOnce(
+ new
JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
+
.withUrl("jdbc:postgresql://dbhost:5432/postgresdb")
+
.withDriverName("org.postgresql.Driver")
+ .withUsername("someUser")
+ .withPassword("somePassword")
+ .build()));
+
env.execute();
}
}
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-env = StreamExecutionEnvironment.get_execution_environment()
-type_info = Types.ROW([Types.INT(), Types.STRING(), Types.STRING(),
Types.INT()])
-env.from_collection(
- [(101, "Stream Processing with Apache Flink", "Fabian Hueske, Vasiliki
Kalavri", 2019),
- (102, "Streaming Systems", "Tyler Akidau, Slava Chernyak, Reuven Lax",
2018),
- (103, "Designing Data-Intensive Applications", "Martin Kleppmann", 2017),
- (104, "Kafka: The Definitive Guide", "Gwen Shapira, Neha Narkhede, Todd
Palino", 2017)
- ], type_info=type_info) \
- .add_sink(
- JdbcSink.sink(
- "insert into books (id, title, authors, year) values (?, ?, ?, ?)",
- type_info,
- JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
- .with_url('jdbc:postgresql://dbhost:5432/postgresdb')
- .with_driver_name('org.postgresql.Driver')
- .with_user_name('someUser')
- .with_password('somePassword')
- .build(),
- JdbcExecutionOptions.builder()
- .with_batch_interval_ms(1000)
- .with_batch_size(200)
- .with_max_retries(5)
- .build()
- ))
-
-env.execute()
-```
-{{< /tab >}}
-{{< /tabs >}}
-## `JdbcSink.exactlyOnceSink`
+### Exactly-once sink
-Since 1.13, Flink JDBC sink supports exactly-once mode.
-The implementation relies on the JDBC driver support of XA
+The exactly-once implementation relies on the JDBC driver support of XA
[standard](https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf).
Most drivers support XA if the database also supports XA (so the driver is
usually the same).
-To use it, create a sink using `exactlyOnceSink()` method as above and
additionally provide:
+To use it, call `buildExactlyOnce()` instead of `buildAtLeastOnce()` and
provide:
- `JdbcExactlyOnceOptions`
-- `JdbcExecutionOptions`
-- [XA
DataSource](https://docs.oracle.com/javase/8/docs/api/javax/sql/XADataSource.html)
Supplier
+- an [XA
DataSource](https://docs.oracle.com/javase/8/docs/api/javax/sql/XADataSource.html)
Supplier
For example:
-{{< tabs "4ab65f13-608a-411a-8d24-e304f627ac8f" >}}
-{{< tab "Java" >}}
```java
StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
env
- .fromElements(...)
- .addSink(JdbcSink.exactlyOnceSink(
- "insert into books (id, title, author, price, qty) values
(?,?,?,?,?)",
- (ps, t) -> {
- ps.setInt(1, t.id);
- ps.setString(2, t.title);
- ps.setString(3, t.author);
- ps.setDouble(4, t.price);
- ps.setInt(5, t.qty);
- },
- JdbcExecutionOptions.builder()
- .withMaxRetries(0)
- .build(),
- JdbcExactlyOnceOptions.defaults(),
- () -> {
- // create a driver-specific XA DataSource
- // The following example is for derby
- EmbeddedXADataSource ds = new EmbeddedXADataSource();
- ds.setDatabaseName("my_db");
- return ds;
- });
+ .fromData(...)
+ .sinkTo(JdbcSink.<Book>builder()
+ .withQueryStatement(
+ "insert into books (id, title, author, price, qty)
values (?,?,?,?,?)",
+ (ps, t) -> {
+ ps.setInt(1, t.id);
+ ps.setString(2, t.title);
+ ps.setString(3, t.author);
+ ps.setDouble(4, t.price);
+ ps.setInt(5, t.qty);
+ })
+ .withExecutionOptions(
+ JdbcExecutionOptions.builder()
+ .withMaxRetries(0)
+ .build())
+ .buildExactlyOnce(
+ JdbcExactlyOnceOptions.defaults(),
+ () -> {
+ // create a driver-specific XA DataSource
+ PGXADataSource ds = new PGXADataSource();
+
ds.setUrl("jdbc:postgresql://localhost:5432/postgres");
+ ds.setUser(username);
+ ds.setPassword(password);
+ return ds;
+ }));
env.execute();
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
+
+An `XaConnectionProvider` can be passed instead of the supplier when the
connection strategy has to
+be controlled directly.
**NOTE:** Some databases only allow a single XA transaction per connection
(e.g. PostgreSQL, MySQL).
In such cases, please use the following API to construct
`JdbcExactlyOnceOptions`:
-{{< tabs "4ab65f13-608a-411a-8d24-e304f627cd4e" >}}
-{{< tab "Java" >}}
```java
JdbcExactlyOnceOptions.builder()
.withTransactionPerConnection(true)
.build();
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
This will make Flink use a separate connection for every XA transaction. This
may require adjusting connection limits.
For PostgreSQL and MySQL, this can be done by increasing `max_connections`.
@@ -463,63 +455,48 @@ Furthermore, XA needs to be enabled and/or configured in
some databases.
For PostgreSQL, you should set `max_prepared_transactions` to some value
greater than zero.
For MySQL v8+, you should grant `XA_RECOVER_ADMIN` to Flink DB user.
-**ATTENTION:** Currently, `JdbcSink.exactlyOnceSink` can ensure exactly once
semantics
-with `JdbcExecutionOptions.maxRetries == 0`; otherwise, duplicated results
maybe produced.
+**ATTENTION:** The exactly-once sink requires
`JdbcExecutionOptions.maxRetries` to be set to `0`.
+The default is `3`, so set it explicitly with
`JdbcExecutionOptions.builder().withMaxRetries(0)`;
+otherwise the sink fails at startup.
### `XADataSource` examples
PostgreSQL `XADataSource` example:
-{{< tabs "4ab65f13-608a-411a-8d24-e304f323ab3a" >}}
-{{< tab "Java" >}}
```java
PGXADataSource xaDataSource = new org.postgresql.xa.PGXADataSource();
xaDataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
xaDataSource.setUser(username);
xaDataSource.setPassword(password);
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
MySQL `XADataSource` example:
-{{< tabs "4ab65f13-608a-411a-8d24-b213f323ca3c" >}}
-{{< tab "Java" >}}
```java
MysqlXADataSource xaDataSource = new com.mysql.cj.jdbc.MysqlXADataSource();
xaDataSource.setUrl("jdbc:mysql://localhost:3306/");
xaDataSource.setUser(username);
xaDataSource.setPassword(password);
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
Oracle `XADataSource` example:
-{{< tabs "4ab65f13-608a-411a-8d24-b213f337ad5f" >}}
-{{< tab "Java" >}}
```java
OracleXADataSource xaDataSource = new oracle.jdbc.xa.OracleXADataSource();
xaDataSource.setURL("jdbc:oracle:oci8:@");
xaDataSource.setUser("scott");
xaDataSource.setPassword("tiger");
```
-{{< /tab >}}
-{{< tab "Python" >}}
-```python
-Still not supported in Python API.
-```
-{{< /tab >}}
-{{< /tabs >}}
Please also take Oracle connection pooling into account.
-Please refer to the `JdbcXaSinkFunction` documentation for more details.
+## Lineage
+
+`JdbcSource` and `JdbcSink` expose lineage information as described in
+[FLIP-314](https://cwiki.apache.org/confluence/display/FLINK/FLIP-314%3A+Support+Customized+Job+Lineage+Listener),
+as do the Table API source and lookup function. No configuration is needed on
the connector side; the
+information is delivered to any `JobStatusChangedListener` registered in the
cluster.
+
+The dataset name is derived from the queries the job executes. The dataset
namespace is derived
+from the JDBC URL, but only when the connector opens connections from that URL
itself, i.e. a
+`JdbcSource` configured with `setDBUrl` or a `JdbcSink` built with
+`buildAtLeastOnce(JdbcConnectionOptions)`. With `buildExactlyOnce` or a custom
+`JdbcConnectionProvider` the namespace is empty.
{{< top >}}
diff --git a/docs/content/docs/connectors/table/jdbc.md
b/docs/content/docs/connectors/table/jdbc.md
index f2cc56b3..b23f965e 100644
--- a/docs/content/docs/connectors/table/jdbc.md
+++ b/docs/content/docs/connectors/table/jdbc.md
@@ -38,8 +38,34 @@ The JDBC sink operate in upsert mode for exchange
UPDATE/DELETE messages with th
Dependencies
------------
+Since version 4.0 the JDBC connector is no longer published as a single
artifact. It is split into
+`flink-connector-jdbc-core`, which contains the shared runtime, and one
artifact per supported
+database, which contains the dialect and the catalog for that database. You
need the artifact of the
+database you are connecting to; it pulls in `flink-connector-jdbc-core`
transitively.
+
+| Database | Connector artifact |
+|:-----------|:----------------------------------|
+| MySQL | `flink-connector-jdbc-mysql` |
+| Oracle | `flink-connector-jdbc-oracle` |
+| PostgreSQL | `flink-connector-jdbc-postgres` |
+| SQL Server | `flink-connector-jdbc-sqlserver` |
+| CrateDB | `flink-connector-jdbc-cratedb` |
+| Db2 | `flink-connector-jdbc-db2` |
+| Trino | `flink-connector-jdbc-trino` |
+| OceanBase | `flink-connector-jdbc-oceanbase` |
+| Spanner | `flink-connector-jdbc-spanner` |
+| Derby | `flink-connector-jdbc-core` |
+
{{< sql_connector_download_table "jdbc" >}}
+None of these artifacts is a self-contained SQL uber jar. When using the SQL
Client or a session
+cluster, put both `flink-connector-jdbc-core` and the artifact for your
database into the `lib/`
+directory, together with the `io.openlineage:openlineage-java` and
`io.openlineage:openlineage-sql-java`
+jars that `flink-connector-jdbc-core` depends on. Without them, building the
job fails with a
+`NoClassDefFoundError`. Some dialects build on others and need their artifacts
as well: CrateDB also needs
+`flink-connector-jdbc-postgres`, and OceanBase needs both
`flink-connector-jdbc-mysql` and
+`flink-connector-jdbc-oracle`.
+
The JDBC connector is not part of the binary distribution.
See how to link with it for cluster execution [here]({{< ref
"docs/dev/configuration/overview" >}}).
@@ -147,7 +173,10 @@ Connector Options
<td>yes</td>
<td style="word-wrap: break-word;">(none)</td>
<td>String</td>
- <td>The compatible mode of database.</td>
+ <td>The compatible mode of database. Only OceanBase supports this
option, with the values
+ <code>'mysql'</code> (the default when unset) and
<code>'oracle'</code>, selecting which
+ tenant mode the dialect targets. Setting it for any other database
fails with
+ <code>Not supported option 'compatible-mode'</code>.</td>
</tr>
<tr>
<td><h5>username</h5></td>
@@ -194,7 +223,7 @@ Connector Options
<td>optional</td>
<td>no</td>
<td style="word-wrap: break-word;">(none)</td>
- <td>Integer</td>
+ <td>Long</td>
<td>The smallest value of the first partition.</td>
</tr>
<tr>
@@ -202,7 +231,7 @@ Connector Options
<td>optional</td>
<td>no</td>
<td style="word-wrap: break-word;">(none)</td>
- <td>Integer</td>
+ <td>Long</td>
<td>The largest value of the last partition.</td>
</tr>
<tr>
@@ -452,15 +481,29 @@ As there is no standard syntax for upsert, the following
table describes the dat
<td>Spanner</td>
<td>INSERT OR UPDATE INTO .. (..) VALUES (..)</td>
</tr>
+ <tr>
+ <td>CrateDB</td>
+ <td>INSERT .. ON CONFLICT .. DO UPDATE SET ..</td>
+ </tr>
+ <tr>
+ <td>OceanBase</td>
+ <td>Depends on <code>'compatible-mode'</code>: the MySQL grammar
in <code>'mysql'</code>
+ mode, the Oracle grammar in <code>'oracle'</code> mode.</td>
+ </tr>
</tbody>
</table>
+Trino and Derby have no upsert grammar. If a primary key is defined in the
DDL, the sink instead
+checks whether a row with that key exists and then issues an `UPDATE` or an
`INSERT`, and issues a
+`DELETE` for retractions. For Trino this only works if the Trino connector
behind the table supports
+`UPDATE` and `DELETE`.
+
JDBC Catalog
------------
The `JdbcCatalog` enables users to connect Flink to relational databases over
JDBC protocol.
-Currently, there are following JDBC catalog implementations: Postgres Catalog,
MySQL Catalog, CrateDB Catalog and OceanBase Catalog. They support the
following catalog methods. Other methods are currently not supported.
+Currently, there are following JDBC catalog implementations: Postgres Catalog,
MySQL Catalog, CrateDB Catalog, OceanBase Catalog and Spanner Catalog. They
support the following catalog methods. Other methods are currently not
supported.
```java
databaseExists(String databaseName);
@@ -480,15 +523,45 @@ Please refer to [Dependencies](#dependencies) section for
how to setup a JDBC co
The JDBC catalog supports the following options:
- `name`: required, name of the catalog.
-- `default-database`: required, default database to connect to.
- `username`: required, username of database account.
- `password`: required, password of the account.
-- `base-url`: required (should not contain the database name)
+- `base-url`: required
- for Postgres Catalog this should be `"jdbc:postgresql://<ip>:<port>"`
- for MySQL Catalog this should be `"jdbc:mysql://<ip>:<port>"`
- for OceanBase Catalog this should be `"jdbc:oceanbase://<ip>:<port>"`
- for Spanner Catalog this should be
`"jdbc:cloudspanner:/projects/<project>/instances/<instance>/databases/"`
-- `compatible-mode`: optional, the compatible mode of database.
+- `default-database`: optional, default database to connect to. Required for
the Spanner Catalog.
+- `compatible-mode`: optional, the compatible mode of database. Only OceanBase
supports this option,
+ see [Connector Options](#connector-options).
+
+Except for the Spanner Catalog, the database the catalog connects to has to be
identifiable from
+`base-url`, `default-database`, or both. Any one of the following is valid:
+
+- `default-database` is set and `base-url` carries no database name, for
example
+ `'base-url' = 'jdbc:postgresql://localhost:5432'` with `'default-database' =
'mydb'`
+- `base-url` carries the database name and `default-database` is omitted, for
example
+ `'base-url' = 'jdbc:postgresql://localhost:5432/mydb'`. Without a query
string, this form pins
+ the catalog to `mydb`: every database in the catalog is accessed through
this same URL.
+- both are set and the database names match
+
+If the two disagree, or if neither carries a database name, catalog creation
fails.
+
+`base-url` may also carry arbitrary driver options as query parameters, which
are passed through to
+every connection the catalog opens. The database name is substituted ahead of
the query string, so
+the options apply to all databases in the catalog. When `base-url` has a query
string, it must
+carry the database name itself; setting only `default-database` is rejected:
+
+```sql
+CREATE CATALOG my_catalog WITH(
+ 'type' = 'jdbc',
+ 'username' = '...',
+ 'password' = '...',
+ 'base-url' =
'jdbc:postgresql://localhost:5432/mydb?ssl=true&connectTimeout=10'
+);
+```
+
+The Spanner Catalog always takes the database from `default-database`. Driver
options are appended
+to its `base-url` as `;key=value` pairs instead of a query string.
{{< tabs "10bd8bfb-674c-46aa-8a36-385537df5791" >}}
{{< tab "SQL" >}}
@@ -976,7 +1049,9 @@ Flink supports the connection to several databases using
dialect like MySQL, Ora
<code>CHARACTER(n)</code><br>
<code>VARCHAR(n)</code><br>
<code>CHARACTER VARYING(n)</code><br>
- <code>TEXT</code></td>
+ <code>TEXT</code><br>
+ <code>JSON</code><br>
+ <code>JSONB</code></td>
<td>
<code>CHAR(n)</code><br>
<code>CHARACTER(n)</code><br>
@@ -1037,6 +1112,19 @@ Flink supports the connection to several databases using
dialect like MySQL, Ora
<td><code>BYTES(n)</code></td>
<td><code>BYTES</code></td>
</tr>
+ <tr>
+ <td></td>
+ <td></td>
+ <td><code>UUID</code></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td><code>VARCHAR(36)</code></td>
+ </tr>
<tr>
<td></td>
<td></td>
@@ -1053,4 +1141,10 @@ Flink supports the connection to several databases using
dialect like MySQL, Ora
</tbody>
</table>
+PostgreSQL `JSON` and `JSONB` columns are read as their textual
representation. `UUID` columns are
+mapped to `VARCHAR(36)`; declaring such a column as `STRING` in the DDL also
works. The sink binds
+all three as strings, which PostgreSQL rejects for these column types unless
the JDBC URL sets
+`stringtype=unspecified`, for example
+`'url' = 'jdbc:postgresql://localhost:5432/mydb?stringtype=unspecified'`.
+
{{< top >}}
diff --git a/docs/data/jdbc.yml b/docs/data/jdbc.yml
index 1e23686c..7f07e7f8 100644
--- a/docs/data/jdbc.yml
+++ b/docs/data/jdbc.yml
@@ -16,7 +16,26 @@
# limitations under the License.
################################################################################
-version: 3.3.0-SNAPSHOT
+version: 5.0-SNAPSHOT
+flink_compatibility: ["2.1", "2.2", "2.3"]
variants:
- - maven: flink-connector-jdbc
- sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc/$full_version/flink-connector-jdbc-$full_version.jar
+ - maven: flink-connector-jdbc-core
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-core/$full_version/flink-connector-jdbc-core-$full_version.jar
+ - maven: flink-connector-jdbc-cratedb
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-cratedb/$full_version/flink-connector-jdbc-cratedb-$full_version.jar
+ - maven: flink-connector-jdbc-db2
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-db2/$full_version/flink-connector-jdbc-db2-$full_version.jar
+ - maven: flink-connector-jdbc-mysql
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-mysql/$full_version/flink-connector-jdbc-mysql-$full_version.jar
+ - maven: flink-connector-jdbc-oceanbase
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-oceanbase/$full_version/flink-connector-jdbc-oceanbase-$full_version.jar
+ - maven: flink-connector-jdbc-oracle
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-oracle/$full_version/flink-connector-jdbc-oracle-$full_version.jar
+ - maven: flink-connector-jdbc-postgres
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-postgres/$full_version/flink-connector-jdbc-postgres-$full_version.jar
+ - maven: flink-connector-jdbc-spanner
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-spanner/$full_version/flink-connector-jdbc-spanner-$full_version.jar
+ - maven: flink-connector-jdbc-sqlserver
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-sqlserver/$full_version/flink-connector-jdbc-sqlserver-$full_version.jar
+ - maven: flink-connector-jdbc-trino
+ sql_url:
https://repo.maven.apache.org/maven2/org/apache/flink/flink-connector-jdbc-trino/$full_version/flink-connector-jdbc-trino-$full_version.jar