sql_ast/ast/
value.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13#[cfg(feature = "bigdecimal")]
14use bigdecimal::BigDecimal;
15use std::fmt;
16
17/// Primitive SQL values such as number and string
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub enum Value {
20    /// Numeric literal
21    #[cfg(not(feature = "bigdecimal"))]
22    Number(String),
23    #[cfg(feature = "bigdecimal")]
24    Number(BigDecimal),
25    /// 'string value'
26    SingleQuotedString(String),
27    /// N'string value'
28    NationalStringLiteral(String),
29    /// X'hex value'
30    HexStringLiteral(String),
31    /// Boolean value true or false
32    Boolean(bool),
33    /// `DATE '...'` literals
34    Date(String),
35    /// `TIME '...'` literals
36    Time(String),
37    /// `TIMESTAMP '...'` literals
38    Timestamp(String),
39    /// INTERVAL literals, roughly in the following format:
40    /// `INTERVAL '<value>' <leading_field> [ (<leading_precision>) ]
41    /// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
42    /// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
43    ///
44    /// The parser does not validate the `<value>`, nor does it ensure
45    /// that the `<leading_field>` units >= the units in `<last_field>`,
46    /// so the user will have to reject intervals like `HOUR TO YEAR`.
47    Interval {
48        value: String,
49        leading_field: DateTimeField,
50        leading_precision: Option<u64>,
51        last_field: Option<DateTimeField>,
52        /// The seconds precision can be specified in SQL source as
53        /// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field`
54        /// will be `Second` and the `last_field` will be `None`),
55        /// or as `__ TO SECOND(x)`.
56        fractional_seconds_precision: Option<u64>,
57    },
58    /// `NULL` value
59    Null,
60    /// use the default value
61    Default,
62}
63
64impl fmt::Display for Value {
65    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66        match self {
67            Value::Number(v) => write!(f, "{}", v),
68            Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
69            Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
70            Value::HexStringLiteral(v) => write!(f, "X'{}'", v),
71            Value::Boolean(v) => write!(f, "{}", v),
72            Value::Date(v) => write!(f, "DATE '{}'", escape_single_quote_string(v)),
73            Value::Time(v) => write!(f, "TIME '{}'", escape_single_quote_string(v)),
74            Value::Timestamp(v) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)),
75            Value::Interval {
76                value,
77                leading_field: DateTimeField::Second,
78                leading_precision: Some(leading_precision),
79                last_field,
80                fractional_seconds_precision: Some(fractional_seconds_precision),
81            } => {
82                // When the leading field is SECOND, the parser guarantees that
83                // the last field is None.
84                assert!(last_field.is_none());
85                write!(
86                    f,
87                    "INTERVAL '{}' SECOND ({}, {})",
88                    escape_single_quote_string(value),
89                    leading_precision,
90                    fractional_seconds_precision
91                )
92            }
93            Value::Interval {
94                value,
95                leading_field,
96                leading_precision,
97                last_field,
98                fractional_seconds_precision,
99            } => {
100                write!(
101                    f,
102                    "INTERVAL '{}' {}",
103                    escape_single_quote_string(value),
104                    leading_field
105                )?;
106                if let Some(leading_precision) = leading_precision {
107                    write!(f, " ({})", leading_precision)?;
108                }
109                if let Some(last_field) = last_field {
110                    write!(f, " TO {}", last_field)?;
111                }
112                if let Some(fractional_seconds_precision) = fractional_seconds_precision {
113                    write!(f, " ({})", fractional_seconds_precision)?;
114                }
115                Ok(())
116            }
117            Value::Null => write!(f, "NULL"),
118            Value::Default => write!(f, "DEFAULT"),
119        }
120    }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Hash)]
124pub enum DateTimeField {
125    Year,
126    Month,
127    Day,
128    Hour,
129    Minute,
130    Second,
131}
132
133impl fmt::Display for DateTimeField {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        f.write_str(match self {
136            DateTimeField::Year => "YEAR",
137            DateTimeField::Month => "MONTH",
138            DateTimeField::Day => "DAY",
139            DateTimeField::Hour => "HOUR",
140            DateTimeField::Minute => "MINUTE",
141            DateTimeField::Second => "SECOND",
142        })
143    }
144}
145
146pub struct EscapeSingleQuoteString<'a>(&'a str);
147
148impl<'a> fmt::Display for EscapeSingleQuoteString<'a> {
149    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150        for c in self.0.chars() {
151            if c == '\'' {
152                write!(f, "\'\'")?;
153            } else {
154                write!(f, "{}", c)?;
155            }
156        }
157        Ok(())
158    }
159}
160
161pub fn escape_single_quote_string(s: &str) -> EscapeSingleQuoteString<'_> {
162    EscapeSingleQuoteString(s)
163}