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'{v}'"),
277 Value::QuoteDelimitedStringLiteral(v) => v.fmt(f),
278 Value::NationalQuoteDelimitedStringLiteral(v) => write!(f, "N{v}"),
279 Value::HexStringLiteral(v) => write!(f, "X'{v}'"),
280 Value::Boolean(v) => write!(f, "{v}"),
281 Value::SingleQuotedByteStringLiteral(v) => write!(f, "B'{v}'"),
282 Value::DoubleQuotedByteStringLiteral(v) => write!(f, "B\"{v}\""),
283 Value::TripleSingleQuotedByteStringLiteral(v) => write!(f, "B'''{v}'''"),
284 Value::TripleDoubleQuotedByteStringLiteral(v) => write!(f, r#"B"""{v}""""#),
285 Value::SingleQuotedRawStringLiteral(v) => write!(f, "R'{v}'"),
286 Value::DoubleQuotedRawStringLiteral(v) => write!(f, "R\"{v}\""),
287 Value::TripleSingleQuotedRawStringLiteral(v) => write!(f, "R'''{v}'''"),
288 Value::TripleDoubleQuotedRawStringLiteral(v) => write!(f, r#"R"""{v}""""#),
289 Value::Null => write!(f, "NULL"),
290 Value::Placeholder(v) => write!(f, "{v}"),
291 }
292 }
293}
294
295/// A dollar-quoted string literal, e.g. `$$...$$` or `$tag$...$tag$`.
296#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
297#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
298#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
299pub struct DollarQuotedString {
300 /// Inner string contents.
301 pub value: String,
302 /// Optional tag used in the opening/closing delimiter.
303 pub tag: Option<String>,
304}
305
306impl fmt::Display for DollarQuotedString {
307 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
308 match &self.tag {
309 Some(tag) => {
310 write!(f, "${}${}${}$", tag, self.value, tag)
311 }
312 None => {
313 write!(f, "$${}$$", self.value)
314 }
315 }
316 }
317}
318
319/// A quote delimited string literal, e.g. `Q'_abc_'`.
320///
321/// See [Value::QuoteDelimitedStringLiteral] and/or
322/// [Value::NationalQuoteDelimitedStringLiteral].
323#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
324#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
325#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
326pub struct QuoteDelimitedString {
327 /// the quote start character; i.e. the character _after_ the opening `Q'`
328 pub start_quote: char,
329 /// the string literal value itself
330 pub value: String,
331 /// the quote end character; i.e. the character _before_ the closing `'`
332 pub end_quote: char,
333}
334
335impl fmt::Display for QuoteDelimitedString {
336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337 write!(f, "Q'{}{}{}'", self.start_quote, self.value, self.end_quote)
338 }
339}
340
341/// Represents the date/time fields used by functions like `EXTRACT`.
342///
343/// Each variant corresponds to a supported date/time part (for example
344/// `YEAR`, `MONTH`, `DAY`, etc.). The `Custom` variant allows arbitrary
345/// identifiers (e.g. dialect-specific abbreviations).
346#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
347#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
348#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
349pub enum DateTimeField {
350 /// `YEAR`
351 Year,
352 /// `YEARS` (plural form)
353 Years,
354 /// `MONTH`
355 Month,
356 /// `MONTHS` (plural form)
357 Months,
358 /// `WEEK`, optionally followed by a weekday, e.g. `WEEK(MONDAY)`.
359 ///
360 /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions#extract)
361 Week(Option<Ident>),
362 /// `WEEKS` (plural form)
363 Weeks,
364 /// `DAY`
365 Day,
366 /// `DAYOFWEEK`
367 DayOfWeek,
368 /// `DAYOFYEAR`
369 DayOfYear,
370 /// `DAYS` (plural form)
371 Days,
372 /// `DATE`
373 Date,
374 /// `DATETIME`
375 Datetime,
376 /// `HOUR`
377 Hour,
378 /// `HOURS` (plural form)
379 Hours,
380 /// `MINUTE`
381 Minute,
382 /// `MINUTES` (plural form)
383 Minutes,
384 /// `SECOND`
385 Second,
386 /// `SECONDS` (plural form)
387 Seconds,
388 /// `CENTURY`
389 Century,
390 /// `DECADE`
391 Decade,
392 /// `DOW` (day of week short form)
393 Dow,
394 /// `DOY` (day of year short form)
395 Doy,
396 /// `EPOCH`
397 Epoch,
398 /// `ISODOW`
399 Isodow,
400 /// `ISOYEAR`
401 Isoyear,
402 /// `ISOWEEK`
403 IsoWeek,
404 /// `JULIAN`
405 Julian,
406 /// `MICROSECOND`
407 Microsecond,
408 /// `MICROSECONDS` (plural form)
409 Microseconds,
410 /// `MILLENIUM` (alternate spelling)
411 Millenium,
412 /// `MILLENNIUM` (alternate spelling)
413 Millennium,
414 /// `MILLISECOND`
415 Millisecond,
416 /// `MILLISECONDS` (plural form)
417 Milliseconds,
418 /// `NANOSECOND`
419 Nanosecond,
420 /// `NANOSECONDS` (plural form)
421 Nanoseconds,
422 /// `QUARTER`
423 Quarter,
424 /// `TIME`
425 Time,
426 /// `TIMEZONE`
427 Timezone,
428 /// `TIMEZONE_ABBR`
429 TimezoneAbbr,
430 /// `TIMEZONE_HOUR`
431 TimezoneHour,
432 /// `TIMEZONE_MINUTE`
433 TimezoneMinute,
434 /// `TIMEZONE_REGION`
435 TimezoneRegion,
436 /// `NODATETIME` indicates no date/time part
437 NoDateTime,
438 /// Arbitrary abbreviation or custom date-time part.
439 ///
440 /// ```sql
441 /// EXTRACT(q FROM CURRENT_TIMESTAMP)
442 /// ```
443 /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions-date-time#supported-date-and-time-parts)
444 Custom(Ident),
445}
446
447impl fmt::Display for DateTimeField {
448 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449 match self {
450 DateTimeField::Year => write!(f, "YEAR"),
451 DateTimeField::Years => write!(f, "YEARS"),
452 DateTimeField::Month => write!(f, "MONTH"),
453 DateTimeField::Months => write!(f, "MONTHS"),
454 DateTimeField::Week(week_day) => {
455 write!(f, "WEEK")?;
456 if let Some(week_day) = week_day {
457 write!(f, "({week_day})")?
458 }
459 Ok(())
460 }
461 DateTimeField::Weeks => write!(f, "WEEKS"),
462 DateTimeField::Day => write!(f, "DAY"),
463 DateTimeField::DayOfWeek => write!(f, "DAYOFWEEK"),
464 DateTimeField::DayOfYear => write!(f, "DAYOFYEAR"),
465 DateTimeField::Days => write!(f, "DAYS"),
466 DateTimeField::Date => write!(f, "DATE"),
467 DateTimeField::Datetime => write!(f, "DATETIME"),
468 DateTimeField::Hour => write!(f, "HOUR"),
469 DateTimeField::Hours => write!(f, "HOURS"),
470 DateTimeField::Minute => write!(f, "MINUTE"),
471 DateTimeField::Minutes => write!(f, "MINUTES"),
472 DateTimeField::Second => write!(f, "SECOND"),
473 DateTimeField::Seconds => write!(f, "SECONDS"),
474 DateTimeField::Century => write!(f, "CENTURY"),
475 DateTimeField::Decade => write!(f, "DECADE"),
476 DateTimeField::Dow => write!(f, "DOW"),
477 DateTimeField::Doy => write!(f, "DOY"),
478 DateTimeField::Epoch => write!(f, "EPOCH"),
479 DateTimeField::Isodow => write!(f, "ISODOW"),
480 DateTimeField::Isoyear => write!(f, "ISOYEAR"),
481 DateTimeField::IsoWeek => write!(f, "ISOWEEK"),
482 DateTimeField::Julian => write!(f, "JULIAN"),
483 DateTimeField::Microsecond => write!(f, "MICROSECOND"),
484 DateTimeField::Microseconds => write!(f, "MICROSECONDS"),
485 DateTimeField::Millenium => write!(f, "MILLENIUM"),
486 DateTimeField::Millennium => write!(f, "MILLENNIUM"),
487 DateTimeField::Millisecond => write!(f, "MILLISECOND"),
488 DateTimeField::Milliseconds => write!(f, "MILLISECONDS"),
489 DateTimeField::Nanosecond => write!(f, "NANOSECOND"),
490 DateTimeField::Nanoseconds => write!(f, "NANOSECONDS"),
491 DateTimeField::Quarter => write!(f, "QUARTER"),
492 DateTimeField::Time => write!(f, "TIME"),
493 DateTimeField::Timezone => write!(f, "TIMEZONE"),
494 DateTimeField::TimezoneAbbr => write!(f, "TIMEZONE_ABBR"),
495 DateTimeField::TimezoneHour => write!(f, "TIMEZONE_HOUR"),
496 DateTimeField::TimezoneMinute => write!(f, "TIMEZONE_MINUTE"),
497 DateTimeField::TimezoneRegion => write!(f, "TIMEZONE_REGION"),
498 DateTimeField::NoDateTime => write!(f, "NODATETIME"),
499 DateTimeField::Custom(custom) => write!(f, "{custom}"),
500 }
501 }
502}
503
504#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
505#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
506#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
507/// The Unicode Standard defines four normalization forms, which are intended to eliminate
508/// certain distinctions between visually or functionally identical characters.
509///
510/// See [Unicode Normalization Forms](https://unicode.org/reports/tr15/) for details.
511pub enum NormalizationForm {
512 /// Canonical Decomposition, followed by Canonical Composition.
513 NFC,
514 /// Canonical Decomposition.
515 NFD,
516 /// Compatibility Decomposition, followed by Canonical Composition.
517 NFKC,
518 /// Compatibility Decomposition.
519 NFKD,
520}
521
522impl fmt::Display for NormalizationForm {
523 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
524 match self {
525 NormalizationForm::NFC => write!(f, "NFC"),
526 NormalizationForm::NFD => write!(f, "NFD"),
527 NormalizationForm::NFKC => write!(f, "NFKC"),
528 NormalizationForm::NFKD => write!(f, "NFKD"),
529 }
530 }
531}
532
533pub struct EscapeQuotedString<'a> {
534 string: &'a str,
535 quote: char,
536}
537
538impl fmt::Display for EscapeQuotedString<'_> {
539 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
540 // EscapeQuotedString doesn't know which mode of escape was
541 // chosen by the user. So this code must to correctly display
542 // strings without knowing if the strings are already escaped
543 // or not.
544 //
545 // If the quote symbol in the string is repeated twice, OR, if
546 // the quote symbol is after backslash, display all the chars
547 // without any escape. However, if the quote symbol is used
548 // just between usual chars, `fmt()` should display it twice."
549 //
550 // The following table has examples
551 //
552 // | original query | mode | AST Node | serialized |
553 // | ------------- | --------- | -------------------------------------------------- | ------------ |
554 // | `"A""B""A"` | no-escape | `DoubleQuotedString(String::from("A\"\"B\"\"A"))` | `"A""B""A"` |
555 // | `"A""B""A"` | default | `DoubleQuotedString(String::from("A\"B\"A"))` | `"A""B""A"` |
556 // | `"A\"B\"A"` | no-escape | `DoubleQuotedString(String::from("A\\\"B\\\"A"))` | `"A\"B\"A"` |
557 // | `"A\"B\"A"` | default | `DoubleQuotedString(String::from("A\"B\"A"))` | `"A""B""A"` |
558 let quote = self.quote;
559 let mut previous_char = char::default();
560 let mut start_idx = 0;
561 let mut peekable_chars = self.string.char_indices().peekable();
562 while let Some(&(idx, ch)) = peekable_chars.peek() {
563 match ch {
564 char if char == quote => {
565 if previous_char == '\\' {
566 // the quote is already escaped with a backslash, skip
567 peekable_chars.next();
568 continue;
569 }
570 peekable_chars.next();
571 match peekable_chars.peek() {
572 Some((_, c)) if *c == quote => {
573 // the quote is already escaped with another quote, skip
574 peekable_chars.next();
575 }
576 _ => {
577 // The quote is not escaped.
578 // Including idx in the range, so the quote at idx will be printed twice:
579 // in this call to write_str() and in the next one.
580 f.write_str(&self.string[start_idx..=idx])?;
581 start_idx = idx;
582 }
583 }
584 }
585 _ => {
586 peekable_chars.next();
587 }
588 }
589 previous_char = ch;
590 }
591 f.write_str(&self.string[start_idx..])?;
592 Ok(())
593 }
594}
595
596/// Return a helper which formats `string` for inclusion inside a quoted
597/// literal that uses `quote` as the delimiter.
598pub fn escape_quoted_string(string: &str, quote: char) -> EscapeQuotedString<'_> {
599 EscapeQuotedString { string, quote }
600}
601
602/// Convenience wrapper for escaping strings for single-quoted literals (`'`).
603pub fn escape_single_quote_string(s: &str) -> EscapeQuotedString<'_> {
604 escape_quoted_string(s, '\'')
605}
606
607/// Convenience wrapper for escaping strings for double-quoted literals (`").`
608pub fn escape_double_quote_string(s: &str) -> EscapeQuotedString<'_> {
609 escape_quoted_string(s, '\"')
610}
611
612pub struct EscapeEscapedStringLiteral<'a>(&'a str);
613
614impl fmt::Display for EscapeEscapedStringLiteral<'_> {
615 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
616 for c in self.0.chars() {
617 match c {
618 '\'' => {
619 write!(f, r#"\'"#)?;
620 }
621 '\\' => {
622 write!(f, r#"\\"#)?;
623 }
624 '\n' => {
625 write!(f, r#"\n"#)?;
626 }
627 '\t' => {
628 write!(f, r#"\t"#)?;
629 }
630 '\r' => {
631 write!(f, r#"\r"#)?;
632 }
633 _ => {
634 write!(f, "{c}")?;
635 }
636 }
637 }
638 Ok(())
639 }
640}
641
642/// Return a helper which escapes characters for string literals that use
643/// PostgreSQL-style escaped string literals (e.g. `E'...')`.
644pub fn escape_escaped_string(s: &str) -> EscapeEscapedStringLiteral<'_> {
645 EscapeEscapedStringLiteral(s)
646}
647
648pub struct EscapeUnicodeStringLiteral<'a>(&'a str);
649
650impl fmt::Display for EscapeUnicodeStringLiteral<'_> {
651 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652 for c in self.0.chars() {
653 match c {
654 '\'' => {
655 write!(f, "''")?;
656 }
657 '\\' => {
658 write!(f, r#"\\"#)?;
659 }
660 x if x.is_ascii() => {
661 write!(f, "{c}")?;
662 }
663 _ => {
664 let codepoint = c as u32;
665 // if the character fits in 32 bits, we can use the \XXXX format
666 // otherwise, we need to use the \+XXXXXX format
667 if codepoint <= 0xFFFF {
668 write!(f, "\\{codepoint:04X}")?;
669 } else {
670 write!(f, "\\+{codepoint:06X}")?;
671 }
672 }
673 }
674 }
675 Ok(())
676 }
677}
678
679/// Return a helper which escapes non-ASCII characters using `\XXXX` or
680/// `\+XXXXXX` Unicode escape formats (used for `U&'...'` style literals).
681pub fn escape_unicode_string(s: &str) -> EscapeUnicodeStringLiteral<'_> {
682 EscapeUnicodeStringLiteral(s)
683}
684
685/// The side on which `TRIM` should be applied.
686///
687/// Corresponds to `TRIM(BOTH|LEADING|TRAILING)` SQL syntax.
688#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
689#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
690#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
691pub enum TrimWhereField {
692 /// `BOTH` (trim from both ends)
693 Both,
694 /// `LEADING` (trim from start)
695 Leading,
696 /// `TRAILING` (trim from end)
697 Trailing,
698}
699
700impl fmt::Display for TrimWhereField {
701 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702 use TrimWhereField::*;
703 f.write_str(match self {
704 Both => "BOTH",
705 Leading => "LEADING",
706 Trailing => "TRAILING",
707 })
708 }
709}