diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 357df1be8..59725dfbd 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1036,6 +1036,13 @@ pub enum Expr { Lambda(LambdaFunction), } +impl Expr { + /// Creates a new [`Expr::Value`] + pub fn value(value: impl Into) -> Self { + Expr::Value(value.into()) + } +} + /// The contents inside the `[` and `]` in a subscript expression. #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/ast/value.rs b/src/ast/value.rs index c340813d3..d08db190b 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -30,8 +30,41 @@ use crate::{ast::Ident, tokenizer::Span}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -/// Primitive SQL values such as number and string -#[derive(Debug, Clone, Eq, Ord)] +/// 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 { @@ -47,7 +80,13 @@ impl PartialEq for ValueWithSpan { impl PartialOrd for ValueWithSpan { fn partial_cmp(&self, other: &Self) -> Option { - self.value.partial_cmp(&other.value) + Some(self.cmp(other)) + } +} + +impl Ord for ValueWithSpan { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.value.cmp(&other.value) } } @@ -63,6 +102,12 @@ impl From for Value { } } +impl From for ValueWithSpan { + fn from(value: Value) -> Self { + value.with_empty_span() + } +} + /// Primitive SQL values such as number and string #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index 34f684c69..37235094c 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -55,7 +55,7 @@ fn parse_map_access_expr() { "indexOf", [ Expr::Identifier(Ident::new("string_names")), - Expr::Value(Value::SingleQuotedString("endpoint".to_string())) + Expr::value(Value::SingleQuotedString("endpoint".to_string())) ] ), })], @@ -71,7 +71,7 @@ fn parse_map_access_expr() { left: Box::new(BinaryOp { left: Box::new(Identifier(Ident::new("id"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::SingleQuotedString("test".to_string()))), + right: Box::new(Expr::value(Value::SingleQuotedString("test".to_string()))), }), op: BinaryOperator::And, right: Box::new(BinaryOp { @@ -82,13 +82,13 @@ fn parse_map_access_expr() { "indexOf", [ Expr::Identifier(Ident::new("string_name")), - Expr::Value(Value::SingleQuotedString("app".to_string())) + Expr::value(Value::SingleQuotedString("app".to_string())) ] ), })], }), op: BinaryOperator::NotEq, - right: Box::new(Expr::Value(Value::SingleQuotedString("foo".to_string()))), + right: Box::new(Expr::value(Value::SingleQuotedString("foo".to_string()))), }), }), group_by: GroupByExpr::Expressions(vec![], vec![]), @@ -114,8 +114,8 @@ fn parse_array_expr() { assert_eq!( &Expr::Array(Array { elem: vec![ - Expr::Value(Value::SingleQuotedString("1".to_string())), - Expr::Value(Value::SingleQuotedString("2".to_string())), + Expr::value(Value::SingleQuotedString("1".to_string())), + Expr::value(Value::SingleQuotedString("2".to_string())), ], named: false, }), @@ -1014,14 +1014,14 @@ fn parse_select_parametric_function() { assert_eq!(parameters.args.len(), 2); assert_eq!( parameters.args[0], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Number( + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::value(Value::Number( "0.5".parse().unwrap(), false )))) ); assert_eq!( parameters.args[1], - FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Number( + FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::value(Value::Number( "0.6".parse().unwrap(), false )))) @@ -1074,9 +1074,9 @@ fn parse_select_order_by_with_fill_interpolate() { asc: Some(true), nulls_first: Some(true), with_fill: Some(WithFill { - from: Some(Expr::Value(number("10"))), - to: Some(Expr::Value(number("20"))), - step: Some(Expr::Value(number("2"))), + from: Some(Expr::value(number("10"))), + to: Some(Expr::value(number("20"))), + step: Some(Expr::value(number("2"))), }), }, OrderByExpr { @@ -1084,9 +1084,9 @@ fn parse_select_order_by_with_fill_interpolate() { asc: Some(false), nulls_first: Some(false), with_fill: Some(WithFill { - from: Some(Expr::Value(number("30"))), - to: Some(Expr::Value(number("40"))), - step: Some(Expr::Value(number("3"))), + from: Some(Expr::value(number("30"))), + to: Some(Expr::value(number("40"))), + step: Some(Expr::value(number("3"))), }), }, ], @@ -1096,14 +1096,14 @@ fn parse_select_order_by_with_fill_interpolate() { expr: Some(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("col1"))), op: BinaryOperator::Plus, - right: Box::new(Expr::Value(number("1"))), + right: Box::new(Expr::value(number("1"))), }), }]) }) }, select.order_by.expect("ORDER BY expected") ); - assert_eq!(Some(Expr::Value(number("2"))), select.limit); + assert_eq!(Some(Expr::value(number("2"))), select.limit); } #[test] @@ -1144,9 +1144,9 @@ fn parse_with_fill() { let select = clickhouse().verified_query(sql); assert_eq!( Some(WithFill { - from: Some(Expr::Value(number("10"))), - to: Some(Expr::Value(number("20"))), - step: Some(Expr::Value(number("2"))), + from: Some(Expr::value(number("10"))), + to: Some(Expr::value(number("20"))), + step: Some(Expr::value(number("2"))), }), select.order_by.expect("ORDER BY expected").exprs[0].with_fill ); @@ -1183,7 +1183,7 @@ fn parse_interpolate_body_with_columns() { expr: Some(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("col1"))), op: BinaryOperator::Plus, - right: Box::new(Expr::Value(number("1"))), + right: Box::new(Expr::value(number("1"))), }), }, InterpolateExpr { @@ -1195,7 +1195,7 @@ fn parse_interpolate_body_with_columns() { expr: Some(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("col4"))), op: BinaryOperator::Plus, - right: Box::new(Expr::Value(number("4"))), + right: Box::new(Expr::value(number("4"))), }), }, ]) @@ -1236,7 +1236,7 @@ fn test_prewhere() { Some(&BinaryOp { left: Box::new(Identifier(Ident::new("x"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), false))), + right: Box::new(Expr::value(Value::Number("1".parse().unwrap(), false))), }) ); let selection = query.as_ref().body.as_select().unwrap().selection.as_ref(); @@ -1245,7 +1245,7 @@ fn test_prewhere() { Some(&BinaryOp { left: Box::new(Identifier(Ident::new("y"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("2".parse().unwrap(), false))), + right: Box::new(Expr::value(Value::Number("2".parse().unwrap(), false))), }) ); } @@ -1261,13 +1261,13 @@ fn test_prewhere() { left: Box::new(BinaryOp { left: Box::new(Identifier(Ident::new("x"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), false))), + right: Box::new(Expr::value(Value::Number("1".parse().unwrap(), false))), }), op: BinaryOperator::And, right: Box::new(BinaryOp { left: Box::new(Identifier(Ident::new("y"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("2".parse().unwrap(), false))), + right: Box::new(Expr::value(Value::Number("2".parse().unwrap(), false))), }), }) ); @@ -1375,7 +1375,7 @@ fn parse_create_table_on_commit_and_as_query() { assert_eq!(on_commit, Some(OnCommit::PreserveRows)); assert_eq!( query.unwrap().body.as_select().unwrap().projection, - vec![UnnamedExpr(Expr::Value(Value::Number( + vec![UnnamedExpr(Expr::value(Value::Number( "1".parse().unwrap(), false )))] @@ -1391,7 +1391,7 @@ fn parse_freeze_and_unfreeze_partition() { for operation_name in &["FREEZE", "UNFREEZE"] { let sql = format!("ALTER TABLE t {operation_name} PARTITION '2024-08-14'"); - let expected_partition = Partition::Expr(Expr::Value(Value::SingleQuotedString( + let expected_partition = Partition::Expr(Expr::value(Value::SingleQuotedString( "2024-08-14".to_string(), ))); match clickhouse_and_generic().verified_stmt(&sql) { @@ -1421,7 +1421,7 @@ fn parse_freeze_and_unfreeze_partition() { match clickhouse_and_generic().verified_stmt(&sql) { Statement::AlterTable { operations, .. } => { assert_eq!(operations.len(), 1); - let expected_partition = Partition::Expr(Expr::Value(Value::SingleQuotedString( + let expected_partition = Partition::Expr(Expr::value(Value::SingleQuotedString( "2024-08-14".to_string(), ))); let expected_operation = if operation_name == &"FREEZE" {