Skip to main content

risingwave_connector/
lib.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![expect(clippy::derive_partial_eq_without_eq)]
16#![warn(clippy::large_futures, clippy::large_stack_frames)]
17#![feature(coroutines)]
18#![feature(proc_macro_hygiene)]
19#![feature(stmt_expr_attributes)]
20#![feature(trait_alias)]
21#![feature(type_alias_impl_trait)]
22#![feature(associated_type_defaults)]
23#![feature(iter_from_coroutine)]
24#![feature(iterator_try_collect)]
25#![feature(try_blocks)]
26#![feature(error_generic_member_access)]
27#![feature(negative_impls)]
28#![feature(register_tool)]
29#![feature(never_type)]
30#![feature(map_try_insert)]
31#![register_tool(rw)]
32#![recursion_limit = "256"]
33#![feature(min_specialization)]
34#![feature(custom_inner_attributes)]
35#![feature(iter_array_chunks)]
36
37use std::sync::OnceLock;
38use std::time::Duration;
39
40use duration_str::parse_std;
41use risingwave_common::config::MetricLevel;
42use serde::de;
43
44pub mod aws_utils;
45
46pub mod allow_alter_on_fly_fields;
47
48mod enforce_secret;
49pub mod error;
50mod macros;
51
52pub mod parser;
53pub mod schema;
54pub mod sink;
55pub mod source;
56
57pub mod connector_common;
58
59pub use paste::paste;
60pub use risingwave_jni_core::{call_method, call_static_method, jvm_runtime};
61
62mod with_options;
63pub use with_options::{Get, GetKeyIter, WithOptionsSecResolved, WithPropertiesExt};
64
65#[cfg(test)]
66mod with_options_test;
67
68pub const AUTO_SCHEMA_CHANGE_KEY: &str = "auto.schema.change";
69pub const SINK_CREATE_TABLE_IF_NOT_EXISTS_KEY: &str = "create_table_if_not_exists";
70pub const SINK_TARGET_TABLE_NAME: &str = "table.name";
71pub const SINK_INTERMEDIATE_TABLE_NAME: &str = "intermediate.table.name";
72
73static CONNECTOR_METRICS_LEVEL: OnceLock<MetricLevel> = OnceLock::new();
74
75pub fn init_connector_metrics(metric_level: MetricLevel) {
76    CONNECTOR_METRICS_LEVEL.get_or_init(|| metric_level);
77}
78
79fn connector_metrics_level() -> MetricLevel {
80    CONNECTOR_METRICS_LEVEL
81        .get()
82        .copied()
83        .unwrap_or(MetricLevel::Debug)
84}
85
86pub(crate) fn deserialize_u32_from_string<'de, D>(deserializer: D) -> Result<u32, D::Error>
87where
88    D: de::Deserializer<'de>,
89{
90    let s: String = de::Deserialize::deserialize(deserializer)?;
91    s.parse().map_err(|_| {
92        de::Error::invalid_value(
93            de::Unexpected::Str(&s),
94            &"integer greater than or equal to 0",
95        )
96    })
97}
98
99pub(crate) fn deserialize_optional_string_seq_from_string<'de, D>(
100    deserializer: D,
101) -> std::result::Result<Option<Vec<String>>, D::Error>
102where
103    D: de::Deserializer<'de>,
104{
105    let s: Option<String> = de::Deserialize::deserialize(deserializer)?;
106    if let Some(s) = s {
107        let s = s.to_ascii_lowercase();
108        let s = s.split(',').map(|s| s.trim().to_owned()).collect();
109        Ok(Some(s))
110    } else {
111        Ok(None)
112    }
113}
114
115pub(crate) fn deserialize_optional_u64_seq_from_string<'de, D>(
116    deserializer: D,
117) -> std::result::Result<Option<Vec<u64>>, D::Error>
118where
119    D: de::Deserializer<'de>,
120{
121    let s: Option<String> = de::Deserialize::deserialize(deserializer)?;
122    if let Some(s) = s {
123        let numbers = s
124            .split(',')
125            .map(|s| s.trim().parse())
126            .collect::<Result<Vec<u64>, _>>()
127            .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(&s), &"invalid number"));
128        Ok(Some(numbers?))
129    } else {
130        Ok(None)
131    }
132}
133
134pub(crate) fn deserialize_bool_from_string<'de, D>(deserializer: D) -> Result<bool, D::Error>
135where
136    D: de::Deserializer<'de>,
137{
138    let s: String = de::Deserialize::deserialize(deserializer)?;
139    let s = s.to_ascii_lowercase();
140    match s.as_str() {
141        "true" => Ok(true),
142        "false" => Ok(false),
143        _ => Err(de::Error::invalid_value(
144            de::Unexpected::Str(&s),
145            &"true or false",
146        )),
147    }
148}
149
150pub(crate) fn deserialize_optional_bool_from_string<'de, D>(
151    deserializer: D,
152) -> std::result::Result<Option<bool>, D::Error>
153where
154    D: de::Deserializer<'de>,
155{
156    let s: Option<String> = de::Deserialize::deserialize(deserializer)?;
157    if let Some(s) = s {
158        let s = s.to_ascii_lowercase();
159        match s.as_str() {
160            "true" => Ok(Some(true)),
161            "false" => Ok(Some(false)),
162            _ => Err(de::Error::invalid_value(
163                de::Unexpected::Str(&s),
164                &"true or false",
165            )),
166        }
167    } else {
168        Ok(None)
169    }
170}
171
172pub(crate) fn deserialize_duration_from_string<'de, D>(
173    deserializer: D,
174) -> Result<Duration, D::Error>
175where
176    D: de::Deserializer<'de>,
177{
178    let s: String = de::Deserialize::deserialize(deserializer)?;
179    parse_std(&s).map_err(|_| de::Error::invalid_value(
180        de::Unexpected::Str(&s),
181        &"The String value unit support for one of:[“y”,“mon”,“w”,“d”,“h”,“m”,“s”, “ms”, “µs”, “ns”]",
182    ))
183}
184
185pub(crate) fn deserialize_optional_duration_from_string<'de, D>(
186    deserializer: D,
187) -> Result<Option<Duration>, D::Error>
188where
189    D: de::Deserializer<'de>,
190{
191    let s: Option<String> = de::Deserialize::deserialize(deserializer)?;
192    if let Some(s) = s {
193        let duration = parse_std(&s).map_err(|_| de::Error::invalid_value(
194            de::Unexpected::Str(&s),
195            &"The String value unit support for one of:[“y”,“mon”,“w”,“d”,“h”,“m”,“s”, “ms”, “µs”, “ns”]",
196        ))?;
197        Ok(Some(duration))
198    } else {
199        Ok(None)
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use expect_test::expect_file;
206
207    use crate::with_options_test::{
208        generate_allow_alter_on_fly_fields_combined, generate_iceberg_engine_fields,
209        generate_with_options_yaml_connection, generate_with_options_yaml_sink,
210        generate_with_options_yaml_source,
211    };
212
213    /// This test ensures that `src/connector/with_options.yaml` is up-to-date with the default values specified
214    /// in this file. Developer should run `./risedev generate-with-options` to update it if this
215    /// test fails.
216    #[test]
217    fn test_with_options_yaml_up_to_date() {
218        expect_file!("../with_options_source.yaml").assert_eq(&generate_with_options_yaml_source());
219
220        expect_file!("../with_options_sink.yaml").assert_eq(&generate_with_options_yaml_sink());
221
222        expect_file!("../with_options_connection.yaml")
223            .assert_eq(&generate_with_options_yaml_connection());
224    }
225
226    /// This test ensures that the `allow_alter_on_fly` fields Rust file is up-to-date.
227    #[test]
228    fn test_allow_alter_on_fly_fields_rust_up_to_date() {
229        expect_file!("../src/allow_alter_on_fly_fields.rs")
230            .assert_eq(&generate_allow_alter_on_fly_fields_combined());
231    }
232
233    /// This test ensures that Iceberg Engine sink fields are up-to-date.
234    #[test]
235    fn test_iceberg_engine_fields_rust_up_to_date() {
236        expect_file!("../src/sink/iceberg/engine_options.rs")
237            .assert_eq(&generate_iceberg_engine_fields());
238    }
239
240    #[test]
241    fn test_orphan_file_cleanup_options_supported_by_iceberg_engine() {
242        use crate::sink::iceberg::is_iceberg_engine_option;
243
244        assert!(is_iceberg_engine_option("enable_orphan_file_cleanup"));
245        assert!(is_iceberg_engine_option(
246            "orphan_file_cleanup_min_age_millis"
247        ));
248    }
249
250    /// Test some serde behavior we rely on.
251    mod serde {
252        #![expect(dead_code)]
253
254        use std::collections::BTreeMap;
255
256        use expect_test::expect;
257        use serde::Deserialize;
258
259        // test deny_unknown_fields and flatten
260
261        // TL;DR: deny_unknown_fields
262        // - doesn't work with flatten map
263        // - can work with flatten struct
264        // - doesn't work with nested flatten struct (This makes a flatten struct behave like a flatten map)
265
266        #[test]
267        fn test_outer_deny() {
268            #[derive(Deserialize, Debug)]
269            #[serde(deny_unknown_fields)]
270            struct FlattenMap {
271                #[serde(flatten)]
272                flatten: BTreeMap<String, String>,
273            }
274            #[derive(Deserialize, Debug)]
275            #[serde(deny_unknown_fields)]
276            struct FlattenStruct {
277                #[serde(flatten)]
278                flatten_struct: Inner,
279            }
280
281            #[derive(Deserialize, Debug)]
282            #[serde(deny_unknown_fields)]
283            struct FlattenBoth {
284                #[serde(flatten)]
285                flatten: BTreeMap<String, String>,
286                #[serde(flatten)]
287                flatten_struct: Inner,
288            }
289
290            #[derive(Deserialize, Debug)]
291            struct Inner {
292                a: Option<String>,
293                b: Option<String>,
294            }
295
296            let json = r#"{
297                "a": "b"
298            }"#;
299            let foo: Result<FlattenMap, _> = serde_json::from_str(json);
300            let foo1: Result<FlattenStruct, _> = serde_json::from_str(json);
301            let foo2: Result<FlattenBoth, _> = serde_json::from_str(json);
302
303            // with `deny_unknown_fields`, we can't flatten ONLY a map
304            expect![[r#"
305                Err(
306                    Error("unknown field `a`", line: 3, column: 13),
307                )
308            "#]]
309            .assert_debug_eq(&foo);
310
311            // but can flatten a struct!
312            expect![[r#"
313                Ok(
314                    FlattenStruct {
315                        flatten_struct: Inner {
316                            a: Some(
317                                "b",
318                            ),
319                            b: None,
320                        },
321                    },
322                )
323            "#]]
324            .assert_debug_eq(&foo1);
325            // unknown fields can be denied.
326            let foo11: Result<FlattenStruct, _> =
327                serde_json::from_str(r#"{ "a": "b", "unknown":1 }"#);
328            expect_test::expect![[r#"
329                Err(
330                    Error("unknown field `unknown`", line: 1, column: 25),
331                )
332            "#]]
333            .assert_debug_eq(&foo11);
334
335            // When both struct and map are flattened, the map also works...
336            expect![[r#"
337                Ok(
338                    FlattenBoth {
339                        flatten: {
340                            "a": "b",
341                        },
342                        flatten_struct: Inner {
343                            a: Some(
344                                "b",
345                            ),
346                            b: None,
347                        },
348                    },
349                )
350            "#]]
351            .assert_debug_eq(&foo2);
352
353            let foo21: Result<FlattenBoth, _> =
354                serde_json::from_str(r#"{ "a": "b", "unknown":1 }"#);
355            expect_test::expect![[r#"
356                Err(
357                    Error("invalid type: integer `1`, expected a string", line: 1, column: 25),
358                )
359            "#]]
360            .assert_debug_eq(&foo21);
361            // This error above is a little funny, since even if we use string, it will still fail.
362            let foo22: Result<FlattenBoth, _> =
363                serde_json::from_str(r#"{ "a": "b", "unknown":"1" }"#);
364            expect_test::expect![[r#"
365                Err(
366                    Error("unknown field `unknown`", line: 1, column: 27),
367                )
368            "#]]
369            .assert_debug_eq(&foo22);
370        }
371
372        #[test]
373        fn test_inner_deny() {
374            // no outer deny now.
375            #[derive(Deserialize, Debug)]
376            struct FlattenStruct {
377                #[serde(flatten)]
378                flatten_struct: Inner,
379            }
380            #[derive(Deserialize, Debug)]
381            #[serde(deny_unknown_fields)]
382            struct Inner {
383                a: Option<String>,
384                b: Option<String>,
385            }
386
387            let json = r#"{
388                "a": "b", "unknown":1
389            }"#;
390            let foo: Result<FlattenStruct, _> = serde_json::from_str(json);
391            // unknown fields cannot be denied.
392            // I think this is because `deserialize_struct` is called, and required fields are passed.
393            // Other fields are left for the outer struct to consume.
394            expect_test::expect![[r#"
395                Ok(
396                    FlattenStruct {
397                        flatten_struct: Inner {
398                            a: Some(
399                                "b",
400                            ),
401                            b: None,
402                        },
403                    },
404                )
405            "#]]
406            .assert_debug_eq(&foo);
407        }
408
409        #[test]
410        fn test_multiple_flatten() {
411            #[derive(Deserialize, Debug)]
412            struct Foo {
413                /// struct will "consume" the used fields!
414                #[serde(flatten)]
415                flatten_struct: Inner1,
416
417                /// map will keep the unknown fields!
418                #[serde(flatten)]
419                flatten_map1: BTreeMap<String, String>,
420
421                #[serde(flatten)]
422                flatten_map2: BTreeMap<String, String>,
423
424                #[serde(flatten)]
425                flatten_struct2: Inner2,
426            }
427
428            #[derive(Deserialize, Debug)]
429            #[serde(deny_unknown_fields)]
430            struct Inner1 {
431                a: Option<String>,
432                b: Option<String>,
433            }
434            #[derive(Deserialize, Debug)]
435            struct Inner11 {
436                c: Option<String>,
437            }
438            #[derive(Deserialize, Debug)]
439            #[serde(deny_unknown_fields)]
440            struct Inner2 {
441                c: Option<String>,
442            }
443
444            let json = r#"{
445                "a": "b", "c":"d"
446            }"#;
447            let foo2: Result<Foo, _> = serde_json::from_str(json);
448
449            // When there are multiple flatten, all of them will be used.
450            // Also, with outer `flatten``, the inner `deny_unknown_fields` is ignored.
451            expect![[r#"
452            Ok(
453                Foo {
454                    flatten_struct: Inner1 {
455                        a: Some(
456                            "b",
457                        ),
458                        b: None,
459                    },
460                    flatten_map1: {
461                        "c": "d",
462                    },
463                    flatten_map2: {
464                        "c": "d",
465                    },
466                    flatten_struct2: Inner2 {
467                        c: Some(
468                            "d",
469                        ),
470                    },
471                },
472            )
473        "#]]
474            .assert_debug_eq(&foo2);
475        }
476
477        #[test]
478        fn test_nested_flatten() {
479            #[derive(Deserialize, Debug)]
480            #[serde(deny_unknown_fields)]
481            struct Outer {
482                #[serde(flatten)]
483                inner: Inner,
484            }
485
486            #[derive(Deserialize, Debug)]
487            struct Inner {
488                a: Option<String>,
489                b: Option<String>,
490                #[serde(flatten)]
491                nested: InnerInner,
492            }
493
494            #[derive(Deserialize, Debug)]
495            struct InnerInner {
496                c: Option<String>,
497            }
498
499            let json = r#"{ "a": "b", "unknown":"1" }"#;
500
501            let foo: Result<Outer, _> = serde_json::from_str(json);
502
503            // This is very unfortunate...
504            expect_test::expect![[r#"
505            Err(
506                Error("unknown field `a`", line: 1, column: 27),
507            )
508        "#]]
509            .assert_debug_eq(&foo);
510
511            // Actually, the nested `flatten` will makes the struct behave like a map.
512            // Let's remove `deny_unknown_fields` and see
513            #[derive(Deserialize, Debug)]
514            struct Outer2 {
515                #[serde(flatten)]
516                inner: Inner,
517                /// We can see the fields of `inner` are not consumed.
518                #[serde(flatten)]
519                map: BTreeMap<String, String>,
520            }
521            let foo2: Result<Outer2, _> = serde_json::from_str(json);
522            expect_test::expect![[r#"
523                Ok(
524                    Outer2 {
525                        inner: Inner {
526                            a: Some(
527                                "b",
528                            ),
529                            b: None,
530                            nested: InnerInner {
531                                c: None,
532                            },
533                        },
534                        map: {
535                            "a": "b",
536                            "unknown": "1",
537                        },
538                    },
539                )
540            "#]]
541            .assert_debug_eq(&foo2);
542        }
543
544        #[test]
545        fn test_flatten_option() {
546            #[derive(Deserialize, Debug)]
547            struct Foo {
548                /// flatten option struct can still consume the field
549                #[serde(flatten)]
550                flatten_struct: Option<Inner1>,
551
552                /// flatten option map is always `Some`
553                #[serde(flatten)]
554                flatten_map1: Option<BTreeMap<String, String>>,
555
556                /// flatten option struct is `None` if the required field is absent
557                #[serde(flatten)]
558                flatten_struct2: Option<Inner2>,
559
560                /// flatten option struct is `Some` if the required field is present and optional field is absent.
561                /// Note: if all fields are optional, the struct is always `Some`
562                #[serde(flatten)]
563                flatten_struct3: Option<Inner3>,
564            }
565
566            #[derive(Deserialize, Debug)]
567            struct Inner1 {
568                a: Option<String>,
569                b: Option<String>,
570            }
571            #[derive(Deserialize, Debug)]
572            struct Inner11 {
573                c: Option<String>,
574            }
575
576            #[derive(Deserialize, Debug)]
577            struct Inner2 {
578                c: Option<String>,
579                d: String,
580            }
581
582            #[derive(Deserialize, Debug)]
583            struct Inner3 {
584                e: Option<String>,
585                f: String,
586            }
587
588            let json = r#"{
589        "a": "b", "c": "d", "f": "g"
590     }"#;
591            let foo: Result<Foo, _> = serde_json::from_str(json);
592            expect![[r#"
593                Ok(
594                    Foo {
595                        flatten_struct: Some(
596                            Inner1 {
597                                a: Some(
598                                    "b",
599                                ),
600                                b: None,
601                            },
602                        ),
603                        flatten_map1: Some(
604                            {
605                                "c": "d",
606                                "f": "g",
607                            },
608                        ),
609                        flatten_struct2: None,
610                        flatten_struct3: Some(
611                            Inner3 {
612                                e: None,
613                                f: "g",
614                            },
615                        ),
616                    },
617                )
618            "#]]
619            .assert_debug_eq(&foo);
620        }
621    }
622}