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