1#![rustfmt::skip]
26
27use std::collections::{HashMap, HashSet};
28use std::sync::LazyLock;
29use crate::error::ConnectorError;
30use crate::sink::remote::JdbcSink;
31use crate::sink::Sink;
32
33macro_rules! use_source_properties {
34 ({ $({ $variant_name:ident, $prop_name:ty, $split:ty }),* }) => {
35 $(
36 #[allow(unused_imports)]
37 pub(super) use $prop_name;
38 )*
39 };
40}
41
42mod source_properties {
43 use crate::for_all_sources;
44 use crate::source::base::SourceProperties;
45
46 for_all_sources!(use_source_properties);
47
48 macro_rules! impl_source_name_to_prop_type_name_inner {
54 ({ $({$variant:ident, $prop_name:ty, $split:ty}),* }) => {
55 pub fn source_name_to_prop_type_name(source_name: &str) -> Option<&'static str> {
56 match source_name {
57 $(
58 <$prop_name>::SOURCE_NAME => Some(std::any::type_name::<$prop_name>()),
59 )*
60 _ => None,
61 }
62 }
63 };
64 }
65
66 macro_rules! impl_source_name_to_prop_type_name {
67 () => {
68 $crate::for_all_sources! { impl_source_name_to_prop_type_name_inner }
69 };
70 }
71
72 impl_source_name_to_prop_type_name!();
73}
74
75mod sink_properties {
76 use crate::use_all_sink_configs;
77 use crate::sink::Sink;
78 use crate::sink::file_sink::fs::FsSink;
79
80 use_all_sink_configs!();
81
82 macro_rules! impl_sink_name_to_config_type_name_inner {
83 ({ $({ $variant_name:ident, $sink_type:ty, $config_type:ty }),* }) => {
84 pub fn sink_name_to_config_type_name(sink_name: &str) -> Option<&'static str> {
85 match sink_name {
86 $(
87 <$sink_type>::SINK_NAME => Some(std::any::type_name::<$config_type>()),
88 )*
89 _ => None,
90 }
91 }
92 };
93 }
94
95 macro_rules! impl_sink_name_to_config_type_name {
96 () => {
97 $crate::for_all_sinks! { impl_sink_name_to_config_type_name_inner }
98 };
99 }
100
101 impl_sink_name_to_config_type_name!();
102}
103
104pub static SOURCE_ALLOW_ALTER_ON_FLY_FIELDS: LazyLock<HashMap<String, HashSet<String>>> = LazyLock::new(|| {
106 use source_properties::*;
107 let mut map = HashMap::new();
108 map.try_insert(
110 std::any::type_name::<MysqlCdcProperties>().to_owned(),
111 [
112 "cdc.source.wait.streaming.start.timeout".to_owned(),
113 "debezium.max.queue.size".to_owned(),
114 "debezium.queue.memory.ratio".to_owned(),
115 "hostname".to_owned(),
116 "port".to_owned(),
117 "password".to_owned(),
118 ].into_iter().collect(),
119 ).unwrap();
120 map.try_insert(
121 std::any::type_name::<PostgresCdcProperties>().to_owned(),
122 [
123 "cdc.source.wait.streaming.start.timeout".to_owned(),
124 "debezium.max.queue.size".to_owned(),
125 "debezium.queue.memory.ratio".to_owned(),
126 "debezium.heartbeat.interval.ms".to_owned(),
127 "hostname".to_owned(),
128 "port".to_owned(),
129 "password".to_owned(),
130 ].into_iter().collect(),
131 ).unwrap();
132 map.try_insert(
133 std::any::type_name::<SqlServerCdcProperties>().to_owned(),
134 [
135 "cdc.source.wait.streaming.start.timeout".to_owned(),
136 "debezium.max.queue.size".to_owned(),
137 "debezium.queue.memory.ratio".to_owned(),
138 "hostname".to_owned(),
139 "port".to_owned(),
140 "password".to_owned(),
141 ].into_iter().collect(),
142 ).unwrap();
143
144 map.try_insert(
145 std::any::type_name::<MongodbCdcProperties>().to_owned(),
146 [
147 "cdc.source.wait.streaming.start.timeout".to_owned(),
148 "debezium.max.queue.size".to_owned(),
149 "debezium.queue.memory.ratio".to_owned(),
150 ].into_iter().collect(),
151 ).unwrap();
152 map.try_insert(
154 std::any::type_name::<KafkaProperties>().to_owned(),
155 [
156 "group.id.prefix".to_owned(),
157 "properties.sync.call.timeout".to_owned(),
158 "properties.security.protocol".to_owned(),
159 "properties.ssl.endpoint.identification.algorithm".to_owned(),
160 "properties.ssl.ca.location".to_owned(),
161 "properties.ssl.ca.pem".to_owned(),
162 "properties.ssl.certificate.location".to_owned(),
163 "properties.ssl.certificate.pem".to_owned(),
164 "properties.ssl.key.location".to_owned(),
165 "properties.ssl.key.pem".to_owned(),
166 "properties.ssl.key.password".to_owned(),
167 "properties.sasl.mechanism".to_owned(),
168 "properties.sasl.username".to_owned(),
169 "properties.sasl.password".to_owned(),
170 "properties.message.max.bytes".to_owned(),
171 "properties.receive.message.max.bytes".to_owned(),
172 "properties.statistics.interval.ms".to_owned(),
173 "properties.client.id".to_owned(),
174 "properties.enable.ssl.certificate.verification".to_owned(),
175 "properties.reconnect.backoff.ms".to_owned(),
176 "properties.reconnect.backoff.max.ms".to_owned(),
177 "properties.socket.connection.setup.timeout.ms".to_owned(),
178 "properties.retry.backoff.ms".to_owned(),
179 "properties.retry.backoff.max.ms".to_owned(),
180 "properties.queued.min.messages".to_owned(),
181 "properties.queued.max.messages.kbytes".to_owned(),
182 "properties.fetch.wait.max.ms".to_owned(),
183 "properties.fetch.queue.backoff.ms".to_owned(),
184 "properties.fetch.max.bytes".to_owned(),
185 "properties.enable.auto.commit".to_owned(),
186 "properties.auto.commit.interval.ms".to_owned(),
187 ].into_iter().collect(),
188 ).unwrap();
189 map.try_insert(
191 std::any::type_name::<PubsubProperties>().to_owned(),
192 [
193 "pubsub.ack_deadline_seconds".to_owned(),
194 ].into_iter().collect(),
195 ).unwrap();
196 map.try_insert(
198 std::any::type_name::<PulsarProperties>().to_owned(),
199 [
200 "pulsar.operation.retry.max.retries".to_owned(),
201 "pulsar.operation.retry.delay".to_owned(),
202 ].into_iter().collect(),
203 ).unwrap();
204 map
205});
206
207pub static SINK_ALLOW_ALTER_ON_FLY_FIELDS: LazyLock<HashMap<String, HashSet<String>>> = LazyLock::new(|| {
209 use sink_properties::*;
210 let mut map = HashMap::new();
211 map.try_insert(
213 std::any::type_name::<ClickHouseConfig>().to_owned(),
214 [
215 "commit_checkpoint_interval".to_owned(),
216 ].into_iter().collect(),
217 ).unwrap();
218 map.try_insert(
220 std::any::type_name::<DeltaLakeConfig>().to_owned(),
221 [
222 "commit_checkpoint_interval".to_owned(),
223 ].into_iter().collect(),
224 ).unwrap();
225 map.try_insert(
227 std::any::type_name::<DorisConfig>().to_owned(),
228 [
229 "doris.stream_load.http.timeout.ms".to_owned(),
230 ].into_iter().collect(),
231 ).unwrap();
232 map.try_insert(
234 std::any::type_name::<IcebergConfig>().to_owned(),
235 [
236 "commit_checkpoint_interval".to_owned(),
237 "enable_compaction".to_owned(),
238 "compaction_interval_sec".to_owned(),
239 "enable_snapshot_expiration".to_owned(),
240 "snapshot_expiration_max_age_millis".to_owned(),
241 "snapshot_expiration_retain_last".to_owned(),
242 "snapshot_expiration_clear_expired_files".to_owned(),
243 "snapshot_expiration_clear_expired_meta_data".to_owned(),
244 "compaction.max_snapshots_num".to_owned(),
245 "compaction.small_files_threshold_mb".to_owned(),
246 "compaction.delete_files_count_threshold".to_owned(),
247 "compaction.trigger_snapshot_count".to_owned(),
248 "compaction.target_file_size_mb".to_owned(),
249 "compaction.type".to_owned(),
250 "compaction.write_parquet_compression".to_owned(),
251 "compaction.write_parquet_max_row_group_rows".to_owned(),
252 "compaction.write_parquet_max_row_group_bytes".to_owned(),
253 ].into_iter().collect(),
254 ).unwrap();
255 map.try_insert(
257 std::any::type_name::<KafkaConfig>().to_owned(),
258 [
259 "properties.sync.call.timeout".to_owned(),
260 "properties.security.protocol".to_owned(),
261 "properties.ssl.endpoint.identification.algorithm".to_owned(),
262 "properties.ssl.ca.location".to_owned(),
263 "properties.ssl.ca.pem".to_owned(),
264 "properties.ssl.certificate.location".to_owned(),
265 "properties.ssl.certificate.pem".to_owned(),
266 "properties.ssl.key.location".to_owned(),
267 "properties.ssl.key.pem".to_owned(),
268 "properties.ssl.key.password".to_owned(),
269 "properties.sasl.mechanism".to_owned(),
270 "properties.sasl.username".to_owned(),
271 "properties.sasl.password".to_owned(),
272 "properties.message.max.bytes".to_owned(),
273 "properties.receive.message.max.bytes".to_owned(),
274 "properties.statistics.interval.ms".to_owned(),
275 "properties.client.id".to_owned(),
276 "properties.enable.ssl.certificate.verification".to_owned(),
277 "properties.reconnect.backoff.ms".to_owned(),
278 "properties.reconnect.backoff.max.ms".to_owned(),
279 "properties.socket.connection.setup.timeout.ms".to_owned(),
280 "properties.retry.backoff.ms".to_owned(),
281 "properties.retry.backoff.max.ms".to_owned(),
282 "properties.allow.auto.create.topics".to_owned(),
283 "properties.queue.buffering.max.messages".to_owned(),
284 "properties.queue.buffering.max.kbytes".to_owned(),
285 "properties.queue.buffering.max.ms".to_owned(),
286 "properties.enable.idempotence".to_owned(),
287 "properties.message.send.max.retries".to_owned(),
288 "properties.batch.num.messages".to_owned(),
289 "properties.batch.size".to_owned(),
290 "properties.message.timeout.ms".to_owned(),
291 "properties.max.in.flight.requests.per.connection".to_owned(),
292 "properties.request.required.acks".to_owned(),
293 ].into_iter().collect(),
294 ).unwrap();
295 map.try_insert(
297 std::any::type_name::<PulsarConfig>().to_owned(),
298 [
299 "properties.routing.mode".to_owned(),
300 "properties.routing_mode".to_owned(),
301 "routing_mode".to_owned(),
302 "pulsar.routing_mode".to_owned(),
303 "pulsar.routing.mode".to_owned(),
304 "pulsar.properties.routing.mode".to_owned(),
305 "pulsar.properties.routing_mode".to_owned(),
306 ].into_iter().collect(),
307 ).unwrap();
308 map.try_insert(
310 std::any::type_name::<SnowflakeV2Config>().to_owned(),
311 [
312 "commit_checkpoint_interval".to_owned(),
313 ].into_iter().collect(),
314 ).unwrap();
315 map.try_insert(
317 std::any::type_name::<StarrocksConfig>().to_owned(),
318 [
319 "starrocks.stream_load.http.timeout.ms".to_owned(),
320 "commit_checkpoint_interval".to_owned(),
321 "starrocks.max_batch_size_bytes".to_owned(),
322 ].into_iter().collect(),
323 ).unwrap();
324 map.try_insert(
326 std::any::type_name::<TurbopufferConfig>().to_owned(),
327 [
328 "write_batch_size".to_owned(),
329 "max_linger_second".to_owned(),
330 ].into_iter().collect(),
331 ).unwrap();
332 map.try_insert(
334 JdbcSink::SINK_NAME.to_owned(),
335 [
336 "jdbc.url".to_owned(),
337 "user".to_owned(),
338 "password".to_owned(),
339 ].into_iter().collect(),
340 ).unwrap();
341 map
342});
343
344pub static CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS: LazyLock<HashMap<String, HashSet<String>>> = LazyLock::new(|| {
346 use crate::connector_common::*;
347 let mut map = HashMap::new();
348 map.try_insert(
350 std::any::type_name::<KafkaConnection>().to_owned(),
351 [
352 "properties.security.protocol".to_owned(),
353 "properties.ssl.endpoint.identification.algorithm".to_owned(),
354 "properties.ssl.ca.location".to_owned(),
355 "properties.ssl.ca.pem".to_owned(),
356 "properties.ssl.certificate.location".to_owned(),
357 "properties.ssl.certificate.pem".to_owned(),
358 "properties.ssl.key.location".to_owned(),
359 "properties.ssl.key.pem".to_owned(),
360 "properties.ssl.key.password".to_owned(),
361 "properties.sasl.mechanism".to_owned(),
362 "properties.sasl.username".to_owned(),
363 "properties.sasl.password".to_owned(),
364 ].into_iter().collect(),
365 ).unwrap();
366 map.try_insert(
368 JdbcSink::SINK_NAME.to_owned(),
369 [
370 "jdbc.url".to_owned(),
371 "user".to_owned(),
372 "password".to_owned(),
373 ].into_iter().collect(),
374 ).unwrap();
375 map
376});
377
378pub fn get_source_connectors_with_allow_alter_on_fly_fields() -> Vec<&'static str> {
380 SOURCE_ALLOW_ALTER_ON_FLY_FIELDS.keys().map(|s| s.as_str()).collect()
381}
382
383pub fn get_sink_connectors_with_allow_alter_on_fly_fields() -> Vec<&'static str> {
385 SINK_ALLOW_ALTER_ON_FLY_FIELDS.keys().map(|s| s.as_str()).collect()
386}
387
388pub fn get_connection_names_with_allow_alter_on_fly_fields() -> Vec<&'static str> {
390 CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS.keys().map(|s| s.as_str()).collect()
391}
392
393pub fn check_source_allow_alter_on_fly_fields(
396 connector_name: &str,
397 fields: &[String],
398) -> crate::error::ConnectorResult<()> {
399 let Some(type_name) = source_properties::source_name_to_prop_type_name(connector_name) else {
401 return Err(ConnectorError::from(anyhow::anyhow!(
402 "Unknown source connector: {connector_name}"
403 )));
404 };
405 let Some(allowed_fields) = SOURCE_ALLOW_ALTER_ON_FLY_FIELDS.get(type_name) else {
406 return Err(ConnectorError::from(anyhow::anyhow!(
407 "No allow_alter_on_fly fields registered for connector: {connector_name}"
408 )));
409 };
410 for field in fields {
411 if !allowed_fields.contains(field) {
412 return Err(ConnectorError::from(anyhow::anyhow!(
413 "Field '{field}' is not allowed to be altered on the fly for connector: {connector_name}"
414 )));
415 }
416 }
417 Ok(())
418}
419
420pub fn check_connection_allow_alter_on_fly_fields(
421 connection_name: &str,
422 fields: &[String],
423) -> crate::error::ConnectorResult<()> {
424 use crate::source::connection_name_to_prop_type_name;
425
426 let Some(type_name) = connection_name_to_prop_type_name(connection_name) else {
428 return Err(ConnectorError::from(anyhow::anyhow!(
429 "Unknown connection: {connection_name}"
430 )));
431 };
432 let Some(allowed_fields) = CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS.get(type_name) else {
433 return Err(ConnectorError::from(anyhow::anyhow!(
434 "No allow_alter_on_fly fields registered for connection: {connection_name}"
435 )));
436 };
437 for field in fields {
438 if !allowed_fields.contains(field) {
439 return Err(ConnectorError::from(anyhow::anyhow!(
440 "Field '{field}' is not allowed to be altered on the fly for connection: {connection_name}"
441 )));
442 }
443 }
444 Ok(())
445}
446
447pub fn check_sink_allow_alter_on_fly_fields(
450 sink_name: &str,
451 fields: &[String],
452) -> crate::error::ConnectorResult<()> {
453 let allowed_fields = if sink_name == JdbcSink::SINK_NAME {
459 CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS.get(JdbcSink::SINK_NAME)
460 } else {
461 let Some(type_name) = sink_properties::sink_name_to_config_type_name(sink_name) else {
463 return Err(ConnectorError::from(anyhow::anyhow!(
464 "Unknown sink connector: {sink_name}"
465 )));
466 };
467 SINK_ALLOW_ALTER_ON_FLY_FIELDS.get(type_name)
468 };
469 let Some(allowed_fields) = allowed_fields else {
470 return Err(ConnectorError::from(anyhow::anyhow!(
471 "No allow_alter_on_fly fields registered for sink: {sink_name}"
472 )));
473 };
474 for field in fields {
475 if !allowed_fields.contains(field) {
476 return Err(ConnectorError::from(anyhow::anyhow!(
477 "Field '{field}' is not allowed to be altered on the fly for sink: {sink_name}"
478 )));
479 }
480 }
481 Ok(())
482}