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    TimezoneAbbr,
147    TimezoneHour,
148    TimezoneMinute,
149    TimezoneRegion,
150    NoDateTime,
151}
152
153impl fmt::Display for DateTimeField {
154    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155        f.write_str(match self {
156            DateTimeField::Year => "YEAR",
157            DateTimeField::Month => "MONTH",
158            DateTimeField::Week => "WEEK",
159            DateTimeField::Day => "DAY",
160            DateTimeField::DayOfWeek => "DAYOFWEEK",
161            DateTimeField::DayOfYear => "DAYOFYEAR",
162            DateTimeField::Date => "DATE",
163            DateTimeField::Hour => "HOUR",
164            DateTimeField::Minute => "MINUTE",
165            DateTimeField::Second => "SECOND",
166            DateTimeField::Century => "CENTURY",
167            DateTimeField::Decade => "DECADE",
168            DateTimeField::Dow => "DOW",
169            DateTimeField::Doy => "DOY",
170            DateTimeField::Epoch => "EPOCH",
171            DateTimeField::Isodow => "ISODOW",
172            DateTimeField::Isoyear => "ISOYEAR",
173            DateTimeField::IsoWeek => "ISOWEEK",
174            DateTimeField::Julian => "JULIAN",
175            DateTimeField::Microsecond => "MICROSECOND",
176            DateTimeField::Microseconds => "MICROSECONDS",
177            DateTimeField::Millenium => "MILLENIUM",
178            DateTimeField::Millennium => "MILLENNIUM",
179            DateTimeField::Millisecond => "MILLISECOND",
180            DateTimeField::Milliseconds => "MILLISECONDS",
181            DateTimeField::Nanosecond => "NANOSECOND",
182            DateTimeField::Nanoseconds => "NANOSECONDS",
183            DateTimeField::Quarter => "QUARTER",
184            DateTimeField::Time => "TIME",
185            DateTimeField::Timezone => "TIMEZONE",
186            DateTimeField::TimezoneAbbr => "TIMEZONE_ABBR",
187            DateTimeField::TimezoneHour => "TIMEZONE_HOUR",
188            DateTimeField::TimezoneMinute => "TIMEZONE_MINUTE",
189            DateTimeField::TimezoneRegion => "TIMEZONE_REGION",
190            DateTimeField::NoDateTime => "NODATETIME",
191        })
192    }
193}
194
195pub struct EscapeQuotedString<'a> {
196    string: &'a str,
197    quote: char,
198}
199
200impl<'a> fmt::Display for EscapeQuotedString<'a> {
201    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202        // EscapeQuotedString doesn't know which mode of escape was
203        // chosen by the user. So this code must to correctly display
204        // strings without knowing if the strings are already escaped
205        // or not.
206        //
207        // If the quote symbol in the string is repeated twice, OR, if
208        // the quote symbol is after backslash, display all the chars
209        // without any escape. However, if the quote symbol is used
210        // just between usual chars, `fmt()` should display it twice."
211        //
212        // The following table has examples
213        //
214        // | original query | mode      | AST Node                                           | serialized   |
215        // | -------------  | --------- | -------------------------------------------------- | ------------ |
216        // | `"A""B""A"`    | no-escape | `DoubleQuotedString(String::from("A\"\"B\"\"A"))`  | `"A""B""A"`  |
217        // | `"A""B""A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
218        // | `"A\"B\"A"`    | no-escape | `DoubleQuotedString(String::from("A\\\"B\\\"A"))`  | `"A\"B\"A"`  |
219        // | `"A\"B\"A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
220        let quote = self.quote;
221        let mut previous_char = char::default();
222        let mut peekable_chars = self.string.chars().peekable();
223        while let Some(&ch) = peekable_chars.peek() {
224            match ch {
225                char if char == quote => {
226                    if previous_char == '\\' {
227                        write!(f, "{char}")?;
228                        peekable_chars.next();
229                        continue;
230                    }
231                    peekable_chars.next();
232                    if peekable_chars.peek().map(|c| *c == quote).unwrap_or(false) {
233                        write!(f, "{char}{char}")?;
234                        peekable_chars.next();
235                    } else {
236                        write!(f, "{char}{char}")?;
237                    }
238                }
239                _ => {
240                    write!(f, "{ch}")?;
241                    peekable_chars.next();
242                }
243            }
244            previous_char = ch;
245        }
246        Ok(())
247    }
248}
249
250pub fn escape_quoted_string(string: &str, quote: char) -> EscapeQuotedString<'_> {
251    EscapeQuotedString { string, quote }
252}
253
254pub fn escape_single_quote_string(s: &str) -> EscapeQuotedString<'_> {
255    escape_quoted_string(s, '\'')
256}
257
258pub fn escape_double_quote_string(s: &str) -> EscapeQuotedString<'_> {
259    escape_quoted_string(s, '\"')
260}
261
262pub struct EscapeEscapedStringLiteral<'a>(&'a str);
263
264impl<'a> fmt::Display for EscapeEscapedStringLiteral<'a> {
265    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266        for c in self.0.chars() {
267            match c {
268                '\'' => {
269                    write!(f, r#"\'"#)?;
270                }
271                '\\' => {
272                    write!(f, r#"\\"#)?;
273                }
274                '\n' => {
275                    write!(f, r#"\n"#)?;
276                }
277                '\t' => {
278                    write!(f, r#"\t"#)?;
279                }
280                '\r' => {
281                    write!(f, r#"\r"#)?;
282                }
283                _ => {
284                    write!(f, "{c}")?;
285                }
286            }
287        }
288        Ok(())
289    }
290}
291
292pub fn escape_escaped_string(s: &str) -> EscapeEscapedStringLiteral<'_> {
293    EscapeEscapedStringLiteral(s)
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
297#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
298#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
299pub enum TrimWhereField {
300    Both,
301    Leading,
302    Trailing,
303}
304
305impl fmt::Display for TrimWhereField {
306    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307        use TrimWhereField::*;
308        f.write_str(match self {
309            Both => "BOTH",
310            Leading => "LEADING",
311            Trailing => "TRAILING",
312        })
313    }
314}