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// TODO: Remove - make the properties public!
226/// Configuration options for Arrow serialization and deserialization with `ClickHouse`.
227///
228/// The `ArrowOptions` struct defines settings that control how Apache Arrow data types
229/// are mapped to `ClickHouse` types during serialization (e.g., inserts), deserialization
230/// (e.g., queries), and schema creation (e.g., DDL operations). These options are used
231/// by [`ArrowClient`] and set via [`ClientBuilder::with_arrow_options`] or directly in
232/// [`ClientOptions`].
233///
234/// # Fields
235/// - `strings_as_strings`: If `true`, maps `ClickHouse` `String` to Arrow `Utf8`; if `false`, maps
236///   to `Binary` (default).
237/// - `use_date32_for_date`: If `true`, maps Arrow `Date32` to `ClickHouse` `Date32`; if `false`,
238///   maps to `Date` (default).
239/// - `strict_schema`: If `true`, enforces strict type mappings during serialization (inserts) and
240///   schema creation, causing errors on `ClickHouse` invariant violations (e.g.,
241///   `Nullable(LowCardinality(String))`); if `false`, attempts to correct violations (e.g., mapping
242///   to `LowCardinality(Nullable(String))`) (default).
243/// - `disable_strict_schema_ddl`: If `true`, prevents automatic strict mode during schema creation
244///   (via [`ArrowOptions::into_strict_ddl`]); if `false`, schema creation defaults to strict mode
245///   (default).
246/// - `nullable_array_default_empty`: If `true`, maps `Nullable(Array(...))` to `Array(...)` with
247///   `[]` for nulls during inserts and schema creation (if `disable_strict_schema_ddl = true`); if
248///   `false`, errors on `Nullable(Array(...))` (default).
249///
250/// # Notes
251/// - During schema creation, options are converted to strict mode (via
252///   [`ArrowOptions::into_strict_ddl`]) unless `disable_strict_schema_ddl` is `true`. Strict mode
253///   sets `strict_schema = true` and effectively enforces `nullable_array_default_empty = false`,
254///   ensuring non-nullable arrays.
255/// - When `strict_schema` is `false`, violations like `Nullable(LowCardinality(String))` are
256///   corrected, but arrays are handled per `nullable_array_default_empty` for inserts, while
257///   nullable arrays are ignored during schema creation.
258/// - If `strict_schema = true` and `nullable_array_default_empty = true`, non-array violations
259///   (e.g., `LowCardinality`) error, but arrays map to `[]` for nulls during insert. This is useful
260///   in cases where the arrow `Schema` is used to create the table, but arrays may come from
261///   different `RecordBatch`es.
262/// - This struct is `#[non_exhaustive]`, so future fields may be added (e.g., for new `ClickHouse`
263///   types or serialization options). Use [`ArrowOptions::new`] or [`ArrowOptions::default`] to
264///   construct instances.
265///
266/// # Examples
267/// ```rust,ignore
268/// use clickhouse_arrow::prelude::*;
269///
270/// let arrow_options = ArrowOptions::new()
271///     .with_strings_as_strings(true)
272///     .with_strict_schema(true)
273///     .with_nullable_array_default_empty(false);
274/// let options = ClientOptions {
275///     arrow: Some(arrow_options),
276///     ..ClientOptions::default()
277/// };
278/// ```
279#[expect(clippy::struct_excessive_bools)]
280#[non_exhaustive]
281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
282#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
283pub struct ArrowOptions {
284    pub strings_as_strings:           bool,
285    pub use_date32_for_date:          bool,
286    pub strict_schema:                bool,
287    pub disable_strict_schema_ddl:    bool,
288    pub nullable_array_default_empty: bool,
289}
290
291impl Default for ArrowOptions {
292    /// Creates an `ArrowOptions` instance with default values.
293    ///
294    /// The default configuration uses relaxed type mappings suitable for most
295    /// `ClickHouse` and `Arrow` use cases:
296    /// - `ClickHouse` `String` maps to Arrow `Binary`.
297    /// - Arrow `Date32` maps to `ClickHouse` `Date`.
298    /// - Type mappings are relaxed, correcting `ClickHouse` invariant violations (e.g., mapping
299    ///   `Nullable(LowCardinality(String))` to `LowCardinality(Nullable(String))`).
300    /// - Schema creation defaults to strict mode (via [`ArrowOptions::into_strict_ddl`]).
301    /// - `Nullable(Array(...))` defaults to `Array(...)` with `[]` for nulls.
302    ///
303    /// Use this as a starting point and customize with methods like
304    /// [`ArrowOptions::with_strings_as_strings`].
305    ///
306    /// # Returns
307    /// An [`ArrowOptions`] instance with default settings.
308    ///
309    /// # Examples
310    /// ```rust,ignore
311    /// use clickhouse_arrow::arrow::ArrowOptions;
312    ///
313    /// let arrow_options = ArrowOptions::default();
314    /// println!("Nullable array default empty: {}", arrow_options.nullable_array_default_empty); // true
315    /// ```
316    fn default() -> Self { Self::new() }
317}
318
319impl ArrowOptions {
320    /// Creates a new `ArrowOptions` instance with default values.
321    ///
322    /// This method is equivalent to [`ArrowOptions::default`], initializing fields for
323    /// relaxed type mappings. Use this to start configuring Arrow
324    /// serialization/deserialization options for `ClickHouse`.
325    ///
326    /// # Returns
327    /// A new [`ArrowOptions`] instance with default settings.
328    ///
329    /// # Examples
330    /// ```rust,ignore
331    /// use clickhouse_arrow::arrow::ArrowOptions;
332    ///
333    /// let arrow_options = ArrowOptions::new();
334    /// println!("Nullable array default empty: {}", arrow_options.nullable_array_default_empty); // true
335    /// ```
336    pub const fn new() -> Self {
337        Self {
338            strings_as_strings:           false,
339            use_date32_for_date:          false,
340            strict_schema:                false,
341            disable_strict_schema_ddl:    false,
342            nullable_array_default_empty: true,
343        }
344    }
345
346    /// Creates an `ArrowOptions` instance with strict type mapping settings.
347    ///
348    /// This method configures options for strict type mappings, where `ClickHouse`
349    /// invariant violations (e.g., `Nullable(LowCardinality(String))` or
350    /// `Nullable(Array(...))`) cause errors during serialization (inserts) and schema
351    /// creation. It sets `strict_schema` to `true` and `nullable_array_default_empty` to
352    /// `false`, leaving other fields as `false`. Use this for operations where
353    /// `ClickHouse` invariants must be strictly enforced.
354    ///
355    /// # Returns
356    /// An [`ArrowOptions`] instance with strict settings.
357    ///
358    /// # Examples
359    /// ```rust,ignore
360    /// use clickhouse_arrow::arrow::ArrowOptions;
361    ///
362    /// let arrow_options = ArrowOptions::strict();
363    /// println!("Strict schema: {}", arrow_options.strict_schema); // true
364    /// ```
365    pub const fn strict() -> Self {
366        Self {
367            strings_as_strings:           false,
368            use_date32_for_date:          false,
369            strict_schema:                true,
370            disable_strict_schema_ddl:    false,
371            nullable_array_default_empty: false,
372        }
373    }
374
375    /// Converts the options to strict mode for schema creation, unless disabled.
376    ///
377    /// This method returns a new [`ArrowOptions`] with strict settings (equivalent to
378    /// [`ArrowOptions::strict`]) unless `disable_strict_schema_ddl` is `true`. If
379    /// `disable_strict_schema_ddl` is `true`, the original options are returned
380    /// unchanged. This method is called automatically during schema creation to enforce
381    /// `ClickHouse` invariants, including non-nullable arrays, unless explicitly disabled.
382    ///
383    /// # Returns
384    /// A new [`ArrowOptions`] instance with strict settings or the original options.
385    ///
386    /// # Examples
387    /// ```rust,ignore
388    /// use clickhouse_arrow::arrow::ArrowOptions;
389    ///
390    /// let options_strict_off = ArrowOptions::new()
391    ///     .with_disable_strict_schema_ddl(true)
392    ///     .into_strict_ddl();
393    /// assert!(!options_strict_off.strict_schema);
394    /// assert!(options_strict_off.nullable_array_default_empty);
395    ///
396    /// let options_strict = ArrowOptions::new()
397    ///     .with_disable_strict_schema_ddl(false) // Default
398    ///     .into_strict_ddl();
399    /// assert!(options_strict.strict_schema);
400    /// assert!(!options_strict.nullable_array_default_empty);
401    /// ```
402    #[must_use]
403    pub fn into_strict_ddl(self) -> Self {
404        if self.disable_strict_schema_ddl {
405            return self;
406        }
407
408        Self {
409            strings_as_strings: self.strings_as_strings,
410            use_date32_for_date: self.use_date32_for_date,
411            ..Self::strict()
412        }
413    }
414
415    /// Sets whether `ClickHouse` `String` types are deserialized as Arrow `Utf8`.
416    ///
417    /// By default, `ClickHouse` `String` types map to Arrow `Binary`. When this option
418    /// is enabled (`true`), they map to Arrow `Utf8`, which is more suitable for text
419    /// data. Use this to control serialization/deserialization behavior for string
420    /// columns.
421    ///
422    /// # Parameters
423    /// - `enabled`: If `true`, maps [`crate::Type::String`] to
424    ///   [`arrow::datatypes::DataType::Utf8`]; if `false`, maps to
425    ///   [`arrow::datatypes::DataType::Binary`].
426    ///
427    /// # Returns
428    /// A new [`ArrowOptions`] with the updated setting.
429    ///
430    /// # Examples
431    /// ```rust,ignore
432    /// use clickhouse_arrow::prelude::*;
433    ///
434    /// let arrow_options = ArrowOptions::new()
435    ///     .with_strings_as_strings(true);
436    /// println!("Strings as strings: {}", arrow_options.strings_as_strings); // true
437    /// ```
438    #[must_use]
439    pub fn with_strings_as_strings(mut self, enabled: bool) -> Self {
440        self.strings_as_strings = enabled;
441        self
442    }
443
444    /// Sets whether Arrow `Date32` is mapped to `ClickHouse` `Date` or `Date32`.
445    ///
446    /// By default, Arrow `Date32` maps to `ClickHouse` `Date` (days since 1970-01-01).
447    /// When this option is enabled (`true`), it maps to `ClickHouse` `Date32` (days
448    /// since 1900-01-01). Use this to control date serialization/deserialization
449    /// behavior.
450    ///
451    /// # Parameters
452    /// - `enabled`: If `true`, maps `Date32` to `ClickHouse` `Date32`; if `false`, maps to `Date`.
453    ///
454    /// # Returns
455    /// A new [`ArrowOptions`] with the updated setting.
456    ///
457    /// # Examples
458    /// ```rust,ignore
459    /// use clickhouse_arrow::prelude::*;
460    ///
461    /// let arrow_options = ArrowOptions::new()
462    ///     .with_use_date32_for_date(true);
463    /// println!("Use Date32 for Date: {}", arrow_options.use_date32_for_date); // true
464    /// ```
465    #[must_use]
466    pub fn with_use_date32_for_date(mut self, enabled: bool) -> Self {
467        self.use_date32_for_date = enabled;
468        self
469    }
470
471    /// Sets whether type mappings are strict during serialization and schema creation.
472    ///
473    /// By default, type mappings are relaxed, allowing `ClickHouse` invariant violations
474    /// (e.g., `Nullable(LowCardinality(String))`) to be corrected automatically (e.g.,
475    /// mapping to `LowCardinality(Nullable(String))`). When this option is enabled
476    /// (`true`), non-array violations cause errors during serialization (inserts) and
477    /// schema creation. Array violations are controlled by
478    /// [`ArrowOptions::with_nullable_array_default_empty`]. Schema creation defaults to
479    /// strict mode unless [`ArrowOptions::with_disable_strict_schema_ddl`] is enabled.
480    ///
481    /// # Parameters
482    /// - `enabled`: If `true`, enforces strict type mappings for non-array types; if `false`,
483    ///   allows relaxed corrections.
484    ///
485    /// # Returns
486    /// A new [`ArrowOptions`] with the updated setting.
487    ///
488    /// # Examples
489    /// ```rust,ignore
490    /// use clickhouse_arrow::arrow::ArrowOptions;
491    ///
492    /// let arrow_options = ArrowOptions::new()
493    ///     .with_strict_schema(true);
494    /// println!("Strict schema: {}", arrow_options.strict_schema); // true
495    /// ```
496    #[must_use]
497    pub fn with_strict_schema(mut self, enabled: bool) -> Self {
498        self.strict_schema = enabled;
499        self
500    }
501
502    /// Sets whether strict mode is disabled during schema creation.
503    ///
504    /// By default, schema creation (e.g., DDL operations) uses strict type mappings (via
505    /// [`ArrowOptions::into_strict_ddl`]), enforcing `ClickHouse` invariants and causing
506    /// errors on violations, including `Nullable(Array(...))`. When this option is
507    /// enabled (`true`), strict mode is disabled for schema creation, using the user’s
508    /// `strict_schema` and `nullable_array_default_empty` settings.
509    ///
510    /// # Parameters
511    /// - `enabled`: If `true`, disables strict mode for schema creation; if `false`, enables strict
512    ///   mode.
513    ///
514    /// # Returns
515    /// A new [`ArrowOptions`] with the updated setting.
516    ///
517    /// # Examples
518    /// ```rust,ignore
519    /// use clickhouse_arrow::arrow::ArrowOptions;
520    ///
521    /// let arrow_options = ArrowOptions::new()
522    ///     .with_disable_strict_schema_ddl(true);
523    /// assert!(arrow_options.disable_strict_schema_ddl);
524    /// ```
525    #[must_use]
526    pub fn with_disable_strict_schema_ddl(mut self, enabled: bool) -> Self {
527        self.disable_strict_schema_ddl = enabled;
528        self
529    }
530
531    /// Sets whether `Nullable(Array(...))` types default to empty arrays during inserts and are
532    /// coerced to non-nullable during DDL.
533    ///
534    /// By default, `Nullable(Array(...))` types are mapped to `Array(...)` with `[]` for
535    /// nulls during serialization (inserts) and schema creation (if
536    /// `disable_strict_schema_ddl = true`). When this option is disabled (`false`),
537    /// `Nullable(Array(...))` causes errors, enforcing non-nullable arrays. Schema
538    /// creation defaults to non-nullable arrays unless
539    /// [`ArrowOptions::with_disable_strict_schema_ddl`] is enabled.
540    ///
541    /// # Parameters
542    /// - `enabled`: If `true`, maps `Nullable(Array(...))` to `Array(...)` with `[]` for nulls; if
543    ///   `false`, errors on `Nullable(Array(...))`.
544    ///
545    /// # Returns
546    /// A new [`ArrowOptions`] with the updated setting.
547    ///
548    /// # Examples
549    /// ```rust,ignore
550    /// use clickhouse_arrow::arrow::ArrowOptions;
551    ///
552    /// let arrow_options = ArrowOptions::new()
553    ///     .with_nullable_array_default_empty(false);
554    /// assert!(!arrow_options.nullable_array_default_empty);
555    /// ```
556    #[must_use]
557    pub fn with_nullable_array_default_empty(mut self, enabled: bool) -> Self {
558        self.nullable_array_default_empty = enabled;
559        self
560    }
561
562    /// Sets an Arrow option by name and value.
563    ///
564    /// This method updates a specific option identified by `name` to the given boolean
565    /// `value`. Currently supported names are:
566    /// - `"strings_as_strings"`: Maps `ClickHouse` `String` to Arrow `Utf8`.
567    /// - `"use_date32_for_date"`: Maps Arrow `Date32` to `ClickHouse` `Date32`.
568    /// - `"strict_schema"`: Enforces strict type mappings for non-array types.
569    /// - `"disable_strict_schema_ddl"`: Disables strict mode for schema creation.
570    /// - `"nullable_array_default_empty"`: Maps `Nullable(Array(...))` to `Array(...)` with `[]`
571    ///   for nulls.
572    ///
573    /// If an unrecognized name is provided, a warning is logged, and the options are
574    /// returned unchanged. Use this for dynamic configuration or when options are
575    /// specified as key-value pairs.
576    ///
577    /// # Parameters
578    /// - `name`: The name of the option to set.
579    /// - `value`: The boolean value to set for the option.
580    ///
581    /// # Returns
582    /// A new [`ArrowOptions`] with the updated setting.
583    ///
584    /// # Examples
585    /// ```rust,ignore
586    /// use clickhouse_arrow::arrow::ArrowOptions;
587    ///
588    /// let arrow_options = ArrowOptions::new()
589    ///     .with_setting("strings_as_strings", true)
590    ///     .with_setting("nullable_array_default_empty", false);
591    /// assert!(arrow_options.strings_as_strings);
592    /// assert!(!arrow_options.nullable_array_default_empty);
593    /// ```
594    #[must_use]
595    pub fn with_setting(self, name: &str, value: bool) -> Self {
596        match name {
597            "strings_as_strings" => self.with_strings_as_strings(value),
598            "use_date32_for_date" => self.with_use_date32_for_date(value),
599            "strict_schema" => self.with_strict_schema(value),
600            "disable_strict_schema_ddl" => self.with_disable_strict_schema_ddl(value),
601            "nullable_array_default_empty" => self.with_nullable_array_default_empty(value),
602            k => {
603                warn!("Unrecognized option for ArrowOptions: {k}");
604                self
605            }
606        }
607    }
608}
609
610impl<'a, S, I> From<I> for ArrowOptions
611where
612    S: AsRef<str> + 'a,
613    I: Iterator<Item = &'a (S, bool)> + 'a,
614{
615    /// Creates an `ArrowOptions` instance from an iterator of key-value pairs.
616    ///
617    /// This method constructs an [`ArrowOptions`] by applying settings from an iterator
618    /// of `(key, value)` pairs, where `key` is a string (e.g., `"strict_schema"`) and
619    /// `value` is a boolean. It uses [`ArrowOptions::with_setting`] to apply each
620    /// setting. Unrecognized keys trigger a warning but do not cause an error.
621    ///
622    /// See currently supported keys by inspecting [`ArrowOptions::with_setting`].
623    ///
624    /// # Parameters
625    /// - `value`: An iterator of `(key, value)` pairs, where `key` is a string-like type and
626    ///   `value` is a boolean.
627    ///
628    /// # Returns
629    /// An [`ArrowOptions`] instance with the applied settings.
630    ///
631    /// # Examples
632    /// ```rust,ignore
633    /// use clickhouse_arrow::arrow::ArrowOptions;
634    ///
635    /// let settings = vec![("strings_as_strings", true), ("nullable_array_default_empty", false)];
636    /// let arrow_options: ArrowOptions = settings.iter().collect();
637    /// assert!(arrow_options.strings_as_strings);
638    /// assert!(!arrow_options.nullable_array_default_empty);
639    /// ```
640    fn from(value: I) -> Self {
641        let mut options = ArrowOptions::default();
642        for (k, v) in value {
643            options = options.with_setting(k.as_ref(), *v);
644        }
645        options
646    }
647}
648
649impl<'a> FromIterator<(&'a str, bool)> for ArrowOptions {
650    /// Creates an `ArrowOptions` instance from an iterator of string-boolean pairs.
651    ///
652    /// This method constructs an [`ArrowOptions`] by applying settings from an iterator
653    /// of `(key, value)` pairs, where `key` is a string slice (e.g., `"strict_schema"`)
654    /// and `value` is a boolean. It uses [`ArrowOptions::with_setting`] to apply each
655    /// setting. Unrecognized keys trigger a warning but do not cause an error.
656    ///
657    /// See currently supported keys by inspecting [`ArrowOptions::with_setting`].
658    ///
659    /// # Parameters
660    /// - `iter`: An iterator of `(key, value)` pairs, where `key` is a string slice and `value` is
661    ///   a boolean.
662    ///
663    /// # Returns
664    /// An [`ArrowOptions`] instance with the applied settings.
665    ///
666    /// # Examples
667    /// ```rust,ignore
668    /// use clickhouse_arrow::arrow::ArrowOptions;
669    ///
670    /// let settings = vec![("strings_as_strings", true), ("nullable_array_default_empty", false)];
671    /// let arrow_options = ArrowOptions::from_iter(settings);
672    /// assert!(arrow_options.strings_as_strings);
673    /// assert!(!arrow_options.nullable_array_default_empty);
674    /// ```
675    fn from_iter<I: IntoIterator<Item = (&'a str, bool)>>(iter: I) -> Self {
676        let mut options = ArrowOptions::default();
677        for (k, v) in iter {
678            options = options.with_setting(k, v);
679        }
680        options
681    }
682}
683
684/// Configuration options for connecting to `ClickHouse` cloud instances.
685///
686/// The `CloudOptions` struct defines settings specific to `ClickHouse` cloud
687/// deployments, used within [`ClientOptions`]. These options control the behavior
688/// of cloud wakeup pings to ensure the instance is active before connecting.
689///
690/// # Fields
691/// - `timeout`: Optional timeout (in seconds) for the cloud wakeup ping; if `None`, uses a default
692///   timeout.
693/// - `wakeup`: If `true`, sends a wakeup ping before connecting; if `false`, skips the ping.
694///
695/// # Feature
696/// Requires the `cloud` feature to be enabled.
697///
698/// # Examples
699/// ```rust,ignore
700/// use clickhouse_arrow::prelude::*;
701///
702/// let cloud_options = CloudOptions {
703///     timeout: Some(10),
704///     wakeup: true,
705/// };
706/// let options = ClientOptions {
707///     cloud: cloud_options,
708///     ..ClientOptions::default()
709/// };
710/// ```
711#[derive(Debug, Clone, Copy, Default, PartialEq)]
712#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
713pub struct CloudOptions {
714    #[cfg_attr(feature = "serde", serde(default))]
715    pub timeout: Option<u64>,
716    #[cfg_attr(feature = "serde", serde(default))]
717    pub wakeup:  bool,
718}