Skip to content

Commit 56f4b82

Browse files
graupVedin
authored andcommitted
Make TypedString preserve quote style (apache#1679)
1 parent 2174cab commit 56f4b82

File tree

7 files changed

+132
-55
lines changed

7 files changed

+132
-55
lines changed

src/ast/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -898,14 +898,18 @@ pub enum Expr {
898898
/// <https://dev.mysql.com/doc/refman/8.0/en/charset-introducer.html>
899899
IntroducedString {
900900
introducer: String,
901+
/// The value of the constant.
902+
/// Hint: you can unwrap the string value using `value.into_string()`.
901903
value: Value,
902904
},
903905
/// A constant of form `<data_type> 'value'`.
904906
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
905907
/// as well as constants of other types (a non-standard PostgreSQL extension).
906908
TypedString {
907909
data_type: DataType,
908-
value: String,
910+
/// The value of the constant.
911+
/// Hint: you can unwrap the string value using `value.into_string()`.
912+
value: Value,
909913
},
910914
/// Scalar function call e.g. `LEFT(foo, 5)`
911915
Function(Function),
@@ -1622,7 +1626,7 @@ impl fmt::Display for Expr {
16221626
Expr::IntroducedString { introducer, value } => write!(f, "{introducer} {value}"),
16231627
Expr::TypedString { data_type, value } => {
16241628
write!(f, "{data_type}")?;
1625-
write!(f, " '{}'", &value::escape_single_quote_string(value))
1629+
write!(f, " {value}")
16261630
}
16271631
Expr::Function(fun) => write!(f, "{fun}"),
16281632
Expr::Method(method) => write!(f, "{method}"),

src/ast/spans.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1266,7 +1266,7 @@ impl Spanned for AssignmentTarget {
12661266
/// f.e. `IS NULL <expr>` reports as `<expr>::span`.
12671267
///
12681268
/// Missing spans:
1269-
/// - [Expr::TypedString]
1269+
/// - [Expr::TypedString] # missing span for data_type
12701270
/// - [Expr::MatchAgainst] # MySQL specific
12711271
/// - [Expr::RLike] # MySQL specific
12721272
/// - [Expr::Struct] # BigQuery specific
@@ -1362,7 +1362,7 @@ impl Spanned for Expr {
13621362
.union(&union_spans(collation.0.iter().map(|i| i.span()))),
13631363
Expr::Nested(expr) => expr.span(),
13641364
Expr::Value(value) => value.span(),
1365-
Expr::TypedString { .. } => Span::empty(),
1365+
Expr::TypedString { value, .. } => value.span(),
13661366
Expr::Function(function) => function.span(),
13671367
Expr::GroupingSets(vec) => {
13681368
union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span())))

src/ast/value.rs

+26
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,32 @@ pub enum Value {
9797
Placeholder(String),
9898
}
9999

100+
impl Value {
101+
/// If the underlying literal is a string, regardless of quote style, returns the associated string value
102+
pub fn into_string(self) -> Option<String> {
103+
match self {
104+
Value::SingleQuotedString(s)
105+
| Value::DoubleQuotedString(s)
106+
| Value::TripleSingleQuotedString(s)
107+
| Value::TripleDoubleQuotedString(s)
108+
| Value::SingleQuotedByteStringLiteral(s)
109+
| Value::DoubleQuotedByteStringLiteral(s)
110+
| Value::TripleSingleQuotedByteStringLiteral(s)
111+
| Value::TripleDoubleQuotedByteStringLiteral(s)
112+
| Value::SingleQuotedRawStringLiteral(s)
113+
| Value::DoubleQuotedRawStringLiteral(s)
114+
| Value::TripleSingleQuotedRawStringLiteral(s)
115+
| Value::TripleDoubleQuotedRawStringLiteral(s)
116+
| Value::EscapedStringLiteral(s)
117+
| Value::UnicodeStringLiteral(s)
118+
| Value::NationalStringLiteral(s)
119+
| Value::HexStringLiteral(s) => Some(s),
120+
Value::DollarQuotedString(s) => Some(s.value),
121+
_ => None,
122+
}
123+
}
124+
}
125+
100126
impl fmt::Display for Value {
101127
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102128
match self {

src/parser/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1316,7 +1316,7 @@ impl<'a> Parser<'a> {
13161316
DataType::Custom(..) => parser_err!("dummy", loc),
13171317
data_type => Ok(Expr::TypedString {
13181318
data_type,
1319-
value: parser.parse_literal_string()?,
1319+
value: parser.parse_value()?,
13201320
}),
13211321
}
13221322
})?;

tests/sqlparser_bigquery.rs

+64-36
Original file line numberDiff line numberDiff line change
@@ -39,43 +39,45 @@ fn parse_literal_string() {
3939
r#"'''triple-single'unescaped''', "#,
4040
r#""double\"escaped", "#,
4141
r#""""triple-double\"escaped""", "#,
42-
r#""""triple-double"unescaped""""#,
42+
r#""""triple-double"unescaped""", "#,
43+
r#""""triple-double'unescaped""", "#,
44+
r#"'''triple-single"unescaped'''"#,
4345
);
4446
let dialect = TestedDialects::new_with_options(
4547
vec![Box::new(BigQueryDialect {})],
4648
ParserOptions::new().with_unescape(false),
4749
);
4850
let select = dialect.verified_only_select(sql);
49-
assert_eq!(10, select.projection.len());
51+
assert_eq!(12, select.projection.len());
5052
assert_eq!(
51-
&Expr::Value(Value::SingleQuotedString("single".to_string())),
53+
&Expr::Value(Value::SingleQuotedString("single".into())),
5254
expr_from_projection(&select.projection[0])
5355
);
5456
assert_eq!(
55-
&Expr::Value(Value::DoubleQuotedString("double".to_string())),
57+
&Expr::Value(Value::DoubleQuotedString("double".into())),
5658
expr_from_projection(&select.projection[1])
5759
);
5860
assert_eq!(
59-
&Expr::Value(Value::TripleSingleQuotedString("triple-single".to_string())),
61+
&Expr::Value(Value::TripleSingleQuotedString("triple-single".into())),
6062
expr_from_projection(&select.projection[2])
6163
);
6264
assert_eq!(
63-
&Expr::Value(Value::TripleDoubleQuotedString("triple-double".to_string())),
65+
&Expr::Value(Value::TripleDoubleQuotedString("triple-double".into())),
6466
expr_from_projection(&select.projection[3])
6567
);
6668
assert_eq!(
67-
&Expr::Value(Value::SingleQuotedString(r#"single\'escaped"#.to_string())),
69+
&Expr::Value(Value::SingleQuotedString(r#"single\'escaped"#.into())),
6870
expr_from_projection(&select.projection[4])
6971
);
7072
assert_eq!(
7173
&Expr::Value(Value::TripleSingleQuotedString(
72-
r#"triple-single\'escaped"#.to_string()
74+
r#"triple-single\'escaped"#.into()
7375
)),
7476
expr_from_projection(&select.projection[5])
7577
);
7678
assert_eq!(
7779
&Expr::Value(Value::TripleSingleQuotedString(
78-
r#"triple-single'unescaped"#.to_string()
80+
r#"triple-single'unescaped"#.into()
7981
)),
8082
expr_from_projection(&select.projection[6])
8183
);
@@ -95,6 +97,18 @@ fn parse_literal_string() {
9597
)),
9698
expr_from_projection(&select.projection[9])
9799
);
100+
assert_eq!(
101+
&Expr::Value(Value::TripleDoubleQuotedString(
102+
r#"triple-double'unescaped"#.to_string()
103+
)),
104+
expr_from_projection(&select.projection[10])
105+
);
106+
assert_eq!(
107+
&Expr::Value(Value::TripleSingleQuotedString(
108+
r#"triple-single"unescaped"#.to_string()
109+
)),
110+
expr_from_projection(&select.projection[11])
111+
);
98112
}
99113

100114
#[test]
@@ -588,7 +602,7 @@ fn parse_tuple_struct_literal() {
588602
&Expr::Tuple(vec![
589603
Expr::Value(number("1")),
590604
Expr::Value(number("1.0")),
591-
Expr::Value(Value::SingleQuotedString("123".to_string())),
605+
Expr::Value(Value::SingleQuotedString("123".into())),
592606
Expr::Value(Value::Boolean(true))
593607
]),
594608
expr_from_projection(&select.projection[1])
@@ -616,7 +630,7 @@ fn parse_typeless_struct_syntax() {
616630

617631
assert_eq!(
618632
&Expr::Struct {
619-
values: vec![Expr::Value(Value::SingleQuotedString("abc".to_string())),],
633+
values: vec![Expr::Value(Value::SingleQuotedString("abc".into())),],
620634
fields: Default::default()
621635
},
622636
expr_from_projection(&select.projection[1])
@@ -639,7 +653,7 @@ fn parse_typeless_struct_syntax() {
639653
name: Ident::from("a")
640654
},
641655
Expr::Named {
642-
expr: Expr::Value(Value::SingleQuotedString("abc".to_string())).into(),
656+
expr: Expr::Value(Value::SingleQuotedString("abc".into())).into(),
643657
name: Ident::from("b")
644658
},
645659
],
@@ -804,9 +818,7 @@ fn parse_typed_struct_syntax_bigquery() {
804818
assert_eq!(4, select.projection.len());
805819
assert_eq!(
806820
&Expr::Struct {
807-
values: vec![Expr::Value(Value::DoubleQuotedString(
808-
"2011-05-05".to_string()
809-
)),],
821+
values: vec![Expr::Value(Value::DoubleQuotedString("2011-05-05".into())),],
810822
fields: vec![StructField {
811823
field_name: None,
812824
field_type: DataType::Date
@@ -818,7 +830,7 @@ fn parse_typed_struct_syntax_bigquery() {
818830
&Expr::Struct {
819831
values: vec![Expr::TypedString {
820832
data_type: DataType::Datetime(None),
821-
value: "1999-01-01 01:23:34.45".to_string()
833+
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into())
822834
},],
823835
fields: vec![StructField {
824836
field_name: None,
@@ -854,7 +866,7 @@ fn parse_typed_struct_syntax_bigquery() {
854866
assert_eq!(
855867
&Expr::Struct {
856868
values: vec![Expr::Interval(Interval {
857-
value: Box::new(Expr::Value(Value::SingleQuotedString("2".to_string()))),
869+
value: Box::new(Expr::Value(Value::SingleQuotedString("2".into()))),
858870
leading_field: Some(DateTimeField::Hour),
859871
leading_precision: None,
860872
last_field: None,
@@ -871,7 +883,9 @@ fn parse_typed_struct_syntax_bigquery() {
871883
&Expr::Struct {
872884
values: vec![Expr::TypedString {
873885
data_type: DataType::JSON,
874-
value: r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.to_string()
886+
value: Value::SingleQuotedString(
887+
r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into()
888+
)
875889
},],
876890
fields: vec![StructField {
877891
field_name: None,
@@ -886,7 +900,7 @@ fn parse_typed_struct_syntax_bigquery() {
886900
assert_eq!(3, select.projection.len());
887901
assert_eq!(
888902
&Expr::Struct {
889-
values: vec![Expr::Value(Value::DoubleQuotedString("foo".to_string())),],
903+
values: vec![Expr::Value(Value::DoubleQuotedString("foo".into())),],
890904
fields: vec![StructField {
891905
field_name: None,
892906
field_type: DataType::String(Some(42))
@@ -898,7 +912,7 @@ fn parse_typed_struct_syntax_bigquery() {
898912
&Expr::Struct {
899913
values: vec![Expr::TypedString {
900914
data_type: DataType::Timestamp(None, TimezoneInfo::None),
901-
value: "2008-12-25 15:30:00 America/Los_Angeles".to_string()
915+
value: Value::SingleQuotedString("2008-12-25 15:30:00 America/Los_Angeles".into())
902916
},],
903917
fields: vec![StructField {
904918
field_name: None,
@@ -912,7 +926,7 @@ fn parse_typed_struct_syntax_bigquery() {
912926
&Expr::Struct {
913927
values: vec![Expr::TypedString {
914928
data_type: DataType::Time(None, TimezoneInfo::None),
915-
value: "15:30:00".to_string()
929+
value: Value::SingleQuotedString("15:30:00".into())
916930
},],
917931
fields: vec![StructField {
918932
field_name: None,
@@ -929,7 +943,7 @@ fn parse_typed_struct_syntax_bigquery() {
929943
&Expr::Struct {
930944
values: vec![Expr::TypedString {
931945
data_type: DataType::Numeric(ExactNumberInfo::None),
932-
value: "1".to_string()
946+
value: Value::SingleQuotedString("1".into())
933947
},],
934948
fields: vec![StructField {
935949
field_name: None,
@@ -942,7 +956,7 @@ fn parse_typed_struct_syntax_bigquery() {
942956
&Expr::Struct {
943957
values: vec![Expr::TypedString {
944958
data_type: DataType::BigNumeric(ExactNumberInfo::None),
945-
value: "1".to_string()
959+
value: Value::SingleQuotedString("1".into())
946960
},],
947961
fields: vec![StructField {
948962
field_name: None,
@@ -1119,9 +1133,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
11191133
assert_eq!(4, select.projection.len());
11201134
assert_eq!(
11211135
&Expr::Struct {
1122-
values: vec![Expr::Value(Value::SingleQuotedString(
1123-
"2011-05-05".to_string()
1124-
)),],
1136+
values: vec![Expr::Value(Value::SingleQuotedString("2011-05-05".into())),],
11251137
fields: vec![StructField {
11261138
field_name: None,
11271139
field_type: DataType::Date
@@ -1133,7 +1145,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
11331145
&Expr::Struct {
11341146
values: vec![Expr::TypedString {
11351147
data_type: DataType::Datetime(None),
1136-
value: "1999-01-01 01:23:34.45".to_string()
1148+
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into())
11371149
},],
11381150
fields: vec![StructField {
11391151
field_name: None,
@@ -1169,7 +1181,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
11691181
assert_eq!(
11701182
&Expr::Struct {
11711183
values: vec![Expr::Interval(Interval {
1172-
value: Box::new(Expr::Value(Value::SingleQuotedString("1".to_string()))),
1184+
value: Box::new(Expr::Value(Value::SingleQuotedString("1".into()))),
11731185
leading_field: Some(DateTimeField::Month),
11741186
leading_precision: None,
11751187
last_field: None,
@@ -1186,7 +1198,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
11861198
&Expr::Struct {
11871199
values: vec![Expr::TypedString {
11881200
data_type: DataType::JSON,
1189-
value: r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.to_string()
1201+
value: Value::SingleQuotedString(
1202+
r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into()
1203+
)
11901204
},],
11911205
fields: vec![StructField {
11921206
field_name: None,
@@ -1201,7 +1215,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
12011215
assert_eq!(3, select.projection.len());
12021216
assert_eq!(
12031217
&Expr::Struct {
1204-
values: vec![Expr::Value(Value::SingleQuotedString("foo".to_string())),],
1218+
values: vec![Expr::Value(Value::SingleQuotedString("foo".into())),],
12051219
fields: vec![StructField {
12061220
field_name: None,
12071221
field_type: DataType::String(Some(42))
@@ -1213,7 +1227,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
12131227
&Expr::Struct {
12141228
values: vec![Expr::TypedString {
12151229
data_type: DataType::Timestamp(None, TimezoneInfo::None),
1216-
value: "2008-12-25 15:30:00 America/Los_Angeles".to_string()
1230+
value: Value::SingleQuotedString("2008-12-25 15:30:00 America/Los_Angeles".into())
12171231
},],
12181232
fields: vec![StructField {
12191233
field_name: None,
@@ -1227,7 +1241,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
12271241
&Expr::Struct {
12281242
values: vec![Expr::TypedString {
12291243
data_type: DataType::Time(None, TimezoneInfo::None),
1230-
value: "15:30:00".to_string()
1244+
value: Value::SingleQuotedString("15:30:00".into())
12311245
},],
12321246
fields: vec![StructField {
12331247
field_name: None,
@@ -1244,7 +1258,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
12441258
&Expr::Struct {
12451259
values: vec![Expr::TypedString {
12461260
data_type: DataType::Numeric(ExactNumberInfo::None),
1247-
value: "1".to_string()
1261+
value: Value::SingleQuotedString("1".into())
12481262
},],
12491263
fields: vec![StructField {
12501264
field_name: None,
@@ -1257,7 +1271,7 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
12571271
&Expr::Struct {
12581272
values: vec![Expr::TypedString {
12591273
data_type: DataType::BigNumeric(ExactNumberInfo::None),
1260-
value: "1".to_string()
1274+
value: Value::SingleQuotedString("1".into())
12611275
},],
12621276
fields: vec![StructField {
12631277
field_name: None,
@@ -1285,7 +1299,7 @@ fn parse_typed_struct_with_field_name_bigquery() {
12851299
);
12861300
assert_eq!(
12871301
&Expr::Struct {
1288-
values: vec![Expr::Value(Value::DoubleQuotedString("foo".to_string())),],
1302+
values: vec![Expr::Value(Value::DoubleQuotedString("foo".into())),],
12891303
fields: vec![StructField {
12901304
field_name: Some(Ident::from("y")),
12911305
field_type: DataType::String(None)
@@ -1332,7 +1346,7 @@ fn parse_typed_struct_with_field_name_bigquery_and_generic() {
13321346
);
13331347
assert_eq!(
13341348
&Expr::Struct {
1335-
values: vec![Expr::Value(Value::SingleQuotedString("foo".to_string())),],
1349+
values: vec![Expr::Value(Value::SingleQuotedString("foo".into())),],
13361350
fields: vec![StructField {
13371351
field_name: Some(Ident::from("y")),
13381352
field_type: DataType::String(None)
@@ -2234,6 +2248,20 @@ fn test_select_as_value() {
22342248
assert_eq!(Some(ValueTableMode::AsValue), select.value_table_mode);
22352249
}
22362250

2251+
#[test]
2252+
fn test_triple_quote_typed_strings() {
2253+
bigquery().verified_expr(r#"JSON '''{"foo":"bar's"}'''"#);
2254+
2255+
let expr = bigquery().verified_expr(r#"JSON """{"foo":"bar's"}""""#);
2256+
assert_eq!(
2257+
Expr::TypedString {
2258+
data_type: DataType::JSON,
2259+
value: Value::TripleDoubleQuotedString(r#"{"foo":"bar's"}"#.into())
2260+
},
2261+
expr
2262+
);
2263+
}
2264+
22372265
#[test]
22382266
fn test_array_agg() {
22392267
bigquery_and_generic().verified_expr("ARRAY_AGG(state)");

0 commit comments

Comments
 (0)