## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/common/src/format.rs

    Line
    Count
    Source
    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::fmt::{self, Display};
    19
    use std::str::FromStr;
    20
    21
    use arrow::compute::CastOptions;
    22
    use arrow::util::display::{DurationFormat, FormatOptions};
    23
    24
    use crate::config::{ConfigField, Visit};
    25
    use crate::error::{DataFusionError, Result};
    26
    27
    /// The default [`FormatOptions`] to use within DataFusion
    28
    /// Also see [`crate::config::FormatOptions`]
    29
    pub const DEFAULT_FORMAT_OPTIONS: FormatOptions<'static> =
    30
        FormatOptions::new().with_duration_format(DurationFormat::Pretty);
    31
    32
    /// The default [`CastOptions`] to use within DataFusion
    33
    pub const DEFAULT_CAST_OPTIONS: CastOptions<'static> = CastOptions {
    34
        safe: false,
    35
        format_options: DEFAULT_FORMAT_OPTIONS,
    36
    };
    37
    38
    /// Output formats for controlling for Explain plans
    39
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    40
    pub enum ExplainFormat {
    41
        /// Indent mode
    42
        ///
    43
        /// Example:
    44
        /// ```text
    45
        /// > explain format indent select x from values (1) t(x);
    46
        /// +---------------+-----------------------------------------------------+
    47
        /// | plan_type     | plan                                                |
    48
        /// +---------------+-----------------------------------------------------+
    49
        /// | logical_plan  | SubqueryAlias: t                                    |
    50
        /// |               |   Projection: column1 AS x                          |
    51
        /// |               |     Values: (Int64(1))                              |
    52
        /// | physical_plan | ProjectionExec: expr=[column1@0 as x]               |
    53
        /// |               |   DataSourceExec: partitions=1, partition_sizes=[1] |
    54
        /// |               |                                                     |
    55
        /// +---------------+-----------------------------------------------------+
    56
        /// ```
    57
        Indent,
    58
        /// Tree mode
    59
        ///
    60
        /// Example:
    61
        /// ```text
    62
        /// > explain format tree select x from values (1) t(x);
    63
        /// +---------------+-------------------------------+
    64
        /// | plan_type     | plan                          |
    65
        /// +---------------+-------------------------------+
    66
        /// | physical_plan | ┌───────────────────────────┐ |
    67
        /// |               | │       ProjectionExec      │ |
    68
        /// |               | │    --------------------   │ |
    69
        /// |               | │        x: column1@0       │ |
    70
        /// |               | └─────────────┬─────────────┘ |
    71
        /// |               | ┌─────────────┴─────────────┐ |
    72
        /// |               | │       DataSourceExec      │ |
    73
        /// |               | │    --------------------   │ |
    74
        /// |               | │         bytes: 128        │ |
    75
        /// |               | │       format: memory      │ |
    76
        /// |               | │          rows: 1          │ |
    77
        /// |               | └───────────────────────────┘ |
    78
        /// |               |                               |
    79
        /// +---------------+-------------------------------+
    80
        /// ```
    81
        Tree,
    82
        /// Postgres Json mode
    83
        ///
    84
        /// A displayable structure that produces plan in postgresql JSON format.
    85
        ///
    86
        /// Users can use this format to visualize the plan in existing plan
    87
        /// visualization tools, for example [dalibo](https://explain.dalibo.com/)
    88
        ///
    89
        /// Example:
    90
        /// ```text
    91
        /// > explain format pgjson select x from values (1) t(x);
    92
        /// +--------------+--------------------------------------+
    93
        /// | plan_type    | plan                                 |
    94
        /// +--------------+--------------------------------------+
    95
        /// | logical_plan | [                                    |
    96
        /// |              |   {                                  |
    97
        /// |              |     "Plan": {                        |
    98
        /// |              |       "Alias": "t",                  |
    99
        /// |              |       "Node Type": "Subquery",       |
    100
        /// |              |       "Output": [                    |
    101
        /// |              |         "x"                          |
    102
        /// |              |       ],                             |
    103
        /// |              |       "Plans": [                     |
    104
        /// |              |         {                            |
    105
        /// |              |           "Expressions": [           |
    106
        /// |              |             "column1 AS x"           |
    107
        /// |              |           ],                         |
    108
        /// |              |           "Node Type": "Projection", |
    109
        /// |              |           "Output": [                |
    110
        /// |              |             "x"                      |
    111
        /// |              |           ],                         |
    112
        /// |              |           "Plans": [                 |
    113
        /// |              |             {                        |
    114
        /// |              |               "Node Type": "Values", |
    115
        /// |              |               "Output": [            |
    116
        /// |              |                 "column1"            |
    117
        /// |              |               ],                     |
    118
        /// |              |               "Plans": [],           |
    119
        /// |              |               "Values": "(Int64(1))" |
    120
        /// |              |             }                        |
    121
        /// |              |           ]                          |
    122
        /// |              |         }                            |
    123
        /// |              |       ]                              |
    124
        /// |              |     }                                |
    125
        /// |              |   }                                  |
    126
        /// |              | ]                                    |
    127
        /// +--------------+--------------------------------------+
    128
        /// ```
    129
        PostgresJSON,
    130
        /// Graphviz mode
    131
        ///
    132
        /// Example:
    133
        /// ```text
    134
        /// > explain format graphviz select x from values (1) t(x);
    135
        /// +--------------+------------------------------------------------------------------------+
    136
        /// | plan_type    | plan                                                                   |
    137
        /// +--------------+------------------------------------------------------------------------+
    138
        /// | logical_plan |                                                                        |
    139
        /// |              | // Begin DataFusion GraphViz Plan,                                     |
    140
        /// |              | // display it online here: https://dreampuf.github.io/GraphvizOnline   |
    141
        /// |              |                                                                        |
    142
        /// |              | digraph {                                                              |
    143
        /// |              |   subgraph cluster_1                                                   |
    144
        /// |              |   {                                                                    |
    145
        /// |              |     graph[label="LogicalPlan"]                                         |
    146
        /// |              |     2[shape=box label="SubqueryAlias: t"]                              |
    147
        /// |              |     3[shape=box label="Projection: column1 AS x"]                      |
    148
        /// |              |     2 -> 3 [arrowhead=none, arrowtail=normal, dir=back]                |
    149
        /// |              |     4[shape=box label="Values: (Int64(1))"]                            |
    150
        /// |              |     3 -> 4 [arrowhead=none, arrowtail=normal, dir=back]                |
    151
        /// |              |   }                                                                    |
    152
        /// |              |   subgraph cluster_5                                                   |
    153
        /// |              |   {                                                                    |
    154
        /// |              |     graph[label="Detailed LogicalPlan"]                                |
    155
        /// |              |     6[shape=box label="SubqueryAlias: t\nSchema: [x:Int64;N]"]         |
    156
        /// |              |     7[shape=box label="Projection: column1 AS x\nSchema: [x:Int64;N]"] |
    157
        /// |              |     6 -> 7 [arrowhead=none, arrowtail=normal, dir=back]                |
    158
        /// |              |     8[shape=box label="Values: (Int64(1))\nSchema: [column1:Int64;N]"] |
    159
        /// |              |     7 -> 8 [arrowhead=none, arrowtail=normal, dir=back]                |
    160
        /// |              |   }                                                                    |
    161
        /// |              | }                                                                      |
    162
        /// |              | // End DataFusion GraphViz Plan                                        |
    163
        /// |              |                                                                        |
    164
        /// +--------------+------------------------------------------------------------------------+
    165
        /// ```
    166
        Graphviz,
    167
    }
    168
    169
    /// Implement  parsing strings to `ExplainFormat`
    170
    impl FromStr for ExplainFormat {
    171
        type Err = DataFusionError;
    172
    173
    0
        fn from_str(format: &str) -> Result<Self, Self::Err> {
    174
    0
            match format.to_lowercase().as_str() {
    175
    0
                "indent" => Ok(ExplainFormat::Indent),
    176
    0
                "tree" => Ok(ExplainFormat::Tree),
    177
    0
                "pgjson" => Ok(ExplainFormat::PostgresJSON),
    178
    0
                "graphviz" => Ok(ExplainFormat::Graphviz),
    179
                _ => {
    180
    0
                    Err(DataFusionError::Configuration(format!("Invalid explain format. Expected 'indent', 'tree', 'pgjson' or 'graphviz'. Got '{format}'")))
    181
                }
    182
            }
    183
    0
        }
    184
    }
    185
    186
    impl Display for ExplainFormat {
    187
    0
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    188
    0
            let s = match self {
    189
    0
                ExplainFormat::Indent => "indent",
    190
    0
                ExplainFormat::Tree => "tree",
    191
    0
                ExplainFormat::PostgresJSON => "pgjson",
    192
    0
                ExplainFormat::Graphviz => "graphviz",
    193
            };
    194
    0
            write!(f, "{s}")
    195
    0
        }
    196
    }
    197
    198
    impl ConfigField for ExplainFormat {
    199
    0
        fn visit<V: Visit>(&self, v: &mut V, key: &str, description: &'static str) {
    200
    0
            v.some(key, self, description)
    201
    0
        }
    202
    203
    0
        fn set(&mut self, _: &str, value: &str) -> Result<()> {
    204
    0
            *self = ExplainFormat::from_str(value)?;
    205
    0
            Ok(())
    206
    0
        }
    207
    }
    208
    209
    /// Verbosity levels controlling how `EXPLAIN ANALYZE` renders metrics
    210
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    211
    pub enum ExplainAnalyzeLevel {
    212
        /// Show a compact view containing high-level metrics
    213
        Summary,
    214
        /// Show a developer-focused view with per-operator details
    215
        Dev,
    216
        // When adding new enum, update the error message in `from_str()` accordingly.
    217
    }
    218
    219
    impl FromStr for ExplainAnalyzeLevel {
    220
        type Err = DataFusionError;
    221
    222
    0
        fn from_str(level: &str) -> Result<Self, Self::Err> {
    223
    0
            match level.to_lowercase().as_str() {
    224
    0
                "summary" => Ok(ExplainAnalyzeLevel::Summary),
    225
    0
                "dev" => Ok(ExplainAnalyzeLevel::Dev),
    226
    0
                other => Err(DataFusionError::Configuration(format!(
    227
    0
                    "Invalid explain analyze level. Expected 'summary' or 'dev'. Got '{other}'"
    228
    0
                ))),
    229
            }
    230
    0
        }
    231
    }
    232
    233
    impl Display for ExplainAnalyzeLevel {
    234
    0
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    235
    0
            let s = match self {
    236
    0
                ExplainAnalyzeLevel::Summary => "summary",
    237
    0
                ExplainAnalyzeLevel::Dev => "dev",
    238
            };
    239
    0
            write!(f, "{s}")
    240
    0
        }
    241
    }
    242
    243
    impl ConfigField for ExplainAnalyzeLevel {
    244
    0
        fn visit<V: Visit>(&self, v: &mut V, key: &str, description: &'static str) {
    245
    0
            v.some(key, self, description)
    246
    0
        }
    247
    248
    0
        fn set(&mut self, _: &str, value: &str) -> Result<()> {
    249
    0
            *self = ExplainAnalyzeLevel::from_str(value)?;
    250
    0
            Ok(())
    251
    0
        }
    252
    }