// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #[cfg(not(feature = "std"))] use alloc::string::String; use core::fmt; #[cfg(feature = "bigdecimal")] use bigdecimal::BigDecimal; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ast::Ident, tokenizer::Span}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; /// Wraps a primitive SQL [`Value`] with its [`Span`] location /// /// # Example: create a `ValueWithSpan` from a `Value` /// ``` /// # use sqlparser::ast::{Value, ValueWithSpan}; /// # use sqlparser::tokenizer::{Location, Span}; /// let value = Value::SingleQuotedString(String::from("endpoint")); /// // from line 1, column 1 to line 1, column 7 /// let span = Span::new(Location::new(1, 1), Location::new(1, 7)); /// let value_with_span = value.with_span(span); /// ``` /// /// # Example: create a `ValueWithSpan` from a `Value` with an empty span /// /// You can call [`Value::with_empty_span`] to create a `ValueWithSpan` with an empty span /// ``` /// # use sqlparser::ast::{Value, ValueWithSpan}; /// # use sqlparser::tokenizer::{Location, Span}; /// let value = Value::SingleQuotedString(String::from("endpoint")); /// let value_with_span = value.with_empty_span(); /// assert_eq!(value_with_span.span, Span::empty()); /// ``` /// /// You can also use the [`From`] trait to convert `ValueWithSpan` to/from `Value`s /// ``` /// # use sqlparser::ast::{Value, ValueWithSpan}; /// # use sqlparser::tokenizer::{Location, Span}; /// let value = Value::SingleQuotedString(String::from("endpoint")); /// // converting `Value` to `ValueWithSpan` results in an empty span /// let value_with_span: ValueWithSpan = value.into(); /// assert_eq!(value_with_span.span, Span::empty()); /// // convert back to `Value` /// let value: Value = value_with_span.into(); /// ``` #[derive(Debug, Clone, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct ValueWithSpan { pub value: Value, pub span: Span, } impl PartialEq for ValueWithSpan { fn eq(&self, other: &Self) -> bool { self.value == other.value } } impl Ord for ValueWithSpan { fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.value.cmp(&other.value) } } impl PartialOrd for ValueWithSpan { fn partial_cmp(&self, other: &Self) -> Option { Some(Ord::cmp(self, other)) } } impl core::hash::Hash for ValueWithSpan { fn hash(&self, state: &mut H) { self.value.hash(state); } } impl From for ValueWithSpan { fn from(value: Value) -> Self { value.with_empty_span() } } impl From for Value { fn from(value: ValueWithSpan) -> Self { value.value } } /// Primitive SQL values such as number and string #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr( feature = "visitor", derive(Visit, VisitMut), visit(with = "visit_value") )] pub enum Value { /// Numeric literal #[cfg(not(feature = "bigdecimal"))] Number(String, bool), #[cfg(feature = "bigdecimal")] // HINT: use `test_utils::number` to make an instance of // Value::Number This might help if you your tests pass locally // but fail on CI with the `--all-features` flag enabled Number(BigDecimal, bool), /// 'string value' SingleQuotedString(String), // $$string value$$ (postgres syntax) DollarQuotedString(DollarQuotedString), /// Triple single quoted strings: Example '''abc''' /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) TripleSingleQuotedString(String), /// Triple double quoted strings: Example """abc""" /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) TripleDoubleQuotedString(String), /// e'string value' (postgres extension) /// See [Postgres docs](https://www.postgresql.org/docs/8.3/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS) /// for more details. EscapedStringLiteral(String), /// u&'string value' (postgres extension) /// See [Postgres docs](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-UESCAPE) /// for more details. UnicodeStringLiteral(String), /// B'string value' SingleQuotedByteStringLiteral(String), /// B"string value" DoubleQuotedByteStringLiteral(String), /// Triple single quoted literal with byte string prefix. Example `B'''abc'''` /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) TripleSingleQuotedByteStringLiteral(String), /// Triple double quoted literal with byte string prefix. Example `B"""abc"""` /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) TripleDoubleQuotedByteStringLiteral(String), /// Single quoted literal with raw string prefix. Example `R'abc'` /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) SingleQuotedRawStringLiteral(String), /// Double quoted literal with raw string prefix. Example `R"abc"` /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) DoubleQuotedRawStringLiteral(String), /// Triple single quoted literal with raw string prefix. Example `R'''abc'''` /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) TripleSingleQuotedRawStringLiteral(String), /// Triple double quoted literal with raw string prefix. Example `R"""abc"""` /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) TripleDoubleQuotedRawStringLiteral(String), /// N'string value' NationalStringLiteral(String), /// X'hex value' HexStringLiteral(String), DoubleQuotedString(String), /// Boolean value true or false Boolean(bool), /// `NULL` value Null, /// `?` or `$` Prepared statement arg placeholder Placeholder(String), } impl ValueWithSpan { /// If the underlying literal is a string, regardless of quote style, returns the associated string value pub fn into_string(self) -> Option { self.value.into_string() } } impl Value { /// If the underlying literal is a string, regardless of quote style, returns the associated string value pub fn into_string(self) -> Option { match self { Value::SingleQuotedString(s) | Value::DoubleQuotedString(s) | Value::TripleSingleQuotedString(s) | Value::TripleDoubleQuotedString(s) | Value::SingleQuotedByteStringLiteral(s) | Value::DoubleQuotedByteStringLiteral(s) | Value::TripleSingleQuotedByteStringLiteral(s) | Value::TripleDoubleQuotedByteStringLiteral(s) | Value::SingleQuotedRawStringLiteral(s) | Value::DoubleQuotedRawStringLiteral(s) | Value::TripleSingleQuotedRawStringLiteral(s) | Value::TripleDoubleQuotedRawStringLiteral(s) | Value::EscapedStringLiteral(s) | Value::UnicodeStringLiteral(s) | Value::NationalStringLiteral(s) | Value::HexStringLiteral(s) => Some(s), Value::DollarQuotedString(s) => Some(s.value), _ => None, } } pub fn with_span(self, span: Span) -> ValueWithSpan { ValueWithSpan { value: self, span } } pub fn with_empty_span(self) -> ValueWithSpan { self.with_span(Span::empty()) } } impl fmt::Display for ValueWithSpan { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.value) } } impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Value::Number(v, l) => write!(f, "{}{long}", v, long = if *l { "L" } else { "" }), Value::DoubleQuotedString(v) => write!(f, "\"{}\"", escape_double_quote_string(v)), Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)), Value::TripleSingleQuotedString(v) => { write!(f, "'''{v}'''") } Value::TripleDoubleQuotedString(v) => { write!(f, r#""""{v}""""#) } Value::DollarQuotedString(v) => write!(f, "{v}"), Value::EscapedStringLiteral(v) => write!(f, "E'{}'", escape_escaped_string(v)), Value::UnicodeStringLiteral(v) => write!(f, "U&'{}'", escape_unicode_string(v)), Value::NationalStringLiteral(v) => write!(f, "N'{v}'"), Value::HexStringLiteral(v) => write!(f, "X'{v}'"), Value::Boolean(v) => write!(f, "{v}"), Value::SingleQuotedByteStringLiteral(v) => write!(f, "B'{v}'"), Value::DoubleQuotedByteStringLiteral(v) => write!(f, "B\"{v}\""), Value::TripleSingleQuotedByteStringLiteral(v) => write!(f, "B'''{v}'''"), Value::TripleDoubleQuotedByteStringLiteral(v) => write!(f, r#"B"""{v}""""#), Value::SingleQuotedRawStringLiteral(v) => write!(f, "R'{v}'"), Value::DoubleQuotedRawStringLiteral(v) => write!(f, "R\"{v}\""), Value::TripleSingleQuotedRawStringLiteral(v) => write!(f, "R'''{v}'''"), Value::TripleDoubleQuotedRawStringLiteral(v) => write!(f, r#"R"""{v}""""#), Value::Null => write!(f, "NULL"), Value::Placeholder(v) => write!(f, "{v}"), } } } #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct DollarQuotedString { pub value: String, pub tag: Option, } impl fmt::Display for DollarQuotedString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.tag { Some(tag) => { write!(f, "${}${}${}$", tag, self.value, tag) } None => { write!(f, "$${}$$", self.value) } } } } #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum DateTimeField { Year, Years, Month, Months, /// Week optionally followed by a WEEKDAY. /// /// ```sql /// WEEK(MONDAY) /// ``` /// /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions#extract) Week(Option), Weeks, Day, DayOfWeek, DayOfYear, Days, Date, Datetime, Hour, Hours, Minute, Minutes, Second, Seconds, Century, Decade, Dow, Doy, Epoch, Isodow, IsoWeek, Isoyear, Julian, Microsecond, Microseconds, Millenium, Millennium, Millisecond, Milliseconds, Nanosecond, Nanoseconds, Quarter, Time, Timezone, TimezoneAbbr, TimezoneHour, TimezoneMinute, TimezoneRegion, NoDateTime, /// Arbitrary abbreviation or custom date-time part. /// /// ```sql /// EXTRACT(q FROM CURRENT_TIMESTAMP) /// ``` /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions-date-time#supported-date-and-time-parts) Custom(Ident), } impl fmt::Display for DateTimeField { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { DateTimeField::Year => write!(f, "YEAR"), DateTimeField::Years => write!(f, "YEARS"), DateTimeField::Month => write!(f, "MONTH"), DateTimeField::Months => write!(f, "MONTHS"), DateTimeField::Week(week_day) => { write!(f, "WEEK")?; if let Some(week_day) = week_day { write!(f, "({week_day})")? } Ok(()) } DateTimeField::Weeks => write!(f, "WEEKS"), DateTimeField::Day => write!(f, "DAY"), DateTimeField::DayOfWeek => write!(f, "DAYOFWEEK"), DateTimeField::DayOfYear => write!(f, "DAYOFYEAR"), DateTimeField::Days => write!(f, "DAYS"), DateTimeField::Date => write!(f, "DATE"), DateTimeField::Datetime => write!(f, "DATETIME"), DateTimeField::Hour => write!(f, "HOUR"), DateTimeField::Hours => write!(f, "HOURS"), DateTimeField::Minute => write!(f, "MINUTE"), DateTimeField::Minutes => write!(f, "MINUTES"), DateTimeField::Second => write!(f, "SECOND"), DateTimeField::Seconds => write!(f, "SECONDS"), DateTimeField::Century => write!(f, "CENTURY"), DateTimeField::Decade => write!(f, "DECADE"), DateTimeField::Dow => write!(f, "DOW"), DateTimeField::Doy => write!(f, "DOY"), DateTimeField::Epoch => write!(f, "EPOCH"), DateTimeField::Isodow => write!(f, "ISODOW"), DateTimeField::Isoyear => write!(f, "ISOYEAR"), DateTimeField::IsoWeek => write!(f, "ISOWEEK"), DateTimeField::Julian => write!(f, "JULIAN"), DateTimeField::Microsecond => write!(f, "MICROSECOND"), DateTimeField::Microseconds => write!(f, "MICROSECONDS"), DateTimeField::Millenium => write!(f, "MILLENIUM"), DateTimeField::Millennium => write!(f, "MILLENNIUM"), DateTimeField::Millisecond => write!(f, "MILLISECOND"), DateTimeField::Milliseconds => write!(f, "MILLISECONDS"), DateTimeField::Nanosecond => write!(f, "NANOSECOND"), DateTimeField::Nanoseconds => write!(f, "NANOSECONDS"), DateTimeField::Quarter => write!(f, "QUARTER"), DateTimeField::Time => write!(f, "TIME"), DateTimeField::Timezone => write!(f, "TIMEZONE"), DateTimeField::TimezoneAbbr => write!(f, "TIMEZONE_ABBR"), DateTimeField::TimezoneHour => write!(f, "TIMEZONE_HOUR"), DateTimeField::TimezoneMinute => write!(f, "TIMEZONE_MINUTE"), DateTimeField::TimezoneRegion => write!(f, "TIMEZONE_REGION"), DateTimeField::NoDateTime => write!(f, "NODATETIME"), DateTimeField::Custom(custom) => write!(f, "{custom}"), } } } #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] /// The Unicode Standard defines four normalization forms, which are intended to eliminate /// certain distinctions between visually or functionally identical characters. /// /// See [Unicode Normalization Forms](https://unicode.org/reports/tr15/) for details. pub enum NormalizationForm { /// Canonical Decomposition, followed by Canonical Composition. NFC, /// Canonical Decomposition. NFD, /// Compatibility Decomposition, followed by Canonical Composition. NFKC, /// Compatibility Decomposition. NFKD, } impl fmt::Display for NormalizationForm { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { NormalizationForm::NFC => write!(f, "NFC"), NormalizationForm::NFD => write!(f, "NFD"), NormalizationForm::NFKC => write!(f, "NFKC"), NormalizationForm::NFKD => write!(f, "NFKD"), } } } pub struct EscapeQuotedString<'a> { string: &'a str, quote: char, } impl fmt::Display for EscapeQuotedString<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // EscapeQuotedString doesn't know which mode of escape was // chosen by the user. So this code must to correctly display // strings without knowing if the strings are already escaped // or not. // // If the quote symbol in the string is repeated twice, OR, if // the quote symbol is after backslash, display all the chars // without any escape. However, if the quote symbol is used // just between usual chars, `fmt()` should display it twice." // // The following table has examples // // | original query | mode | AST Node | serialized | // | ------------- | --------- | -------------------------------------------------- | ------------ | // | `"A""B""A"` | no-escape | `DoubleQuotedString(String::from("A\"\"B\"\"A"))` | `"A""B""A"` | // | `"A""B""A"` | default | `DoubleQuotedString(String::from("A\"B\"A"))` | `"A""B""A"` | // | `"A\"B\"A"` | no-escape | `DoubleQuotedString(String::from("A\\\"B\\\"A"))` | `"A\"B\"A"` | // | `"A\"B\"A"` | default | `DoubleQuotedString(String::from("A\"B\"A"))` | `"A""B""A"` | let quote = self.quote; let mut previous_char = char::default(); let mut start_idx = 0; let mut peekable_chars = self.string.char_indices().peekable(); while let Some(&(idx, ch)) = peekable_chars.peek() { match ch { char if char == quote => { if previous_char == '\\' { // the quote is already escaped with a backslash, skip peekable_chars.next(); continue; } peekable_chars.next(); match peekable_chars.peek() { Some((_, c)) if *c == quote => { // the quote is already escaped with another quote, skip peekable_chars.next(); } _ => { // The quote is not escaped. // Including idx in the range, so the quote at idx will be printed twice: // in this call to write_str() and in the next one. f.write_str(&self.string[start_idx..=idx])?; start_idx = idx; } } } _ => { peekable_chars.next(); } } previous_char = ch; } f.write_str(&self.string[start_idx..])?; Ok(()) } } pub fn escape_quoted_string(string: &str, quote: char) -> EscapeQuotedString<'_> { EscapeQuotedString { string, quote } } pub fn escape_single_quote_string(s: &str) -> EscapeQuotedString<'_> { escape_quoted_string(s, '\'') } pub fn escape_double_quote_string(s: &str) -> EscapeQuotedString<'_> { escape_quoted_string(s, '\"') } pub struct EscapeEscapedStringLiteral<'a>(&'a str); impl fmt::Display for EscapeEscapedStringLiteral<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for c in self.0.chars() { match c { '\'' => { write!(f, r#"\'"#)?; } '\\' => { write!(f, r#"\\"#)?; } '\n' => { write!(f, r#"\n"#)?; } '\t' => { write!(f, r#"\t"#)?; } '\r' => { write!(f, r#"\r"#)?; } _ => { write!(f, "{c}")?; } } } Ok(()) } } pub fn escape_escaped_string(s: &str) -> EscapeEscapedStringLiteral<'_> { EscapeEscapedStringLiteral(s) } pub struct EscapeUnicodeStringLiteral<'a>(&'a str); impl fmt::Display for EscapeUnicodeStringLiteral<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for c in self.0.chars() { match c { '\'' => { write!(f, "''")?; } '\\' => { write!(f, r#"\\"#)?; } x if x.is_ascii() => { write!(f, "{c}")?; } _ => { let codepoint = c as u32; // if the character fits in 32 bits, we can use the \XXXX format // otherwise, we need to use the \+XXXXXX format if codepoint <= 0xFFFF { write!(f, "\\{codepoint:04X}")?; } else { write!(f, "\\+{codepoint:06X}")?; } } } } Ok(()) } } pub fn escape_unicode_string(s: &str) -> EscapeUnicodeStringLiteral<'_> { EscapeUnicodeStringLiteral(s) } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum TrimWhereField { Both, Leading, Trailing, } impl fmt::Display for TrimWhereField { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use TrimWhereField::*; f.write_str(match self { Both => "BOTH", Leading => "LEADING", Trailing => "TRAILING", }) } }