sqltk_parser/ast/
value.rs

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#[cfg(not(feature = "std"))]
19use alloc::string::String;
20
21use core::fmt;
22
23#[cfg(feature = "bigdecimal")]
24use bigdecimal::BigDecimal;
25
26#[cfg(feature = "serde")]
27use serde::{Deserialize, Serialize};
28
29use crate::ast::Ident;
30#[cfg(feature = "visitor")]
31use sqltk_parser_derive::{Visit, VisitMut};
32
33/// Primitive SQL values such as number and string
34#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
37pub enum Value {
38    /// Numeric literal
39    #[cfg(not(feature = "bigdecimal"))]
40    Number(String, bool),
41    #[cfg(feature = "bigdecimal")]
42    // HINT: use `test_utils::number` to make an instance of
43    // Value::Number This might help if you your tests pass locally
44    // but fail on CI with the `--all-features` flag enabled
45    Number(BigDecimal, bool),
46    /// 'string value'
47    SingleQuotedString(String),
48    // $<tag_name>$string value$<tag_name>$ (postgres syntax)
49    DollarQuotedString(DollarQuotedString),
50    /// Triple single quoted strings: Example '''abc'''
51    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
52    TripleSingleQuotedString(String),
53    /// Triple double quoted strings: Example """abc"""
54    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
55    TripleDoubleQuotedString(String),
56    /// e'string value' (postgres extension)
57    /// See [Postgres docs](https://www.postgresql.org/docs/8.3/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS)
58    /// for more details.
59    EscapedStringLiteral(String),
60    /// u&'string value' (postgres extension)
61    /// See [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-UESCAPE)
62    /// for more details.
63    UnicodeStringLiteral(String),
64    /// B'string value'
65    SingleQuotedByteStringLiteral(String),
66    /// B"string value"
67    DoubleQuotedByteStringLiteral(String),
68    /// Triple single quoted literal with byte string prefix. Example `B'''abc'''`
69    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
70    TripleSingleQuotedByteStringLiteral(String),
71    /// Triple double quoted literal with byte string prefix. Example `B"""abc"""`
72    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
73    TripleDoubleQuotedByteStringLiteral(String),
74    /// Single quoted literal with raw string prefix. Example `R'abc'`
75    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
76    SingleQuotedRawStringLiteral(String),
77    /// Double quoted literal with raw string prefix. Example `R"abc"`
78    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
79    DoubleQuotedRawStringLiteral(String),
80    /// Triple single quoted literal with raw string prefix. Example `R'''abc'''`
81    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
82    TripleSingleQuotedRawStringLiteral(String),
83    /// Triple double quoted literal with raw string prefix. Example `R"""abc"""`
84    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
85    TripleDoubleQuotedRawStringLiteral(String),
86    /// N'string value'
87    NationalStringLiteral(String),
88    /// X'hex value'
89    HexStringLiteral(String),
90
91    DoubleQuotedString(String),
92    /// Boolean value true or false
93    Boolean(bool),
94    /// `NULL` value
95    Null,
96    /// `?` or `$` Prepared statement arg placeholder
97    Placeholder(String),
98}
99
100impl fmt::Display for Value {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        match self {
103            Value::Number(v, l) => write!(f, "{}{long}", v, long = if *l { "L" } else { "" }),
104            Value::DoubleQuotedString(v) => write!(f, "\"{}\"", escape_double_quote_string(v)),
105            Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
106            Value::TripleSingleQuotedString(v) => {
107                write!(f, "'''{v}'''")
108            }
109            Value::TripleDoubleQuotedString(v) => {
110                write!(f, r#""""{v}""""#)
111            }
112            Value::DollarQuotedString(v) => write!(f, "{v}"),
113            Value::EscapedStringLiteral(v) => write!(f, "E'{}'", escape_escaped_string(v)),
114            Value::UnicodeStringLiteral(v) => write!(f, "U&'{}'", escape_unicode_string(v)),
115            Value::NationalStringLiteral(v) => write!(f, "N'{v}'"),
116            Value::HexStringLiteral(v) => write!(f, "X'{v}'"),
117            Value::Boolean(v) => write!(f, "{v}"),
118            Value::SingleQuotedByteStringLiteral(v) => write!(f, "B'{v}'"),
119            Value::DoubleQuotedByteStringLiteral(v) => write!(f, "B\"{v}\""),
120            Value::TripleSingleQuotedByteStringLiteral(v) => write!(f, "B'''{v}'''"),
121            Value::TripleDoubleQuotedByteStringLiteral(v) => write!(f, r#"B"""{v}""""#),
122            Value::SingleQuotedRawStringLiteral(v) => write!(f, "R'{v}'"),
123            Value::DoubleQuotedRawStringLiteral(v) => write!(f, "R\"{v}\""),
124            Value::TripleSingleQuotedRawStringLiteral(v) => write!(f, "R'''{v}'''"),
125            Value::TripleDoubleQuotedRawStringLiteral(v) => write!(f, r#"R"""{v}""""#),
126            Value::Null => write!(f, "NULL"),
127            Value::Placeholder(v) => write!(f, "{v}"),
128        }
129    }
130}
131
132#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
133#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
134#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
135pub struct DollarQuotedString {
136    pub value: String,
137    pub tag: Option<String>,
138}
139
140impl fmt::Display for DollarQuotedString {
141    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142        match &self.tag {
143            Some(tag) => {
144                write!(f, "${}${}${}$", tag, self.value, tag)
145            }
146            None => {
147                write!(f, "$${}$$", self.value)
148            }
149        }
150    }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
154#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
155#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
156pub enum DateTimeField {
157    Year,
158    Month,
159    /// Week optionally followed by a WEEKDAY.
160    ///
161    /// ```sql
162    /// WEEK(MONDAY)
163    /// ```
164    ///
165    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions#extract)
166    Week(Option<Ident>),
167    Day,
168    DayOfWeek,
169    DayOfYear,
170    Date,
171    Datetime,
172    Hour,
173    Minute,
174    Second,
175    Century,
176    Decade,
177    Dow,
178    Doy,
179    Epoch,
180    Isodow,
181    IsoWeek,
182    Isoyear,
183    Julian,
184    Microsecond,
185    Microseconds,
186    Millenium,
187    Millennium,
188    Millisecond,
189    Milliseconds,
190    Nanosecond,
191    Nanoseconds,
192    Quarter,
193    Time,
194    Timezone,
195    TimezoneAbbr,
196    TimezoneHour,
197    TimezoneMinute,
198    TimezoneRegion,
199    NoDateTime,
200    /// Arbitrary abbreviation or custom date-time part.
201    ///
202    /// ```sql
203    /// EXTRACT(q FROM CURRENT_TIMESTAMP)
204    /// ```
205    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions-date-time#supported-date-and-time-parts)
206    Custom(Ident),
207}
208
209impl fmt::Display for DateTimeField {
210    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        match self {
212            DateTimeField::Year => write!(f, "YEAR"),
213            DateTimeField::Month => write!(f, "MONTH"),
214            DateTimeField::Week(week_day) => {
215                write!(f, "WEEK")?;
216                if let Some(week_day) = week_day {
217                    write!(f, "({week_day})")?
218                }
219                Ok(())
220            }
221            DateTimeField::Day => write!(f, "DAY"),
222            DateTimeField::DayOfWeek => write!(f, "DAYOFWEEK"),
223            DateTimeField::DayOfYear => write!(f, "DAYOFYEAR"),
224            DateTimeField::Date => write!(f, "DATE"),
225            DateTimeField::Datetime => write!(f, "DATETIME"),
226            DateTimeField::Hour => write!(f, "HOUR"),
227            DateTimeField::Minute => write!(f, "MINUTE"),
228            DateTimeField::Second => write!(f, "SECOND"),
229            DateTimeField::Century => write!(f, "CENTURY"),
230            DateTimeField::Decade => write!(f, "DECADE"),
231            DateTimeField::Dow => write!(f, "DOW"),
232            DateTimeField::Doy => write!(f, "DOY"),
233            DateTimeField::Epoch => write!(f, "EPOCH"),
234            DateTimeField::Isodow => write!(f, "ISODOW"),
235            DateTimeField::Isoyear => write!(f, "ISOYEAR"),
236            DateTimeField::IsoWeek => write!(f, "ISOWEEK"),
237            DateTimeField::Julian => write!(f, "JULIAN"),
238            DateTimeField::Microsecond => write!(f, "MICROSECOND"),
239            DateTimeField::Microseconds => write!(f, "MICROSECONDS"),
240            DateTimeField::Millenium => write!(f, "MILLENIUM"),
241            DateTimeField::Millennium => write!(f, "MILLENNIUM"),
242            DateTimeField::Millisecond => write!(f, "MILLISECOND"),
243            DateTimeField::Milliseconds => write!(f, "MILLISECONDS"),
244            DateTimeField::Nanosecond => write!(f, "NANOSECOND"),
245            DateTimeField::Nanoseconds => write!(f, "NANOSECONDS"),
246            DateTimeField::Quarter => write!(f, "QUARTER"),
247            DateTimeField::Time => write!(f, "TIME"),
248            DateTimeField::Timezone => write!(f, "TIMEZONE"),
249            DateTimeField::TimezoneAbbr => write!(f, "TIMEZONE_ABBR"),
250            DateTimeField::TimezoneHour => write!(f, "TIMEZONE_HOUR"),
251            DateTimeField::TimezoneMinute => write!(f, "TIMEZONE_MINUTE"),
252            DateTimeField::TimezoneRegion => write!(f, "TIMEZONE_REGION"),
253            DateTimeField::NoDateTime => write!(f, "NODATETIME"),
254            DateTimeField::Custom(custom) => write!(f, "{custom}"),
255        }
256    }
257}
258
259pub struct EscapeQuotedString<'a> {
260    string: &'a str,
261    quote: char,
262}
263
264impl fmt::Display for EscapeQuotedString<'_> {
265    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266        // EscapeQuotedString doesn't know which mode of escape was
267        // chosen by the user. So this code must to correctly display
268        // strings without knowing if the strings are already escaped
269        // or not.
270        //
271        // If the quote symbol in the string is repeated twice, OR, if
272        // the quote symbol is after backslash, display all the chars
273        // without any escape. However, if the quote symbol is used
274        // just between usual chars, `fmt()` should display it twice."
275        //
276        // The following table has examples
277        //
278        // | original query | mode      | AST Node                                           | serialized   |
279        // | -------------  | --------- | -------------------------------------------------- | ------------ |
280        // | `"A""B""A"`    | no-escape | `DoubleQuotedString(String::from("A\"\"B\"\"A"))`  | `"A""B""A"`  |
281        // | `"A""B""A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
282        // | `"A\"B\"A"`    | no-escape | `DoubleQuotedString(String::from("A\\\"B\\\"A"))`  | `"A\"B\"A"`  |
283        // | `"A\"B\"A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
284        let quote = self.quote;
285        let mut previous_char = char::default();
286        let mut peekable_chars = self.string.chars().peekable();
287        while let Some(&ch) = peekable_chars.peek() {
288            match ch {
289                char if char == quote => {
290                    if previous_char == '\\' {
291                        write!(f, "{char}")?;
292                        peekable_chars.next();
293                        continue;
294                    }
295                    peekable_chars.next();
296                    if peekable_chars.peek().map(|c| *c == quote).unwrap_or(false) {
297                        write!(f, "{char}{char}")?;
298                        peekable_chars.next();
299                    } else {
300                        write!(f, "{char}{char}")?;
301                    }
302                }
303                _ => {
304                    write!(f, "{ch}")?;
305                    peekable_chars.next();
306                }
307            }
308            previous_char = ch;
309        }
310        Ok(())
311    }
312}
313
314pub fn escape_quoted_string(string: &str, quote: char) -> EscapeQuotedString<'_> {
315    EscapeQuotedString { string, quote }
316}
317
318pub fn escape_single_quote_string(s: &str) -> EscapeQuotedString<'_> {
319    escape_quoted_string(s, '\'')
320}
321
322pub fn escape_double_quote_string(s: &str) -> EscapeQuotedString<'_> {
323    escape_quoted_string(s, '\"')
324}
325
326pub struct EscapeEscapedStringLiteral<'a>(&'a str);
327
328impl fmt::Display for EscapeEscapedStringLiteral<'_> {
329    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
330        for c in self.0.chars() {
331            match c {
332                '\'' => {
333                    write!(f, r#"\'"#)?;
334                }
335                '\\' => {
336                    write!(f, r#"\\"#)?;
337                }
338                '\n' => {
339                    write!(f, r#"\n"#)?;
340                }
341                '\t' => {
342                    write!(f, r#"\t"#)?;
343                }
344                '\r' => {
345                    write!(f, r#"\r"#)?;
346                }
347                _ => {
348                    write!(f, "{c}")?;
349                }
350            }
351        }
352        Ok(())
353    }
354}
355
356pub fn escape_escaped_string(s: &str) -> EscapeEscapedStringLiteral<'_> {
357    EscapeEscapedStringLiteral(s)
358}
359
360pub struct EscapeUnicodeStringLiteral<'a>(&'a str);
361
362impl fmt::Display for EscapeUnicodeStringLiteral<'_> {
363    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
364        for c in self.0.chars() {
365            match c {
366                '\'' => {
367                    write!(f, "''")?;
368                }
369                '\\' => {
370                    write!(f, r#"\\"#)?;
371                }
372                x if x.is_ascii() => {
373                    write!(f, "{}", c)?;
374                }
375                _ => {
376                    let codepoint = c as u32;
377                    // if the character fits in 32 bits, we can use the \XXXX format
378                    // otherwise, we need to use the \+XXXXXX format
379                    if codepoint <= 0xFFFF {
380                        write!(f, "\\{:04X}", codepoint)?;
381                    } else {
382                        write!(f, "\\+{:06X}", codepoint)?;
383                    }
384                }
385            }
386        }
387        Ok(())
388    }
389}
390
391pub fn escape_unicode_string(s: &str) -> EscapeUnicodeStringLiteral<'_> {
392    EscapeUnicodeStringLiteral(s)
393}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
396#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
397#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
398pub enum TrimWhereField {
399    Both,
400    Leading,
401    Trailing,
402}
403
404impl fmt::Display for TrimWhereField {
405    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
406        use TrimWhereField::*;
407        f.write_str(match self {
408            Both => "BOTH",
409            Leading => "LEADING",
410            Trailing => "TRAILING",
411        })
412    }
413}