Skip to content

Commit 0c4a030

Browse files
lovasoaayman-sigma
authored andcommitted
Store spans for Value expressions (apache#1738)
1 parent dd2f851 commit 0c4a030

18 files changed

+1620
-1042
lines changed

src/ast/mod.rs

Lines changed: 15 additions & 6 deletions
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;
@@ -915,7 +915,7 @@ pub enum Expr {
915915
/// Nested expression e.g. `(foo > bar)` or `(1)`
916916
Nested(Box<Expr>),
917917
/// A literal value, such as string, number, date or NULL
918-
Value(Value),
918+
Value(ValueWithSpan),
919919
/// <https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html>
920920
IntroducedString {
921921
introducer: String,
@@ -1058,6 +1058,13 @@ pub enum Expr {
10581058
Lambda(LambdaFunction),
10591059
}
10601060

1061+
impl Expr {
1062+
/// Creates a new [`Expr::Value`]
1063+
pub fn value(value: impl Into<ValueWithSpan>) -> Self {
1064+
Expr::Value(value.into())
1065+
}
1066+
}
1067+
10611068
/// The contents inside the `[` and `]` in a subscript expression.
10621069
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10631070
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -8807,9 +8814,9 @@ mod tests {
88078814
#[test]
88088815
fn test_interval_display() {
88098816
let interval = Expr::Interval(Interval {
8810-
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from(
8811-
"123:45.67",
8812-
)))),
8817+
value: Box::new(Expr::Value(
8818+
Value::SingleQuotedString(String::from("123:45.67")).with_empty_span(),
8819+
)),
88138820
leading_field: Some(DateTimeField::Minute),
88148821
leading_precision: Some(10),
88158822
last_field: Some(DateTimeField::Second),
@@ -8821,7 +8828,9 @@ mod tests {
88218828
);
88228829

88238830
let interval = Expr::Interval(Interval {
8824-
value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("5")))),
8831+
value: Box::new(Expr::Value(
8832+
Value::SingleQuotedString(String::from("5")).with_empty_span(),
8833+
)),
88258834
leading_field: Some(DateTimeField::Second),
88268835
leading_precision: Some(1),
88278836
last_field: None,

src/ast/spans.rs

Lines changed: 22 additions & 19 deletions
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.
@@ -1983,10 +1983,13 @@ impl Spanned for TableAliasColumnDef {
19831983
}
19841984
}
19851985

1986-
/// # missing span
1987-
///
1988-
/// The span of a `Value` is currently not implemented, as doing so
1989-
/// requires a breaking changes, which may be done in a future release.
1986+
impl Spanned for ValueWithSpan {
1987+
fn span(&self) -> Span {
1988+
self.span
1989+
}
1990+
}
1991+
1992+
/// The span is stored in the `ValueWrapper` struct
19901993
impl Spanned for Value {
19911994
fn span(&self) -> Span {
19921995
Span::empty() // # todo: Value needs to store spans before this is possible

src/ast/value.rs

Lines changed: 100 additions & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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)