diff --git a/src/ast/helpers/key_value_options.rs b/src/ast/helpers/key_value_options.rs index e8e543b01..2aa59d9d7 100644 --- a/src/ast/helpers/key_value_options.rs +++ b/src/ast/helpers/key_value_options.rs @@ -29,7 +29,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::ast::{display_comma_separated, display_separated, Value}; +use crate::ast::{display_comma_separated, display_separated, ValueWithSpan}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -75,9 +75,9 @@ pub struct KeyValueOption { /// The kind of value for a key-value option. pub enum KeyValueOptionKind { /// A single value. - Single(Value), + Single(ValueWithSpan), /// Multiple values. - Multi(Vec), + Multi(Vec), /// A nested list of key-value options. KeyValueOptions(Box), } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 36c41d6ca..d7a3679a4 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -624,9 +624,9 @@ impl fmt::Display for MapEntry { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum CastFormat { /// A simple cast format specified by a `Value`. - Value(Value), + Value(ValueWithSpan), /// A cast format with an explicit time zone: `(format, timezone)`. - ValueAtTimeZone(Value, Value), + ValueAtTimeZone(ValueWithSpan, ValueWithSpan), } /// An element of a JSON path. @@ -778,7 +778,7 @@ pub enum CeilFloorKind { /// `CEIL( TO )` DateTimeField(DateTimeField), /// `CEIL( [, ])` - Scale(Value), + Scale(ValueWithSpan), } /// A WHEN clause in a CASE expression containing both @@ -956,7 +956,7 @@ pub enum Expr { /// Pattern expression. pattern: Box, /// Optional escape character. - escape_char: Option, + escape_char: Option, }, /// `ILIKE` (case-insensitive `LIKE`) ILike { @@ -970,7 +970,7 @@ pub enum Expr { /// Pattern expression. pattern: Box, /// Optional escape character. - escape_char: Option, + escape_char: Option, }, /// `SIMILAR TO` regex SimilarTo { @@ -981,7 +981,7 @@ pub enum Expr { /// Pattern expression. pattern: Box, /// Optional escape character. - escape_char: Option, + escape_char: Option, }, /// MySQL: `RLIKE` regex or `REGEXP` regex RLike { @@ -1146,12 +1146,12 @@ pub enum Expr { /// TRIM(, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic /// ``` Trim { - /// The expression to trim from. - expr: Box, /// Which side to trim: `BOTH`, `LEADING`, or `TRAILING`. trim_where: Option, - /// Optional expression specifying what to trim from the value. + /// Optional expression specifying what to trim from the value `expr`. trim_what: Option>, + /// The expression to trim from. + expr: Box, /// Optional list of characters to trim (dialect-specific). trim_characters: Option>, }, @@ -1292,7 +1292,7 @@ pub enum Expr { /// `(, , ...)`. columns: Vec, /// ``. - match_value: Value, + match_value: ValueWithSpan, /// `` opt_search_modifier: Option, }, @@ -3295,7 +3295,7 @@ pub enum Set { /// Transaction modes (e.g., ISOLATION LEVEL, READ ONLY). modes: Vec, /// Optional snapshot value for transaction snapshot control. - snapshot: Option, + snapshot: Option, /// `true` when the `SESSION` keyword was used. session: bool, }, @@ -4630,7 +4630,7 @@ pub enum Statement { /// Pragma name (possibly qualified). name: ObjectName, /// Optional pragma value. - value: Option, + value: Option, /// Whether the pragma used `=`. is_eq: bool, }, @@ -6752,7 +6752,7 @@ pub enum FetchDirection { /// Fetch a specific count of rows. Count { /// The limit value for the count. - limit: Value, + limit: ValueWithSpan, }, /// Fetch the next row. Next, @@ -6765,12 +6765,12 @@ pub enum FetchDirection { /// Fetch an absolute row by index. Absolute { /// The absolute index value. - limit: Value, + limit: ValueWithSpan, }, /// Fetch a row relative to the current position. Relative { /// The relative offset value. - limit: Value, + limit: ValueWithSpan, }, /// Fetch all rows. All, @@ -6779,7 +6779,7 @@ pub enum FetchDirection { /// Fetch forward by an optional limit. Forward { /// Optional forward limit. - limit: Option, + limit: Option, }, /// Fetch all forward rows. ForwardAll, @@ -6788,7 +6788,7 @@ pub enum FetchDirection { /// Fetch backward by an optional limit. Backward { /// Optional backward limit. - limit: Option, + limit: Option, }, /// Fetch all backward rows. BackwardAll, @@ -8116,7 +8116,7 @@ pub enum FunctionArgumentClause { /// The `SEPARATOR` clause to the [`GROUP_CONCAT`] function in MySQL. /// /// [`GROUP_CONCAT`]: https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat - Separator(Value), + Separator(ValueWithSpan), /// The `ON NULL` clause for some JSON functions. /// /// [MSSQL `JSON_ARRAY`](https://learn.microsoft.com/en-us/sql/t-sql/functions/json-array-transact-sql?view=sql-server-ver16) @@ -9465,7 +9465,7 @@ impl fmt::Display for CopyLegacyOption { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct FileSize { /// Numeric size value. - pub size: Value, + pub size: ValueWithSpan, /// Optional unit for the size (MB or GB). pub unit: Option, } @@ -10654,11 +10654,11 @@ pub struct ShowStatementOptions { /// Optional scope to show in (for example: TABLE, SCHEMA). pub show_in: Option, /// Optional `STARTS WITH` filter value. - pub starts_with: Option, + pub starts_with: Option, /// Optional `LIMIT` expression. pub limit: Option, /// Optional `FROM` value used with `LIMIT`. - pub limit_from: Option, + pub limit_from: Option, /// Optional filter position (infix or suffix) for `LIKE`/`FILTER`. pub filter_position: Option, } @@ -11474,7 +11474,7 @@ pub struct AlterUserRemoveRoleDelegation { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub struct AlterUserAddMfaMethodOtp { /// Optional OTP count parameter. - pub count: Option, + pub count: Option, } /// ```sql @@ -11795,7 +11795,7 @@ pub struct VacuumStatement { /// Optional table to run `VACUUM` on. pub table_name: Option, /// Optional threshold value (percent) for `TO threshold PERCENT`. - pub threshold: Option, + pub threshold: Option, /// Whether `BOOST` was specified. pub boost: bool, } diff --git a/src/ast/query.rs b/src/ast/query.rs index ca74db440..a52d518b1 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -1552,7 +1552,7 @@ pub enum TableFactor { json_expr: Expr, /// The path to the array or object to be iterated over. /// It must evaluate to a json array or object. - json_path: Value, + json_path: ValueWithSpan, /// The columns to be extracted from each element of the array or object. /// Each column must have a name and a type. columns: Vec, @@ -1573,7 +1573,7 @@ pub enum TableFactor { json_expr: Expr, /// The path to the array or object to be iterated over. /// It must evaluate to a json array or object. - json_path: Option, + json_path: Option, /// The columns to be extracted from each element of the array or object. /// Each column must have a name and a type. columns: Vec, @@ -1833,7 +1833,7 @@ pub struct TableSampleSeed { /// Seed modifier (e.g. `REPEATABLE` or `SEED`). pub modifier: TableSampleSeedModifier, /// The seed value expression. - pub value: Value, + pub value: ValueWithSpan, } impl fmt::Display for TableSampleSeed { @@ -1889,9 +1889,9 @@ impl fmt::Display for TableSampleUnit { /// Bucket-based sampling clause: `BUCKET OUT OF [ON ]`. pub struct TableSampleBucket { /// The bucket index expression. - pub bucket: Value, + pub bucket: ValueWithSpan, /// The total number of buckets expression. - pub total: Value, + pub total: ValueWithSpan, /// Optional `ON ` specification. pub on: Option, } @@ -3979,7 +3979,7 @@ impl fmt::Display for JsonTableColumn { /// A nested column in a `JSON_TABLE` column list. pub struct JsonTableNestedColumn { /// JSON path expression (must be a literal `Value`). - pub path: Value, + pub path: ValueWithSpan, /// Columns extracted from the matched nested array. pub columns: Vec, } @@ -4011,7 +4011,7 @@ pub struct JsonTableNamedColumn { /// The type of the column to be extracted. pub r#type: DataType, /// The path to the column to be extracted. Must be a literal string. - pub path: Value, + pub path: ValueWithSpan, /// true if the column is a boolean set to true if the given path exists pub exists: bool, /// The empty handling clause of the column @@ -4050,7 +4050,7 @@ pub enum JsonTableColumnErrorHandling { /// `NULL` — return NULL when the path does not match. Null, /// `DEFAULT ` — use the provided `Value` as a default. - Default(Value), + Default(ValueWithSpan), /// `ERROR` — raise an error. Error, } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 2af57d98e..d80a3f4d5 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -46,8 +46,8 @@ use super::{ RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered, TableWithJoins, Update, - UpdateTableFromKind, Use, Value, Values, ViewColumnDef, WhileStatement, - WildcardAdditionalOptions, With, WithFill, + UpdateTableFromKind, Use, Values, ViewColumnDef, WhileStatement, WildcardAdditionalOptions, + With, WithFill, }; /// Given an iterator of spans, return the [Span::union] of all spans. @@ -2185,13 +2185,6 @@ impl Spanned for ValueWithSpan { } } -/// The span is stored in the `ValueWrapper` struct -impl Spanned for Value { - fn span(&self) -> Span { - Span::empty() // # todo: Value needs to store spans before this is possible - } -} - impl Spanned for Join { fn span(&self) -> Span { let Join { @@ -2565,6 +2558,7 @@ impl Spanned for comments::CommentWithSpan { #[cfg(test)] pub mod tests { + use crate::ast::Value; use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect}; use crate::parser::Parser; use crate::tokenizer::{Location, Span}; diff --git a/src/ast/value.rs b/src/ast/value.rs index 8879a252b..5f069f36c 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -18,7 +18,10 @@ #[cfg(not(feature = "std"))] use alloc::string::String; -use core::fmt; +use core::{ + fmt, + ops::{Deref, DerefMut}, +}; #[cfg(feature = "bigdecimal")] use bigdecimal::BigDecimal; @@ -67,7 +70,11 @@ use sqlparser_derive::{Visit, VisitMut}; /// A `Value` paired with its source `Span` location. #[derive(Debug, Clone, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +#[cfg_attr( + feature = "visitor", + derive(Visit, VisitMut), + visit(with = "visit_value") +)] pub struct ValueWithSpan { /// The wrapped `Value`. pub value: Value, @@ -111,14 +118,24 @@ impl From for Value { } } +impl Deref for ValueWithSpan { + type Target = Value; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl DerefMut for ValueWithSpan { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.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") -)] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum Value { /// Numeric literal #[cfg(not(feature = "bigdecimal"))] diff --git a/src/ast/visitor.rs b/src/ast/visitor.rs index 5f9b37489..30673dfa0 100644 --- a/src/ast/visitor.rs +++ b/src/ast/visitor.rs @@ -17,7 +17,7 @@ //! Recursive visitors for ast Nodes. See [`Visitor`] for more details. -use crate::ast::{Expr, ObjectName, Query, Select, Statement, TableFactor, Value}; +use crate::ast::{Expr, ObjectName, Query, Select, Statement, TableFactor, ValueWithSpan}; use core::ops::ControlFlow; /// A type that can be visited by a [`Visitor`]. See [`Visitor`] for @@ -258,12 +258,12 @@ pub trait Visitor { } /// Invoked for any Value that appear in the AST before visiting children - fn pre_visit_value(&mut self, _value: &Value) -> ControlFlow { + fn pre_visit_value(&mut self, _value: &ValueWithSpan) -> ControlFlow { ControlFlow::Continue(()) } /// Invoked for any Value that appear in the AST after visiting children - fn post_visit_value(&mut self, _value: &Value) -> ControlFlow { + fn post_visit_value(&mut self, _value: &ValueWithSpan) -> ControlFlow { ControlFlow::Continue(()) } } @@ -386,12 +386,12 @@ pub trait VisitorMut { } /// Invoked for any value that appear in the AST before visiting children - fn pre_visit_value(&mut self, _value: &mut Value) -> ControlFlow { + fn pre_visit_value(&mut self, _value: &mut ValueWithSpan) -> ControlFlow { ControlFlow::Continue(()) } /// Invoked for any statements that appear in the AST after visiting children - fn post_visit_value(&mut self, _value: &mut Value) -> ControlFlow { + fn post_visit_value(&mut self, _value: &mut ValueWithSpan) -> ControlFlow { ControlFlow::Continue(()) } } @@ -1015,7 +1015,7 @@ mod tests { #[cfg(test)] mod visit_mut_tests { - use crate::ast::{Statement, Value, VisitMut, VisitorMut}; + use crate::ast::{Statement, Value, ValueWithSpan, VisitMut, VisitorMut}; use crate::dialect::GenericDialect; use crate::parser::Parser; use crate::tokenizer::Tokenizer; @@ -1029,13 +1029,13 @@ mod visit_mut_tests { impl VisitorMut for MutatorVisitor { type Break = (); - fn pre_visit_value(&mut self, value: &mut Value) -> ControlFlow { + fn pre_visit_value(&mut self, value: &mut ValueWithSpan) -> ControlFlow { self.index += 1; - *value = Value::SingleQuotedString(format!("REDACTED_{}", self.index)); + value.value = Value::SingleQuotedString(format!("REDACTED_{}", self.index)); ControlFlow::Continue(()) } - fn post_visit_value(&mut self, _value: &mut Value) -> ControlFlow { + fn post_visit_value(&mut self, _value: &mut ValueWithSpan) -> ControlFlow { ControlFlow::Continue(()) } } diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 416e5051d..1ac21d007 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -39,8 +39,8 @@ use crate::ast::{ use crate::dialect::{Dialect, Precedence}; use crate::keywords::Keyword; use crate::parser::{IsOptional, Parser, ParserError}; -use crate::tokenizer::Token; use crate::tokenizer::TokenWithSpan; +use crate::tokenizer::{Span, Token}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; #[cfg(not(feature = "std"))] @@ -1634,8 +1634,8 @@ fn parse_session_options( let mut options: Vec = Vec::new(); let empty = String::new; loop { - let next_token = parser.peek_token(); - match next_token.token { + let peeked_token = parser.peek_token(); + match peeked_token.token { Token::SemiColon | Token::EOF => break, Token::Comma => { parser.advance_token(); @@ -1649,12 +1649,17 @@ fn parse_session_options( } else { options.push(KeyValueOption { option_name: key.value, - option_value: KeyValueOptionKind::Single(Value::Placeholder(empty())), + option_value: KeyValueOptionKind::Single( + Value::Placeholder(empty()).with_span(Span { + start: peeked_token.span.end, + end: peeked_token.span.end, + }), + ), }); } } _ => { - return parser.expected("another option or end of statement", next_token); + return parser.expected("another option or end of statement", peeked_token); } } } diff --git a/src/parser/alter.rs b/src/parser/alter.rs index ce1220e16..4000eb26b 100644 --- a/src/parser/alter.rs +++ b/src/parser/alter.rs @@ -219,7 +219,7 @@ impl Parser<'_> { if self.parse_keywords(&[Keyword::ADD, Keyword::MFA, Keyword::METHOD, Keyword::OTP]) { let count = if self.parse_keyword(Keyword::COUNT) { self.expect_token(&Token::Eq)?; - Some(self.parse_value()?.into()) + Some(self.parse_value()?) } else { None }; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index cefc0c6f6..6048c50b8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2688,7 +2688,7 @@ impl<'a> Parser<'a> { /// Parse an optional `FORMAT` clause for `CAST` expressions. pub fn parse_optional_cast_format(&mut self) -> Result, ParserError> { if self.parse_keyword(Keyword::FORMAT) { - let value = self.parse_value()?.value; + let value = self.parse_value()?; match self.parse_optional_time_zone()? { Some(tz) => Ok(Some(CastFormat::ValueAtTimeZone(value, tz))), None => Ok(Some(CastFormat::Value(value))), @@ -2699,9 +2699,9 @@ impl<'a> Parser<'a> { } /// Parse an optional `AT TIME ZONE` clause. - pub fn parse_optional_time_zone(&mut self) -> Result, ParserError> { + pub fn parse_optional_time_zone(&mut self) -> Result, ParserError> { if self.parse_keywords(&[Keyword::AT, Keyword::TIME, Keyword::ZONE]) { - self.parse_value().map(|v| Some(v.value)) + self.parse_value().map(Some) } else { Ok(None) } @@ -2833,13 +2833,13 @@ impl<'a> Parser<'a> { CeilFloorKind::DateTimeField(self.parse_date_time_field()?) } else if self.consume_token(&Token::Comma) { // Parse `CEIL/FLOOR(expr, scale)` - match self.parse_value()?.value { - Value::Number(n, s) => CeilFloorKind::Scale(Value::Number(n, s)), - _ => { - return Err(ParserError::ParserError( - "Scale field can only be of number type".to_string(), - )) - } + let v = self.parse_value()?; + if matches!(v.value, Value::Number(_, _)) { + CeilFloorKind::Scale(v) + } else { + return Err(ParserError::ParserError( + "Scale field can only be of number type".to_string(), + )); } } else { CeilFloorKind::DateTimeField(DateTimeField::NoDateTime) @@ -3191,7 +3191,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; // MySQL is too permissive about the value, IMO we can't validate it perfectly on syntax level. - let match_value = self.parse_value()?.value; + let match_value = self.parse_value()?; let in_natural_language_mode_keywords = &[ Keyword::IN, @@ -4087,9 +4087,9 @@ impl<'a> Parser<'a> { } /// Parse the `ESCAPE CHAR` portion of `LIKE`, `ILIKE`, and `SIMILAR TO` - pub fn parse_escape_char(&mut self) -> Result, ParserError> { + pub fn parse_escape_char(&mut self) -> Result, ParserError> { if self.parse_keyword(Keyword::ESCAPE) { - Ok(Some(self.parse_value()?.into())) + Ok(Some(self.parse_value()?)) } else { Ok(None) } @@ -7845,11 +7845,11 @@ impl<'a> Parser<'a> { FetchDirection::Last } else if self.parse_keyword(Keyword::ABSOLUTE) { FetchDirection::Absolute { - limit: self.parse_number_value()?.value, + limit: self.parse_number_value()?, } } else if self.parse_keyword(Keyword::RELATIVE) { FetchDirection::Relative { - limit: self.parse_number_value()?.value, + limit: self.parse_number_value()?, } } else if self.parse_keyword(Keyword::FORWARD) { if self.parse_keyword(Keyword::ALL) { @@ -7857,7 +7857,7 @@ impl<'a> Parser<'a> { } else { FetchDirection::Forward { // TODO: Support optional - limit: Some(self.parse_number_value()?.value), + limit: Some(self.parse_number_value()?), } } } else if self.parse_keyword(Keyword::BACKWARD) { @@ -7866,14 +7866,14 @@ impl<'a> Parser<'a> { } else { FetchDirection::Backward { // TODO: Support optional - limit: Some(self.parse_number_value()?.value), + limit: Some(self.parse_number_value()?), } } } else if self.parse_keyword(Keyword::ALL) { FetchDirection::All } else { FetchDirection::Count { - limit: self.parse_number_value()?.value, + limit: self.parse_number_value()?, } }; @@ -11391,7 +11391,7 @@ impl<'a> Parser<'a> { } Some(Keyword::MAXFILESIZE) => { let _ = self.parse_keyword(Keyword::AS); - let size = self.parse_number_value()?.value; + let size = self.parse_number_value()?; let unit = match self.parse_one_of_keywords(&[Keyword::MB, Keyword::GB]) { Some(Keyword::MB) => Some(FileSizeUnit::MB), Some(Keyword::GB) => Some(FileSizeUnit::GB), @@ -11464,7 +11464,7 @@ impl<'a> Parser<'a> { } fn parse_file_size(&mut self) -> Result { - let size = self.parse_number_value()?.value; + let size = self.parse_number_value()?; let unit = self.maybe_parse_file_size_unit(); Ok(FileSize { size, unit }) } @@ -14846,7 +14846,7 @@ impl<'a> Parser<'a> { .into()); } else if self.parse_keyword(Keyword::TRANSACTION) { if self.parse_keyword(Keyword::SNAPSHOT) { - let snapshot_id = self.parse_value()?.value; + let snapshot_id = self.parse_value()?; return Ok(Set::SetTransaction { modes: vec![], snapshot: Some(snapshot_id), @@ -15681,7 +15681,7 @@ impl<'a> Parser<'a> { } else if self.parse_keyword_with_tokens(Keyword::JSON_TABLE, &[Token::LParen]) { let json_expr = self.parse_expr()?; self.expect_token(&Token::Comma)?; - let json_path = self.parse_value()?.value; + let json_path = self.parse_value()?; self.expect_keyword_is(Keyword::COLUMNS)?; self.expect_token(&Token::LParen)?; let columns = self.parse_comma_separated(Parser::parse_json_table_column_def)?; @@ -15865,9 +15865,9 @@ impl<'a> Parser<'a> { let parenthesized = self.consume_token(&Token::LParen); let (quantity, bucket) = if parenthesized && self.parse_keyword(Keyword::BUCKET) { - let selected_bucket = self.parse_number_value()?.value; + let selected_bucket = self.parse_number_value()?; self.expect_keywords(&[Keyword::OUT, Keyword::OF])?; - let total = self.parse_number_value()?.value; + let total = self.parse_number_value()?; let on = if self.parse_keyword(Keyword::ON) { Some(self.parse_expr()?) } else { @@ -15945,7 +15945,7 @@ impl<'a> Parser<'a> { modifier: TableSampleSeedModifier, ) -> Result { self.expect_token(&Token::LParen)?; - let value = self.parse_number_value()?.value; + let value = self.parse_number_value()?; self.expect_token(&Token::RParen)?; Ok(TableSampleSeed { modifier, value }) } @@ -15956,7 +15956,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let json_expr = self.parse_expr()?; let json_path = if self.consume_token(&Token::Comma) { - Some(self.parse_value()?.value) + Some(self.parse_value()?) } else { None }; @@ -16418,7 +16418,7 @@ impl<'a> Parser<'a> { pub fn parse_json_table_column_def(&mut self) -> Result { if self.parse_keyword(Keyword::NESTED) { let _has_path_keyword = self.parse_keyword(Keyword::PATH); - let path = self.parse_value()?.value; + let path = self.parse_value()?; self.expect_keyword_is(Keyword::COLUMNS)?; let columns = self.parse_parenthesized(|p| { p.parse_comma_separated(Self::parse_json_table_column_def) @@ -16436,7 +16436,7 @@ impl<'a> Parser<'a> { let r#type = self.parse_data_type()?; let exists = self.parse_keyword(Keyword::EXISTS); self.expect_keyword_is(Keyword::PATH)?; - let path = self.parse_value()?.value; + let path = self.parse_value()?; let mut on_empty = None; let mut on_error = None; while let Some(error_handling) = self.parse_json_table_column_error_handling()? { @@ -16493,7 +16493,7 @@ impl<'a> Parser<'a> { } else if self.parse_keyword(Keyword::ERROR) { JsonTableColumnErrorHandling::Error } else if self.parse_keyword(Keyword::DEFAULT) { - JsonTableColumnErrorHandling::Default(self.parse_value()?.value) + JsonTableColumnErrorHandling::Default(self.parse_value()?) } else { return Ok(None); }; @@ -17964,7 +17964,7 @@ impl<'a> Parser<'a> { if dialect_of!(self is GenericDialect | MySqlDialect) && self.parse_keyword(Keyword::SEPARATOR) { - clauses.push(FunctionArgumentClause::Separator(self.parse_value()?.value)); + clauses.push(FunctionArgumentClause::Separator(self.parse_value()?)); } if let Some(on_overflow) = self.parse_listagg_on_overflow()? { @@ -19023,12 +19023,13 @@ impl<'a> Parser<'a> { }) } - fn parse_pragma_value(&mut self) -> Result { - match self.parse_value()?.value { - v @ Value::SingleQuotedString(_) => Ok(v), - v @ Value::DoubleQuotedString(_) => Ok(v), - v @ Value::Number(_, _) => Ok(v), - v @ Value::Placeholder(_) => Ok(v), + fn parse_pragma_value(&mut self) -> Result { + let v = self.parse_value()?; + match &v.value { + Value::SingleQuotedString(_) => Ok(v), + Value::DoubleQuotedString(_) => Ok(v), + Value::Number(_, _) => Ok(v), + Value::Placeholder(_) => Ok(v), _ => { self.prev_token(); self.expected_ref("number or string or ? placeholder", self.peek_token_ref()) @@ -19808,7 +19809,7 @@ impl<'a> Parser<'a> { let threshold = if self.parse_keyword(Keyword::TO) { let value = self.parse_value()?; self.expect_keyword(Keyword::PERCENT)?; - Some(value.value) + Some(value) } else { None }; @@ -19936,9 +19937,9 @@ impl<'a> Parser<'a> { })) } - fn maybe_parse_show_stmt_starts_with(&mut self) -> Result, ParserError> { + fn maybe_parse_show_stmt_starts_with(&mut self) -> Result, ParserError> { if self.parse_keywords(&[Keyword::STARTS, Keyword::WITH]) { - Ok(Some(self.parse_value()?.value)) + Ok(Some(self.parse_value()?)) } else { Ok(None) } @@ -19952,9 +19953,9 @@ impl<'a> Parser<'a> { } } - fn maybe_parse_show_stmt_from(&mut self) -> Result, ParserError> { + fn maybe_parse_show_stmt_from(&mut self) -> Result, ParserError> { if self.parse_keyword(Keyword::FROM) { - Ok(Some(self.parse_value()?.value)) + Ok(Some(self.parse_value()?)) } else { Ok(None) } @@ -20017,30 +20018,31 @@ impl<'a> Parser<'a> { key: &Word, ) -> Result { self.expect_token(&Token::Eq)?; - match self.peek_token().token { + let peeked_token = self.peek_token(); + match peeked_token.token { Token::SingleQuotedString(_) => Ok(KeyValueOption { option_name: key.value.clone(), - option_value: KeyValueOptionKind::Single(self.parse_value()?.into()), + option_value: KeyValueOptionKind::Single(self.parse_value()?), }), Token::Word(word) if word.keyword == Keyword::TRUE || word.keyword == Keyword::FALSE => { Ok(KeyValueOption { option_name: key.value.clone(), - option_value: KeyValueOptionKind::Single(self.parse_value()?.into()), + option_value: KeyValueOptionKind::Single(self.parse_value()?), }) } Token::Number(..) => Ok(KeyValueOption { option_name: key.value.clone(), - option_value: KeyValueOptionKind::Single(self.parse_value()?.into()), + option_value: KeyValueOptionKind::Single(self.parse_value()?), }), Token::Word(word) => { self.next_token(); Ok(KeyValueOption { option_name: key.value.clone(), - option_value: KeyValueOptionKind::Single(Value::Placeholder( - word.value.clone(), - )), + option_value: KeyValueOptionKind::Single( + Value::Placeholder(word.value.clone()).with_span(peeked_token.span), + ), }) } Token::LParen => { @@ -20053,13 +20055,10 @@ impl<'a> Parser<'a> { parser.expect_token(&Token::RParen)?; values })? { - Some(values) => { - let values = values.into_iter().map(|v| v.value).collect(); - Ok(KeyValueOption { - option_name: key.value.clone(), - option_value: KeyValueOptionKind::Multi(values), - }) - } + Some(values) => Ok(KeyValueOption { + option_name: key.value.clone(), + option_value: KeyValueOptionKind::Multi(values), + }), None => Ok(KeyValueOption { option_name: key.value.clone(), option_value: KeyValueOptionKind::KeyValueOptions(Box::new( diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 6f9e46959..e5cdb1cdb 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2104,7 +2104,7 @@ fn parse_ilike() { pattern: Box::new(Expr::Value( (Value::SingleQuotedString("%a".to_string())).with_empty_span() )), - escape_char: Some(Value::SingleQuotedString('^'.to_string())), + escape_char: Some(Value::SingleQuotedString('^'.to_string()).with_empty_span()), any: false, }, select.selection.unwrap() @@ -2168,7 +2168,7 @@ fn parse_like() { pattern: Box::new(Expr::Value( (Value::SingleQuotedString("%a".to_string())).with_empty_span() )), - escape_char: Some(Value::SingleQuotedString('^'.to_string())), + escape_char: Some(Value::SingleQuotedString('^'.to_string()).with_empty_span()), any: false, }, select.selection.unwrap() @@ -2231,7 +2231,7 @@ fn parse_similar_to() { pattern: Box::new(Expr::Value( (Value::SingleQuotedString("%a".to_string())).with_empty_span() )), - escape_char: Some(Value::SingleQuotedString('^'.to_string())), + escape_char: Some(Value::SingleQuotedString('^'.to_string()).with_empty_span()), }, select.selection.unwrap() ); @@ -2248,7 +2248,7 @@ fn parse_similar_to() { pattern: Box::new(Expr::Value( (Value::SingleQuotedString("%a".to_string())).with_empty_span() )), - escape_char: Some(Value::Null), + escape_char: Some(Value::Null.with_empty_span()), }, select.selection.unwrap() ); @@ -2266,7 +2266,7 @@ fn parse_similar_to() { pattern: Box::new(Expr::Value( (Value::SingleQuotedString("%a".to_string())).with_empty_span() )), - escape_char: Some(Value::SingleQuotedString('^'.to_string())), + escape_char: Some(Value::SingleQuotedString('^'.to_string()).with_empty_span()), })), select.selection.unwrap() ); @@ -3324,7 +3324,9 @@ fn parse_ceil_scale() { assert_eq!( &Expr::Ceil { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(bigdecimal::BigDecimal::from(2), false)), + field: CeilFloorKind::Scale( + Value::Number(bigdecimal::BigDecimal::from(2), false).with_empty_span() + ), }, expr_from_projection(only(&select.projection)), ); @@ -3333,7 +3335,7 @@ fn parse_ceil_scale() { assert_eq!( &Expr::Ceil { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(2.to_string(), false)), + field: CeilFloorKind::Scale(Value::Number(2.to_string(), false).with_empty_span()), }, expr_from_projection(only(&select.projection)), ); @@ -3348,7 +3350,9 @@ fn parse_floor_scale() { assert_eq!( &Expr::Floor { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(bigdecimal::BigDecimal::from(2), false)), + field: CeilFloorKind::Scale( + Value::Number(bigdecimal::BigDecimal::from(2), false).with_empty_span() + ), }, expr_from_projection(only(&select.projection)), ); @@ -3357,7 +3361,7 @@ fn parse_floor_scale() { assert_eq!( &Expr::Floor { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(2.to_string(), false)), + field: CeilFloorKind::Scale(Value::Number(2.to_string(), false).with_empty_span()), }, expr_from_projection(only(&select.projection)), ); @@ -17638,19 +17642,21 @@ fn parse_create_user() { options: vec![ KeyValueOption { option_name: "PASSWORD".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "secret".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("secret".to_string()).with_empty_span() + ), }, KeyValueOption { option_name: "MUST_CHANGE_PASSWORD".to_string(), - option_value: KeyValueOptionKind::Single(Value::Boolean(false)), + option_value: KeyValueOptionKind::Single( + Value::Boolean(false).with_empty_span() + ), }, KeyValueOption { option_name: "TYPE".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder( - "SERVICE".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("SERVICE".to_string()).with_empty_span() + ), }, ], }, @@ -17663,15 +17669,15 @@ fn parse_create_user() { options: vec![ KeyValueOption { option_name: "t1".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "v1".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("v1".to_string()).with_empty_span() + ), }, KeyValueOption { option_name: "t2".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "v2".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("v2".to_string()).with_empty_span() + ), }, ] } @@ -18297,9 +18303,9 @@ fn test_parse_alter_user() { alter.set_tag.options, vec![KeyValueOption { option_name: "k1".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "v1".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("v1".to_string()).with_empty_span() + ), },] ); } @@ -18333,17 +18339,21 @@ fn test_parse_alter_user() { options: vec![ KeyValueOption { option_name: "PASSWORD".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "secret".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("secret".to_string()).with_empty_span() + ), }, KeyValueOption { option_name: "MUST_CHANGE_PASSWORD".to_string(), - option_value: KeyValueOptionKind::Single(Value::Boolean(true)), + option_value: KeyValueOptionKind::Single( + Value::Boolean(true).with_empty_span() + ), }, KeyValueOption { option_name: "MINS_TO_UNLOCK".to_string(), - option_value: KeyValueOptionKind::Single(number("10")), + option_value: KeyValueOptionKind::Single( + number("10").with_empty_span() + ), }, ] } @@ -18370,7 +18380,8 @@ fn test_parse_alter_user() { option_name: "DEFAULT_SECONDARY_ROLES".to_string(), option_value: KeyValueOptionKind::Multi(vec![Value::SingleQuotedString( "ALL".to_string() - )]) + ) + .with_empty_span()]) }] ); } @@ -18394,9 +18405,9 @@ fn test_parse_alter_user() { options: vec![ KeyValueOption { option_name: "TYPE".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder( - "AWS".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("AWS".to_string()).with_empty_span() + ), }, KeyValueOption { option_name: "ARN".to_string(), @@ -18404,6 +18415,7 @@ fn test_parse_alter_user() { Value::SingleQuotedString( "arn:aws:iam::123456789:r1/".to_string() ) + .with_empty_span() ), }, ] diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 733923f4d..e8ed79492 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -496,7 +496,7 @@ fn parse_mssql_openjson() { json_expr: Expr::CompoundIdentifier( vec![Ident::new("A"), Ident::new("param"),] ), - json_path: Some(Value::SingleQuotedString("$.config".into())), + json_path: Some(Value::SingleQuotedString("$.config".into()).with_empty_span()), columns: vec![ OpenJsonTableColumn { name: Ident::new("kind"), @@ -658,7 +658,7 @@ fn parse_mssql_openjson() { json_expr: Expr::CompoundIdentifier( vec![Ident::new("A"), Ident::new("param"),] ), - json_path: Some(Value::SingleQuotedString("$.config".into())), + json_path: Some(Value::SingleQuotedString("$.config".into()).with_empty_span()), columns: vec![], alias: table_alias(true, "B") }, diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 6c59997c3..269787c29 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -3810,14 +3810,14 @@ fn parse_json_table() { .relation, TableFactor::JsonTable { json_expr: Expr::Value((Value::SingleQuotedString("[1,2]".to_string())).with_empty_span()), - json_path: Value::SingleQuotedString("$[*]".to_string()), + json_path: Value::SingleQuotedString("$[*]".to_string()).with_empty_span(), columns: vec![ JsonTableColumn::Named(JsonTableNamedColumn { name: Ident::new("x"), r#type: DataType::Int(None), - path: Value::SingleQuotedString("$".to_string()), + path: Value::SingleQuotedString("$".to_string()).with_empty_span(), exists: false, - on_empty: Some(JsonTableColumnErrorHandling::Default(Value::SingleQuotedString("0".to_string()))), + on_empty: Some(JsonTableColumnErrorHandling::Default(Value::SingleQuotedString("0".to_string()).with_empty_span())), on_error: Some(JsonTableColumnErrorHandling::Null), }), ], @@ -4233,7 +4233,10 @@ fn parse_match_against_with_alias() { Ident::new("ReferenceID") ])] ); - assert_eq!(match_value, Value::SingleQuotedString("AAA".to_owned())); + assert_eq!( + match_value, + Value::SingleQuotedString("AAA".to_owned()).with_empty_span() + ); assert_eq!(opt_search_modifier, Some(SearchModifier::InBooleanMode)); } _ => unreachable!(), diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 9a4ff418e..af0f2be33 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -3287,7 +3287,7 @@ fn test_transaction_statement() { statement, Statement::Set(Set::SetTransaction { modes: vec![], - snapshot: Some(Value::SingleQuotedString(String::from("000003A1-1"))), + snapshot: Some(Value::SingleQuotedString(String::from("000003A1-1")).with_empty_span()), session: false }) ); diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 319a818cd..e68368fd4 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -446,7 +446,7 @@ fn parse_vacuum() { Ident::new("tbl1"), ])) ); - assert_eq!(v.threshold, Some(number("20"))); + assert_eq!(v.threshold, Some(number("20").with_empty_span())); assert!(v.boost); } _ => unreachable!(), diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 666876fa7..790bf1515 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -2043,27 +2043,27 @@ fn test_create_stage_with_stage_params() { ); assert!(stage_params.credentials.options.contains(&KeyValueOption { option_name: "AWS_KEY_ID".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "1a2b3c".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("1a2b3c".to_string()).with_empty_span() + ), })); assert!(stage_params.credentials.options.contains(&KeyValueOption { option_name: "AWS_SECRET_KEY".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "4x5y6z".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("4x5y6z".to_string()).with_empty_span() + ), })); assert!(stage_params.encryption.options.contains(&KeyValueOption { option_name: "MASTER_KEY".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "key".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("key".to_string()).with_empty_span() + ), })); assert!(stage_params.encryption.options.contains(&KeyValueOption { option_name: "TYPE".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "AWS_SSE_KMS".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("AWS_SSE_KMS".to_string()).with_empty_span() + ), })); } _ => unreachable!(), @@ -2087,17 +2087,17 @@ fn test_create_stage_with_directory_table_params() { } => { assert!(directory_table_params.options.contains(&KeyValueOption { option_name: "ENABLE".to_string(), - option_value: KeyValueOptionKind::Single(Value::Boolean(true)), + option_value: KeyValueOptionKind::Single(Value::Boolean(true).with_empty_span()), })); assert!(directory_table_params.options.contains(&KeyValueOption { option_name: "REFRESH_ON_CREATE".to_string(), - option_value: KeyValueOptionKind::Single(Value::Boolean(false)), + option_value: KeyValueOptionKind::Single(Value::Boolean(false).with_empty_span()), })); assert!(directory_table_params.options.contains(&KeyValueOption { option_name: "NOTIFICATION_INTEGRATION".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "some-string".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("some-string".to_string()).with_empty_span() + ), })); } _ => unreachable!(), @@ -2117,17 +2117,21 @@ fn test_create_stage_with_file_format() { Statement::CreateStage { file_format, .. } => { assert!(file_format.options.contains(&KeyValueOption { option_name: "COMPRESSION".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder("AUTO".to_string())), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("AUTO".to_string()).with_empty_span() + ), })); assert!(file_format.options.contains(&KeyValueOption { option_name: "BINARY_FORMAT".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder("HEX".to_string())), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("HEX".to_string()).with_empty_span() + ), })); assert!(file_format.options.contains(&KeyValueOption { option_name: "ESCAPE".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - r#"\\"#.to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString(r#"\\"#.to_string()).with_empty_span() + ), })); } _ => unreachable!(), @@ -2149,13 +2153,13 @@ fn test_create_stage_with_copy_options() { Statement::CreateStage { copy_options, .. } => { assert!(copy_options.options.contains(&KeyValueOption { option_name: "ON_ERROR".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder( - "CONTINUE".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("CONTINUE".to_string()).with_empty_span() + ), })); assert!(copy_options.options.contains(&KeyValueOption { option_name: "FORCE".to_string(), - option_value: KeyValueOptionKind::Single(Value::Boolean(true)), + option_value: KeyValueOptionKind::Single(Value::Boolean(true).with_empty_span()), })); } _ => unreachable!(), @@ -2286,27 +2290,27 @@ fn test_copy_into_with_stage_params() { ); assert!(stage_params.credentials.options.contains(&KeyValueOption { option_name: "AWS_KEY_ID".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "1a2b3c".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("1a2b3c".to_string()).with_empty_span() + ), })); assert!(stage_params.credentials.options.contains(&KeyValueOption { option_name: "AWS_SECRET_KEY".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "4x5y6z".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("4x5y6z".to_string()).with_empty_span() + ), })); assert!(stage_params.encryption.options.contains(&KeyValueOption { option_name: "MASTER_KEY".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "key".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("key".to_string()).with_empty_span() + ), })); assert!(stage_params.encryption.options.contains(&KeyValueOption { option_name: "TYPE".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - "AWS_SSE_KMS".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString("AWS_SSE_KMS".to_string()).with_empty_span() + ), })); } _ => unreachable!(), @@ -2457,17 +2461,21 @@ fn test_copy_into_file_format() { Statement::CopyIntoSnowflake { file_format, .. } => { assert!(file_format.options.contains(&KeyValueOption { option_name: "COMPRESSION".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder("AUTO".to_string())), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("AUTO".to_string()).with_empty_span() + ), })); assert!(file_format.options.contains(&KeyValueOption { option_name: "BINARY_FORMAT".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder("HEX".to_string())), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("HEX".to_string()).with_empty_span() + ), })); assert!(file_format.options.contains(&KeyValueOption { option_name: "ESCAPE".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - r#"\\"#.to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString(r#"\\"#.to_string()).with_empty_span() + ), })); } _ => unreachable!(), @@ -2495,17 +2503,21 @@ fn test_copy_into_file_format() { Statement::CopyIntoSnowflake { file_format, .. } => { assert!(file_format.options.contains(&KeyValueOption { option_name: "COMPRESSION".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder("AUTO".to_string())), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("AUTO".to_string()).with_empty_span() + ), })); assert!(file_format.options.contains(&KeyValueOption { option_name: "BINARY_FORMAT".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder("HEX".to_string())), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("HEX".to_string()).with_empty_span() + ), })); assert!(file_format.options.contains(&KeyValueOption { option_name: "ESCAPE".to_string(), - option_value: KeyValueOptionKind::Single(Value::SingleQuotedString( - r#"\\"#.to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::SingleQuotedString(r#"\\"#.to_string()).with_empty_span() + ), })); } _ => unreachable!(), @@ -2526,13 +2538,13 @@ fn test_copy_into_copy_options() { Statement::CopyIntoSnowflake { copy_options, .. } => { assert!(copy_options.options.contains(&KeyValueOption { option_name: "ON_ERROR".to_string(), - option_value: KeyValueOptionKind::Single(Value::Placeholder( - "CONTINUE".to_string() - )), + option_value: KeyValueOptionKind::Single( + Value::Placeholder("CONTINUE".to_string()).with_empty_span() + ), })); assert!(copy_options.options.contains(&KeyValueOption { option_name: "FORCE".to_string(), - option_value: KeyValueOptionKind::Single(Value::Boolean(true)), + option_value: KeyValueOptionKind::Single(Value::Boolean(true).with_empty_span()), })); } _ => unreachable!(),