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