Skip to content

Commit c335c88

Browse files
authored
Store spans for Value expressions (#1738)
1 parent aab12ad commit c335c88

18 files changed

+1620
-1042
lines changed

src/ast/mod.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub use self::trigger::{
8686

8787
pub use self::value::{
8888
escape_double_quote_string, escape_quoted_string, DateTimeField, DollarQuotedString,
89-
NormalizationForm, TrimWhereField, Value,
89+
NormalizationForm, TrimWhereField, Value, ValueWithSpan,
9090
};
9191

9292
use crate::ast::helpers::key_value_options::KeyValueOptions;
@@ -908,7 +908,7 @@ pub enum Expr {
908908
/// Nested expression e.g. `(foo > bar)` or `(1)`
909909
Nested(Box<Expr>),
910910
/// A literal value, such as string, number, date or NULL
911-
Value(Value),
911+
Value(ValueWithSpan),
912912
/// <https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html>
913913
IntroducedString {
914914
introducer: String,
@@ -1051,6 +1051,13 @@ pub enum Expr {
10511051
Lambda(LambdaFunction),
10521052
}
10531053

1054+
impl Expr {
1055+
/// Creates a new [`Expr::Value`]
1056+
pub fn value(value: impl Into<ValueWithSpan>) -> Self {
1057+
Expr::Value(value.into())
1058+
}
1059+
}
1060+
10541061
/// The contents inside the `[` and `]` in a subscript expression.
10551062
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10561063
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -8789,9 +8796,9 @@ mod tests {
87898796
#[test]
87908797
fn test_interval_display() {
87918798
let interval = Expr::Interval(Interval {
8792-
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
8793-
"123:45.67",
8794-
)))),
8799+
value: Box::new(Expr::Value(
8800+
Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(),
8801+
)),
87958802
leading_field: Some(DateTimeField::Minute),
87968803
leading_precision: Some(10),
87978804
last_field: Some(DateTimeField::Second),
@@ -8803,7 +8810,9 @@ mod tests {
88038810
);
88048811

88058812
let interval = Expr::Interval(Interval {
8806-
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("5")))),
8813+
value: Box::new(Expr::Value(
8814+
Value::SingleQuotedString(String::from("5")).with_empty_span(),
8815+
)),
88078816
leading_field: Some(DateTimeField::Second),
88088817
leading_precision: Some(1),
88098818
last_field: None,

src/ast/spans.rs

+22-19
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,21 @@ use core::iter;
2121
use crate::tokenizer::Span;
2222

2323
use super::{
24-
dcl::SecondaryRoles, AccessExpr, AlterColumnOperation, AlterIndexOperation,
25-
AlterTableOperation, Array, Assignment, AssignmentTarget, CloseCursor, ClusteredIndex,
26-
ColumnDef, ColumnOption, ColumnOptionDef, ConflictTarget, ConnectBy, ConstraintCharacteristics,
27-
CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate,
28-
ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable, Function,
29-
FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments,
30-
GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate, InterpolateExpr, Join,
31-
JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, MatchRecognizePattern,
32-
Measure, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict,
33-
OnConflictAction, OnInsert, OrderBy, OrderByExpr, OrderByKind, Partition, PivotValueSource,
34-
ProjectionSelect, Query, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
35-
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript,
36-
SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject,
37-
TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use, Value, Values, ViewColumnDef,
38-
WildcardAdditionalOptions, With, WithFill,
24+
dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation,
25+
AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, CloseCursor,
26+
ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConflictTarget, ConnectBy,
27+
ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte,
28+
Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable,
29+
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
30+
FunctionArguments, GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate,
31+
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
32+
MatchRecognizePattern, Measure, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset,
33+
OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, OrderByKind, Partition,
34+
PivotValueSource, ProjectionSelect, Query, ReferentialAction, RenameSelectItem,
35+
ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption,
36+
Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint,
37+
TableFactor, TableObject, TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use,
38+
Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill,
3939
};
4040

4141
/// Given an iterator of spans, return the [Span::union] of all spans.
@@ -1978,10 +1978,13 @@ impl Spanned for TableAliasColumnDef {
19781978
}
19791979
}
19801980

1981-
/// # missing span
1982-
///
1983-
/// The span of a `Value` is currently not implemented, as doing so
1984-
/// requires a breaking changes, which may be done in a future release.
1981+
impl Spanned for ValueWithSpan {
1982+
fn span(&self) -> Span {
1983+
self.span
1984+
}
1985+
}
1986+
1987+
/// The span is stored in the `ValueWrapper` struct
19851988
impl Spanned for Value {
19861989
fn span(&self) -> Span {
19871990
Span::empty() // # todo: Value needs to store spans before this is possible

src/ast/value.rs

+100-1
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,88 @@ use bigdecimal::BigDecimal;
2626
#[cfg(feature = "serde")]
2727
use serde::{Deserialize, Serialize};
2828

29-
use crate::ast::Ident;
29+
use crate::{ast::Ident, tokenizer::Span};
3030
#[cfg(feature = "visitor")]
3131
use sqlparser_derive::{Visit, VisitMut};
3232

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+
#[derive(Debug, Clone, Eq)]
68+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
70+
pub struct ValueWithSpan {
71+
pub value: Value,
72+
pub span: Span,
73+
}
74+
75+
impl PartialEq for ValueWithSpan {
76+
fn eq(&self, other: &Self) -> bool {
77+
self.value == other.value
78+
}
79+
}
80+
81+
impl Ord for ValueWithSpan {
82+
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
83+
self.value.cmp(&other.value)
84+
}
85+
}
86+
87+
impl PartialOrd for ValueWithSpan {
88+
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
89+
Some(Ord::cmp(self, other))
90+
}
91+
}
92+
93+
impl core::hash::Hash for ValueWithSpan {
94+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
95+
self.value.hash(state);
96+
}
97+
}
98+
99+
impl From<Value> for ValueWithSpan {
100+
fn from(value: Value) -> Self {
101+
value.with_empty_span()
102+
}
103+
}
104+
105+
impl From<ValueWithSpan> for Value {
106+
fn from(value: ValueWithSpan) -> Self {
107+
value.value
108+
}
109+
}
110+
33111
/// Primitive SQL values such as number and string
34112
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
35113
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -102,6 +180,13 @@ pub enum Value {
102180
Placeholder(String),
103181
}
104182

183+
impl ValueWithSpan {
184+
/// If the underlying literal is a string, regardless of quote style, returns the associated string value
185+
pub fn into_string(self) -> Option<String> {
186+
self.value.into_string()
187+
}
188+
}
189+
105190
impl Value {
106191
/// If the underlying literal is a string, regardless of quote style, returns the associated string value
107192
pub fn into_string(self) -> Option<String> {
@@ -126,6 +211,20 @@ impl Value {
126211
_ => None,
127212
}
128213
}
214+
215+
pub fn with_span(self, span: Span) -> ValueWithSpan {
216+
ValueWithSpan { value: self, span }
217+
}
218+
219+
pub fn with_empty_span(self) -> ValueWithSpan {
220+
self.with_span(Span::empty())
221+
}
222+
}
223+
224+
impl fmt::Display for ValueWithSpan {
225+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226+
write!(f, "{}", self.value)
227+
}
129228
}
130229

131230
impl fmt::Display for Value {

src/ast/visitor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ where
547547
///
548548
/// visit_expressions_mut(&mut statements, |expr| {
549549
/// if matches!(expr, Expr::Identifier(col_name) if col_name.value == "x") {
550-
/// let old_expr = std::mem::replace(expr, Expr::Value(Value::Null));
550+
/// let old_expr = std::mem::replace(expr, Expr::value(Value::Null));
551551
/// *expr = Expr::Function(Function {
552552
/// name: ObjectName::from(vec![Ident::new("f")]),
553553
/// uses_odbc_syntax: false,

0 commit comments

Comments
 (0)