mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-11 01:38:03 +00:00
Add ODBC escape syntax support for time expressions (#1953)
This commit is contained in:
parent
97a5b61a73
commit
bde269b56f
7 changed files with 259 additions and 110 deletions
|
@ -1014,12 +1014,7 @@ pub enum Expr {
|
||||||
/// A constant of form `<data_type> 'value'`.
|
/// A constant of form `<data_type> 'value'`.
|
||||||
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
|
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
|
||||||
/// as well as constants of other types (a non-standard PostgreSQL extension).
|
/// as well as constants of other types (a non-standard PostgreSQL extension).
|
||||||
TypedString {
|
TypedString(TypedString),
|
||||||
data_type: DataType,
|
|
||||||
/// The value of the constant.
|
|
||||||
/// Hint: you can unwrap the string value using `value.into_string()`.
|
|
||||||
value: ValueWithSpan,
|
|
||||||
},
|
|
||||||
/// Scalar function call e.g. `LEFT(foo, 5)`
|
/// Scalar function call e.g. `LEFT(foo, 5)`
|
||||||
Function(Function),
|
Function(Function),
|
||||||
/// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
|
/// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
|
||||||
|
@ -1734,10 +1729,7 @@ impl fmt::Display for Expr {
|
||||||
Expr::Nested(ast) => write!(f, "({ast})"),
|
Expr::Nested(ast) => write!(f, "({ast})"),
|
||||||
Expr::Value(v) => write!(f, "{v}"),
|
Expr::Value(v) => write!(f, "{v}"),
|
||||||
Expr::Prefixed { prefix, value } => write!(f, "{prefix} {value}"),
|
Expr::Prefixed { prefix, value } => write!(f, "{prefix} {value}"),
|
||||||
Expr::TypedString { data_type, value } => {
|
Expr::TypedString(ts) => ts.fmt(f),
|
||||||
write!(f, "{data_type}")?;
|
|
||||||
write!(f, " {value}")
|
|
||||||
}
|
|
||||||
Expr::Function(fun) => fun.fmt(f),
|
Expr::Function(fun) => fun.fmt(f),
|
||||||
Expr::Case {
|
Expr::Case {
|
||||||
case_token: _,
|
case_token: _,
|
||||||
|
@ -7450,6 +7442,52 @@ pub struct DropDomain {
|
||||||
pub drop_behavior: Option<DropBehavior>,
|
pub drop_behavior: Option<DropBehavior>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A constant of form `<data_type> 'value'`.
|
||||||
|
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
|
||||||
|
/// as well as constants of other types (a non-standard PostgreSQL extension).
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct TypedString {
|
||||||
|
pub data_type: DataType,
|
||||||
|
/// The value of the constant.
|
||||||
|
/// Hint: you can unwrap the string value using `value.into_string()`.
|
||||||
|
pub value: ValueWithSpan,
|
||||||
|
/// Flags whether this TypedString uses the [ODBC syntax].
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```sql
|
||||||
|
/// -- An ODBC date literal:
|
||||||
|
/// SELECT {d '2025-07-16'}
|
||||||
|
/// -- This is equivalent to the standard ANSI SQL literal:
|
||||||
|
/// SELECT DATE '2025-07-16'
|
||||||
|
///
|
||||||
|
/// [ODBC syntax]: https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals?view=sql-server-2017
|
||||||
|
pub uses_odbc_syntax: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TypedString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let data_type = &self.data_type;
|
||||||
|
let value = &self.value;
|
||||||
|
match self.uses_odbc_syntax {
|
||||||
|
false => {
|
||||||
|
write!(f, "{data_type}")?;
|
||||||
|
write!(f, " {value}")
|
||||||
|
}
|
||||||
|
true => {
|
||||||
|
let prefix = match data_type {
|
||||||
|
DataType::Date => "d",
|
||||||
|
DataType::Time(..) => "t",
|
||||||
|
DataType::Timestamp(..) => "ts",
|
||||||
|
_ => "?",
|
||||||
|
};
|
||||||
|
write!(f, "{{{prefix} {value}}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A function call
|
/// A function call
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// specific language governing permissions and limitations
|
// specific language governing permissions and limitations
|
||||||
// under the License.
|
// under the License.
|
||||||
|
|
||||||
use crate::ast::{query::SelectItemQualifiedWildcardKind, ColumnOptions, ExportData};
|
use crate::ast::{query::SelectItemQualifiedWildcardKind, ColumnOptions, ExportData, TypedString};
|
||||||
use core::iter;
|
use core::iter;
|
||||||
|
|
||||||
use crate::tokenizer::Span;
|
use crate::tokenizer::Span;
|
||||||
|
@ -1525,7 +1525,7 @@ impl Spanned for Expr {
|
||||||
.union(&union_spans(collation.0.iter().map(|i| i.span()))),
|
.union(&union_spans(collation.0.iter().map(|i| i.span()))),
|
||||||
Expr::Nested(expr) => expr.span(),
|
Expr::Nested(expr) => expr.span(),
|
||||||
Expr::Value(value) => value.span(),
|
Expr::Value(value) => value.span(),
|
||||||
Expr::TypedString { value, .. } => value.span(),
|
Expr::TypedString(TypedString { value, .. }) => value.span(),
|
||||||
Expr::Function(function) => function.span(),
|
Expr::Function(function) => function.span(),
|
||||||
Expr::GroupingSets(vec) => {
|
Expr::GroupingSets(vec) => {
|
||||||
union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span())))
|
union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span())))
|
||||||
|
|
|
@ -1543,10 +1543,11 @@ impl<'a> Parser<'a> {
|
||||||
// an unary negation `NOT ('a' LIKE 'b')`. To solve this, we don't accept the
|
// an unary negation `NOT ('a' LIKE 'b')`. To solve this, we don't accept the
|
||||||
// `type 'string'` syntax for the custom data types at all.
|
// `type 'string'` syntax for the custom data types at all.
|
||||||
DataType::Custom(..) => parser_err!("dummy", loc),
|
DataType::Custom(..) => parser_err!("dummy", loc),
|
||||||
data_type => Ok(Expr::TypedString {
|
data_type => Ok(Expr::TypedString(TypedString {
|
||||||
data_type,
|
data_type,
|
||||||
value: parser.parse_value()?,
|
value: parser.parse_value()?,
|
||||||
}),
|
uses_odbc_syntax: false,
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -1732,10 +1733,11 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_geometric_type(&mut self, kind: GeometricTypeKind) -> Result<Expr, ParserError> {
|
fn parse_geometric_type(&mut self, kind: GeometricTypeKind) -> Result<Expr, ParserError> {
|
||||||
Ok(Expr::TypedString {
|
Ok(Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(kind),
|
data_type: DataType::GeometricType(kind),
|
||||||
value: self.parse_value()?,
|
value: self.parse_value()?,
|
||||||
})
|
uses_odbc_syntax: false,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to parse an [Expr::CompoundFieldAccess] like `a.b.c` or `a.b[1].c`.
|
/// Try to parse an [Expr::CompoundFieldAccess] like `a.b.c` or `a.b[1].c`.
|
||||||
|
@ -2032,6 +2034,50 @@ impl<'a> Parser<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to parse the body of an [ODBC escaping sequence]
|
||||||
|
/// i.e. without the enclosing braces
|
||||||
|
/// Currently implemented:
|
||||||
|
/// Scalar Function Calls
|
||||||
|
/// Date, Time, and Timestamp Literals
|
||||||
|
/// See <https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/escape-sequences-in-odbc?view=sql-server-2017>
|
||||||
|
fn maybe_parse_odbc_body(&mut self) -> Result<Option<Expr>, ParserError> {
|
||||||
|
// Attempt 1: Try to parse it as a function.
|
||||||
|
if let Some(expr) = self.maybe_parse_odbc_fn_body()? {
|
||||||
|
return Ok(Some(expr));
|
||||||
|
}
|
||||||
|
// Attempt 2: Try to parse it as a Date, Time or Timestamp Literal
|
||||||
|
self.maybe_parse_odbc_body_datetime()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to parse the body of an [ODBC Date, Time, and Timestamp Literals] call.
|
||||||
|
///
|
||||||
|
/// ```sql
|
||||||
|
/// {d '2025-07-17'}
|
||||||
|
/// {t '14:12:01'}
|
||||||
|
/// {ts '2025-07-17 14:12:01'}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [ODBC Date, Time, and Timestamp Literals]:
|
||||||
|
/// https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals?view=sql-server-2017
|
||||||
|
fn maybe_parse_odbc_body_datetime(&mut self) -> Result<Option<Expr>, ParserError> {
|
||||||
|
self.maybe_parse(|p| {
|
||||||
|
let token = p.next_token().clone();
|
||||||
|
let word_string = token.token.to_string();
|
||||||
|
let data_type = match word_string.as_str() {
|
||||||
|
"t" => DataType::Time(None, TimezoneInfo::None),
|
||||||
|
"d" => DataType::Date,
|
||||||
|
"ts" => DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
|
_ => return p.expected("ODBC datetime keyword (t, d, or ts)", token),
|
||||||
|
};
|
||||||
|
let value = p.parse_value()?;
|
||||||
|
Ok(Expr::TypedString(TypedString {
|
||||||
|
data_type,
|
||||||
|
value,
|
||||||
|
uses_odbc_syntax: true,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to parse the body of an [ODBC function] call.
|
/// Tries to parse the body of an [ODBC function] call.
|
||||||
/// i.e. without the enclosing braces
|
/// i.e. without the enclosing braces
|
||||||
///
|
///
|
||||||
|
@ -2786,7 +2832,7 @@ impl<'a> Parser<'a> {
|
||||||
fn parse_lbrace_expr(&mut self) -> Result<Expr, ParserError> {
|
fn parse_lbrace_expr(&mut self) -> Result<Expr, ParserError> {
|
||||||
let token = self.expect_token(&Token::LBrace)?;
|
let token = self.expect_token(&Token::LBrace)?;
|
||||||
|
|
||||||
if let Some(fn_expr) = self.maybe_parse_odbc_fn_body()? {
|
if let Some(fn_expr) = self.maybe_parse_odbc_body()? {
|
||||||
self.expect_token(&Token::RBrace)?;
|
self.expect_token(&Token::RBrace)?;
|
||||||
return Ok(fn_expr);
|
return Ok(fn_expr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -906,13 +906,14 @@ fn parse_typed_struct_syntax_bigquery() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Datetime(None),
|
data_type: DataType::Datetime(None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()),
|
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Datetime(None),
|
field_type: DataType::Datetime(None),
|
||||||
|
@ -968,15 +969,16 @@ fn parse_typed_struct_syntax_bigquery() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::JSON,
|
data_type: DataType::JSON,
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(
|
value: Value::SingleQuotedString(
|
||||||
r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into()
|
r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into()
|
||||||
),
|
),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::JSON,
|
field_type: DataType::JSON,
|
||||||
|
@ -1004,7 +1006,7 @@ fn parse_typed_struct_syntax_bigquery() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(
|
value: Value::SingleQuotedString(
|
||||||
|
@ -1012,7 +1014,8 @@ fn parse_typed_struct_syntax_bigquery() {
|
||||||
),
|
),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Timestamp(None, TimezoneInfo::None),
|
field_type: DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
|
@ -1024,13 +1027,14 @@ fn parse_typed_struct_syntax_bigquery() {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Time(None, TimezoneInfo::None),
|
data_type: DataType::Time(None, TimezoneInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("15:30:00".into()),
|
value: Value::SingleQuotedString("15:30:00".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Time(None, TimezoneInfo::None),
|
field_type: DataType::Time(None, TimezoneInfo::None),
|
||||||
|
@ -1045,13 +1049,14 @@ fn parse_typed_struct_syntax_bigquery() {
|
||||||
assert_eq!(2, select.projection.len());
|
assert_eq!(2, select.projection.len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Numeric(ExactNumberInfo::None),
|
data_type: DataType::Numeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1".into()),
|
value: Value::SingleQuotedString("1".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Numeric(ExactNumberInfo::None),
|
field_type: DataType::Numeric(ExactNumberInfo::None),
|
||||||
|
@ -1062,13 +1067,14 @@ fn parse_typed_struct_syntax_bigquery() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1".into()),
|
value: Value::SingleQuotedString("1".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::BigNumeric(ExactNumberInfo::None),
|
field_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
|
@ -1239,13 +1245,14 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Datetime(None),
|
data_type: DataType::Datetime(None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()),
|
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Datetime(None),
|
field_type: DataType::Datetime(None),
|
||||||
|
@ -1301,15 +1308,16 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::JSON,
|
data_type: DataType::JSON,
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(
|
value: Value::SingleQuotedString(
|
||||||
r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into()
|
r#"{"class" : {"students" : [{"name" : "Jane"}]}}"#.into()
|
||||||
),
|
),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::JSON,
|
field_type: DataType::JSON,
|
||||||
|
@ -1337,15 +1345,16 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(
|
value: Value::SingleQuotedString(
|
||||||
"2008-12-25 15:30:00 America/Los_Angeles".into()
|
"2008-12-25 15:30:00 America/Los_Angeles".into()
|
||||||
),
|
),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Timestamp(None, TimezoneInfo::None),
|
field_type: DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
|
@ -1357,13 +1366,14 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Time(None, TimezoneInfo::None),
|
data_type: DataType::Time(None, TimezoneInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("15:30:00".into()),
|
value: Value::SingleQuotedString("15:30:00".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Time(None, TimezoneInfo::None),
|
field_type: DataType::Time(None, TimezoneInfo::None),
|
||||||
|
@ -1378,13 +1388,14 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
|
||||||
assert_eq!(2, select.projection.len());
|
assert_eq!(2, select.projection.len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Numeric(ExactNumberInfo::None),
|
data_type: DataType::Numeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1".into()),
|
value: Value::SingleQuotedString("1".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::Numeric(ExactNumberInfo::None),
|
field_type: DataType::Numeric(ExactNumberInfo::None),
|
||||||
|
@ -1395,13 +1406,14 @@ fn parse_typed_struct_syntax_bigquery_and_generic() {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::Struct {
|
&Expr::Struct {
|
||||||
values: vec![Expr::TypedString {
|
values: vec![Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1".into()),
|
value: Value::SingleQuotedString("1".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}],
|
uses_odbc_syntax: false
|
||||||
|
})],
|
||||||
fields: vec![StructField {
|
fields: vec![StructField {
|
||||||
field_name: None,
|
field_name: None,
|
||||||
field_type: DataType::BigNumeric(ExactNumberInfo::None),
|
field_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
|
@ -2433,13 +2445,14 @@ fn test_triple_quote_typed_strings() {
|
||||||
|
|
||||||
let expr = bigquery().verified_expr(r#"JSON """{"foo":"bar's"}""""#);
|
let expr = bigquery().verified_expr(r#"JSON """{"foo":"bar's"}""""#);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::JSON,
|
data_type: DataType::JSON,
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::TripleDoubleQuotedString(r#"{"foo":"bar's"}"#.into()),
|
value: Value::TripleDoubleQuotedString(r#"{"foo":"bar's"}"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr
|
expr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5914,13 +5914,14 @@ fn parse_literal_date() {
|
||||||
let sql = "SELECT DATE '1999-01-01'";
|
let sql = "SELECT DATE '1999-01-01'";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Date,
|
data_type: DataType::Date,
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1999-01-01".into()),
|
value: Value::SingleQuotedString("1999-01-01".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5930,13 +5931,14 @@ fn parse_literal_time() {
|
||||||
let sql = "SELECT TIME '01:23:34'";
|
let sql = "SELECT TIME '01:23:34'";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Time(None, TimezoneInfo::None),
|
data_type: DataType::Time(None, TimezoneInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("01:23:34".into()),
|
value: Value::SingleQuotedString("01:23:34".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
},
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5946,13 +5948,14 @@ fn parse_literal_datetime() {
|
||||||
let sql = "SELECT DATETIME '1999-01-01 01:23:34.45'";
|
let sql = "SELECT DATETIME '1999-01-01 01:23:34.45'";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Datetime(None),
|
data_type: DataType::Datetime(None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()),
|
value: Value::SingleQuotedString("1999-01-01 01:23:34.45".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
},
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5962,13 +5965,14 @@ fn parse_literal_timestamp_without_time_zone() {
|
||||||
let sql = "SELECT TIMESTAMP '1999-01-01 01:23:34'";
|
let sql = "SELECT TIMESTAMP '1999-01-01 01:23:34'";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1999-01-01 01:23:34".into()),
|
value: Value::SingleQuotedString("1999-01-01 01:23:34".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
},
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5980,13 +5984,14 @@ fn parse_literal_timestamp_with_time_zone() {
|
||||||
let sql = "SELECT TIMESTAMPTZ '1999-01-01 01:23:34Z'";
|
let sql = "SELECT TIMESTAMPTZ '1999-01-01 01:23:34Z'";
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Timestamp(None, TimezoneInfo::Tz),
|
data_type: DataType::Timestamp(None, TimezoneInfo::Tz),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1999-01-01 01:23:34Z".into()),
|
value: Value::SingleQuotedString("1999-01-01 01:23:34Z".into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
},
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -6556,7 +6561,7 @@ fn parse_json_keyword() {
|
||||||
}'"#;
|
}'"#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::JSON,
|
data_type: DataType::JSON,
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(
|
value: Value::SingleQuotedString(
|
||||||
|
@ -6583,8 +6588,9 @@ fn parse_json_keyword() {
|
||||||
.to_string()
|
.to_string()
|
||||||
),
|
),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false,
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6593,17 +6599,23 @@ fn parse_json_keyword() {
|
||||||
fn parse_typed_strings() {
|
fn parse_typed_strings() {
|
||||||
let expr = verified_expr(r#"JSON '{"foo":"bar"}'"#);
|
let expr = verified_expr(r#"JSON '{"foo":"bar"}'"#);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::JSON,
|
data_type: DataType::JSON,
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(r#"{"foo":"bar"}"#.into()),
|
value: Value::SingleQuotedString(r#"{"foo":"bar"}"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr
|
expr
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Expr::TypedString { data_type, value } = expr {
|
if let Expr::TypedString(TypedString {
|
||||||
|
data_type,
|
||||||
|
value,
|
||||||
|
uses_odbc_syntax: false,
|
||||||
|
}) = expr
|
||||||
|
{
|
||||||
assert_eq!(DataType::JSON, data_type);
|
assert_eq!(DataType::JSON, data_type);
|
||||||
assert_eq!(r#"{"foo":"bar"}"#, value.into_string().unwrap());
|
assert_eq!(r#"{"foo":"bar"}"#, value.into_string().unwrap());
|
||||||
}
|
}
|
||||||
|
@ -6614,13 +6626,14 @@ fn parse_bignumeric_keyword() {
|
||||||
let sql = r#"SELECT BIGNUMERIC '0'"#;
|
let sql = r#"SELECT BIGNUMERIC '0'"#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(r#"0"#.into()),
|
value: Value::SingleQuotedString(r#"0"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
verified_stmt("SELECT BIGNUMERIC '0'");
|
verified_stmt("SELECT BIGNUMERIC '0'");
|
||||||
|
@ -6628,13 +6641,14 @@ fn parse_bignumeric_keyword() {
|
||||||
let sql = r#"SELECT BIGNUMERIC '123456'"#;
|
let sql = r#"SELECT BIGNUMERIC '123456'"#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(r#"123456"#.into()),
|
value: Value::SingleQuotedString(r#"123456"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
verified_stmt("SELECT BIGNUMERIC '123456'");
|
verified_stmt("SELECT BIGNUMERIC '123456'");
|
||||||
|
@ -6642,13 +6656,14 @@ fn parse_bignumeric_keyword() {
|
||||||
let sql = r#"SELECT BIGNUMERIC '-3.14'"#;
|
let sql = r#"SELECT BIGNUMERIC '-3.14'"#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(r#"-3.14"#.into()),
|
value: Value::SingleQuotedString(r#"-3.14"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
verified_stmt("SELECT BIGNUMERIC '-3.14'");
|
verified_stmt("SELECT BIGNUMERIC '-3.14'");
|
||||||
|
@ -6656,13 +6671,14 @@ fn parse_bignumeric_keyword() {
|
||||||
let sql = r#"SELECT BIGNUMERIC '-0.54321'"#;
|
let sql = r#"SELECT BIGNUMERIC '-0.54321'"#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(r#"-0.54321"#.into()),
|
value: Value::SingleQuotedString(r#"-0.54321"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
verified_stmt("SELECT BIGNUMERIC '-0.54321'");
|
verified_stmt("SELECT BIGNUMERIC '-0.54321'");
|
||||||
|
@ -6670,13 +6686,14 @@ fn parse_bignumeric_keyword() {
|
||||||
let sql = r#"SELECT BIGNUMERIC '1.23456e05'"#;
|
let sql = r#"SELECT BIGNUMERIC '1.23456e05'"#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(r#"1.23456e05"#.into()),
|
value: Value::SingleQuotedString(r#"1.23456e05"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
verified_stmt("SELECT BIGNUMERIC '1.23456e05'");
|
verified_stmt("SELECT BIGNUMERIC '1.23456e05'");
|
||||||
|
@ -6684,13 +6701,14 @@ fn parse_bignumeric_keyword() {
|
||||||
let sql = r#"SELECT BIGNUMERIC '-9.876e-3'"#;
|
let sql = r#"SELECT BIGNUMERIC '-9.876e-3'"#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&Expr::TypedString {
|
&Expr::TypedString(TypedString {
|
||||||
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
data_type: DataType::BigNumeric(ExactNumberInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString(r#"-9.876e-3"#.into()),
|
value: Value::SingleQuotedString(r#"-9.876e-3"#.into()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
uses_odbc_syntax: false
|
||||||
|
}),
|
||||||
expr_from_projection(only(&select.projection)),
|
expr_from_projection(only(&select.projection)),
|
||||||
);
|
);
|
||||||
verified_stmt("SELECT BIGNUMERIC '-9.876e-3'");
|
verified_stmt("SELECT BIGNUMERIC '-9.876e-3'");
|
||||||
|
@ -15015,83 +15033,90 @@ fn test_geometry_type() {
|
||||||
let sql = "point '1,2'";
|
let sql = "point '1,2'";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(GeometricTypeKind::Point),
|
data_type: DataType::GeometricType(GeometricTypeKind::Point),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1,2".to_string()),
|
value: Value::SingleQuotedString("1,2".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "line '1,2,3,4'";
|
let sql = "line '1,2,3,4'";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(GeometricTypeKind::Line),
|
data_type: DataType::GeometricType(GeometricTypeKind::Line),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "path '1,2,3,4'";
|
let sql = "path '1,2,3,4'";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(GeometricTypeKind::GeometricPath),
|
data_type: DataType::GeometricType(GeometricTypeKind::GeometricPath),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
let sql = "box '1,2,3,4'";
|
let sql = "box '1,2,3,4'";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(GeometricTypeKind::GeometricBox),
|
data_type: DataType::GeometricType(GeometricTypeKind::GeometricBox),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "circle '1,2,3'";
|
let sql = "circle '1,2,3'";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(GeometricTypeKind::Circle),
|
data_type: DataType::GeometricType(GeometricTypeKind::Circle),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1,2,3".to_string()),
|
value: Value::SingleQuotedString("1,2,3".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let sql = "polygon '1,2,3,4'";
|
let sql = "polygon '1,2,3,4'";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(GeometricTypeKind::Polygon),
|
data_type: DataType::GeometricType(GeometricTypeKind::Polygon),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
let sql = "lseg '1,2,3,4'";
|
let sql = "lseg '1,2,3,4'";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
all_dialects_where(|d| d.supports_geometric_types()).verified_expr(sql),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::GeometricType(GeometricTypeKind::LineSegment),
|
data_type: DataType::GeometricType(GeometricTypeKind::LineSegment),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
value: Value::SingleQuotedString("1,2,3,4".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -16291,6 +16316,31 @@ fn parse_notnull() {
|
||||||
notnull_unsupported_dialects.expr_parses_to("NOT NULL NOTNULL", "NOT NULL");
|
notnull_unsupported_dialects.expr_parses_to("NOT NULL NOTNULL", "NOT NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_odbc_time_date_timestamp() {
|
||||||
|
// Supported statements
|
||||||
|
let sql_d = "SELECT {d '2025-07-17'}, category_name FROM categories";
|
||||||
|
let _ = all_dialects().verified_stmt(sql_d);
|
||||||
|
let sql_t = "SELECT {t '14:12:01'}, category_name FROM categories";
|
||||||
|
let _ = all_dialects().verified_stmt(sql_t);
|
||||||
|
let sql_ts = "SELECT {ts '2025-07-17 14:12:01'}, category_name FROM categories";
|
||||||
|
let _ = all_dialects().verified_stmt(sql_ts);
|
||||||
|
// Unsupported statement
|
||||||
|
let supports_dictionary = all_dialects_where(|d| d.supports_dictionary_syntax());
|
||||||
|
let dictionary_unsupported = all_dialects_where(|d| !d.supports_dictionary_syntax());
|
||||||
|
let sql = "SELECT {tt '14:12:01'} FROM foo";
|
||||||
|
let res = supports_dictionary.parse_sql_statements(sql);
|
||||||
|
let res_dict = dictionary_unsupported.parse_sql_statements(sql);
|
||||||
|
assert_eq!(
|
||||||
|
ParserError::ParserError("Expected: :, found: '14:12:01'".to_string()),
|
||||||
|
res.unwrap_err()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ParserError::ParserError("Expected: an expression, found: {".to_string()),
|
||||||
|
res_dict.unwrap_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_create_user() {
|
fn parse_create_user() {
|
||||||
let create = verified_stmt("CREATE USER u1");
|
let create = verified_stmt("CREATE USER u1");
|
||||||
|
|
|
@ -327,13 +327,14 @@ fn data_type_timestamp_ntz() {
|
||||||
// Literal
|
// Literal
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
databricks().verified_expr("TIMESTAMP_NTZ '2025-03-29T18:52:00'"),
|
databricks().verified_expr("TIMESTAMP_NTZ '2025-03-29T18:52:00'"),
|
||||||
Expr::TypedString {
|
Expr::TypedString(TypedString {
|
||||||
data_type: DataType::TimestampNtz,
|
data_type: DataType::TimestampNtz,
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("2025-03-29T18:52:00".to_owned()),
|
value: Value::SingleQuotedString("2025-03-29T18:52:00".to_owned()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
}
|
},
|
||||||
}
|
uses_odbc_syntax: false
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cast
|
// Cast
|
||||||
|
|
|
@ -4723,7 +4723,7 @@ fn parse_dollar_quoted_string() {
|
||||||
quote_style: None,
|
quote_style: None,
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -5296,13 +5296,14 @@ fn parse_at_time_zone() {
|
||||||
// check precedence
|
// check precedence
|
||||||
let expr = Expr::BinaryOp {
|
let expr = Expr::BinaryOp {
|
||||||
left: Box::new(Expr::AtTimeZone {
|
left: Box::new(Expr::AtTimeZone {
|
||||||
timestamp: Box::new(Expr::TypedString {
|
timestamp: Box::new(Expr::TypedString(TypedString {
|
||||||
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
data_type: DataType::Timestamp(None, TimezoneInfo::None),
|
||||||
value: ValueWithSpan {
|
value: ValueWithSpan {
|
||||||
value: Value::SingleQuotedString("2001-09-28 01:00".to_string()),
|
value: Value::SingleQuotedString("2001-09-28 01:00".to_string()),
|
||||||
span: Span::empty(),
|
span: Span::empty(),
|
||||||
},
|
},
|
||||||
}),
|
uses_odbc_syntax: false,
|
||||||
|
})),
|
||||||
time_zone: Box::new(Expr::Cast {
|
time_zone: Box::new(Expr::Cast {
|
||||||
kind: CastKind::DoubleColon,
|
kind: CastKind::DoubleColon,
|
||||||
expr: Box::new(Expr::Value(
|
expr: Box::new(Expr::Value(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue