sqlparser/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(not(feature = "std"))]
14use alloc::string::String;
15use core::fmt;
16
17#[cfg(feature = "bigdecimal")]
18use bigdecimal::BigDecimal;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23#[cfg(feature = "visitor")]
24use sqlparser_derive::{Visit, VisitMut};
25
26/// Primitive SQL values such as number and string
27#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
30pub enum Value {
31    /// Numeric literal
32    #[cfg(not(feature = "bigdecimal"))]
33    Number(String, bool),
34    #[cfg(feature = "bigdecimal")]
35    // HINT: use `test_utils::number` to make an instance of
36    // Value::Number This might help if you your tests pass locally
37    // but fail on CI with the `--all-features` flag enabled
38    Number(BigDecimal, bool),
39    /// 'string value'
40    SingleQuotedString(String),
41    // $<tag_name>$string value$<tag_name>$ (postgres syntax)
42    DollarQuotedString(DollarQuotedString),
43    /// e'string value' (postgres extension)
44    /// See [Postgres docs](https://www.postgresql.org/docs/8.3/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS)
45    /// for more details.
46    EscapedStringLiteral(String),
47    /// B'string value'
48    SingleQuotedByteStringLiteral(String),
49    /// B"string value"
50    DoubleQuotedByteStringLiteral(String),
51    /// R'string value' or r'string value' or r"string value"
52    /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals>
53    RawStringLiteral(String),
54    /// N'string value'
55    NationalStringLiteral(String),
56    /// X'hex value'
57    HexStringLiteral(String),
58
59    DoubleQuotedString(String),
60    /// Boolean value true or false
61    Boolean(bool),
62    /// `NULL` value
63    Null,
64    /// `?` or `$` Prepared statement arg placeholder
65    Placeholder(String),
66    /// Add support of snowflake field:key - key should be a value
67    UnQuotedString(String),
68}
69
70impl fmt::Display for Value {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        match self {
73            Value::Number(v, l) => write!(f, "{}{long}", v, long = if *l { "L" } else { "" }),
74            Value::DoubleQuotedString(v) => write!(f, "\"{}\"", escape_double_quote_string(v)),
75            Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
76            Value::DollarQuotedString(v) => write!(f, "{v}"),
77            Value::EscapedStringLiteral(v) => write!(f, "E'{}'", escape_escaped_string(v)),
78            Value::NationalStringLiteral(v) => write!(f, "N'{v}'"),
79            Value::HexStringLiteral(v) => write!(f, "X'{v}'"),
80            Value::Boolean(v) => write!(f, "{v}"),
81            Value::SingleQuotedByteStringLiteral(v) => write!(f, "B'{v}'"),
82            Value::DoubleQuotedByteStringLiteral(v) => write!(f, "B\"{v}\""),
83            Value::RawStringLiteral(v) => write!(f, "R'{v}'"),
84            Value::Null => write!(f, "NULL"),
85            Value::Placeholder(v) => write!(f, "{v}"),
86            Value::UnQuotedString(v) => write!(f, "{v}"),
87        }
88    }
89}
90
91#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
92#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
93#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
94pub struct DollarQuotedString {
95    pub value: String,
96    pub tag: Option<String>,
97}
98
99impl fmt::Display for DollarQuotedString {
100    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101        match &self.tag {
102            Some(tag) => {
103                write!(f, "${}${}${}$", tag, self.value, tag)
104            }
105            None => {
106                write!(f, "$${}$$", self.value)
107            }
108        }
109    }
110}
111
112#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
113#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
114#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
115pub enum DateTimeField {
116    Year,
117    Month,
118    Week,
119    Day,
120    DayOfWeek,
121    DayOfYear,
122    Date,
123    Hour,
124    Minute,
125    Second,
126    Century,
127    Decade,
128    Dow,
129    Doy,
130    Epoch,
131    Isodow,
132    IsoWeek,
133    Isoyear,
134    Julian,
135    Microsecond,
136    Microseconds,
137    Millenium,
138    Millennium,
139    Millisecond,
140    Milliseconds,
141    Nanosecond,
142    Nanoseconds,
143    Quarter,
144    Time,
145    Timezone,
146    TimezoneHour,
147    TimezoneMinute,
148    NoDateTime,
149}
150
151impl fmt::Display for DateTimeField {
152    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153        f.write_str(match self {
154            DateTimeField::Year => "YEAR",
155            DateTimeField::Month => "MONTH",
156            DateTimeField::Week => "WEEK",
157            DateTimeField::Day => "DAY",
158            DateTimeField::DayOfWeek => "DAYOFWEEK",
159            DateTimeField::DayOfYear => "DAYOFYEAR",
160            DateTimeField::Date => "DATE",
161            DateTimeField::Hour => "HOUR",
162            DateTimeField::Minute => "MINUTE",
163            DateTimeField::Second => "SECOND",
164            DateTimeField::Century => "CENTURY",
165            DateTimeField::Decade => "DECADE",
166            DateTimeField::Dow => "DOW",
167            DateTimeField::Doy => "DOY",
168            DateTimeField::Epoch => "EPOCH",
169            DateTimeField::Isodow => "ISODOW",
170            DateTimeField::Isoyear => "ISOYEAR",
171            DateTimeField::IsoWeek => "ISOWEEK",
172            DateTimeField::Julian => "JULIAN",
173            DateTimeField::Microsecond => "MICROSECOND",
174            DateTimeField::Microseconds => "MICROSECONDS",
175            DateTimeField::Millenium => "MILLENIUM",
176            DateTimeField::Millennium => "MILLENNIUM",
177            DateTimeField::Millisecond => "MILLISECOND",
178            DateTimeField::Milliseconds => "MILLISECONDS",
179            DateTimeField::Nanosecond => "NANOSECOND",
180            DateTimeField::Nanoseconds => "NANOSECONDS",
181            DateTimeField::Quarter => "QUARTER",
182            DateTimeField::Time => "TIME",
183            DateTimeField::Timezone => "TIMEZONE",
184            DateTimeField::TimezoneHour => "TIMEZONE_HOUR",
185            DateTimeField::TimezoneMinute => "TIMEZONE_MINUTE",
186            DateTimeField::NoDateTime => "NODATETIME",
187        })
188    }
189}
190
191pub struct EscapeQuotedString<'a> {
192    string: &'a str,
193    quote: char,
194}
195
196impl<'a> fmt::Display for EscapeQuotedString<'a> {
197    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
198        // EscapeQuotedString doesn't know which mode of escape was
199        // chosen by the user. So this code must to correctly display
200        // strings without knowing if the strings are already escaped
201        // or not.
202        //
203        // If the quote symbol in the string is repeated twice, OR, if
204        // the quote symbol is after backslash, display all the chars
205        // without any escape. However, if the quote symbol is used
206        // just between usual chars, `fmt()` should display it twice."
207        //
208        // The following table has examples
209        //
210        // | original query | mode      | AST Node                                           | serialized   |
211        // | -------------  | --------- | -------------------------------------------------- | ------------ |
212        // | `"A""B""A"`    | no-escape | `DoubleQuotedString(String::from("A\"\"B\"\"A"))`  | `"A""B""A"`  |
213        // | `"A""B""A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
214        // | `"A\"B\"A"`    | no-escape | `DoubleQuotedString(String::from("A\\\"B\\\"A"))`  | `"A\"B\"A"`  |
215        // | `"A\"B\"A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
216        let quote = self.quote;
217        let mut previous_char = char::default();
218        let mut peekable_chars = self.string.chars().peekable();
219        while let Some(&ch) = peekable_chars.peek() {
220            match ch {
221                char if char == quote => {
222                    if previous_char == '\\' {
223                        write!(f, "{char}")?;
224                        peekable_chars.next();
225                        continue;
226                    }
227                    peekable_chars.next();
228                    if peekable_chars.peek().map(|c| *c == quote).unwrap_or(false) {
229                        write!(f, "{char}{char}")?;
230                        peekable_chars.next();
231                    } else {
232                        write!(f, "{char}{char}")?;
233                    }
234                }
235                _ => {
236                    write!(f, "{ch}")?;
237                    peekable_chars.next();
238                }
239            }
240            previous_char = ch;
241        }
242        Ok(())
243    }
244}
245
246pub fn escape_quoted_string(string: &str, quote: char) -> EscapeQuotedString<'_> {
247    EscapeQuotedString { string, quote }
248}
249
250pub fn escape_single_quote_string(s: &str) -> EscapeQuotedString<'_> {
251    escape_quoted_string(s, '\'')
252}
253
254pub fn escape_double_quote_string(s: &str) -> EscapeQuotedString<'_> {
255    escape_quoted_string(s, '\"')
256}
257
258pub struct EscapeEscapedStringLiteral<'a>(&'a str);
259
260impl<'a> fmt::Display for EscapeEscapedStringLiteral<'a> {
261    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262        for c in self.0.chars() {
263            match c {
264                '\'' => {
265                    write!(f, r#"\'"#)?;
266                }
267                '\\' => {
268                    write!(f, r#"\\"#)?;
269                }
270                '\n' => {
271                    write!(f, r#"\n"#)?;
272                }
273                '\t' => {
274                    write!(f, r#"\t"#)?;
275                }
276                '\r' => {
277                    write!(f, r#"\r"#)?;
278                }
279                _ => {
280                    write!(f, "{c}")?;
281                }
282            }
283        }
284        Ok(())
285    }
286}
287
288pub fn escape_escaped_string(s: &str) -> EscapeEscapedStringLiteral<'_> {
289    EscapeEscapedStringLiteral(s)
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
293#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
294#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
295pub enum TrimWhereField {
296    Both,
297    Leading,
298    Trailing,
299}
300
301impl fmt::Display for TrimWhereField {
302    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303        use TrimWhereField::*;
304        f.write_str(match self {
305            Both => "BOTH",
306            Leading => "LEADING",
307            Trailing => "TRAILING",
308        })
309    }
310}