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