clickhouse_arrow/client/
options.rs

1use std::path::PathBuf;
2
3use tracing::warn;
4
5use super::CompressionMethod;
6use crate::native::protocol::ChunkedProtocolMode;
7use crate::prelude::Secret;
8
9/// Configuration options for a `ClickHouse` client connection and Arrow serialization.
10///
11/// The `ClientOptions` struct defines the settings used to establish a connection
12/// to a `ClickHouse` server and handle data serialization/deserialization with
13/// Apache Arrow. These options are typically set via [`ClientBuilder`] methods
14/// (e.g., [`ClientBuilder::with_username`], [`ClientBuilder::with_tls`]) or
15/// constructed directly for use with [`Client::connect`].
16///
17/// # Fields
18/// - `username`: The username for authenticating with `ClickHouse` (default: `"default"`).
19/// - `password`: The password for authentication, stored securely as a [`Secret`].
20/// - `default_database`: The default database for queries; if empty, uses `ClickHouse`'s
21///   `"default"` database.
22/// - `domain`: Optional domain for TLS verification; inferred from the destination if unset.
23/// - `ipv4_only`: If `true`, restricts address resolution to IPv4; if `false`, allows IPv6.
24/// - `cafile`: Optional path to a certificate authority file for TLS connections.
25/// - `use_tls`: If `true`, enables TLS for secure connections; if `false`, uses plain TCP.
26/// - `compression`: The compression method for data exchange (default: [`CompressionMethod::LZ4`]).
27/// - `arrow`: Optional Arrow-specific serialization options (see [`ArrowOptions`]).
28/// - `cloud`: Cloud-specific options for `ClickHouse` cloud instances (requires `cloud` feature).
29///
30/// # Examples
31/// ```rust,ignore
32/// use clickhouse_arrow::prelude::*;
33///
34/// let options = ClientOptions {
35///     username: "admin".to_string(),
36///     password: Secret::new("secret"),
37///     default_database: "my_db".to_string(),
38///     use_tls: true,
39///     ..ClientOptions::default()
40/// };
41///
42/// let client = Client::connect("localhost:9000", options, None, None)
43///     .await
44///     .unwrap();
45/// ```
46#[derive(Debug, Clone, PartialEq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct ClientOptions {
49    /// Username credential
50    pub username:         String,
51    /// Password credential. [`Secret`] is used to minimize likelihood of exposure through logs
52    pub password:         Secret,
53    /// Scope this client to a specifc database, otherwise 'default' is used
54    pub default_database: String,
55    /// For tls, provide the domain, otherwise it will be determined from the endpoint.
56    pub domain:           Option<String>,
57    /// Whether any non-ipv4 socket addrs should be filtered out.
58    pub ipv4_only:        bool,
59    /// Provide a path to a certificate authority to use for tls.
60    pub cafile:           Option<PathBuf>,
61    /// Whether a connection should be made securely over tls.
62    pub use_tls:          bool,
63    /// The compression to use when sending data to clickhouse.
64    pub compression:      CompressionMethod,
65    /// Additional configuration not core to `ClickHouse` connections
66    #[cfg_attr(feature = "serde", serde(default))]
67    pub ext:              Extension,
68}
69
70impl Default for ClientOptions {
71    fn default() -> Self {
72        ClientOptions {
73            username:         "default".to_string(),
74            password:         Secret::new(""),
75            default_database: String::new(),
76            domain:           None,
77            ipv4_only:        false,
78            cafile:           None,
79            use_tls:          false,
80            compression:      CompressionMethod::default(),
81            ext:              Extension::default(),
82        }
83    }
84}
85
86impl ClientOptions {
87    /// Create a new `ClientOptions` with default values.
88    #[must_use]
89    pub fn new() -> Self { Self::default() }
90
91    #[must_use]
92    pub fn with_username(mut self, username: impl Into<String>) -> Self {
93        self.username = username.into();
94        self
95    }
96
97    #[must_use]
98    pub fn with_password(mut self, password: impl Into<Secret>) -> Self {
99        self.password = password.into();
100        self
101    }
102
103    #[must_use]
104    pub fn with_default_database(mut self, default_database: impl Into<String>) -> Self {
105        self.default_database = default_database.into();
106        self
107    }
108
109    #[must_use]
110    pub fn with_domain(mut self, domain: impl Into<String>) -> Self {
111        self.domain = Some(domain.into());
112        self
113    }
114
115    #[must_use]
116    pub fn with_ipv4_only(mut self, ipv4_only: bool) -> Self {
117        self.ipv4_only = ipv4_only;
118        self
119    }
120
121    #[must_use]
122    pub fn with_cafile<P: AsRef<std::path::Path>>(mut self, cafile: P) -> Self {
123        self.cafile = Some(cafile.as_ref().into());
124        self
125    }
126
127    #[must_use]
128    pub fn with_use_tls(mut self, use_tls: bool) -> Self {
129        self.use_tls = use_tls;
130        self
131    }
132
133    #[must_use]
134    pub fn with_compression(mut self, compression: CompressionMethod) -> Self {
135        self.compression = compression;
136        self
137    }
138
139    #[must_use]
140    pub fn with_extension(mut self, ext: Extension) -> Self {
141        self.ext = ext;
142        self
143    }
144
145    #[must_use]
146    pub fn extend(mut self, ext: impl Fn(Extension) -> Extension) -> Self {
147        self.ext = ext(self.ext);
148        self
149    }
150}
151
152/// Extra configuration options for `ClickHouse`.
153///
154/// These options are separated to allow extending the configuration capabilities of a connection
155/// without breaking the core [`ClientOptions`] that are unlikely to ever change. For this reason,
156/// `Extensions` is `non_exhaustive` so the api can change without breaking existing
157/// implementations.
158#[non_exhaustive]
159#[derive(Debug, Default, Clone, PartialEq)]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161pub struct Extension {
162    /// Options specific to (de)serializing arrow data.
163    pub arrow:          Option<ArrowOptions>,
164    /// Options specific to communicating with `ClickHouse` over their cloud offering.
165    #[cfg(feature = "cloud")]
166    pub cloud:          CloudOptions,
167    /// Options related to server/client protocol send chunking.
168    /// This may be removed, as it may be defaulted.
169    #[cfg_attr(feature = "serde", serde(default))]
170    pub chunked_send:   ChunkedProtocolMode,
171    /// Options related to server/client protocol recv chunking.
172    /// This may be removed, as it may be defaulted
173    #[cfg_attr(feature = "serde", serde(default))]
174    pub chunked_recv:   ChunkedProtocolMode,
175    /// Related to `inner_pool`, how many 'inner clients' to spawn. Currently capped at 4.
176    #[cfg(feature = "inner_pool")]
177    #[cfg_attr(feature = "serde", serde(default))]
178    pub fast_mode_size: Option<u8>,
179}
180
181/// Configuration extensions for specialized `ClickHouse` client behavior.
182///
183/// This type provides additional configuration options beyond the standard
184/// client settings, including Arrow format options and cloud-specific settings.
185impl Extension {
186    #[must_use]
187    pub fn with_arrow(mut self, options: ArrowOptions) -> Self {
188        self.arrow = Some(options);
189        self
190    }
191
192    #[must_use]
193    pub fn with_set_arrow(mut self, f: impl Fn(ArrowOptions) -> ArrowOptions) -> Self {
194        self.arrow = Some(f(self.arrow.unwrap_or_default()));
195        self
196    }
197
198    #[must_use]
199    pub fn with_chunked_send_mode(mut self, mode: ChunkedProtocolMode) -> Self {
200        self.chunked_send = mode;
201        self
202    }
203
204    #[must_use]
205    pub fn with_chunked_recv_mode(mut self, mode: ChunkedProtocolMode) -> Self {
206        self.chunked_recv = mode;
207        self
208    }
209
210    #[cfg(feature = "cloud")]
211    #[must_use]
212    pub fn with_cloud(mut self, options: CloudOptions) -> Self {
213        self.cloud = options;
214        self
215    }
216
217    #[cfg(feature = "inner_pool")]
218    #[must_use]
219    pub fn with_fast_mode_size(mut self, size: u8) -> Self {
220        self.fast_mode_size = Some(size);
221        self
222    }
223}
224
225/// Configuration options for Arrow serialization and deserialization with `ClickHouse`.
226///
227/// The `ArrowOptions` struct defines settings that control how Apache Arrow data types
228/// are mapped to `ClickHouse` types during serialization (e.g., inserts), deserialization
229/// (e.g., queries), and schema creation (e.g., DDL operations). These options are used
230/// by [`ArrowClient`] and set via [`ClientBuilder::with_arrow_options`] or directly in
231/// [`ClientOptions`].
232///
233/// # Fields
234/// - `strings_as_strings`: If `true`, maps `ClickHouse` `String` to Arrow `Utf8`; if `false`, maps
235///   to `Binary` (default).
236/// - `use_date32_for_date`: If `true`, maps Arrow `Date32` to `ClickHouse` `Date32`; if `false`,
237///   maps to `Date` (default).
238/// - `strict_schema`: If `true`, enforces strict type mappings during serialization (inserts) and
239///   schema creation, causing errors on `ClickHouse` invariant violations (e.g.,
240///   `Nullable(LowCardinality(String))`); if `false`, attempts to correct violations (e.g., mapping
241///   to `LowCardinality(Nullable(String))`) (default).
242/// - `disable_strict_schema_ddl`: If `true`, prevents automatic strict mode during schema creation
243///   (via [`ArrowOptions::into_strict_ddl`]); if `false`, schema creation defaults to strict mode
244///   (default).
245/// - `nullable_array_default_empty`: If `true`, maps `Nullable(Array(...))` to `Array(...)` with
246///   `[]` for nulls during inserts and schema creation (if `disable_strict_schema_ddl = true`); if
247///   `false`, errors on `Nullable(Array(...))` (default).
248///
249/// # Notes
250/// - During schema creation, options are converted to strict mode (via
251///   [`ArrowOptions::into_strict_ddl`]) unless `disable_strict_schema_ddl` is `true`. Strict mode
252///   sets `strict_schema = true` and effectively enforces `nullable_array_default_empty = false`,
253///   ensuring non-nullable arrays.
254/// - When `strict_schema` is `false`, violations like `Nullable(LowCardinality(String))` are
255///   corrected, but arrays are handled per `nullable_array_default_empty` for inserts, while
256///   nullable arrays are ignored during schema creation.
257/// - If `strict_schema = true` and `nullable_array_default_empty = true`, non-array violations
258///   (e.g., `LowCardinality`) error, but arrays map to `[]` for nulls during insert. This is useful
259///   in cases where the arrow `Schema` is used to create the table, but arrays may come from
260///   different `RecordBatch`es.
261/// - This struct is `#[non_exhaustive]`, so future fields may be added (e.g., for new `ClickHouse`
262///   types or serialization options). Use [`ArrowOptions::new`] or [`ArrowOptions::default`] to
263///   construct instances.
264///
265/// # Examples
266/// ```rust,ignore
267/// use clickhouse_arrow::prelude::*;
268///
269/// let arrow_options = ArrowOptions::new()
270///     .with_strings_as_strings(true)
271///     .with_strict_schema(true)
272///     .with_nullable_array_default_empty(false);
273/// let options = ClientOptions {
274///     arrow: Some(arrow_options),
275///     ..ClientOptions::default()
276/// };
277/// ```
278#[expect(clippy::struct_excessive_bools)]
279#[non_exhaustive]
280#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282pub struct ArrowOptions {
283    pub(crate) strings_as_strings:           bool,
284    pub(crate) use_date32_for_date:          bool,
285    pub(crate) strict_schema:                bool,
286    pub(crate) disable_strict_schema_ddl:    bool,
287    pub(crate) nullable_array_default_empty: bool,
288}
289
290impl Default for ArrowOptions {
291    /// Creates an `ArrowOptions` instance with default values.
292    ///
293    /// The default configuration uses relaxed type mappings suitable for most
294    /// `ClickHouse` and `Arrow` use cases:
295    /// - `ClickHouse` `String` maps to Arrow `Binary`.
296    /// - Arrow `Date32` maps to `ClickHouse` `Date`.
297    /// - Type mappings are relaxed, correcting `ClickHouse` invariant violations (e.g., mapping
298    ///   `Nullable(LowCardinality(String))` to `LowCardinality(Nullable(String))`).
299    /// - Schema creation defaults to strict mode (via [`ArrowOptions::into_strict_ddl`]).
300    /// - `Nullable(Array(...))` defaults to `Array(...)` with `[]` for nulls.
301    ///
302    /// Use this as a starting point and customize with methods like
303    /// [`ArrowOptions::with_strings_as_strings`].
304    ///
305    /// # Returns
306    /// An [`ArrowOptions`] instance with default settings.
307    ///
308    /// # Examples
309    /// ```rust,ignore
310    /// use clickhouse_arrow::arrow::ArrowOptions;
311    ///
312    /// let arrow_options = ArrowOptions::default();
313    /// println!("Nullable array default empty: {}", arrow_options.nullable_array_default_empty); // true
314    /// ```
315    fn default() -> Self { Self::new() }
316}
317
318impl ArrowOptions {
319    /// Creates a new `ArrowOptions` instance with default values.
320    ///
321    /// This method is equivalent to [`ArrowOptions::default`], initializing fields for
322    /// relaxed type mappings. Use this to start configuring Arrow
323    /// serialization/deserialization options for `ClickHouse`.
324    ///
325    /// # Returns
326    /// A new [`ArrowOptions`] instance with default settings.
327    ///
328    /// # Examples
329    /// ```rust,ignore
330    /// use clickhouse_arrow::arrow::ArrowOptions;
331    ///
332    /// let arrow_options = ArrowOptions::new();
333    /// println!("Nullable array default empty: {}", arrow_options.nullable_array_default_empty); // true
334    /// ```
335    pub const fn new() -> Self {
336        Self {
337            strings_as_strings:           false,
338            use_date32_for_date:          false,
339            strict_schema:                false,
340            disable_strict_schema_ddl:    false,
341            nullable_array_default_empty: true,
342        }
343    }
344
345    /// Creates an `ArrowOptions` instance with strict type mapping settings.
346    ///
347    /// This method configures options for strict type mappings, where `ClickHouse`
348    /// invariant violations (e.g., `Nullable(LowCardinality(String))` or
349    /// `Nullable(Array(...))`) cause errors during serialization (inserts) and schema
350    /// creation. It sets `strict_schema` to `true` and `nullable_array_default_empty` to
351    /// `false`, leaving other fields as `false`. Use this for operations where
352    /// `ClickHouse` invariants must be strictly enforced.
353    ///
354    /// # Returns
355    /// An [`ArrowOptions`] instance with strict settings.
356    ///
357    /// # Examples
358    /// ```rust,ignore
359    /// use clickhouse_arrow::arrow::ArrowOptions;
360    ///
361    /// let arrow_options = ArrowOptions::strict();
362    /// println!("Strict schema: {}", arrow_options.strict_schema); // true
363    /// ```
364    pub const fn strict() -> Self {
365        Self {
366            strings_as_strings:           false,
367            use_date32_for_date:          false,
368            strict_schema:                true,
369            disable_strict_schema_ddl:    false,
370            nullable_array_default_empty: false,
371        }
372    }
373
374    /// Converts the options to strict mode for schema creation, unless disabled.
375    ///
376    /// This method returns a new [`ArrowOptions`] with strict settings (equivalent to
377    /// [`ArrowOptions::strict`]) unless `disable_strict_schema_ddl` is `true`. If
378    /// `disable_strict_schema_ddl` is `true`, the original options are returned
379    /// unchanged. This method is called automatically during schema creation to enforce
380    /// `ClickHouse` invariants, including non-nullable arrays, unless explicitly disabled.
381    ///
382    /// # Returns
383    /// A new [`ArrowOptions`] instance with strict settings or the original options.
384    ///
385    /// # Examples
386    /// ```rust,ignore
387    /// use clickhouse_arrow::arrow::ArrowOptions;
388    ///
389    /// let options_strict_off = ArrowOptions::new()
390    ///     .with_disable_strict_schema_ddl(true)
391    ///     .into_strict_ddl();
392    /// assert!(!options_strict_off.strict_schema);
393    /// assert!(options_strict_off.nullable_array_default_empty);
394    ///
395    /// let options_strict = ArrowOptions::new()
396    ///     .with_disable_strict_schema_ddl(false) // Default
397    ///     .into_strict_ddl();
398    /// assert!(options_strict.strict_schema);
399    /// assert!(!options_strict.nullable_array_default_empty);
400    /// ```
401    #[must_use]
402    pub fn into_strict_ddl(self) -> Self {
403        if self.disable_strict_schema_ddl {
404            return self;
405        }
406
407        Self {
408            strings_as_strings: self.strings_as_strings,
409            use_date32_for_date: self.use_date32_for_date,
410            ..Self::strict()
411        }
412    }
413
414    /// Sets whether `ClickHouse` `String` types are deserialized as Arrow `Utf8`.
415    ///
416    /// By default, `ClickHouse` `String` types map to Arrow `Binary`. When this option
417    /// is enabled (`true`), they map to Arrow `Utf8`, which is more suitable for text
418    /// data. Use this to control serialization/deserialization behavior for string
419    /// columns.
420    ///
421    /// # Parameters
422    /// - `enabled`: If `true`, maps [`crate::Type::String`] to
423    ///   [`arrow::datatypes::DataType::Utf8`]; if `false`, maps to
424    ///   [`arrow::datatypes::DataType::Binary`].
425    ///
426    /// # Returns
427    /// A new [`ArrowOptions`] with the updated setting.
428    ///
429    /// # Examples
430    /// ```rust,ignore
431    /// use clickhouse_arrow::prelude::*;
432    ///
433    /// let arrow_options = ArrowOptions::new()
434    ///     .with_strings_as_strings(true);
435    /// println!("Strings as strings: {}", arrow_options.strings_as_strings); // true
436    /// ```
437    #[must_use]
438    pub fn with_strings_as_strings(mut self, enabled: bool) -> Self {
439        self.strings_as_strings = enabled;
440        self
441    }
442
443    /// Sets whether Arrow `Date32` is mapped to `ClickHouse` `Date` or `Date32`.
444    ///
445    /// By default, Arrow `Date32` maps to `ClickHouse` `Date` (days since 1970-01-01).
446    /// When this option is enabled (`true`), it maps to `ClickHouse` `Date32` (days
447    /// since 1900-01-01). Use this to control date serialization/deserialization
448    /// behavior.
449    ///
450    /// # Parameters
451    /// - `enabled`: If `true`, maps `Date32` to `ClickHouse` `Date32`; if `false`, maps to `Date`.
452    ///
453    /// # Returns
454    /// A new [`ArrowOptions`] with the updated setting.
455    ///
456    /// # Examples
457    /// ```rust,ignore
458    /// use clickhouse_arrow::prelude::*;
459    ///
460    /// let arrow_options = ArrowOptions::new()
461    ///     .with_use_date32_for_date(true);
462    /// println!("Use Date32 for Date: {}", arrow_options.use_date32_for_date); // true
463    /// ```
464    #[must_use]
465    pub fn with_use_date32_for_date(mut self, enabled: bool) -> Self {
466        self.use_date32_for_date = enabled;
467        self
468    }
469
470    /// Sets whether type mappings are strict during serialization and schema creation.
471    ///
472    /// By default, type mappings are relaxed, allowing `ClickHouse` invariant violations
473    /// (e.g., `Nullable(LowCardinality(String))`) to be corrected automatically (e.g.,
474    /// mapping to `LowCardinality(Nullable(String))`). When this option is enabled
475    /// (`true`), non-array violations cause errors during serialization (inserts) and
476    /// schema creation. Array violations are controlled by
477    /// [`ArrowOptions::with_nullable_array_default_empty`]. Schema creation defaults to
478    /// strict mode unless [`ArrowOptions::with_disable_strict_schema_ddl`] is enabled.
479    ///
480    /// # Parameters
481    /// - `enabled`: If `true`, enforces strict type mappings for non-array types; if `false`,
482    ///   allows relaxed corrections.
483    ///
484    /// # Returns
485    /// A new [`ArrowOptions`] with the updated setting.
486    ///
487    /// # Examples
488    /// ```rust,ignore
489    /// use clickhouse_arrow::arrow::ArrowOptions;
490    ///
491    /// let arrow_options = ArrowOptions::new()
492    ///     .with_strict_schema(true);
493    /// println!("Strict schema: {}", arrow_options.strict_schema); // true
494    /// ```
495    #[must_use]
496    pub fn with_strict_schema(mut self, enabled: bool) -> Self {
497        self.strict_schema = enabled;
498        self
499    }
500
501    /// Sets whether strict mode is disabled during schema creation.
502    ///
503    /// By default, schema creation (e.g., DDL operations) uses strict type mappings (via
504    /// [`ArrowOptions::into_strict_ddl`]), enforcing `ClickHouse` invariants and causing
505    /// errors on violations, including `Nullable(Array(...))`. When this option is
506    /// enabled (`true`), strict mode is disabled for schema creation, using the user’s
507    /// `strict_schema` and `nullable_array_default_empty` settings.
508    ///
509    /// # Parameters
510    /// - `enabled`: If `true`, disables strict mode for schema creation; if `false`, enables strict
511    ///   mode.
512    ///
513    /// # Returns
514    /// A new [`ArrowOptions`] with the updated setting.
515    ///
516    /// # Examples
517    /// ```rust,ignore
518    /// use clickhouse_arrow::arrow::ArrowOptions;
519    ///
520    /// let arrow_options = ArrowOptions::new()
521    ///     .with_disable_strict_schema_ddl(true);
522    /// assert!(arrow_options.disable_strict_schema_ddl);
523    /// ```
524    #[must_use]
525    pub fn with_disable_strict_schema_ddl(mut self, enabled: bool) -> Self {
526        self.disable_strict_schema_ddl = enabled;
527        self
528    }
529
530    /// Sets whether `Nullable(Array(...))` types default to empty arrays during inserts and are
531    /// coerced to non-nullable during DDL.
532    ///
533    /// By default, `Nullable(Array(...))` types are mapped to `Array(...)` with `[]` for
534    /// nulls during serialization (inserts) and schema creation (if
535    /// `disable_strict_schema_ddl = true`). When this option is disabled (`false`),
536    /// `Nullable(Array(...))` causes errors, enforcing non-nullable arrays. Schema
537    /// creation defaults to non-nullable arrays unless
538    /// [`ArrowOptions::with_disable_strict_schema_ddl`] is enabled.
539    ///
540    /// # Parameters
541    /// - `enabled`: If `true`, maps `Nullable(Array(...))` to `Array(...)` with `[]` for nulls; if
542    ///   `false`, errors on `Nullable(Array(...))`.
543    ///
544    /// # Returns
545    /// A new [`ArrowOptions`] with the updated setting.
546    ///
547    /// # Examples
548    /// ```rust,ignore
549    /// use clickhouse_arrow::arrow::ArrowOptions;
550    ///
551    /// let arrow_options = ArrowOptions::new()
552    ///     .with_nullable_array_default_empty(false);
553    /// assert!(!arrow_options.nullable_array_default_empty);
554    /// ```
555    #[must_use]
556    pub fn with_nullable_array_default_empty(mut self, enabled: bool) -> Self {
557        self.nullable_array_default_empty = enabled;
558        self
559    }
560
561    /// Sets an Arrow option by name and value.
562    ///
563    /// This method updates a specific option identified by `name` to the given boolean
564    /// `value`. Currently supported names are:
565    /// - `"strings_as_strings"`: Maps `ClickHouse` `String` to Arrow `Utf8`.
566    /// - `"use_date32_for_date"`: Maps Arrow `Date32` to `ClickHouse` `Date32`.
567    /// - `"strict_schema"`: Enforces strict type mappings for non-array types.
568    /// - `"disable_strict_schema_ddl"`: Disables strict mode for schema creation.
569    /// - `"nullable_array_default_empty"`: Maps `Nullable(Array(...))` to `Array(...)` with `[]`
570    ///   for nulls.
571    ///
572    /// If an unrecognized name is provided, a warning is logged, and the options are
573    /// returned unchanged. Use this for dynamic configuration or when options are
574    /// specified as key-value pairs.
575    ///
576    /// # Parameters
577    /// - `name`: The name of the option to set.
578    /// - `value`: The boolean value to set for the option.
579    ///
580    /// # Returns
581    /// A new [`ArrowOptions`] with the updated setting.
582    ///
583    /// # Examples
584    /// ```rust,ignore
585    /// use clickhouse_arrow::arrow::ArrowOptions;
586    ///
587    /// let arrow_options = ArrowOptions::new()
588    ///     .with_setting("strings_as_strings", true)
589    ///     .with_setting("nullable_array_default_empty", false);
590    /// assert!(arrow_options.strings_as_strings);
591    /// assert!(!arrow_options.nullable_array_default_empty);
592    /// ```
593    #[must_use]
594    pub fn with_setting(self, name: &str, value: bool) -> Self {
595        match name {
596            "strings_as_strings" => self.with_strings_as_strings(value),
597            "use_date32_for_date" => self.with_use_date32_for_date(value),
598            "strict_schema" => self.with_strict_schema(value),
599            "disable_strict_schema_ddl" => self.with_disable_strict_schema_ddl(value),
600            "nullable_array_default_empty" => self.with_nullable_array_default_empty(value),
601            k => {
602                warn!("Unrecognized option for ArrowOptions: {k}");
603                self
604            }
605        }
606    }
607}
608
609impl<'a, S, I> From<I> for ArrowOptions
610where
611    S: AsRef<str> + 'a,
612    I: Iterator<Item = &'a (S, bool)> + 'a,
613{
614    /// Creates an `ArrowOptions` instance from an iterator of key-value pairs.
615    ///
616    /// This method constructs an [`ArrowOptions`] by applying settings from an iterator
617    /// of `(key, value)` pairs, where `key` is a string (e.g., `"strict_schema"`) and
618    /// `value` is a boolean. It uses [`ArrowOptions::with_setting`] to apply each
619    /// setting. Unrecognized keys trigger a warning but do not cause an error.
620    ///
621    /// See currently supported keys by inspecting [`ArrowOptions::with_setting`].
622    ///
623    /// # Parameters
624    /// - `value`: An iterator of `(key, value)` pairs, where `key` is a string-like type and
625    ///   `value` is a boolean.
626    ///
627    /// # Returns
628    /// An [`ArrowOptions`] instance with the applied settings.
629    ///
630    /// # Examples
631    /// ```rust,ignore
632    /// use clickhouse_arrow::arrow::ArrowOptions;
633    ///
634    /// let settings = vec![("strings_as_strings", true), ("nullable_array_default_empty", false)];
635    /// let arrow_options: ArrowOptions = settings.iter().collect();
636    /// assert!(arrow_options.strings_as_strings);
637    /// assert!(!arrow_options.nullable_array_default_empty);
638    /// ```
639    fn from(value: I) -> Self {
640        let mut options = ArrowOptions::default();
641        for (k, v) in value {
642            options = options.with_setting(k.as_ref(), *v);
643        }
644        options
645    }
646}
647
648impl<'a> FromIterator<(&'a str, bool)> for ArrowOptions {
649    /// Creates an `ArrowOptions` instance from an iterator of string-boolean pairs.
650    ///
651    /// This method constructs an [`ArrowOptions`] by applying settings from an iterator
652    /// of `(key, value)` pairs, where `key` is a string slice (e.g., `"strict_schema"`)
653    /// and `value` is a boolean. It uses [`ArrowOptions::with_setting`] to apply each
654    /// setting. Unrecognized keys trigger a warning but do not cause an error.
655    ///
656    /// See currently supported keys by inspecting [`ArrowOptions::with_setting`].
657    ///
658    /// # Parameters
659    /// - `iter`: An iterator of `(key, value)` pairs, where `key` is a string slice and `value` is
660    ///   a boolean.
661    ///
662    /// # Returns
663    /// An [`ArrowOptions`] instance with the applied settings.
664    ///
665    /// # Examples
666    /// ```rust,ignore
667    /// use clickhouse_arrow::arrow::ArrowOptions;
668    ///
669    /// let settings = vec![("strings_as_strings", true), ("nullable_array_default_empty", false)];
670    /// let arrow_options = ArrowOptions::from_iter(settings);
671    /// assert!(arrow_options.strings_as_strings);
672    /// assert!(!arrow_options.nullable_array_default_empty);
673    /// ```
674    fn from_iter<I: IntoIterator<Item = (&'a str, bool)>>(iter: I) -> Self {
675        let mut options = ArrowOptions::default();
676        for (k, v) in iter {
677            options = options.with_setting(k, v);
678        }
679        options
680    }
681}
682
683/// Configuration options for connecting to `ClickHouse` cloud instances.
684///
685/// The `CloudOptions` struct defines settings specific to `ClickHouse` cloud
686/// deployments, used within [`ClientOptions`]. These options control the behavior
687/// of cloud wakeup pings to ensure the instance is active before connecting.
688///
689/// # Fields
690/// - `timeout`: Optional timeout (in seconds) for the cloud wakeup ping; if `None`, uses a default
691///   timeout.
692/// - `wakeup`: If `true`, sends a wakeup ping before connecting; if `false`, skips the ping.
693///
694/// # Feature
695/// Requires the `cloud` feature to be enabled.
696///
697/// # Examples
698/// ```rust,ignore
699/// use clickhouse_arrow::prelude::*;
700///
701/// let cloud_options = CloudOptions {
702///     timeout: Some(10),
703///     wakeup: true,
704/// };
705/// let options = ClientOptions {
706///     cloud: cloud_options,
707///     ..ClientOptions::default()
708/// };
709/// ```
710#[derive(Debug, Clone, Copy, Default, PartialEq)]
711#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
712pub struct CloudOptions {
713    #[cfg_attr(feature = "serde", serde(default))]
714    pub timeout: Option<u64>,
715    #[cfg_attr(feature = "serde", serde(default))]
716    pub wakeup:  bool,
717}