## Coverage Report

#### Created: 2024-10-13 08:39

    /Users/andrewlamb/Software/datafusion/datafusion/execution/src/config.rs

    Line
    Count
    Source (jump to first uncovered line)
    1
    // Licensed to the Apache Software Foundation (ASF) under one
    2
    // or more contributor license agreements.  See the NOTICE file
    3
    // distributed with this work for additional information
    4
    // regarding copyright ownership.  The ASF licenses this file
    5
    // to you under the Apache License, Version 2.0 (the
    6
    // "License"); you may not use this file except in compliance
    7
    // with the License.  You may obtain a copy of the License at
    8
    //
    9
    //   http://www.apache.org/licenses/LICENSE-2.0
    10
    //
    11
    // Unless required by applicable law or agreed to in writing,
    12
    // software distributed under the License is distributed on an
    13
    // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14
    // KIND, either express or implied.  See the License for the
    15
    // specific language governing permissions and limitations
    16
    // under the License.
    17
    18
    use std::{
    19
        any::{Any, TypeId},
    20
        collections::HashMap,
    21
        hash::{BuildHasherDefault, Hasher},
    22
        sync::Arc,
    23
    };
    24
    25
    use datafusion_common::{
    26
        config::{ConfigExtension, ConfigOptions},
    27
        Result, ScalarValue,
    28
    };
    29
    30
    /// Configuration options for [`SessionContext`].
    31
    ///
    32
    /// Can be passed to [`SessionContext::new_with_config`] to customize the configuration of DataFusion.
    33
    ///
    34
    /// Options can be set using namespaces keys with `.` as the separator, where the
    35
    /// namespace determines which configuration struct the value to routed to. All
    36
    /// built-in options are under the `datafusion` namespace.
    37
    ///
    38
    /// For example, the key `datafusion.execution.batch_size` will set [ExecutionOptions::batch_size][datafusion_common::config::ExecutionOptions::batch_size],
    39
    /// because [ConfigOptions::execution] is [ExecutionOptions][datafusion_common::config::ExecutionOptions]. Similarly, the key
    40
    /// `datafusion.execution.parquet.pushdown_filters` will set [ParquetOptions::pushdown_filters][datafusion_common::config::ParquetOptions::pushdown_filters],
    41
    /// since [ExecutionOptions::parquet][datafusion_common::config::ExecutionOptions::parquet] is [ParquetOptions][datafusion_common::config::ParquetOptions].
    42
    ///
    43
    /// Some options have convenience methods. For example [SessionConfig::with_batch_size] is
    44
    /// shorthand for setting `datafusion.execution.batch_size`.
    45
    ///
    46
    /// ```
    47
    /// use datafusion_execution::config::SessionConfig;
    48
    /// use datafusion_common::ScalarValue;
    49
    ///
    50
    /// let config = SessionConfig::new()
    51
    ///    .set("datafusion.execution.batch_size", &ScalarValue::UInt64(Some(1234)))
    52
    ///    .set_bool("datafusion.execution.parquet.pushdown_filters", true);
    53
    ///
    54
    /// assert_eq!(config.batch_size(), 1234);
    55
    /// assert_eq!(config.options().execution.batch_size, 1234);
    56
    /// assert_eq!(config.options().execution.parquet.pushdown_filters, true);
    57
    /// ```
    58
    ///
    59
    /// You can also directly mutate the options via [SessionConfig::options_mut].
    60
    /// So the following is equivalent to the above:
    61
    ///
    62
    /// ```
    63
    /// # use datafusion_execution::config::SessionConfig;
    64
    /// # use datafusion_common::ScalarValue;
    65
    /// #
    66
    /// let mut config = SessionConfig::new();
    67
    /// config.options_mut().execution.batch_size = 1234;
    68
    /// config.options_mut().execution.parquet.pushdown_filters = true;
    69
    /// #
    70
    /// # assert_eq!(config.batch_size(), 1234);
    71
    /// # assert_eq!(config.options().execution.batch_size, 1234);
    72
    /// # assert_eq!(config.options().execution.parquet.pushdown_filters, true);
    73
    /// ```
    74
    ///
    75
    /// ## Built-in options
    76
    ///
    77
    /// | Namespace | Config struct |
    78
    /// | --------- | ------------- |
    79
    /// | `datafusion.catalog` | [CatalogOptions][datafusion_common::config::CatalogOptions] |
    80
    /// | `datafusion.execution` | [ExecutionOptions][datafusion_common::config::ExecutionOptions] |
    81
    /// | `datafusion.execution.parquet` | [ParquetOptions][datafusion_common::config::ParquetOptions] |
    82
    /// | `datafusion.optimizer` | [OptimizerOptions][datafusion_common::config::OptimizerOptions] |
    83
    /// | `datafusion.sql_parser` | [SqlParserOptions][datafusion_common::config::SqlParserOptions] |
    84
    /// | `datafusion.explain` | [ExplainOptions][datafusion_common::config::ExplainOptions] |
    85
    ///
    86
    /// ## Custom configuration
    87
    ///
    88
    /// Configuration options can be extended. See [SessionConfig::with_extension] for details.
    89
    ///
    90
    /// [`SessionContext`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html
    91
    /// [`SessionContext::new_with_config`]: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html#method.new_with_config
    92
    #[derive(Clone, Debug)]
    93
    pub struct SessionConfig {
    94
        /// Configuration options
    95
        options: ConfigOptions,
    96
        /// Opaque extensions.
    97
        extensions: AnyMap,
    98
    }
    99
    100
    impl Default for SessionConfig {
    101
    1.38k
        fn default() -> Self {
    102
    1.38k
            Self {
    103
    1.38k
                options: ConfigOptions::new(),
    104
    1.38k
                // Assume no extensions by default.
    105
    1.38k
                extensions: HashMap::with_capacity_and_hasher(
    106
    1.38k
                    0,
    107
    1.38k
                    BuildHasherDefault::default(),
    108
    1.38k
                ),
    109
    1.38k
            }
    110
    1.38k
        }
    111
    }
    112
    113
    impl SessionConfig {
    114
        /// Create an execution config with default setting
    115
    1.07k
        pub fn new() -> Self {
    116
    1.07k
            Default::default()
    117
    1.07k
        }
    118
    119
        /// Create an execution config with config options read from the environment
    120
    0
        pub fn from_env() -> Result<Self> {
    121
    0
            Ok(ConfigOptions::from_env()?.into())
    122
    0
        }
    123
    124
        /// Create new ConfigOptions struct, taking values from a string hash map.
    125
    0
        pub fn from_string_hash_map(settings: &HashMap<String, String>) -> Result<Self> {
    126
    0
            Ok(ConfigOptions::from_string_hash_map(settings)?.into())
    127
    0
        }
    128
    129
        /// Return a handle to the configuration options.
    130
        ///
    131
        /// Can be used to read the current configuration.
    132
        ///
    133
        /// ```
    134
        /// use datafusion_execution::config::SessionConfig;
    135
        ///
    136
        /// let config = SessionConfig::new();
    137
        /// assert!(config.options().execution.batch_size > 0);
    138
        /// ```
    139
    72
        pub fn options(&self) -> &ConfigOptions {
    140
    72
            &self.options
    141
    72
        }
    142
    143
        /// Return a mutable handle to the configuration options.
    144
        ///
    145
        /// Can be used to set configuration options.
    146
        ///
    147
        /// ```
    148
        /// use datafusion_execution::config::SessionConfig;
    149
        ///
    150
        /// let mut config = SessionConfig::new();
    151
        /// config.options_mut().execution.batch_size = 1024;
    152
        /// assert_eq!(config.options().execution.batch_size, 1024);
    153
        /// ```
    154
    0
        pub fn options_mut(&mut self) -> &mut ConfigOptions {
    155
    0
            &mut self.options
    156
    0
        }
    157
    158
        /// Set a configuration option
    159
    4
        pub fn set(self, key: &str, value: &ScalarValue) -> Self {
    160
    4
            self.set_str(key, &value.to_string())
    161
    4
        }
    162
    163
        /// Set a boolean configuration option
    164
    0
        pub fn set_bool(self, key: &str, value: bool) -> Self {
    165
    0
            self.set_str(key, &value.to_string())
    166
    0
        }
    167
    168
        /// Set a generic `u64` configuration option
    169
    0
        pub fn set_u64(self, key: &str, value: u64) -> Self {
    170
    0
            self.set_str(key, &value.to_string())
    171
    0
        }
    172
    173
        /// Set a generic `usize` configuration option
    174
    0
        pub fn set_usize(self, key: &str, value: usize) -> Self {
    175
    0
            self.set_str(key, &value.to_string())
    176
    0
        }
    177
    178
        /// Set a generic `str` configuration option
    179
    4
        pub fn set_str(mut self, key: &str, value: &str) -> Self {
    180
    4
            self.options.set(key, value).unwrap();
    181
    4
            self
    182
    4
        }
    183
    184
        /// Customize batch size
    185
    319
        pub fn with_batch_size(mut self, n: usize) -> Self {
    186
    319
            // batch size must be greater than zero
    187
    319
            assert!(n > 0);
    188
    319
            self.options.execution.batch_size = n;
    189
    319
            self
    190
    319
        }
    191
    192
        /// Customize [`target_partitions`]
    193
        ///
    194
        /// [`target_partitions`]: datafusion_common::config::ExecutionOptions::target_partitions
    195
    2
        pub fn with_target_partitions(mut self, n: usize) -> Self {
    196
    2
            // partition count must be greater than zero
    197
    2
            assert!(n > 0);
    198
    2
            self.options.execution.target_partitions = n;
    199
    2
            self
    200
    2
        }
    201
    202
        /// Insert new [ConfigExtension]
    203
    0
        pub fn with_option_extension<T: ConfigExtension>(mut self, extension: T) -> Self {
    204
    0
            self.options_mut().extensions.insert(extension);
    205
    0
            self
    206
    0
        }
    207
    208
        /// Get [`target_partitions`]
    209
        ///
    210
        /// [`target_partitions`]: datafusion_common::config::ExecutionOptions::target_partitions
    211
    0
        pub fn target_partitions(&self) -> usize {
    212
    0
            self.options.execution.target_partitions
    213
    0
        }
    214
    215
        /// Is the information schema enabled?
    216
    0
        pub fn information_schema(&self) -> bool {
    217
    0
            self.options.catalog.information_schema
    218
    0
        }
    219
    220
        /// Should the context create the default catalog and schema?
    221
    0
        pub fn create_default_catalog_and_schema(&self) -> bool {
    222
    0
            self.options.catalog.create_default_catalog_and_schema
    223
    0
        }
    224
    225
        /// Are joins repartitioned during execution?
    226
    0
        pub fn repartition_joins(&self) -> bool {
    227
    0
            self.options.optimizer.repartition_joins
    228
    0
        }
    229
    230
        /// Are aggregates repartitioned during execution?
    231
    0
        pub fn repartition_aggregations(&self) -> bool {
    232
    0
            self.options.optimizer.repartition_aggregations
    233
    0
        }
    234
    235
        /// Are window functions repartitioned during execution?
    236
    0
        pub fn repartition_window_functions(&self) -> bool {
    237
    0
            self.options.optimizer.repartition_windows
    238
    0
        }
    239
    240
        /// Do we execute sorts in a per-partition fashion and merge afterwards,
    241
        /// or do we coalesce partitions first and sort globally?
    242
    0
        pub fn repartition_sorts(&self) -> bool {
    243
    0
            self.options.optimizer.repartition_sorts
    244
    0
        }
    245
    246
        /// Prefer existing sort (true) or maximize parallelism (false). See
    247
        /// [prefer_existing_sort] for more details
    248
        ///
    249
        /// [prefer_existing_sort]: datafusion_common::config::OptimizerOptions::prefer_existing_sort
    250
    0
        pub fn prefer_existing_sort(&self) -> bool {
    251
    0
            self.options.optimizer.prefer_existing_sort
    252
    0
        }
    253
    254
        /// Are statistics collected during execution?
    255
    0
        pub fn collect_statistics(&self) -> bool {
    256
    0
            self.options.execution.collect_statistics
    257
    0
        }
    258
    259
        /// Selects a name for the default catalog and schema
    260
    0
        pub fn with_default_catalog_and_schema(
    261
    0
            mut self,
    262
    0
            catalog: impl Into<String>,
    263
    0
            schema: impl Into<String>,
    264
    0
        ) -> Self {
    265
    0
            self.options.catalog.default_catalog = catalog.into();
    266
    0
            self.options.catalog.default_schema = schema.into();
    267
    0
            self
    268
    0
        }
    269
    270
        /// Controls whether the default catalog and schema will be automatically created
    271
    0
        pub fn with_create_default_catalog_and_schema(mut self, create: bool) -> Self {
    272
    0
            self.options.catalog.create_default_catalog_and_schema = create;
    273
    0
            self
    274
    0
        }
    275
    276
        /// Enables or disables the inclusion of `information_schema` virtual tables
    277
    0
        pub fn with_information_schema(mut self, enabled: bool) -> Self {
    278
    0
            self.options.catalog.information_schema = enabled;
    279
    0
            self
    280
    0
        }
    281
    282
        /// Enables or disables the use of repartitioning for joins to improve parallelism
    283
    165
        pub fn with_repartition_joins(mut self, enabled: bool) -> Self {
    284
    165
            self.options.optimizer.repartition_joins = enabled;
    285
    165
            self
    286
    165
        }
    287
    288
        /// Enables or disables the use of repartitioning for aggregations to improve parallelism
    289
    0
        pub fn with_repartition_aggregations(mut self, enabled: bool) -> Self {
    290
    0
            self.options.optimizer.repartition_aggregations = enabled;
    291
    0
            self
    292
    0
        }
    293
    294
        /// Sets minimum file range size for repartitioning scans
    295
    0
        pub fn with_repartition_file_min_size(mut self, size: usize) -> Self {
    296
    0
            self.options.optimizer.repartition_file_min_size = size;
    297
    0
            self
    298
    0
        }
    299
    300
        /// Enables or disables the allowing unordered symmetric hash join
    301
    0
        pub fn with_allow_symmetric_joins_without_pruning(mut self, enabled: bool) -> Self {
    302
    0
            self.options.optimizer.allow_symmetric_joins_without_pruning = enabled;
    303
    0
            self
    304
    0
        }
    305
    306
        /// Enables or disables the use of repartitioning for file scans
    307
    0
        pub fn with_repartition_file_scans(mut self, enabled: bool) -> Self {
    308
    0
            self.options.optimizer.repartition_file_scans = enabled;
    309
    0
            self
    310
    0
        }
    311
    312
        /// Enables or disables the use of repartitioning for window functions to improve parallelism
    313
    0
        pub fn with_repartition_windows(mut self, enabled: bool) -> Self {
    314
    0
            self.options.optimizer.repartition_windows = enabled;
    315
    0
            self
    316
    0
        }
    317
    318
        /// Enables or disables the use of per-partition sorting to improve parallelism
    319
    0
        pub fn with_repartition_sorts(mut self, enabled: bool) -> Self {
    320
    0
            self.options.optimizer.repartition_sorts = enabled;
    321
    0
            self
    322
    0
        }
    323
    324
        /// Prefer existing sort (true) or maximize parallelism (false). See
    325
        /// [prefer_existing_sort] for more details
    326
        ///
    327
        /// [prefer_existing_sort]: datafusion_common::config::OptimizerOptions::prefer_existing_sort
    328
    0
        pub fn with_prefer_existing_sort(mut self, enabled: bool) -> Self {
    329
    0
            self.options.optimizer.prefer_existing_sort = enabled;
    330
    0
            self
    331
    0
        }
    332
    333
        /// Prefer existing union (true). See [prefer_existing_union] for more details
    334
        ///
    335
        /// [prefer_existing_union]: datafusion_common::config::OptimizerOptions::prefer_existing_union
    336
    0
        pub fn with_prefer_existing_union(mut self, enabled: bool) -> Self {
    337
    0
            self.options.optimizer.prefer_existing_union = enabled;
    338
    0
            self
    339
    0
        }
    340
    341
        /// Enables or disables the use of pruning predicate for parquet readers to skip row groups
    342
    0
        pub fn with_parquet_pruning(mut self, enabled: bool) -> Self {
    343
    0
            self.options.execution.parquet.pruning = enabled;
    344
    0
            self
    345
    0
        }
    346
    347
        /// Returns true if pruning predicate should be used to skip parquet row groups
    348
    0
        pub fn parquet_pruning(&self) -> bool {
    349
    0
            self.options.execution.parquet.pruning
    350
    0
        }
    351
    352
        /// Returns true if bloom filter should be used to skip parquet row groups
    353
    0
        pub fn parquet_bloom_filter_pruning(&self) -> bool {
    354
    0
            self.options.execution.parquet.bloom_filter_on_read
    355
    0
        }
    356
    357
        /// Enables or disables the use of bloom filter for parquet readers to skip row groups
    358
    0
        pub fn with_parquet_bloom_filter_pruning(mut self, enabled: bool) -> Self {
    359
    0
            self.options.execution.parquet.bloom_filter_on_read = enabled;
    360
    0
            self
    361
    0
        }
    362
    363
        /// Returns true if page index should be used to skip parquet data pages
    364
    0
        pub fn parquet_page_index_pruning(&self) -> bool {
    365
    0
            self.options.execution.parquet.enable_page_index
    366
    0
        }
    367
    368
        /// Enables or disables the use of page index for parquet readers to skip parquet data pages
    369
    0
        pub fn with_parquet_page_index_pruning(mut self, enabled: bool) -> Self {
    370
    0
            self.options.execution.parquet.enable_page_index = enabled;
    371
    0
            self
    372
    0
        }
    373
    374
        /// Enables or disables the collection of statistics after listing files
    375
    0
        pub fn with_collect_statistics(mut self, enabled: bool) -> Self {
    376
    0
            self.options.execution.collect_statistics = enabled;
    377
    0
            self
    378
    0
        }
    379
    380
        /// Get the currently configured batch size
    381
    1.94k
        pub fn batch_size(&self) -> usize {
    382
    1.94k
            self.options.execution.batch_size
    383
    1.94k
        }
    384
    385
        /// Enables or disables the coalescence of small batches into larger batches
    386
    0
        pub fn with_coalesce_batches(mut self, enabled: bool) -> Self {
    387
    0
            self.options.execution.coalesce_batches = enabled;
    388
    0
            self
    389
    0
        }
    390
    391
        /// Returns true if record batches will be examined between each operator
    392
        /// and small batches will be coalesced into larger batches.
    393
    0
        pub fn coalesce_batches(&self) -> bool {
    394
    0
            self.options.execution.coalesce_batches
    395
    0
        }
    396
    397
        /// Enables or disables the round robin repartition for increasing parallelism
    398
    1
        pub fn with_round_robin_repartition(mut self, enabled: bool) -> Self {
    399
    1
            self.options.optimizer.enable_round_robin_repartition = enabled;
    400
    1
            self
    401
    1
        }
    402
    403
        /// Returns true if the physical plan optimizer will try to
    404
        /// add round robin repartition to increase parallelism to leverage more CPU cores.
    405
    0
        pub fn round_robin_repartition(&self) -> bool {
    406
    0
            self.options.optimizer.enable_round_robin_repartition
    407
    0
        }
    408
    409
        /// Set the size of [`sort_spill_reservation_bytes`] to control
    410
        /// memory pre-reservation
    411
        ///
    412
        /// [`sort_spill_reservation_bytes`]: datafusion_common::config::ExecutionOptions::sort_spill_reservation_bytes
    413
    0
        pub fn with_sort_spill_reservation_bytes(
    414
    0
            mut self,
    415
    0
            sort_spill_reservation_bytes: usize,
    416
    0
        ) -> Self {
    417
    0
            self.options.execution.sort_spill_reservation_bytes =
    418
    0
                sort_spill_reservation_bytes;
    419
    0
            self
    420
    0
        }
    421
    422
        /// Set the size of [`sort_in_place_threshold_bytes`] to control
    423
        /// how sort does things.
    424
        ///
    425
        /// [`sort_in_place_threshold_bytes`]: datafusion_common::config::ExecutionOptions::sort_in_place_threshold_bytes
    426
    0
        pub fn with_sort_in_place_threshold_bytes(
    427
    0
            mut self,
    428
    0
            sort_in_place_threshold_bytes: usize,
    429
    0
        ) -> Self {
    430
    0
            self.options.execution.sort_in_place_threshold_bytes =
    431
    0
                sort_in_place_threshold_bytes;
    432
    0
            self
    433
    0
        }
    434
    435
        /// Convert configuration options to name-value pairs with values
    436
        /// converted to strings.
    437
        ///
    438
        /// Note that this method will eventually be deprecated and
    439
        /// replaced by [`options`].
    440
        ///
    441
        /// [`options`]: Self::options
    442
    0
        pub fn to_props(&self) -> HashMap<String, String> {
    443
    0
            let mut map = HashMap::new();
    444
            // copy configs from config_options
    445
    0
            for entry in self.options.entries() {
    446
    0
                map.insert(entry.key, entry.value.unwrap_or_default());
    447
    0
            }
    448
    449
    0
            map
    450
    0
        }
    451
    452
        /// Add extensions.
    453
        ///
    454
        /// Extensions can be used to attach extra data to the session config -- e.g. tracing information or caches.
    455
        /// Extensions are opaque and the types are unknown to DataFusion itself, which makes them extremely flexible. [^1]
    456
        ///
    457
        /// Extensions are stored within an [`Arc`] so they do NOT require [`Clone`]. The are immutable. If you need to
    458
        /// modify their state over their lifetime -- e.g. for caches -- you need to establish some for of interior mutability.
    459
        ///
    460
        /// Extensions are indexed by their type `T`. If multiple values of the same type are provided, only the last one
    461
        /// will be kept.
    462
        ///
    463
        /// You may use [`get_extension`](Self::get_extension) to retrieve extensions.
    464
        ///
    465
        /// # Example
    466
        /// ```
    467
        /// use std::sync::Arc;
    468
        /// use datafusion_execution::config::SessionConfig;
    469
        ///
    470
        /// // application-specific extension types
    471
        /// struct Ext1(u8);
    472
        /// struct Ext2(u8);
    473
        /// struct Ext3(u8);
    474
        ///
    475
        /// let ext1a = Arc::new(Ext1(10));
    476
        /// let ext1b = Arc::new(Ext1(11));
    477
        /// let ext2 = Arc::new(Ext2(2));
    478
        ///
    479
        /// let cfg = SessionConfig::default()
    480
        ///     // will only remember the last Ext1
    481
        ///     .with_extension(Arc::clone(&ext1a))
    482
        ///     .with_extension(Arc::clone(&ext1b))
    483
        ///     .with_extension(Arc::clone(&ext2));
    484
        ///
    485
        /// let ext1_received = cfg.get_extension::<Ext1>().unwrap();
    486
        /// assert!(!Arc::ptr_eq(&ext1_received, &ext1a));
    487
        /// assert!(Arc::ptr_eq(&ext1_received, &ext1b));
    488
        ///
    489
        /// let ext2_received = cfg.get_extension::<Ext2>().unwrap();
    490
        /// assert!(Arc::ptr_eq(&ext2_received, &ext2));
    491
        ///
    492
        /// assert!(cfg.get_extension::<Ext3>().is_none());
    493
        /// ```
    494
        ///
    495
        /// [^1]: Compare that to [`ConfigOptions`] which only supports [`ScalarValue`] payloads.
    496
    0
        pub fn with_extension<T>(mut self, ext: Arc<T>) -> Self
    497
    0
        where
    498
    0
            T: Send + Sync + 'static,
    499
    0
        {
    500
    0
            self.set_extension(ext);
    501
    0
            self
    502
    0
        }
    503
    504
        /// Set extension. Pretty much the same as [`with_extension`](Self::with_extension), but take
    505
        /// mutable reference instead of owning it. Useful if you want to add another extension after
    506
        /// the [`SessionConfig`] is created.
    507
        ///
    508
        /// # Example
    509
        /// ```
    510
        /// use std::sync::Arc;
    511
        /// use datafusion_execution::config::SessionConfig;
    512
        ///
    513
        /// // application-specific extension types
    514
        /// struct Ext1(u8);
    515
        /// struct Ext2(u8);
    516
        /// struct Ext3(u8);
    517
        ///
    518
        /// let ext1a = Arc::new(Ext1(10));
    519
        /// let ext1b = Arc::new(Ext1(11));
    520
        /// let ext2 = Arc::new(Ext2(2));
    521
        ///
    522
        /// let mut cfg = SessionConfig::default();
    523
        ///
    524
        /// // will only remember the last Ext1
    525
        /// cfg.set_extension(Arc::clone(&ext1a));
    526
        /// cfg.set_extension(Arc::clone(&ext1b));
    527
        /// cfg.set_extension(Arc::clone(&ext2));
    528
        ///
    529
        /// let ext1_received = cfg.get_extension::<Ext1>().unwrap();
    530
        /// assert!(!Arc::ptr_eq(&ext1_received, &ext1a));
    531
        /// assert!(Arc::ptr_eq(&ext1_received, &ext1b));
    532
        ///
    533
        /// let ext2_received = cfg.get_extension::<Ext2>().unwrap();
    534
        /// assert!(Arc::ptr_eq(&ext2_received, &ext2));
    535
        ///
    536
        /// assert!(cfg.get_extension::<Ext3>().is_none());
    537
        /// ```
    538
    0
        pub fn set_extension<T>(&mut self, ext: Arc<T>)
    539
    0
        where
    540
    0
            T: Send + Sync + 'static,
    541
    0
        {
    542
    0
            let ext = ext as Arc<dyn Any + Send + Sync + 'static>;
    543
    0
            let id = TypeId::of::<T>();
    544
    0
            self.extensions.insert(id, ext);
    545
    0
        }
    546
    547
        /// Get extension, if any for the specified type `T` exists.
    548
        ///
    549
        /// See [`with_extension`](Self::with_extension) on how to add attach extensions.
    550
    0
        pub fn get_extension<T>(&self) -> Option<Arc<T>>
    551
    0
        where
    552
    0
            T: Send + Sync + 'static,
    553
    0
        {
    554
    0
            let id = TypeId::of::<T>();
    555
    0
            self.extensions
    556
    0
                .get(&id)
    557
    0
                .cloned()
    558
    0
                .map(|ext| Arc::downcast(ext).expect("TypeId unique"))
    559
    0
        }
    560
    }
    561
    562
    impl From<ConfigOptions> for SessionConfig {
    563
    0
        fn from(options: ConfigOptions) -> Self {
    564
    0
            Self {
    565
    0
                options,
    566
    0
                ..Default::default()
    567
    0
            }
    568
    0
        }
    569
    }
    570
    571
    /// Map that holds opaque objects indexed by their type.
    572
    ///
    573
    /// Data is wrapped into an [`Arc`] to enable [`Clone`] while still being [object safe].
    574
    ///
    575
    /// [object safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety
    576
    type AnyMap =
    577
        HashMap<TypeId, Arc<dyn Any + Send + Sync + 'static>, BuildHasherDefault<IdHasher>>;
    578
    579
    /// Hasher for [`AnyMap`].
    580
    ///
    581
    /// With [`TypeId`]s as keys, there's no need to hash them. They are already hashes themselves, coming from the compiler.
    582
    /// The [`IdHasher`] just holds the [`u64`] of the [`TypeId`], and then returns it, instead of doing any bit fiddling.
    583
    #[derive(Default)]
    584
    struct IdHasher(u64);
    585
    586
    impl Hasher for IdHasher {
    587
    0
        fn write(&mut self, _: &[u8]) {
    588
    0
            unreachable!("TypeId calls write_u64");
    589
        }
    590
    591
        #[inline]
    592
    0
        fn write_u64(&mut self, id: u64) {
    593
    0
            self.0 = id;
    594
    0
        }
    595
    596
        #[inline]
    597
    0
        fn finish(&self) -> u64 {
    598
    0
            self.0
    599
    0
        }
    600
    }