Skip to main content

sqlparser/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::{
22    fmt,
23    ops::{Deref, DerefMut},
24};
25
26#[cfg(feature = "bigdecimal")]
27use bigdecimal::BigDecimal;
28
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31
32use crate::{ast::Ident, tokenizer::Span};
33#[cfg(feature = "visitor")]
34use sqlparser_derive::{Visit, VisitMut};
35
36/// Wraps a primitive SQL [`Value`]  with its [`Span`] location
37///
38/// # Example: create a `ValueWithSpan` from a `Value`
39/// ```
40/// # use sqlparser::ast::{Value, ValueWithSpan};
41/// # use sqlparser::tokenizer::{Location, Span};
42/// let value = Value::SingleQuotedString(String::from("endpoint"));
43/// // from line 1, column 1 to line 1, column 7
44/// let span = Span::new(Location::new(1, 1), Location::new(1, 7));
45/// let value_with_span = value.with_span(span);
46/// ```
47///
48/// # Example: create a `ValueWithSpan` from a `Value` with an empty span
49///
50/// You can call [`Value::with_empty_span`] to create a `ValueWithSpan` with an empty span
51/// ```
52/// # use sqlparser::ast::{Value, ValueWithSpan};
53/// # use sqlparser::tokenizer::{Location, Span};
54/// let value = Value::SingleQuotedString(String::from("endpoint"));
55/// let value_with_span = value.with_empty_span();
56/// assert_eq!(value_with_span.span, Span::empty());
57/// ```
58///
59/// You can also use the [`From`] trait to convert  `ValueWithSpan` to/from `Value`s
60/// ```
61/// # use sqlparser::ast::{Value, ValueWithSpan};
62/// # use sqlparser::tokenizer::{Location, Span};
63/// let value = Value::SingleQuotedString(String::from("endpoint"));
64/// // converting `Value` to `ValueWithSpan` results in an empty span
65/// let value_with_span: ValueWithSpan = value.into();
66/// assert_eq!(value_with_span.span, Span::empty());
67/// // convert back to `Value`
68/// let value: Value = value_with_span.into();
69/// ```
70/// A `Value` paired with its source `Span` location.
71#[derive(Debug, Clone, Eq)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73#[cfg_attr(
74    feature = "visitor",
75    derive(Visit, VisitMut),
76    visit(with = "visit_value")
77)]
78pub struct ValueWithSpan {
79    /// The wrapped `Value`.
80    pub value: Value,
81    /// The source `Span` covering the token(s) that produced the value.
82    pub span: Span,
83}
84
85impl PartialEq for ValueWithSpan {
86    fn eq(&self, other: &Self) -> bool {
87        self.value == other.value
88    }
89}
90
91impl Ord for ValueWithSpan {
92    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
93        self.value.cmp(&other.value)
94    }
95}
96
97impl PartialOrd for ValueWithSpan {
98    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
99        Some(Ord::cmp(self, other))
100    }
101}
102
103impl core::hash::Hash for ValueWithSpan {
104    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
105        self.value.hash(state);
106    }
107}
108
109impl From<Value> for ValueWithSpan {
110    fn from(value: Value) -> Self {
111        value.with_empty_span()
112    }
113}
114
115impl From<ValueWithSpan> for Value {
116    fn from(value: ValueWithSpan) -> Self {
117        value.value
118    }
119}
120
121impl Deref for ValueWithSpan {
122    type Target = Value;
123
124    fn deref(&self) -> &Self::Target {
125        &self.value
126    }
127}
128
129impl DerefMut for ValueWithSpan {
130    fn deref_mut(&mut self) -> &mut Self::Target {
131        &mut self.value
132    }
133}
134
135/// Primitive SQL values such as number and string
136#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
139pub enum Value {
140    /// Numeric literal
141    #[cfg(not(feature = "bigdecimal"))]
142    Number(String, bool),
143    #[cfg(feature = "bigdecimal")]
144    /// HINT: use `test_utils::number` to make an instance of
145    /// Value::Number This might help if you your tests pass locally
146    /// but fail on CI with the `--all-features` flag enabled
147    /// Numeric literal (uses `BigDecimal` when the `bigdecimal` feature is enabled).
148    Number(BigDecimal, bool),
149    /// 'string value'
150    SingleQuotedString(String),
151    /// Dollar-quoted string literal, e.g. `$$...$$` or `$tag$...$tag$` (Postgres syntax).
152    DollarQuotedString(DollarQuotedString),
153    /// Triple single quoted strings: Example '''abc'''
154    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
155    TripleSingleQuotedString(String),
156    /// Triple double quoted strings: Example """abc"""
157    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
158    TripleDoubleQuotedString(String),
159    /// e'string value' (postgres extension)
160    /// See [Postgres docs](https://www.postgresql.org/docs/8.3/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS)
161    /// for more details.
162    EscapedStringLiteral(String),
163    /// u&'string value' (postgres extension)
164    /// See [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-UESCAPE)
165    /// for more details.
166    UnicodeStringLiteral(String),
167    /// B'string value'
168    SingleQuotedByteStringLiteral(String),
169    /// B"string value"
170    DoubleQuotedByteStringLiteral(String),
171    /// Triple single quoted literal with byte string prefix. Example `B'''abc'''`
172    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
173    TripleSingleQuotedByteStringLiteral(String),
174    /// Triple double quoted literal with byte string prefix. Example `B"""abc"""`
175    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
176    TripleDoubleQuotedByteStringLiteral(String),
177    /// Single quoted literal with raw string prefix. Example `R'abc'`
178    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
179    SingleQuotedRawStringLiteral(String),
180    /// Double quoted literal with raw string prefix. Example `R"abc"`
181    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
182    DoubleQuotedRawStringLiteral(String),
183    /// Triple single quoted literal with raw string prefix. Example `R'''abc'''`
184    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
185    TripleSingleQuotedRawStringLiteral(String),
186    /// Triple double quoted literal with raw string prefix. Example `R"""abc"""`
187    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals)
188    TripleDoubleQuotedRawStringLiteral(String),
189    /// N'string value'
190    NationalStringLiteral(String),
191    /// Quote delimited literal. Examples `Q'{ab'c}'`, `Q'|ab'c|'`, `Q'|ab|c|'`
192    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA)
193    QuoteDelimitedStringLiteral(QuoteDelimitedString),
194    /// "National" quote delimited literal. Examples `Q'{ab'c}'`, `Q'|ab'c|'`, `Q'|ab|c|'`
195    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA)
196    NationalQuoteDelimitedStringLiteral(QuoteDelimitedString),
197    /// X'hex value'
198    HexStringLiteral(String),
199
200    /// Double quoted string literal, e.g. `"abc"`.
201    DoubleQuotedString(String),
202    /// Boolean value true or false
203    Boolean(bool),
204    /// `NULL` value
205    Null,
206    /// `?` or `$` Prepared statement arg placeholder
207    Placeholder(String),
208}
209
210impl ValueWithSpan {
211    /// If the underlying literal is a string, regardless of quote style, returns the associated string value
212    pub fn into_string(self) -> Option<String> {
213        self.value.into_string()
214    }
215}
216
217impl Value {
218    /// If the underlying literal is a string, regardless of quote style, returns the associated string value
219    pub fn into_string(self) -> Option<String> {
220        match self {
221            Value::SingleQuotedString(s)
222            | Value::DoubleQuotedString(s)
223            | Value::TripleSingleQuotedString(s)
224            | Value::TripleDoubleQuotedString(s)
225            | Value::SingleQuotedByteStringLiteral(s)
226            | Value::DoubleQuotedByteStringLiteral(s)
227            | Value::TripleSingleQuotedByteStringLiteral(s)
228            | Value::TripleDoubleQuotedByteStringLiteral(s)
229            | Value::SingleQuotedRawStringLiteral(s)
230            | Value::DoubleQuotedRawStringLiteral(s)
231            | Value::TripleSingleQuotedRawStringLiteral(s)
232            | Value::TripleDoubleQuotedRawStringLiteral(s)
233            | Value::EscapedStringLiteral(s)
234            | Value::UnicodeStringLiteral(s)
235            | Value::NationalStringLiteral(s)
236            | Value::HexStringLiteral(s) => Some(s),
237            Value::DollarQuotedString(s) => Some(s.value),
238            Value::QuoteDelimitedStringLiteral(s) => Some(s.value),
239            Value::NationalQuoteDelimitedStringLiteral(s) => Some(s.value),
240            _ => None,
241        }
242    }
243
244    /// Attach the provided `span` to this `Value` and return `ValueWithSpan`.
245    pub fn with_span(self, span: Span) -> ValueWithSpan {
246        ValueWithSpan { value: self, span }
247    }
248
249    /// Convenience for attaching an empty span to this `Value`.
250    pub fn with_empty_span(self) -> ValueWithSpan {
251        self.with_span(Span::empty())
252    }
253}
254
255impl fmt::Display for ValueWithSpan {
256    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
257        write!(f, "{}", self.value)
258    }
259}
260
261impl fmt::Display for Value {
262    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263        match self {
264            Value::Number(v, l) => write!(f, "{}{long}", v, long = if *l { "L" } else { "" }),
265            Value::DoubleQuotedString(v) => write!(f, "\"{}\"", escape_double_quote_string(v)),
266            Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
267            Value::TripleSingleQuotedString(v) => {
268                write!(f, "'''{v}'''")
269            }
270            Value::TripleDoubleQuotedString(v) => {
271                write!(f, r#""""{v}""""#)
272            }
273            Value::DollarQuotedString(v) => write!(f, "{v}"),
274            Value::EscapedStringLiteral(v) => write!(f, "E'{}'", escape_escaped_string(v)),
275            Value::UnicodeStringLiteral(v) => write!(f, "U&'{}'", escape_unicode_string(v)),
276            Value::NationalStringLiteral(v) => write!(f, "N'{}'", escape_single_quote_string(v)),
277            Value::QuoteDelimitedStringLiteral(v) => v.fmt(f),
278            Value::NationalQuoteDelimitedStringLiteral(v) => write!(f, "N{v}"),
279            Value::HexStringLiteral(v) => write!(f, "X'{}'", escape_single_quote_string(v)),
280            Value::Boolean(v) => write!(f, "{v}"),
281            Value::SingleQuotedByteStringLiteral(v) => {
282                write!(f, "B'{}'", escape_single_quote_string(v))
283            }
284            Value::DoubleQuotedByteStringLiteral(v) => {
285                write!(f, "B\"{}\"", escape_double_quote_string(v))
286            }
287            Value::TripleSingleQuotedByteStringLiteral(v) => write!(f, "B'''{v}'''"),
288            Value::TripleDoubleQuotedByteStringLiteral(v) => write!(f, r#"B"""{v}""""#),
289            Value::SingleQuotedRawStringLiteral(v) => {
290                write!(f, "R'{}'", escape_single_quote_string(v))
291            }
292            Value::DoubleQuotedRawStringLiteral(v) => {
293                write!(f, "R\"{}\"", escape_double_quote_string(v))
294            }
295            Value::TripleSingleQuotedRawStringLiteral(v) => write!(f, "R'''{v}'''"),
296            Value::TripleDoubleQuotedRawStringLiteral(v) => write!(f, r#"R"""{v}""""#),
297            Value::Null => write!(f, "NULL"),
298            Value::Placeholder(v) => write!(f, "{v}"),
299        }
300    }
301}
302
303/// A dollar-quoted string literal, e.g. `$$...$$` or `$tag$...$tag$`.
304#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
305#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
306#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
307pub struct DollarQuotedString {
308    /// Inner string contents.
309    pub value: String,
310    /// Optional tag used in the opening/closing delimiter.
311    pub tag: Option<String>,
312}
313
314impl fmt::Display for DollarQuotedString {
315    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
316        match &self.tag {
317            Some(tag) => {
318                write!(f, "${}${}${}$", tag, self.value, tag)
319            }
320            None => {
321                write!(f, "$${}$$", self.value)
322            }
323        }
324    }
325}
326
327/// A quote delimited string literal, e.g. `Q'_abc_'`.
328///
329/// See [Value::QuoteDelimitedStringLiteral] and/or
330/// [Value::NationalQuoteDelimitedStringLiteral].
331#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
332#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
333#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
334pub struct QuoteDelimitedString {
335    /// the quote start character; i.e. the character _after_ the opening `Q'`
336    pub start_quote: char,
337    /// the string literal value itself
338    pub value: String,
339    /// the quote end character; i.e. the character _before_ the closing `'`
340    pub end_quote: char,
341}
342
343impl fmt::Display for QuoteDelimitedString {
344    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345        write!(f, "Q'{}{}{}'", self.start_quote, self.value, self.end_quote)
346    }
347}
348
349/// Represents the date/time fields used by functions like `EXTRACT`.
350///
351/// Each variant corresponds to a supported date/time part (for example
352/// `YEAR`, `MONTH`, `DAY`, etc.). The `Custom` variant allows arbitrary
353/// identifiers (e.g. dialect-specific abbreviations).
354#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
355#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
356#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
357pub enum DateTimeField {
358    /// `YEAR`
359    Year,
360    /// `YEARS` (plural form)
361    Years,
362    /// `MONTH`
363    Month,
364    /// `MONTHS` (plural form)
365    Months,
366    /// `WEEK`, optionally followed by a weekday, e.g. `WEEK(MONDAY)`.
367    ///
368    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions#extract)
369    Week(Option<Ident>),
370    /// `WEEKS` (plural form)
371    Weeks,
372    /// `DAY`
373    Day,
374    /// `DAYOFWEEK`
375    DayOfWeek,
376    /// `DAYOFYEAR`
377    DayOfYear,
378    /// `DAYS` (plural form)
379    Days,
380    /// `DATE`
381    Date,
382    /// `DATETIME`
383    Datetime,
384    /// `HOUR`
385    Hour,
386    /// `HOURS` (plural form)
387    Hours,
388    /// `MINUTE`
389    Minute,
390    /// `MINUTES` (plural form)
391    Minutes,
392    /// `SECOND`
393    Second,
394    /// `SECONDS` (plural form)
395    Seconds,
396    /// `CENTURY`
397    Century,
398    /// `DECADE`
399    Decade,
400    /// `DOW` (day of week short form)
401    Dow,
402    /// `DOY` (day of year short form)
403    Doy,
404    /// `EPOCH`
405    Epoch,
406    /// `ISODOW`
407    Isodow,
408    /// `ISOYEAR`
409    Isoyear,
410    /// `ISOWEEK`
411    IsoWeek,
412    /// `JULIAN`
413    Julian,
414    /// `MICROSECOND`
415    Microsecond,
416    /// `MICROSECONDS` (plural form)
417    Microseconds,
418    /// `MILLENIUM` (alternate spelling)
419    Millenium,
420    /// `MILLENNIUM` (alternate spelling)
421    Millennium,
422    /// `MILLISECOND`
423    Millisecond,
424    /// `MILLISECONDS` (plural form)
425    Milliseconds,
426    /// `NANOSECOND`
427    Nanosecond,
428    /// `NANOSECONDS` (plural form)
429    Nanoseconds,
430    /// `QUARTER`
431    Quarter,
432    /// `TIME`
433    Time,
434    /// `TIMEZONE`
435    Timezone,
436    /// `TIMEZONE_ABBR`
437    TimezoneAbbr,
438    /// `TIMEZONE_HOUR`
439    TimezoneHour,
440    /// `TIMEZONE_MINUTE`
441    TimezoneMinute,
442    /// `TIMEZONE_REGION`
443    TimezoneRegion,
444    /// `NODATETIME` indicates no date/time part
445    NoDateTime,
446    /// Arbitrary abbreviation or custom date-time part.
447    ///
448    /// ```sql
449    /// EXTRACT(q FROM CURRENT_TIMESTAMP)
450    /// ```
451    /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions-date-time#supported-date-and-time-parts)
452    Custom(Ident),
453}
454
455impl fmt::Display for DateTimeField {
456    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
457        match self {
458            DateTimeField::Year => write!(f, "YEAR"),
459            DateTimeField::Years => write!(f, "YEARS"),
460            DateTimeField::Month => write!(f, "MONTH"),
461            DateTimeField::Months => write!(f, "MONTHS"),
462            DateTimeField::Week(week_day) => {
463                write!(f, "WEEK")?;
464                if let Some(week_day) = week_day {
465                    write!(f, "({week_day})")?
466                }
467                Ok(())
468            }
469            DateTimeField::Weeks => write!(f, "WEEKS"),
470            DateTimeField::Day => write!(f, "DAY"),
471            DateTimeField::DayOfWeek => write!(f, "DAYOFWEEK"),
472            DateTimeField::DayOfYear => write!(f, "DAYOFYEAR"),
473            DateTimeField::Days => write!(f, "DAYS"),
474            DateTimeField::Date => write!(f, "DATE"),
475            DateTimeField::Datetime => write!(f, "DATETIME"),
476            DateTimeField::Hour => write!(f, "HOUR"),
477            DateTimeField::Hours => write!(f, "HOURS"),
478            DateTimeField::Minute => write!(f, "MINUTE"),
479            DateTimeField::Minutes => write!(f, "MINUTES"),
480            DateTimeField::Second => write!(f, "SECOND"),
481            DateTimeField::Seconds => write!(f, "SECONDS"),
482            DateTimeField::Century => write!(f, "CENTURY"),
483            DateTimeField::Decade => write!(f, "DECADE"),
484            DateTimeField::Dow => write!(f, "DOW"),
485            DateTimeField::Doy => write!(f, "DOY"),
486            DateTimeField::Epoch => write!(f, "EPOCH"),
487            DateTimeField::Isodow => write!(f, "ISODOW"),
488            DateTimeField::Isoyear => write!(f, "ISOYEAR"),
489            DateTimeField::IsoWeek => write!(f, "ISOWEEK"),
490            DateTimeField::Julian => write!(f, "JULIAN"),
491            DateTimeField::Microsecond => write!(f, "MICROSECOND"),
492            DateTimeField::Microseconds => write!(f, "MICROSECONDS"),
493            DateTimeField::Millenium => write!(f, "MILLENIUM"),
494            DateTimeField::Millennium => write!(f, "MILLENNIUM"),
495            DateTimeField::Millisecond => write!(f, "MILLISECOND"),
496            DateTimeField::Milliseconds => write!(f, "MILLISECONDS"),
497            DateTimeField::Nanosecond => write!(f, "NANOSECOND"),
498            DateTimeField::Nanoseconds => write!(f, "NANOSECONDS"),
499            DateTimeField::Quarter => write!(f, "QUARTER"),
500            DateTimeField::Time => write!(f, "TIME"),
501            DateTimeField::Timezone => write!(f, "TIMEZONE"),
502            DateTimeField::TimezoneAbbr => write!(f, "TIMEZONE_ABBR"),
503            DateTimeField::TimezoneHour => write!(f, "TIMEZONE_HOUR"),
504            DateTimeField::TimezoneMinute => write!(f, "TIMEZONE_MINUTE"),
505            DateTimeField::TimezoneRegion => write!(f, "TIMEZONE_REGION"),
506            DateTimeField::NoDateTime => write!(f, "NODATETIME"),
507            DateTimeField::Custom(custom) => write!(f, "{custom}"),
508        }
509    }
510}
511
512#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
513#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
514#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
515/// The Unicode Standard defines four normalization forms, which are intended to eliminate
516/// certain distinctions between visually or functionally identical characters.
517///
518/// See [Unicode Normalization Forms](https://unicode.org/reports/tr15/) for details.
519pub enum NormalizationForm {
520    /// Canonical Decomposition, followed by Canonical Composition.
521    NFC,
522    /// Canonical Decomposition.
523    NFD,
524    /// Compatibility Decomposition, followed by Canonical Composition.
525    NFKC,
526    /// Compatibility Decomposition.
527    NFKD,
528}
529
530impl fmt::Display for NormalizationForm {
531    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
532        match self {
533            NormalizationForm::NFC => write!(f, "NFC"),
534            NormalizationForm::NFD => write!(f, "NFD"),
535            NormalizationForm::NFKC => write!(f, "NFKC"),
536            NormalizationForm::NFKD => write!(f, "NFKD"),
537        }
538    }
539}
540
541pub struct EscapeQuotedString<'a> {
542    string: &'a str,
543    quote: char,
544}
545
546impl fmt::Display for EscapeQuotedString<'_> {
547    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
548        // EscapeQuotedString doesn't know which mode of escape was
549        // chosen by the user. So this code must to correctly display
550        // strings without knowing if the strings are already escaped
551        // or not.
552        //
553        // If the quote symbol in the string is repeated twice, OR, if
554        // the quote symbol is after backslash, display all the chars
555        // without any escape. However, if the quote symbol is used
556        // just between usual chars, `fmt()` should display it twice."
557        //
558        // The following table has examples
559        //
560        // | original query | mode      | AST Node                                           | serialized   |
561        // | -------------  | --------- | -------------------------------------------------- | ------------ |
562        // | `"A""B""A"`    | no-escape | `DoubleQuotedString(String::from("A\"\"B\"\"A"))`  | `"A""B""A"`  |
563        // | `"A""B""A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
564        // | `"A\"B\"A"`    | no-escape | `DoubleQuotedString(String::from("A\\\"B\\\"A"))`  | `"A\"B\"A"`  |
565        // | `"A\"B\"A"`    | default   | `DoubleQuotedString(String::from("A\"B\"A"))`      | `"A""B""A"`  |
566        let quote = self.quote;
567        let mut previous_char = char::default();
568        let mut start_idx = 0;
569        let mut peekable_chars = self.string.char_indices().peekable();
570        while let Some(&(idx, ch)) = peekable_chars.peek() {
571            match ch {
572                char if char == quote => {
573                    if previous_char == '\\' {
574                        // the quote is already escaped with a backslash, skip
575                        peekable_chars.next();
576                        continue;
577                    }
578                    peekable_chars.next();
579                    match peekable_chars.peek() {
580                        Some((_, c)) if *c == quote => {
581                            // the quote is already escaped with another quote, skip
582                            peekable_chars.next();
583                        }
584                        _ => {
585                            // The quote is not escaped.
586                            // Including idx in the range, so the quote at idx will be printed twice:
587                            // in this call to write_str() and in the next one.
588                            let end_idx = idx + ch.len_utf8();
589                            f.write_str(&self.string[start_idx..end_idx])?;
590                            start_idx = idx;
591                        }
592                    }
593                }
594                _ => {
595                    peekable_chars.next();
596                }
597            }
598            previous_char = ch;
599        }
600        f.write_str(&self.string[start_idx..])?;
601        Ok(())
602    }
603}
604
605/// Return a helper which formats `string` for inclusion inside a quoted
606/// literal that uses `quote` as the delimiter.
607pub fn escape_quoted_string(string: &str, quote: char) -> EscapeQuotedString<'_> {
608    EscapeQuotedString { string, quote }
609}
610
611/// Convenience wrapper for escaping strings for single-quoted literals (`'`).
612pub fn escape_single_quote_string(s: &str) -> EscapeQuotedString<'_> {
613    escape_quoted_string(s, '\'')
614}
615
616/// Convenience wrapper for escaping strings for double-quoted literals (`").`
617pub fn escape_double_quote_string(s: &str) -> EscapeQuotedString<'_> {
618    escape_quoted_string(s, '\"')
619}
620
621pub struct EscapeEscapedStringLiteral<'a>(&'a str);
622
623impl fmt::Display for EscapeEscapedStringLiteral<'_> {
624    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
625        for c in self.0.chars() {
626            match c {
627                '\'' => {
628                    write!(f, r#"\'"#)?;
629                }
630                '\\' => {
631                    write!(f, r#"\\"#)?;
632                }
633                '\n' => {
634                    write!(f, r#"\n"#)?;
635                }
636                '\t' => {
637                    write!(f, r#"\t"#)?;
638                }
639                '\r' => {
640                    write!(f, r#"\r"#)?;
641                }
642                _ => {
643                    write!(f, "{c}")?;
644                }
645            }
646        }
647        Ok(())
648    }
649}
650
651/// Return a helper which escapes characters for string literals that use
652/// PostgreSQL-style escaped string literals (e.g. `E'...')`.
653pub fn escape_escaped_string(s: &str) -> EscapeEscapedStringLiteral<'_> {
654    EscapeEscapedStringLiteral(s)
655}
656
657pub struct EscapeUnicodeStringLiteral<'a>(&'a str);
658
659impl fmt::Display for EscapeUnicodeStringLiteral<'_> {
660    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
661        for c in self.0.chars() {
662            match c {
663                '\'' => {
664                    write!(f, "''")?;
665                }
666                '\\' => {
667                    write!(f, r#"\\"#)?;
668                }
669                x if x.is_ascii() => {
670                    write!(f, "{c}")?;
671                }
672                _ => {
673                    let codepoint = c as u32;
674                    // if the character fits in 32 bits, we can use the \XXXX format
675                    // otherwise, we need to use the \+XXXXXX format
676                    if codepoint <= 0xFFFF {
677                        write!(f, "\\{codepoint:04X}")?;
678                    } else {
679                        write!(f, "\\+{codepoint:06X}")?;
680                    }
681                }
682            }
683        }
684        Ok(())
685    }
686}
687
688/// Return a helper which escapes non-ASCII characters using `\XXXX` or
689/// `\+XXXXXX` Unicode escape formats (used for `U&'...'` style literals).
690pub fn escape_unicode_string(s: &str) -> EscapeUnicodeStringLiteral<'_> {
691    EscapeUnicodeStringLiteral(s)
692}
693
694/// The side on which `TRIM` should be applied.
695///
696/// Corresponds to `TRIM(BOTH|LEADING|TRAILING)` SQL syntax.
697#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
698#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
699#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
700pub enum TrimWhereField {
701    /// `BOTH` (trim from both ends)
702    Both,
703    /// `LEADING` (trim from start)
704    Leading,
705    /// `TRAILING` (trim from end)
706    Trailing,
707}
708
709impl fmt::Display for TrimWhereField {
710    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
711        use TrimWhereField::*;
712        f.write_str(match self {
713            Both => "BOTH",
714            Leading => "LEADING",
715            Trailing => "TRAILING",
716        })
717    }
718}
719
720#[cfg(test)]
721#[test]
722fn test_escape_quoted_string_with_multibyte_quote_char() {
723    assert_eq!(
724        format!("{}", escape_quoted_string("a🦀b🦀c", '🦀')),
725        "a🦀🦀b🦀🦀c"
726    );
727}
728#[cfg(test)]
729#[test]
730fn test_hex_string_literal_display_escaping() {
731    assert_eq!(
732        Value::HexStringLiteral("'".to_string()).to_string(),
733        "X''''"
734    );
735    assert_eq!(
736        Value::HexStringLiteral("it's".to_string()).to_string(),
737        "X'it''s'"
738    );
739}
740
741#[cfg(test)]
742#[test]
743fn test_byte_and_raw_string_literal_display_escaping() {
744    assert_eq!(
745        Value::SingleQuotedByteStringLiteral("'".to_string()).to_string(),
746        "B''''"
747    );
748    assert_eq!(
749        Value::SingleQuotedByteStringLiteral("it's".to_string()).to_string(),
750        "B'it''s'"
751    );
752    assert_eq!(
753        Value::DoubleQuotedByteStringLiteral("\"".to_string()).to_string(),
754        "B\"\"\"\""
755    );
756    assert_eq!(
757        Value::DoubleQuotedByteStringLiteral("it\"s".to_string()).to_string(),
758        "B\"it\"\"s\""
759    );
760    assert_eq!(
761        Value::SingleQuotedRawStringLiteral("'".to_string()).to_string(),
762        "R''''"
763    );
764    assert_eq!(
765        Value::SingleQuotedRawStringLiteral("it's".to_string()).to_string(),
766        "R'it''s'"
767    );
768    assert_eq!(
769        Value::DoubleQuotedRawStringLiteral("\"".to_string()).to_string(),
770        "R\"\"\"\""
771    );
772    assert_eq!(
773        Value::DoubleQuotedRawStringLiteral("it\"s".to_string()).to_string(),
774        "R\"it\"\"s\""
775    );
776}