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::<KafkaProperties>().to_owned(),
112 [
113 "group.id.prefix".to_owned(),
114 "properties.sync.call.timeout".to_owned(),
115 "properties.security.protocol".to_owned(),
116 "properties.ssl.endpoint.identification.algorithm".to_owned(),
117 "properties.ssl.ca.location".to_owned(),
118 "properties.ssl.ca.pem".to_owned(),
119 "properties.ssl.certificate.location".to_owned(),
120 "properties.ssl.certificate.pem".to_owned(),
121 "properties.ssl.key.location".to_owned(),
122 "properties.ssl.key.pem".to_owned(),
123 "properties.ssl.key.password".to_owned(),
124 "properties.sasl.mechanism".to_owned(),
125 "properties.sasl.username".to_owned(),
126 "properties.sasl.password".to_owned(),
127 "properties.sasl.kerberos.service.name".to_owned(),
128 "properties.sasl.kerberos.keytab".to_owned(),
129 "properties.sasl.kerberos.principal".to_owned(),
130 "properties.sasl.kerberos.min.time.before.relogin".to_owned(),
131 "properties.sasl.oauthbearer.config".to_owned(),
132 "properties.sasl.oauthbearer.method".to_owned(),
133 "properties.sasl.oauthbearer.client.id".to_owned(),
134 "properties.sasl.oauthbearer.client.secret".to_owned(),
135 "properties.sasl.oauthbearer.token.endpoint.url".to_owned(),
136 "properties.sasl.oauthbearer.scope".to_owned(),
137 "properties.sasl.oauthbearer.extensions".to_owned(),
138 "properties.message.max.bytes".to_owned(),
139 "properties.receive.message.max.bytes".to_owned(),
140 "properties.statistics.interval.ms".to_owned(),
141 "properties.client.id".to_owned(),
142 "properties.enable.ssl.certificate.verification".to_owned(),
143 "properties.reconnect.backoff.ms".to_owned(),
144 "properties.reconnect.backoff.max.ms".to_owned(),
145 "properties.socket.connection.setup.timeout.ms".to_owned(),
146 "properties.retry.backoff.ms".to_owned(),
147 "properties.retry.backoff.max.ms".to_owned(),
148 "properties.queued.min.messages".to_owned(),
149 "properties.queued.max.messages.kbytes".to_owned(),
150 "properties.fetch.wait.max.ms".to_owned(),
151 "properties.fetch.queue.backoff.ms".to_owned(),
152 "properties.fetch.max.bytes".to_owned(),
153 "properties.enable.auto.commit".to_owned(),
154 "properties.auto.commit.interval.ms".to_owned(),
155 ].into_iter().collect(),
156 ).unwrap();
157 map.try_insert(
159 std::any::type_name::<PubsubProperties>().to_owned(),
160 [
161 "pubsub.ack_deadline_seconds".to_owned(),
162 "pubsub.max_outstanding_messages".to_owned(),
163 "pubsub.max_outstanding_bytes".to_owned(),
164 ].into_iter().collect(),
165 ).unwrap();
166 map.try_insert(
168 std::any::type_name::<PulsarProperties>().to_owned(),
169 [
170 "pulsar.operation.retry.max.retries".to_owned(),
171 "pulsar.operation.retry.delay".to_owned(),
172 ].into_iter().collect(),
173 ).unwrap();
174 map
175});
176
177pub static SINK_ALLOW_ALTER_ON_FLY_FIELDS: LazyLock<HashMap<String, HashSet<String>>> = LazyLock::new(|| {
179 use sink_properties::*;
180 let mut map = HashMap::new();
181 map.try_insert(
183 std::any::type_name::<ClickHouseConfig>().to_owned(),
184 [
185 "commit_checkpoint_interval".to_owned(),
186 ].into_iter().collect(),
187 ).unwrap();
188 map.try_insert(
190 std::any::type_name::<DeltaLakeConfig>().to_owned(),
191 [
192 "commit_checkpoint_interval".to_owned(),
193 ].into_iter().collect(),
194 ).unwrap();
195 map.try_insert(
197 std::any::type_name::<DorisConfig>().to_owned(),
198 [
199 "doris.stream_load.http.timeout.ms".to_owned(),
200 ].into_iter().collect(),
201 ).unwrap();
202 map.try_insert(
204 std::any::type_name::<ElasticSearchConfig>().to_owned(),
205 [
206 "batch_num_messages".to_owned(),
207 "batch_size_kb".to_owned(),
208 "concurrent_requests".to_owned(),
209 ].into_iter().collect(),
210 ).unwrap();
211 map.try_insert(
213 std::any::type_name::<IcebergConfig>().to_owned(),
214 [
215 "commit_checkpoint_interval".to_owned(),
216 "enable_compaction".to_owned(),
217 "compaction_interval_sec".to_owned(),
218 "enable_snapshot_expiration".to_owned(),
219 "snapshot_expiration_max_age_millis".to_owned(),
220 "snapshot_expiration_retain_last".to_owned(),
221 "snapshot_expiration_clear_expired_files".to_owned(),
222 "snapshot_expiration_clear_expired_meta_data".to_owned(),
223 "enable_manifest_rewrite".to_owned(),
224 "manifest_rewrite_target_size_bytes".to_owned(),
225 "manifest_rewrite_min_count_to_merge".to_owned(),
226 "enable_orphan_file_cleanup".to_owned(),
227 "orphan_file_cleanup_min_age_millis".to_owned(),
228 "compaction.max_snapshots_num".to_owned(),
229 "compaction.small_files_threshold_mb".to_owned(),
230 "compaction.delete_files_count_threshold".to_owned(),
231 "compaction.delete_ratio_percent".to_owned(),
232 "compaction.trigger_snapshot_count".to_owned(),
233 "compaction.target_file_size_mb".to_owned(),
234 "compaction.type".to_owned(),
235 "compaction.write_parquet_compression".to_owned(),
236 "compaction.write_parquet_max_row_group_rows".to_owned(),
237 "compaction.write_parquet_max_row_group_bytes".to_owned(),
238 ].into_iter().collect(),
239 ).unwrap();
240 map.try_insert(
242 std::any::type_name::<KafkaConfig>().to_owned(),
243 [
244 "properties.sync.call.timeout".to_owned(),
245 "properties.security.protocol".to_owned(),
246 "properties.ssl.endpoint.identification.algorithm".to_owned(),
247 "properties.ssl.ca.location".to_owned(),
248 "properties.ssl.ca.pem".to_owned(),
249 "properties.ssl.certificate.location".to_owned(),
250 "properties.ssl.certificate.pem".to_owned(),
251 "properties.ssl.key.location".to_owned(),
252 "properties.ssl.key.pem".to_owned(),
253 "properties.ssl.key.password".to_owned(),
254 "properties.sasl.mechanism".to_owned(),
255 "properties.sasl.username".to_owned(),
256 "properties.sasl.password".to_owned(),
257 "properties.sasl.kerberos.service.name".to_owned(),
258 "properties.sasl.kerberos.keytab".to_owned(),
259 "properties.sasl.kerberos.principal".to_owned(),
260 "properties.sasl.kerberos.min.time.before.relogin".to_owned(),
261 "properties.sasl.oauthbearer.config".to_owned(),
262 "properties.sasl.oauthbearer.method".to_owned(),
263 "properties.sasl.oauthbearer.client.id".to_owned(),
264 "properties.sasl.oauthbearer.client.secret".to_owned(),
265 "properties.sasl.oauthbearer.token.endpoint.url".to_owned(),
266 "properties.sasl.oauthbearer.scope".to_owned(),
267 "properties.sasl.oauthbearer.extensions".to_owned(),
268 "properties.message.max.bytes".to_owned(),
269 "properties.receive.message.max.bytes".to_owned(),
270 "properties.statistics.interval.ms".to_owned(),
271 "properties.client.id".to_owned(),
272 "properties.enable.ssl.certificate.verification".to_owned(),
273 "properties.reconnect.backoff.ms".to_owned(),
274 "properties.reconnect.backoff.max.ms".to_owned(),
275 "properties.socket.connection.setup.timeout.ms".to_owned(),
276 "properties.retry.backoff.ms".to_owned(),
277 "properties.retry.backoff.max.ms".to_owned(),
278 "properties.allow.auto.create.topics".to_owned(),
279 "properties.queue.buffering.max.messages".to_owned(),
280 "properties.queue.buffering.max.kbytes".to_owned(),
281 "properties.queue.buffering.max.ms".to_owned(),
282 "properties.enable.idempotence".to_owned(),
283 "properties.message.send.max.retries".to_owned(),
284 "properties.batch.num.messages".to_owned(),
285 "properties.batch.size".to_owned(),
286 "properties.message.timeout.ms".to_owned(),
287 "properties.max.in.flight.requests.per.connection".to_owned(),
288 "properties.request.required.acks".to_owned(),
289 ].into_iter().collect(),
290 ).unwrap();
291 map.try_insert(
293 std::any::type_name::<LanceDbConfig>().to_owned(),
294 [
295 "commit_checkpoint_interval".to_owned(),
296 ].into_iter().collect(),
297 ).unwrap();
298 map.try_insert(
300 std::any::type_name::<OpenSearchConfig>().to_owned(),
301 [
302 "batch_num_messages".to_owned(),
303 "batch_size_kb".to_owned(),
304 "concurrent_requests".to_owned(),
305 ].into_iter().collect(),
306 ).unwrap();
307 map.try_insert(
309 std::any::type_name::<PulsarConfig>().to_owned(),
310 [
311 "properties.routing.mode".to_owned(),
312 "properties.routing_mode".to_owned(),
313 "routing_mode".to_owned(),
314 "pulsar.routing_mode".to_owned(),
315 "pulsar.routing.mode".to_owned(),
316 "pulsar.properties.routing.mode".to_owned(),
317 "pulsar.properties.routing_mode".to_owned(),
318 ].into_iter().collect(),
319 ).unwrap();
320 map.try_insert(
322 std::any::type_name::<SnowflakeV2Config>().to_owned(),
323 [
324 "commit_checkpoint_interval".to_owned(),
325 ].into_iter().collect(),
326 ).unwrap();
327 map.try_insert(
329 std::any::type_name::<StarrocksConfig>().to_owned(),
330 [
331 "starrocks.stream_load.http.timeout.ms".to_owned(),
332 "commit_checkpoint_interval".to_owned(),
333 "starrocks.max_batch_size_bytes".to_owned(),
334 ].into_iter().collect(),
335 ).unwrap();
336 map.try_insert(
338 std::any::type_name::<TurbopufferConfig>().to_owned(),
339 [
340 "write_batch_size".to_owned(),
341 "max_linger_second".to_owned(),
342 ].into_iter().collect(),
343 ).unwrap();
344 map.try_insert(
346 JdbcSink::SINK_NAME.to_owned(),
347 [
348 "jdbc.url".to_owned(),
349 "user".to_owned(),
350 "password".to_owned(),
351 ].into_iter().collect(),
352 ).unwrap();
353 map
354});
355
356pub static CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS: LazyLock<HashMap<String, HashSet<String>>> = LazyLock::new(|| {
358 use crate::connector_common::*;
359 let mut map = HashMap::new();
360 map.try_insert(
362 std::any::type_name::<KafkaConnection>().to_owned(),
363 [
364 "properties.security.protocol".to_owned(),
365 "properties.ssl.endpoint.identification.algorithm".to_owned(),
366 "properties.ssl.ca.location".to_owned(),
367 "properties.ssl.ca.pem".to_owned(),
368 "properties.ssl.certificate.location".to_owned(),
369 "properties.ssl.certificate.pem".to_owned(),
370 "properties.ssl.key.location".to_owned(),
371 "properties.ssl.key.pem".to_owned(),
372 "properties.ssl.key.password".to_owned(),
373 "properties.sasl.mechanism".to_owned(),
374 "properties.sasl.username".to_owned(),
375 "properties.sasl.password".to_owned(),
376 "properties.sasl.kerberos.service.name".to_owned(),
377 "properties.sasl.kerberos.keytab".to_owned(),
378 "properties.sasl.kerberos.principal".to_owned(),
379 "properties.sasl.kerberos.min.time.before.relogin".to_owned(),
380 "properties.sasl.oauthbearer.config".to_owned(),
381 "properties.sasl.oauthbearer.method".to_owned(),
382 "properties.sasl.oauthbearer.client.id".to_owned(),
383 "properties.sasl.oauthbearer.client.secret".to_owned(),
384 "properties.sasl.oauthbearer.token.endpoint.url".to_owned(),
385 "properties.sasl.oauthbearer.scope".to_owned(),
386 "properties.sasl.oauthbearer.extensions".to_owned(),
387 ].into_iter().collect(),
388 ).unwrap();
389 map.try_insert(
391 JdbcSink::SINK_NAME.to_owned(),
392 [
393 "jdbc.url".to_owned(),
394 "user".to_owned(),
395 "password".to_owned(),
396 ].into_iter().collect(),
397 ).unwrap();
398 map
399});
400
401pub fn get_source_connectors_with_allow_alter_on_fly_fields() -> Vec<&'static str> {
403 SOURCE_ALLOW_ALTER_ON_FLY_FIELDS.keys().map(|s| s.as_str()).collect()
404}
405
406pub fn get_sink_connectors_with_allow_alter_on_fly_fields() -> Vec<&'static str> {
408 SINK_ALLOW_ALTER_ON_FLY_FIELDS.keys().map(|s| s.as_str()).collect()
409}
410
411pub fn get_connection_names_with_allow_alter_on_fly_fields() -> Vec<&'static str> {
413 CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS.keys().map(|s| s.as_str()).collect()
414}
415
416pub fn check_source_allow_alter_on_fly_fields(
419 connector_name: &str,
420 fields: &[String],
421) -> crate::error::ConnectorResult<()> {
422 let Some(type_name) = source_properties::source_name_to_prop_type_name(connector_name) else {
424 return Err(ConnectorError::from(anyhow::anyhow!(
425 "Unknown source connector: {connector_name}"
426 )));
427 };
428 let Some(allowed_fields) = SOURCE_ALLOW_ALTER_ON_FLY_FIELDS.get(type_name) else {
429 return Err(ConnectorError::from(anyhow::anyhow!(
430 "No allow_alter_on_fly fields registered for connector: {connector_name}"
431 )));
432 };
433 for field in fields {
434 if !allowed_fields.contains(field) {
435 return Err(ConnectorError::from(anyhow::anyhow!(
436 "Field '{field}' is not allowed to be altered on the fly for connector: {connector_name}"
437 )));
438 }
439 }
440 Ok(())
441}
442
443pub fn check_connection_allow_alter_on_fly_fields(
444 connection_name: &str,
445 fields: &[String],
446) -> crate::error::ConnectorResult<()> {
447 use crate::source::connection_name_to_prop_type_name;
448
449 let Some(type_name) = connection_name_to_prop_type_name(connection_name) else {
451 return Err(ConnectorError::from(anyhow::anyhow!(
452 "Unknown connection: {connection_name}"
453 )));
454 };
455 let Some(allowed_fields) = CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS.get(type_name) else {
456 return Err(ConnectorError::from(anyhow::anyhow!(
457 "No allow_alter_on_fly fields registered for connection: {connection_name}"
458 )));
459 };
460 for field in fields {
461 if !allowed_fields.contains(field) {
462 return Err(ConnectorError::from(anyhow::anyhow!(
463 "Field '{field}' is not allowed to be altered on the fly for connection: {connection_name}"
464 )));
465 }
466 }
467 Ok(())
468}
469
470pub fn check_sink_allow_alter_on_fly_fields(
473 sink_name: &str,
474 fields: &[String],
475) -> crate::error::ConnectorResult<()> {
476 let allowed_fields = if sink_name == JdbcSink::SINK_NAME {
482 CONNECTION_ALLOW_ALTER_ON_FLY_FIELDS.get(JdbcSink::SINK_NAME)
483 } else {
484 let Some(type_name) = sink_properties::sink_name_to_config_type_name(sink_name) else {
486 return Err(ConnectorError::from(anyhow::anyhow!(
487 "Unknown sink connector: {sink_name}"
488 )));
489 };
490 SINK_ALLOW_ALTER_ON_FLY_FIELDS.get(type_name)
491 };
492 let Some(allowed_fields) = allowed_fields else {
493 return Err(ConnectorError::from(anyhow::anyhow!(
494 "No allow_alter_on_fly fields registered for sink: {sink_name}"
495 )));
496 };
497 for field in fields {
498 if !allowed_fields.contains(field) {
499 return Err(ConnectorError::from(anyhow::anyhow!(
500 "Field '{field}' is not allowed to be altered on the fly for sink: {sink_name}"
501 )));
502 }
503 }
504 Ok(())
505}