diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b5744e863..7d787e887 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -90,6 +90,6 @@ jobs: with: crate: cargo-tarpaulin version: 0.14.2 - use-tool-cache: true + use-tool-cache: false - name: Test run: cargo test --all-features diff --git a/README.md b/README.md index 41a44d3d7..94ef9edba 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,14 @@ under the License. --> +# Patching SQL Parser for LakeSail + +1. Use `dev` as the base branch when creating PRs in the fork. +2. Please confirm the base repository when creating PRs. You should manually choose `lakehq/sqlparser-rs` when proposing changes to the fork. +3. For patching, use a squash commit to merge the PR. This ensures that each patch appears as a single commit in the `dev` branch of the fork. +4. For merging from upstream, use a merge commit to merge the PR. This ensures that the upstream history is kept in the `dev` branch of the fork. +5. Please avoid mixing code changes and upstream merge in a single PR. + # Extensible SQL Lexer and Parser for Rust [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 02aa6cc9f..d07786182 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -25,7 +25,9 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::ast::{display_comma_separated, Expr, ObjectName, StructField, UnionField}; +use crate::ast::{ + display_comma_separated, Expr, IntervalUnit, ObjectName, StructField, UnionField, +}; use super::{value::escape_single_quote_string, ColumnDef}; @@ -45,6 +47,10 @@ pub enum EnumMember { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum DataType { + /// Null type + Null, + /// Void type + Void, /// Fixed-length character type e.g. CHARACTER(10) Character(Option), /// Fixed-length char type e.g. CHAR(10) @@ -190,14 +196,26 @@ pub enum DataType { /// /// [clickhouse]: https://clickhouse.com/docs/en/sql-reference/data-types/int-uint Int256, + /// Byte with optional display width e.g. BYTE or BYTE(6) + Byte(Option), + /// Short with optional display width e.g. SHORT or SHORT(9) + Short(Option), /// Integer with optional display width e.g. INTEGER or INTEGER(11) Integer(Option), + /// Long with optional display width e.g. LONG or LONG(20) + Long(Option), /// Unsigned int with optional display width e.g. INT UNSIGNED or INT(11) UNSIGNED UnsignedInt(Option), /// Unsigned int4 with optional display width e.g. INT4 UNSIGNED or INT4(11) UNSIGNED UnsignedInt4(Option), + /// Unsigned byte with optional display width e.g. BYTE UNSIGNED or BYTE(6) UNSIGNED + UnsignedByte(Option), + /// Unsigned short with optional display width e.g. SHORT UNSIGNED or SHORT(9) UNSIGNED + UnsignedShort(Option), /// Unsigned integer with optional display width e.g. INTEGER UNSIGNED or INTEGER(11) UNSIGNED UnsignedInteger(Option), + /// Unsigned long with optional display width e.g. LONG UNSIGNED or LONG(20) UNSIGNED + UnsignedLong(Option), /// Unsigned integer type in [clickhouse] /// Note: UInt8 mean 8 bits in [clickhouse] /// @@ -289,7 +307,7 @@ pub enum DataType { /// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#datetime-type Timestamp(Option, TimezoneInfo), /// Interval - Interval, + Interval(IntervalUnit), /// JSON type JSON, /// Binary JSON type @@ -382,6 +400,8 @@ pub enum DataType { impl fmt::Display for DataType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + DataType::Null => write!(f, "NULL"), + DataType::Void => write!(f, "VOID"), DataType::Character(size) => format_character_string_type(f, "CHARACTER", size), DataType::Char(size) => format_character_string_type(f, "CHAR", size), DataType::CharacterVarying(size) => { @@ -471,12 +491,30 @@ impl fmt::Display for DataType { DataType::UnsignedInt4(zerofill) => { format_type_with_optional_length(f, "INT4", zerofill, true) } + DataType::Byte(zerofill) => { + format_type_with_optional_length(f, "BYTE", zerofill, false) + } + DataType::UnsignedByte(zerofill) => { + format_type_with_optional_length(f, "BYTE", zerofill, true) + } + DataType::Short(zerofill) => { + format_type_with_optional_length(f, "SHORT", zerofill, false) + } + DataType::UnsignedShort(zerofill) => { + format_type_with_optional_length(f, "SHORT", zerofill, true) + } DataType::Integer(zerofill) => { format_type_with_optional_length(f, "INTEGER", zerofill, false) } DataType::UnsignedInteger(zerofill) => { format_type_with_optional_length(f, "INTEGER", zerofill, true) } + DataType::Long(zerofill) => { + format_type_with_optional_length(f, "LONG", zerofill, false) + } + DataType::UnsignedLong(zerofill) => { + format_type_with_optional_length(f, "LONG", zerofill, true) + } DataType::BigInt(zerofill) => { format_type_with_optional_length(f, "BIGINT", zerofill, false) } @@ -532,7 +570,7 @@ impl fmt::Display for DataType { timezone, ) } - DataType::Interval => write!(f, "INTERVAL"), + DataType::Interval(unit) => write!(f, "INTERVAL{unit}"), DataType::JSON => write!(f, "JSON"), DataType::JSONB => write!(f, "JSONB"), DataType::Regclass => write!(f, "REGCLASS"), @@ -731,6 +769,8 @@ pub enum TimezoneInfo { /// [standard]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#datetime-type /// [Postgresql]: https://www.postgresql.org/docs/current/datatype-datetime.html WithoutTimeZone, + /// Temporal type 'WITH LOCAL TIME ZONE'. E.g., TIMESTAMP WITH LOCAL TIME ZONE + WithLocalTimeZone, /// Postgresql specific `WITH TIME ZONE` formatting, for both TIME and TIMESTAMP. E.g., TIMETZ, [Postgresql] /// /// [Postgresql]: https://www.postgresql.org/docs/current/datatype-datetime.html @@ -749,6 +789,9 @@ impl fmt::Display for TimezoneInfo { TimezoneInfo::WithoutTimeZone => { write!(f, " WITHOUT TIME ZONE") } + TimezoneInfo::WithLocalTimeZone => { + write!(f, " WITH LOCAL TIME ZONE") + } TimezoneInfo::Tz => { // TZ is the only one that is displayed BEFORE the precision, so the datatype display // must be aware of that. Check diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 2d79f7d6b..20fde2321 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -310,8 +310,29 @@ impl fmt::Display for Array { #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -pub struct Interval { - pub value: Box, +pub enum Interval { + Standard { + value: Box, + unit: IntervalUnit, + }, + MultiUnit { + values: Vec, + }, +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct IntervalValueWithUnit { + pub value: Expr, + pub unit: IntervalUnit, +} + +/// Represents the unit in INTERVAL data types and expressions. +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub struct IntervalUnit { pub leading_field: Option, pub leading_precision: Option, pub last_field: Option, @@ -324,7 +345,23 @@ pub struct Interval { impl fmt::Display for Interval { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let value = self.value.as_ref(); + match &self { + Interval::Standard { value, unit } => { + write!(f, "INTERVAL {}{}", value, unit) + } + Interval::MultiUnit { values } => { + write!(f, "INTERVAL")?; + for IntervalValueWithUnit { value, unit } in values.iter() { + write!(f, " {}{}", value, unit)?; + } + Ok(()) + } + } + } +} + +impl fmt::Display for IntervalUnit { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match ( &self.leading_field, self.leading_precision, @@ -340,11 +377,10 @@ impl fmt::Display for Interval { assert!(self.last_field.is_none()); write!( f, - "INTERVAL {value} SECOND ({leading_precision}, {fractional_seconds_precision})" + " SECOND ({leading_precision}, {fractional_seconds_precision})" ) } _ => { - write!(f, "INTERVAL {value}")?; if let Some(leading_field) = &self.leading_field { write!(f, " {leading_field}")?; } @@ -372,15 +408,24 @@ impl fmt::Display for Interval { pub struct StructField { pub field_name: Option, pub field_type: DataType, + pub not_null: bool, + pub comment: Option, } impl fmt::Display for StructField { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(name) = &self.field_name { - write!(f, "{name} {}", self.field_type) + write!(f, "{name} {}", self.field_type)?; } else { - write!(f, "{}", self.field_type) + write!(f, "{}", self.field_type)?; + } + if self.not_null { + write!(f, " NOT NULL")?; + } + if let Some(comment) = &self.comment { + write!(f, " COMMENT '{comment}'")?; } + Ok(()) } } @@ -8162,26 +8207,30 @@ mod tests { #[test] fn test_interval_display() { - let interval = Expr::Interval(Interval { + let interval = Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( "123:45.67", )))), - leading_field: Some(DateTimeField::Minute), - leading_precision: Some(10), - last_field: Some(DateTimeField::Second), - fractional_seconds_precision: Some(9), + unit: IntervalUnit { + leading_field: Some(DateTimeField::Minute), + leading_precision: Some(10), + last_field: Some(DateTimeField::Second), + fractional_seconds_precision: Some(9), + }, }); assert_eq!( "INTERVAL '123:45.67' MINUTE (10) TO SECOND (9)", format!("{interval}"), ); - let interval = Expr::Interval(Interval { + let interval = Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("5")))), - leading_field: Some(DateTimeField::Second), - leading_precision: Some(1), - last_field: None, - fractional_seconds_precision: Some(3), + unit: IntervalUnit { + leading_field: Some(DateTimeField::Second), + leading_precision: Some(1), + last_field: None, + fractional_seconds_precision: Some(3), + }, }); assert_eq!("INTERVAL '5' SECOND (1, 3)", format!("{interval}")); } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 183bebf8c..8e74f5d04 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -26,8 +26,8 @@ use super::{ CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, - GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate, InterpolateExpr, Join, - JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, MatchRecognizePattern, + GroupByExpr, HavingBound, IlikeSelectItem, Insert, Interpolate, InterpolateExpr, Interval, + Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, MatchRecognizePattern, Measure, NamedWindowDefinition, ObjectName, Offset, OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, Partition, PivotValueSource, ProjectionSelect, Query, ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, @@ -1444,7 +1444,7 @@ impl Spanned for Expr { Expr::Named { .. } => Span::empty(), Expr::Dictionary(_) => Span::empty(), Expr::Map(_) => Span::empty(), - Expr::Interval(interval) => interval.value.span(), + Expr::Interval(interval) => interval.span(), Expr::Wildcard(token) => token.0.span, Expr::QualifiedWildcard(object_name, token) => union_spans( object_name @@ -1510,6 +1510,17 @@ impl Spanned for Array { } } +impl Spanned for Interval { + fn span(&self) -> Span { + match self { + Interval::Standard { value, .. } => value.span(), + Interval::MultiUnit { values, .. } => { + union_spans(values.iter().map(|i| i.value.span())) + } + } + } +} + impl Spanned for Function { fn span(&self) -> Span { let Function { diff --git a/src/ast/value.rs b/src/ast/value.rs index 45cc06a07..00ba9d198 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -37,12 +37,12 @@ use sqlparser_derive::{Visit, VisitMut}; pub enum Value { /// Numeric literal #[cfg(not(feature = "bigdecimal"))] - Number(String, bool), + Number(String, Option), #[cfg(feature = "bigdecimal")] // HINT: use `test_utils::number` to make an instance of // Value::Number This might help if you your tests pass locally // but fail on CI with the `--all-features` flag enabled - Number(BigDecimal, bool), + Number(BigDecimal, Option), /// 'string value' SingleQuotedString(String), // $$string value$$ (postgres syntax) @@ -100,7 +100,7 @@ pub enum Value { impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Value::Number(v, l) => write!(f, "{}{long}", v, long = if *l { "L" } else { "" }), + Value::Number(v, postfix) => write!(f, "{}{}", v, postfix.as_deref().unwrap_or("")), Value::DoubleQuotedString(v) => write!(f, "\"{}\"", escape_double_quote_string(v)), Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)), Value::TripleSingleQuotedString(v) => { diff --git a/src/keywords.rs b/src/keywords.rs index 8c8077f51..a66c344ec 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -141,6 +141,7 @@ define_keywords!( BUCKETS, BY, BYPASSRLS, + BYTE, BYTEA, BYTES, CACHE, @@ -470,6 +471,7 @@ define_keywords!( LOCKED, LOGIN, LOGS, + LONG, LONGBLOB, LONGTEXT, LOWCARDINALITY, @@ -731,6 +733,7 @@ define_keywords!( SETS, SETTINGS, SHARE, + SHORT, SHOW, SIMILAR, SKIP, @@ -799,6 +802,8 @@ define_keywords!( TIME, TIMESTAMP, TIMESTAMPTZ, + TIMESTAMP_LTZ, + TIMESTAMP_NTZ, TIMETZ, TIMEZONE, TIMEZONE_ABBR, @@ -880,6 +885,7 @@ define_keywords!( VIEW, VIEWS, VIRTUAL, + VOID, VOLATILE, WAREHOUSE, WEEK, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3cf3c585e..45868be65 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1307,7 +1307,7 @@ impl<'a> Parser<'a> { let loc = self.peek_token_ref().span.start; let opt_expr = self.maybe_parse(|parser| { match parser.parse_data_type()? { - DataType::Interval => parser.parse_interval(), + DataType::Interval(_) => parser.parse_interval(), // PostgreSQL allows almost any identifier to be used as custom data type name, // and we support that in `parse_data_type()`. But unlike Postgres we don't // have a list of globally reserved keywords (since they vary across dialects), @@ -2530,35 +2530,86 @@ impl<'a> Parser<'a> { /// /// Some syntactically valid intervals: /// - /// ```sql - /// 1. INTERVAL '1' DAY - /// 2. INTERVAL '1-1' YEAR TO MONTH - /// 3. INTERVAL '1' SECOND - /// 4. INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5) - /// 5. INTERVAL '1.1' SECOND (2, 2) - /// 6. INTERVAL '1:1' HOUR (5) TO MINUTE (5) - /// 7. (MySql & BigQuery only): INTERVAL 1 DAY - /// ``` + /// 1. `INTERVAL '1' DAY` + /// 2. `INTERVAL '1-1' YEAR TO MONTH` + /// 3. `INTERVAL '1' SECOND` + /// 4. `INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5)` + /// 5. `INTERVAL '1.1' SECOND (2, 2)` + /// 6. `INTERVAL '1:1' HOUR (5) TO MINUTE (5)` + /// 7. (MySql & BigQuey only) `INTERVAL 1 DAY` + /// 8. (Spark only) `INTERVAL -2 HOURS '3' MINUTES` /// /// Note that we do not currently attempt to parse the quoted value. pub fn parse_interval(&mut self) -> Result { - // The SQL standard allows an optional sign before the value string, but - // it is not clear if any implementations support that syntax, so we - // don't currently try to parse it. (The sign can instead be included - // inside the value string.) - - // to match the different flavours of INTERVAL syntax, we only allow expressions - // if the dialect requires an interval qualifier, - // see https://github.com/sqlparser-rs/sqlparser-rs/pull/1398 for more details - let value = if self.dialect.require_interval_qualifier() { - // parse a whole expression so `INTERVAL 1 + 1 DAY` is valid - self.parse_expr()? + // The SQL standard allows an optional sign before the value string, and + // it is supported by implementations such as Spark. + // The sign is parsed in the interval expression whose nested expression + // is the value string. + let mut values: Vec<(Expr, IntervalUnit)> = vec![]; + loop { + let item = self.maybe_parse(|parser| { + if !values.is_empty() && parser.parse_keyword(Keyword::TO) { + // Treat "TO" as an unexpected token from the previous invalid interval unit, + // rather than an identifier for the next interval value. + return parser_err!("Unexpected TO", parser.peek_token().span.start); + } + // to match the different flavours of INTERVAL syntax, we only allow expressions + // if the dialect requires an interval qualifier, + // see https://github.com/sqlparser-rs/sqlparser-rs/pull/1398 for more details + let value = if parser.dialect.require_interval_qualifier() { + // parse a whole expression so `INTERVAL 1 + 1 DAY` is valid + parser.parse_expr()? + } else { + // parse a prefix expression so `INTERVAL 1 DAY` is valid, but `INTERVAL 1 + 1 DAY` is not + // this also means that `INTERVAL '5 days' > INTERVAL '1 day'` treated properly + parser.parse_prefix()? + }; + let unit = parser.parse_interval_unit()?; + if !values.is_empty() + && !matches!( + &unit, + IntervalUnit { + leading_field: Some(_), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + ) + { + return parser_err!( + "Expecting a single date/time field for multi-unit interval value", + parser.peek_token().span.start + ); + } + Ok((value, unit)) + })?; + if let Some(item) = item { + values.push(item); + } else { + break; + } + } + if values.is_empty() { + parser_err!( + "INTERVAL must have at least one valid value along with a unit if required", + self.peek_token().span.start + ) + } else if values.len() == 1 { + let (value, unit) = values.pop().unwrap(); + Ok(Expr::Interval(Interval::Standard { + value: Box::new(value), + unit, + })) } else { - // parse a prefix expression so `INTERVAL 1 DAY` is valid, but `INTERVAL 1 + 1 DAY` is not - // this also means that `INTERVAL '5 days' > INTERVAL '1 day'` treated properly - self.parse_prefix()? - }; + let values = values + .into_iter() + .map(|(value, unit)| IntervalValueWithUnit { value, unit }) + .collect(); + Ok(Expr::Interval(Interval::MultiUnit { values })) + } + } + pub fn parse_interval_unit(&mut self) -> Result { // Following the string literal is a qualifier which indicates the units // of the duration specified in the string literal. // @@ -2600,13 +2651,12 @@ impl<'a> Parser<'a> { } }; - Ok(Expr::Interval(Interval { - value: Box::new(value), + Ok(IntervalUnit { leading_field, leading_precision, last_field, fractional_seconds_precision: fsec_precision, - })) + }) } /// Peek at the next token and determine if it is a temporal unit @@ -2737,11 +2787,25 @@ impl<'a> Parser<'a> { let start_token = self.peek_token(); self.expect_keyword_is(Keyword::STRUCT)?; + // Handle empty struct type for all tokenization possibilities + // (e.g. `STRUCT <>`, `STRUCT < >`, or `STRUCT < >>`). + if Token::Neq == self.peek_token() { + self.next_token(); + return Ok((Default::default(), false.into())); + } // Nothing to do if we have no type information. if Token::Lt != self.peek_token() { return Ok((Default::default(), false.into())); } self.next_token(); + if Token::Gt == self.peek_token() { + self.next_token(); + return Ok((Default::default(), false.into())); + } + if Token::ShiftRight == self.peek_token() { + self.next_token(); + return Ok((Default::default(), true.into())); + } let mut field_defs = vec![]; let trailing_bracket = loop { @@ -2776,6 +2840,8 @@ impl<'a> Parser<'a> { Ok(StructField { field_name: Some(field_name), field_type, + not_null: false, + comment: None, }) }); self.expect_token(&Token::RParen)?; @@ -2786,7 +2852,7 @@ impl<'a> Parser<'a> { /// Syntax: /// /// ```sql - /// [field_name] field_type + /// [field_name [:]] field_type [NOT NULL] [COMMENT comment] /// ``` /// /// [struct]: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#declaring_a_struct_type @@ -2796,23 +2862,35 @@ impl<'a> Parser<'a> { ) -> Result<(StructField, MatchedTrailingBracket), ParserError> { // Look beyond the next item to infer whether both field name // and type are specified. - let is_anonymous_field = !matches!( - (self.peek_nth_token(0).token, self.peek_nth_token(1).token), - (Token::Word(_), Token::Word(_)) - ); - - let field_name = if is_anonymous_field { - None - } else { - Some(self.parse_identifier()?) + // This heuristic assumes that only single-token data types are + // used when the field name is omitted. + let field_name = match (self.peek_nth_token(0).token, self.peek_nth_token(1).token) { + (Token::Word(_), Token::Word(_)) => { + let name = self.parse_identifier()?; + Some(name) + } + (Token::Word(_), Token::Colon) => { + let name = self.parse_identifier()?; + self.expect_token(&Token::Colon)?; + Some(name) + } + _ => None, }; let (field_type, trailing_bracket) = self.parse_data_type_helper()?; + let not_null = self.parse_keywords(&[Keyword::NOT, Keyword::NULL]); + let comment = if self.parse_keyword(Keyword::COMMENT) { + Some(self.parse_literal_string()?) + } else { + None + }; Ok(( StructField { field_name, field_type, + not_null, + comment, }, trailing_bracket, )) @@ -8275,10 +8353,10 @@ impl<'a> Parser<'a> { }, ), }, - // The call to n.parse() returns a bigdecimal when the + // The call to parse() returns a bigdecimal when the // bigdecimal feature is enabled, and is otherwise a no-op // (i.e., it returns the input string). - Token::Number(n, l) => Ok(Value::Number(Self::parse(n, span.start)?, l)), + Token::Number(n, p) => Ok(Value::Number(Self::parse(n, span.start)?, p)), Token::SingleQuotedString(ref s) => Ok(Value::SingleQuotedString(s.to_string())), Token::DoubleQuotedString(ref s) => Ok(Value::DoubleQuotedString(s.to_string())), Token::TripleSingleQuotedString(ref s) => { @@ -8323,7 +8401,7 @@ impl<'a> Parser<'a> { let next_token = self.next_token(); let ident = match next_token.token { Token::Word(w) => Ok(w.into_ident(next_token.span)), - Token::Number(w, false) => Ok(Ident::new(w)), + Token::Number(w, None) => Ok(Ident::new(w)), _ => self.expected("placeholder", next_token), }?; let placeholder = tok.to_string() + &ident.value; @@ -8473,6 +8551,8 @@ impl<'a> Parser<'a> { let mut trailing_bracket: MatchedTrailingBracket = false.into(); let mut data = match &next_token.token { Token::Word(w) => match w.keyword { + Keyword::NULL => Ok(DataType::Null), + Keyword::VOID => Ok(DataType::Void), Keyword::BOOLEAN => Ok(DataType::Boolean), Keyword::BOOL => Ok(DataType::Bool), Keyword::FLOAT => Ok(DataType::Float(self.parse_optional_precision()?)), @@ -8551,6 +8631,22 @@ impl<'a> Parser<'a> { Keyword::INT64 => Ok(DataType::Int64), Keyword::INT128 => Ok(DataType::Int128), Keyword::INT256 => Ok(DataType::Int256), + Keyword::BYTE => { + let optional_precision = self.parse_optional_precision(); + if self.parse_keyword(Keyword::UNSIGNED) { + Ok(DataType::UnsignedByte(optional_precision?)) + } else { + Ok(DataType::Byte(optional_precision?)) + } + } + Keyword::SHORT => { + let optional_precision = self.parse_optional_precision(); + if self.parse_keyword(Keyword::UNSIGNED) { + Ok(DataType::UnsignedShort(optional_precision?)) + } else { + Ok(DataType::Short(optional_precision?)) + } + } Keyword::INTEGER => { let optional_precision = self.parse_optional_precision(); if self.parse_keyword(Keyword::UNSIGNED) { @@ -8559,6 +8655,14 @@ impl<'a> Parser<'a> { Ok(DataType::Integer(optional_precision?)) } } + Keyword::LONG => { + let optional_precision = self.parse_optional_precision(); + if self.parse_keyword(Keyword::UNSIGNED) { + Ok(DataType::UnsignedLong(optional_precision?)) + } else { + Ok(DataType::Long(optional_precision?)) + } + } Keyword::BIGINT => { let optional_precision = self.parse_optional_precision(); if self.parse_keyword(Keyword::UNSIGNED) { @@ -8628,8 +8732,13 @@ impl<'a> Parser<'a> { Keyword::TIMESTAMP => { let precision = self.parse_optional_precision()?; let tz = if self.parse_keyword(Keyword::WITH) { - self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; - TimezoneInfo::WithTimeZone + if self.parse_keyword(Keyword::LOCAL) { + self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; + TimezoneInfo::WithLocalTimeZone + } else { + self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; + TimezoneInfo::WithTimeZone + } } else if self.parse_keyword(Keyword::WITHOUT) { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithoutTimeZone @@ -8638,6 +8747,14 @@ impl<'a> Parser<'a> { }; Ok(DataType::Timestamp(precision, tz)) } + Keyword::TIMESTAMP_LTZ => Ok(DataType::Timestamp( + self.parse_optional_precision()?, + TimezoneInfo::WithLocalTimeZone, + )), + Keyword::TIMESTAMP_NTZ => Ok(DataType::Timestamp( + self.parse_optional_precision()?, + TimezoneInfo::WithoutTimeZone, + )), Keyword::TIMESTAMPTZ => Ok(DataType::Timestamp( self.parse_optional_precision()?, TimezoneInfo::Tz, @@ -8645,8 +8762,13 @@ impl<'a> Parser<'a> { Keyword::TIME => { let precision = self.parse_optional_precision()?; let tz = if self.parse_keyword(Keyword::WITH) { - self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; - TimezoneInfo::WithTimeZone + if self.parse_keyword(Keyword::LOCAL) { + self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; + TimezoneInfo::WithLocalTimeZone + } else { + self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; + TimezoneInfo::WithTimeZone + } } else if self.parse_keyword(Keyword::WITHOUT) { self.expect_keywords(&[Keyword::TIME, Keyword::ZONE])?; TimezoneInfo::WithoutTimeZone @@ -8659,10 +8781,17 @@ impl<'a> Parser<'a> { self.parse_optional_precision()?, TimezoneInfo::Tz, )), - // Interval types can be followed by a complicated interval - // qualifier that we don't currently support. See - // parse_interval for a taste. - Keyword::INTERVAL => Ok(DataType::Interval), + Keyword::INTERVAL => { + let unit = self + .maybe_parse(|parser| parser.parse_interval_unit())? + .unwrap_or(IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }); + Ok(DataType::Interval(unit)) + } Keyword::JSON => Ok(DataType::JSON), Keyword::JSONB => Ok(DataType::JSONB), Keyword::REGCLASS => Ok(DataType::Regclass), @@ -8718,7 +8847,8 @@ impl<'a> Parser<'a> { let field_defs = self.parse_duckdb_struct_type_def()?; Ok(DataType::Struct(field_defs, StructBracketKind::Parentheses)) } - Keyword::STRUCT if dialect_is!(dialect is BigQueryDialect | GenericDialect) => { + Keyword::STRUCT if dialect_is!(dialect is HiveDialect | BigQueryDialect | GenericDialect) => + { self.prev_token(); let (field_defs, _trailing_bracket) = self.parse_struct_type_def(Self::parse_struct_field_def)?; @@ -8728,6 +8858,20 @@ impl<'a> Parser<'a> { StructBracketKind::AngleBrackets, )) } + Keyword::MAP if dialect_of!(self is HiveDialect | GenericDialect) => { + self.expect_token(&Token::Lt)?; + let (key_type, _trailing_bracket) = self.parse_data_type_helper()?; + if _trailing_bracket.0 { + return parser_err!( + "unmatched > after parsing key type of map", + self.peek_token().span.start + ); + } + self.expect_token(&Token::Comma)?; + let (value_type, _trailing_bracket) = self.parse_data_type_helper()?; + trailing_bracket = self.expect_closing_angle_bracket(_trailing_bracket)?; + Ok(DataType::Map(Box::new(key_type), Box::new(value_type))) + } Keyword::UNION if dialect_is!(dialect is DuckDbDialect | GenericDialect) => { self.prev_token(); let fields = self.parse_union_type_def()?; @@ -8739,6 +8883,7 @@ impl<'a> Parser<'a> { Keyword::LOWCARDINALITY if dialect_is!(dialect is ClickHouseDialect | GenericDialect) => { Ok(self.parse_sub_type(DataType::LowCardinality)?) } + // FIXME: consolidate map type parsing Keyword::MAP if dialect_is!(dialect is ClickHouseDialect | GenericDialect) => { self.prev_token(); let (key_data_type, value_data_type) = self.parse_click_house_map_def()?; @@ -9093,12 +9238,12 @@ impl<'a> Parser<'a> { Token::EOF => { return Err(ParserError::ParserError( "Empty input when parsing identifier".to_string(), - ))? + ))?; } token => { return Err(ParserError::ParserError(format!( "Unexpected token in identifier: {token}" - )))? + )))?; } }; @@ -9126,7 +9271,7 @@ impl<'a> Parser<'a> { token => { return Err(ParserError::ParserError(format!( "Unexpected token in identifier: {token}" - )))? + )))?; } } } @@ -9175,7 +9320,7 @@ impl<'a> Parser<'a> { ident.value.push_str(&next_word.value); false } - Token::Number(s, false) => { + Token::Number(s, None) => { // A number token can represent a decimal value ending with a period, e.g., `Number('123.')`. // However, for an [ObjectName], it is part of a hyphenated identifier, e.g., `foo-123.bar`. // @@ -9188,7 +9333,7 @@ impl<'a> Parser<'a> { }) else { return self.expected( "continuation of hyphenated identifier", - TokenWithSpan::new(Token::Number(s, false), token.span), + TokenWithSpan::new(Token::Number(s, None), token.span), ); }; ident.value.push_str(s); @@ -10827,7 +10972,7 @@ impl<'a> Parser<'a> { _ => { return Err(ParserError::ParserError(format!( "expected OUTER, SEMI, ANTI or JOIN after {kw:?}" - ))) + ))); } } } @@ -13566,12 +13711,17 @@ impl<'a> Parser<'a> { _ => None, }; - let partition_by = if self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) { + let partition_by = if self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) + || self.parse_keywords(&[Keyword::DISTRIBUTE, Keyword::BY]) + || self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) + { self.parse_comma_separated(Parser::parse_expr)? } else { vec![] }; - let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { + let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) + || self.parse_keywords(&[Keyword::SORT, Keyword::BY]) + { self.parse_comma_separated(Parser::parse_order_by_expr)? } else { vec![] diff --git a/src/test_utils.rs b/src/test_utils.rs index 914be7d9f..e2bd101ba 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -325,7 +325,7 @@ pub fn alter_table_op(stmt: Statement) -> AlterTableOperation { /// Creates a `Value::Number`, panic'ing if n is not a number pub fn number(n: &str) -> Value { - Value::Number(n.parse().unwrap(), false) + Value::Number(n.parse().unwrap(), None) } pub fn table_alias(name: impl Into) -> Option { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 39ca84c9f..d0791cbbd 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -57,8 +57,8 @@ pub enum Token { EOF, /// A keyword (like SELECT) or an optionally quoted SQL identifier Word(Word), - /// An unsigned numeric literal - Number(String, bool), + /// An unsigned numeric literal with an optional postfix (e.g. 'L') + Number(String, Option), /// A character that could not be tokenized Char(char), /// Single quoted string: i.e: 'string' @@ -250,7 +250,7 @@ impl fmt::Display for Token { match self { Token::EOF => f.write_str("EOF"), Token::Word(ref w) => write!(f, "{w}"), - Token::Number(ref n, l) => write!(f, "{}{long}", n, long = if *l { "L" } else { "" }), + Token::Number(ref n, postfix) => write!(f, "{}{}", n, postfix.as_deref().unwrap_or("")), Token::Char(ref c) => write!(f, "{c}"), Token::SingleQuotedString(ref s) => write!(f, "'{s}'"), Token::TripleSingleQuotedString(ref s) => write!(f, "'''{s}'''"), @@ -877,7 +877,7 @@ impl<'a> Tokenizer<'a> { let mut s = peeking_take_while(&mut inner_state, |ch| matches!(ch, '0'..='9' | '.')); let s2 = peeking_take_while(chars, |ch| matches!(ch, '0'..='9' | '.')); s += s2.as_str(); - return Ok(Some(Token::Number(s, false))); + return Ok(Some(Token::Number(s, None))); } Ok(Some(Token::make_word(&word, None))) @@ -902,7 +902,9 @@ impl<'a> Tokenizer<'a> { b @ 'B' | b @ 'b' if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | MySqlDialect | GenericDialect) => { chars.next(); // consume - match chars.peek() { + match peeking_skip_whitespace_take_if(chars, |ch| { + matches!(ch, '\'') || matches!(ch, '\"') + }) { Some('\'') => { if self.dialect.supports_triple_quoted_string() { return self @@ -941,7 +943,9 @@ impl<'a> Tokenizer<'a> { // BigQuery uses r or R for raw string literal b @ 'R' | b @ 'r' if dialect_of!(self is BigQueryDialect | GenericDialect) => { chars.next(); // consume - match chars.peek() { + match peeking_skip_whitespace_take_if(chars, |ch| { + matches!(ch, '\'') || matches!(ch, '\"') + }) { Some('\'') => self .tokenize_single_or_triple_quoted_string:: Token>( chars, @@ -968,12 +972,19 @@ impl<'a> Tokenizer<'a> { // Redshift uses lower case n for national string literal n @ 'N' | n @ 'n' => { chars.next(); // consume, to check the next char - match chars.peek() { + match peeking_skip_whitespace_take_if(chars, |ch| { + matches!(ch, '\'') || matches!(ch, '\"') + }) { Some('\'') => { // N'...' - a let s = self.tokenize_single_quoted_string(chars, '\'', true)?; Ok(Some(Token::NationalStringLiteral(s))) } + Some('\"') => { + // N"..." - a + let s = self.tokenize_single_quoted_string(chars, '\"', true)?; + Ok(Some(Token::NationalStringLiteral(s))) + } _ => { // regular identifier starting with an "N" let s = self.tokenize_word(n, chars); @@ -985,7 +996,7 @@ impl<'a> Tokenizer<'a> { x @ 'e' | x @ 'E' => { let starting_loc = chars.location(); chars.next(); // consume, to check the next char - match chars.peek() { + match peeking_skip_whitespace_take_if(chars, |ch| matches!(ch, '\'')) { Some('\'') => { let s = self.tokenize_escaped_single_quoted_string(starting_loc, chars)?; @@ -1019,12 +1030,19 @@ impl<'a> Tokenizer<'a> { // string, but PostgreSQL, at least, allows a lowercase 'x' too. x @ 'x' | x @ 'X' => { chars.next(); // consume, to check the next char - match chars.peek() { + match peeking_skip_whitespace_take_if(chars, |ch| { + matches!(ch, '\'') || matches!(ch, '\"') + }) { Some('\'') => { // X'...' - a let s = self.tokenize_single_quoted_string(chars, '\'', true)?; Ok(Some(Token::HexStringLiteral(s))) } + Some('\"') => { + // X"..." - a + let s = self.tokenize_single_quoted_string(chars, '\"', true)?; + Ok(Some(Token::HexStringLiteral(s))) + } _ => { // regular identifier starting with an "X" let s = self.tokenize_word(x, chars); @@ -1196,13 +1214,13 @@ impl<'a> Tokenizer<'a> { } } - let long = if chars.peek() == Some(&'L') { - chars.next(); - true + let postfix = peeking_take_while(chars, |ch| ch.is_ascii_alphanumeric()); + let postfix = if postfix.is_empty() { + None } else { - false + Some(postfix) }; - Ok(Some(Token::Number(s, long))) + Ok(Some(Token::Number(s, postfix))) } // punctuation '(' => self.consume_and_return(chars, Token::LParen), @@ -1943,6 +1961,47 @@ fn peeking_take_while(chars: &mut State, mut predicate: impl FnMut(char) -> bool s } +/// Peek ahead in a clone of `self.peekable`, skipping whitespace, +/// until `predicate` returns `true` or a non-whitespace character is encountered. +/// If a character matching the predicate is found: +/// - Advance the original iterator by the number of whitespace characters skipped +/// - Return the peeked character matching the predicate +/// +/// If a non-whitespace character not matching the predicate is encountered, or EOF is reached, +/// return `self.peek()` without advancing the iterator. +/// +/// Note: This function may advance the original iterator if a match is found after skipping whitespace. +fn peeking_skip_whitespace_take_if( + chars: &mut State, + mut predicate: impl FnMut(char) -> bool, +) -> Option { + // Check if the next character is a match to avoid unnecessary cloning. + if let Some(&ch) = chars.peek() { + if predicate(ch) { + return Some(ch); + } + } + + let mut chars_clone = chars.peekable.clone(); + let mut next_count = 0; + loop { + match chars_clone.peek() { + Some(&ch) if predicate(ch) => { + // Advance the original iterator + for _ in 0..next_count { + chars.next(); + } + return chars.peek().copied(); + } + Some(ch) if ch.is_whitespace() || matches!(ch, ' ' | '\t' | '\n' | '\r') => { + next_count += 1; + chars_clone.next(); + } + _ => return chars.peek().copied(), + } + } +} + fn unescape_single_quoted_string(chars: &mut State<'_>) -> Option { Unescape::new(chars).unescape() } @@ -2180,7 +2239,7 @@ mod tests { let expected = vec![ Token::make_keyword("SELECT"), Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1"), false), + Token::Number(String::from("1"), None), ]; compare(expected, tokens); @@ -2195,7 +2254,7 @@ mod tests { let expected = vec![ Token::make_keyword("SELECT"), Token::Whitespace(Whitespace::Space), - Token::Number(String::from(".1"), false), + Token::Number(String::from(".1"), None), ]; compare(expected, tokens); @@ -2232,26 +2291,24 @@ mod tests { let expected = vec![ Token::make_keyword("SELECT"), Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1e10"), false), + Token::Number(String::from("1e10"), None), Token::Comma, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1e-10"), false), + Token::Number(String::from("1e-10"), None), Token::Comma, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1e+10"), false), + Token::Number(String::from("1e+10"), None), Token::Comma, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1"), false), - Token::make_word("ea", None), + Token::Number(String::from("1"), Some("ea".to_string())), Token::Comma, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1e-10"), false), - Token::make_word("a", None), + Token::Number(String::from("1e-10"), Some("a".to_string())), Token::Comma, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1e-10"), false), + Token::Number(String::from("1e-10"), None), Token::Minus, - Token::Number(String::from("10"), false), + Token::Number(String::from("10"), None), ]; compare(expected, tokens); @@ -2268,7 +2325,7 @@ mod tests { Token::Whitespace(Whitespace::Space), Token::make_word("sqrt", None), Token::LParen, - Token::Number(String::from("1"), false), + Token::Number(String::from("1"), None), Token::RParen, ]; @@ -2376,11 +2433,11 @@ mod tests { Token::Whitespace(Whitespace::Space), Token::Eq, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1"), false), + Token::Number(String::from("1"), None), Token::Whitespace(Whitespace::Space), Token::make_keyword("LIMIT"), Token::Whitespace(Whitespace::Space), - Token::Number(String::from("5"), false), + Token::Number(String::from("5"), None), ]; compare(expected, tokens); @@ -2409,7 +2466,7 @@ mod tests { Token::Whitespace(Whitespace::Space), Token::Eq, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1"), false), + Token::Number(String::from("1"), None), ]; compare(expected, tokens); @@ -2440,7 +2497,7 @@ mod tests { Token::Whitespace(Whitespace::Space), Token::Eq, Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1"), false), + Token::Number(String::from("1"), None), ]; compare(expected, tokens); @@ -2594,12 +2651,12 @@ mod tests { ( String::from("0$abc$$abc$1"), vec![ - Token::Number("0".into(), false), + Token::Number("0".into(), None), Token::DollarQuotedString(DollarQuotedString { value: "".into(), tag: Some("abc".into()), }), - Token::Number("1".into(), false), + Token::Number("1".into(), None), ] ), ( @@ -2782,18 +2839,18 @@ mod tests { ( String::from("0--this is a comment\n1"), vec![ - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), Token::Whitespace(Whitespace::SingleLineComment { prefix: "--".to_string(), comment: "this is a comment\n".to_string(), }), - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), ], ), ( String::from("0--this is a comment\r1"), vec![ - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), Token::Whitespace(Whitespace::SingleLineComment { prefix: "--".to_string(), comment: "this is a comment\r1".to_string(), @@ -2803,12 +2860,12 @@ mod tests { ( String::from("0--this is a comment\r\n1"), vec![ - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), Token::Whitespace(Whitespace::SingleLineComment { prefix: "--".to_string(), comment: "this is a comment\r\n".to_string(), }), - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), ], ), ]; @@ -2828,12 +2885,12 @@ mod tests { let dialect = PostgreSqlDialect {}; let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap(); let expected = vec![ - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), Token::Whitespace(Whitespace::SingleLineComment { prefix: "--".to_string(), comment: "\r".to_string(), }), - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), ]; compare(expected, tokens); } @@ -2858,11 +2915,11 @@ mod tests { let dialect = GenericDialect {}; let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap(); let expected = vec![ - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), Token::Whitespace(Whitespace::MultiLineComment( "multi-line\n* /comment".to_string(), )), - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), ]; compare(expected, tokens); } @@ -2874,7 +2931,7 @@ mod tests { ( "0/*multi-line\n* \n/* comment \n /*comment*/*/ */ /comment*/1", vec![ - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), Token::Whitespace(Whitespace::MultiLineComment( "multi-line\n* \n/* comment \n /*comment*/*/ ".into(), )), @@ -2887,17 +2944,17 @@ mod tests { }), Token::Mul, Token::Div, - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), ], ), ( "0/*multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/*/1", vec![ - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), Token::Whitespace(Whitespace::MultiLineComment( "multi-line\n* \n/* comment \n /*comment/**/ */ /comment*/".into(), )), - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), ], ), ( @@ -2905,9 +2962,9 @@ mod tests { vec![ Token::make_keyword("SELECT"), Token::Whitespace(Whitespace::Space), - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), Token::Whitespace(Whitespace::MultiLineComment(" a /* b */ c ".to_string())), - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), ], ), ]; @@ -2927,9 +2984,9 @@ mod tests { let expected = vec![ Token::make_keyword("select"), Token::Whitespace(Whitespace::Space), - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), Token::Whitespace(Whitespace::MultiLineComment("/**/".to_string())), - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), ]; compare(expected, tokens); @@ -2943,13 +3000,13 @@ mod tests { let expected = vec![ Token::make_keyword("SELECT"), Token::Whitespace(Whitespace::Space), - Token::Number("1".to_string(), false), + Token::Number("1".to_string(), None), Token::Whitespace(Whitespace::MultiLineComment( "/* nested comment ".to_string(), )), Token::Mul, Token::Div, - Token::Number("0".to_string(), false), + Token::Number("0".to_string(), None), ]; compare(expected, tokens.unwrap()); @@ -3027,7 +3084,7 @@ mod tests { Token::Whitespace(Whitespace::Space), Token::make_keyword("TOP"), Token::Whitespace(Whitespace::Space), - Token::Number(String::from("5"), false), + Token::Number(String::from("5"), None), Token::Whitespace(Whitespace::Space), Token::make_word("bar", Some('[')), Token::Whitespace(Whitespace::Space), @@ -3139,7 +3196,7 @@ mod tests { let expected = vec![ Token::make_word(r#"field"#, None), Token::Div, - Token::Number("1000".to_string(), false), + Token::Number("1000".to_string(), None), ]; compare(expected, tokens); } @@ -3316,7 +3373,7 @@ mod tests { Token::Whitespace(Whitespace::Space), Token::make_keyword("FROM"), Token::Whitespace(Whitespace::Space), - Token::Number(String::from("1"), false), + Token::Number(String::from("1"), None), ]; compare(expected, tokens); } diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 9dfabc014..d9751e675 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -492,11 +492,15 @@ fn parse_nested_data_types() { field_name: Some("a".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket( Box::new(DataType::Int64,) - )) + )), + not_null: false, + comment: None, }, StructField { field_name: Some("b".into()), - field_type: DataType::Bytes(Some(42)) + field_type: DataType::Bytes(Some(42)), + not_null: false, + comment: None, }, ], StructBracketKind::AngleBrackets @@ -511,6 +515,8 @@ fn parse_nested_data_types() { vec![StructField { field_name: None, field_type: DataType::Int64, + not_null: false, + comment: None, }], StructBracketKind::AngleBrackets ), @@ -658,6 +664,8 @@ fn parse_typed_struct_syntax_bigquery() { fields: vec![StructField { field_name: None, field_type: DataType::Int64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -686,7 +694,9 @@ fn parse_typed_struct_syntax_bigquery() { quote_style: None, span: Span::empty(), }), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }, StructField { field_name: Some(Ident { @@ -694,7 +704,9 @@ fn parse_typed_struct_syntax_bigquery() { quote_style: None, span: Span::empty(), }), - field_type: DataType::String(None) + field_type: DataType::String(None), + not_null: false, + comment: None, }, ] }, @@ -712,17 +724,23 @@ fn parse_typed_struct_syntax_bigquery() { field_name: Some("arr".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( DataType::Float64 - ))) + ))), + not_null: false, + comment: None, }, StructField { field_name: Some("str".into()), field_type: DataType::Struct( vec![StructField { field_name: None, - field_type: DataType::Bool + field_type: DataType::Bool, + not_null: false, + comment: None, }], StructBracketKind::AngleBrackets - ) + ), + not_null: false, + comment: None, }, ] }, @@ -745,13 +763,17 @@ fn parse_typed_struct_syntax_bigquery() { field_type: DataType::Struct( Default::default(), StructBracketKind::AngleBrackets - ) + ), + not_null: false, + comment: None, }, StructField { field_name: Some("y".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( DataType::Struct(Default::default(), StructBracketKind::AngleBrackets) - ))) + ))), + not_null: false, + comment: None, }, ] }, @@ -766,7 +788,9 @@ fn parse_typed_struct_syntax_bigquery() { values: vec![Expr::Value(Value::Boolean(true)),], fields: vec![StructField { field_name: None, - field_type: DataType::Bool + field_type: DataType::Bool, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -778,7 +802,9 @@ fn parse_typed_struct_syntax_bigquery() { )),], fields: vec![StructField { field_name: None, - field_type: DataType::Bytes(Some(42)) + field_type: DataType::Bytes(Some(42)), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -794,7 +820,9 @@ fn parse_typed_struct_syntax_bigquery() { )),], fields: vec![StructField { field_name: None, - field_type: DataType::Date + field_type: DataType::Date, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -807,7 +835,9 @@ fn parse_typed_struct_syntax_bigquery() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Datetime(None) + field_type: DataType::Datetime(None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -817,7 +847,9 @@ fn parse_typed_struct_syntax_bigquery() { values: vec![Expr::Value(number("5.0")),], fields: vec![StructField { field_name: None, - field_type: DataType::Float64 + field_type: DataType::Float64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[2]) @@ -827,7 +859,9 @@ fn parse_typed_struct_syntax_bigquery() { values: vec![Expr::Value(number("1")),], fields: vec![StructField { field_name: None, - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[3]) @@ -838,16 +872,25 @@ fn parse_typed_struct_syntax_bigquery() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::Interval(Interval { + values: vec![Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString("2".to_string()))), - leading_field: Some(DateTimeField::Hour), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None - }),], + unit: IntervalUnit { + leading_field: Some(DateTimeField::Hour), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + } + })], fields: vec![StructField { field_name: None, - field_type: DataType::Interval + field_type: DataType::Interval(IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + }), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -860,7 +903,9 @@ fn parse_typed_struct_syntax_bigquery() { },], fields: vec![StructField { field_name: None, - field_type: DataType::JSON + field_type: DataType::JSON, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -874,7 +919,9 @@ fn parse_typed_struct_syntax_bigquery() { values: vec![Expr::Value(Value::DoubleQuotedString("foo".to_string())),], fields: vec![StructField { field_name: None, - field_type: DataType::String(Some(42)) + field_type: DataType::String(Some(42)), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -887,7 +934,9 @@ fn parse_typed_struct_syntax_bigquery() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Timestamp(None, TimezoneInfo::None) + field_type: DataType::Timestamp(None, TimezoneInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -901,7 +950,9 @@ fn parse_typed_struct_syntax_bigquery() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Time(None, TimezoneInfo::None) + field_type: DataType::Time(None, TimezoneInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[2]) @@ -918,7 +969,9 @@ fn parse_typed_struct_syntax_bigquery() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Numeric(ExactNumberInfo::None) + field_type: DataType::Numeric(ExactNumberInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -931,7 +984,9 @@ fn parse_typed_struct_syntax_bigquery() { },], fields: vec![StructField { field_name: None, - field_type: DataType::BigNumeric(ExactNumberInfo::None) + field_type: DataType::BigNumeric(ExactNumberInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -948,10 +1003,14 @@ fn parse_typed_struct_syntax_bigquery() { StructField { field_name: Some("key".into()), field_type: DataType::Int64, + not_null: false, + comment: None, }, StructField { field_name: Some("value".into()), field_type: DataType::Int64, + not_null: false, + comment: None, }, ] }, @@ -973,6 +1032,8 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { fields: vec![StructField { field_name: None, field_type: DataType::Int64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1001,7 +1062,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { quote_style: None, span: Span::empty(), }), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }, StructField { field_name: Some(Ident { @@ -1009,7 +1072,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { quote_style: None, span: Span::empty(), }), - field_type: DataType::String(None) + field_type: DataType::String(None), + not_null: false, + comment: None, }, ] }, @@ -1027,17 +1092,23 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { field_name: Some("arr".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( DataType::Float64 - ))) + ))), + not_null: false, + comment: None, }, StructField { field_name: Some("str".into()), field_type: DataType::Struct( vec![StructField { field_name: None, - field_type: DataType::Bool + field_type: DataType::Bool, + not_null: false, + comment: None, }], StructBracketKind::AngleBrackets - ) + ), + not_null: false, + comment: None, }, ] }, @@ -1060,13 +1131,17 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { field_type: DataType::Struct( Default::default(), StructBracketKind::AngleBrackets - ) + ), + not_null: false, + comment: None, }, StructField { field_name: Some("y".into()), field_type: DataType::Array(ArrayElemTypeDef::AngleBracket(Box::new( DataType::Struct(Default::default(), StructBracketKind::AngleBrackets) - ))) + ))), + not_null: false, + comment: None, }, ] }, @@ -1081,7 +1156,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { values: vec![Expr::Value(Value::Boolean(true)),], fields: vec![StructField { field_name: None, - field_type: DataType::Bool + field_type: DataType::Bool, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1093,7 +1170,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { )),], fields: vec![StructField { field_name: None, - field_type: DataType::Bytes(Some(42)) + field_type: DataType::Bytes(Some(42)), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -1109,7 +1188,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { )),], fields: vec![StructField { field_name: None, - field_type: DataType::Date + field_type: DataType::Date, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1122,7 +1203,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Datetime(None) + field_type: DataType::Datetime(None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -1132,7 +1215,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { values: vec![Expr::Value(number("5.0")),], fields: vec![StructField { field_name: None, - field_type: DataType::Float64 + field_type: DataType::Float64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[2]) @@ -1142,7 +1227,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { values: vec![Expr::Value(number("1")),], fields: vec![StructField { field_name: None, - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[3]) @@ -1153,16 +1240,25 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { assert_eq!(2, select.projection.len()); assert_eq!( &Expr::Struct { - values: vec![Expr::Interval(Interval { + values: vec![Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString("1".to_string()))), - leading_field: Some(DateTimeField::Month), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None + unit: IntervalUnit { + leading_field: Some(DateTimeField::Month), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + }, }),], fields: vec![StructField { field_name: None, - field_type: DataType::Interval + field_type: DataType::Interval(IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None + }), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1175,7 +1271,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { },], fields: vec![StructField { field_name: None, - field_type: DataType::JSON + field_type: DataType::JSON, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -1189,7 +1287,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { values: vec![Expr::Value(Value::SingleQuotedString("foo".to_string())),], fields: vec![StructField { field_name: None, - field_type: DataType::String(Some(42)) + field_type: DataType::String(Some(42)), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1202,7 +1302,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Timestamp(None, TimezoneInfo::None) + field_type: DataType::Timestamp(None, TimezoneInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -1216,7 +1318,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Time(None, TimezoneInfo::None) + field_type: DataType::Time(None, TimezoneInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[2]) @@ -1233,7 +1337,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { },], fields: vec![StructField { field_name: None, - field_type: DataType::Numeric(ExactNumberInfo::None) + field_type: DataType::Numeric(ExactNumberInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1246,7 +1352,9 @@ fn parse_typed_struct_syntax_bigquery_and_generic() { },], fields: vec![StructField { field_name: None, - field_type: DataType::BigNumeric(ExactNumberInfo::None) + field_type: DataType::BigNumeric(ExactNumberInfo::None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -1263,7 +1371,9 @@ fn parse_typed_struct_with_field_name_bigquery() { values: vec![Expr::Value(number("5")),], fields: vec![StructField { field_name: Some(Ident::from("x")), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1273,7 +1383,9 @@ fn parse_typed_struct_with_field_name_bigquery() { values: vec![Expr::Value(Value::DoubleQuotedString("foo".to_string())),], fields: vec![StructField { field_name: Some(Ident::from("y")), - field_type: DataType::String(None) + field_type: DataType::String(None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -1288,11 +1400,15 @@ fn parse_typed_struct_with_field_name_bigquery() { fields: vec![ StructField { field_name: Some(Ident::from("x")), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }, StructField { field_name: Some(Ident::from("y")), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, } ] }, @@ -1310,7 +1426,9 @@ fn parse_typed_struct_with_field_name_bigquery_and_generic() { values: vec![Expr::Value(number("5")),], fields: vec![StructField { field_name: Some(Ident::from("x")), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[0]) @@ -1320,7 +1438,9 @@ fn parse_typed_struct_with_field_name_bigquery_and_generic() { values: vec![Expr::Value(Value::SingleQuotedString("foo".to_string())),], fields: vec![StructField { field_name: Some(Ident::from("y")), - field_type: DataType::String(None) + field_type: DataType::String(None), + not_null: false, + comment: None, }] }, expr_from_projection(&select.projection[1]) @@ -1335,11 +1455,15 @@ fn parse_typed_struct_with_field_name_bigquery_and_generic() { fields: vec![ StructField { field_name: Some(Ident::from("x")), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, }, StructField { field_name: Some(Ident::from("y")), - field_type: DataType::Int64 + field_type: DataType::Int64, + not_null: false, + comment: None, } ] }, @@ -1504,17 +1628,6 @@ fn parse_hyphenated_table_identifiers() { "SELECT * FROM foo-bar AS f JOIN baz-qux AS b ON f.id = b.id", ); - assert_eq!( - bigquery() - .verified_only_select_with_canonical( - "select * from foo-123.bar", - "SELECT * FROM foo-123.bar" - ) - .from[0] - .relation, - table_from_name(ObjectName(vec![Ident::new("foo-123"), Ident::new("bar")])), - ); - assert_eq!( bigquery() .verified_only_select_with_canonical( diff --git a/tests/sqlparser_clickhouse.rs b/tests/sqlparser_clickhouse.rs index fed4308fc..e6f984bab 100644 --- a/tests/sqlparser_clickhouse.rs +++ b/tests/sqlparser_clickhouse.rs @@ -666,11 +666,15 @@ fn parse_create_table_with_nested_data_types() { DataType::Tuple(vec![ StructField { field_name: None, - field_type: DataType::FixedString(128) + field_type: DataType::FixedString(128), + not_null: false, + comment: None, }, StructField { field_name: None, - field_type: DataType::Int128 + field_type: DataType::Int128, + not_null: false, + comment: None, } ]) ))), @@ -683,12 +687,16 @@ fn parse_create_table_with_nested_data_types() { StructField { field_name: Some("a".into()), field_type: DataType::Datetime64(9, None), + not_null: false, + comment: None, }, StructField { field_name: Some("b".into()), field_type: DataType::Array(ArrayElemTypeDef::Parenthesis( Box::new(DataType::Uuid) - )) + )), + not_null: false, + comment: None, }, ]), collation: None, @@ -963,11 +971,11 @@ fn parse_settings_in_query() { Some(vec![ Setting { key: Ident::new("max_threads"), - value: Number("1".parse().unwrap(), false) + value: Number("1".parse().unwrap(), None) }, Setting { key: Ident::new("max_block_size"), - value: Number("10000".parse().unwrap(), false) + value: Number("10000".parse().unwrap(), None) }, ]) ); @@ -1024,14 +1032,14 @@ fn parse_select_parametric_function() { parameters.args[0], FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Number( "0.5".parse().unwrap(), - false + None, )))) ); assert_eq!( parameters.args[1], FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(Value::Number( "0.6".parse().unwrap(), - false + None, )))) ); } @@ -1299,7 +1307,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(), None))), }) ); let selection = query.as_ref().body.as_select().unwrap().selection.as_ref(); @@ -1308,7 +1316,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(), None))), }) ); } @@ -1324,13 +1332,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(), None))), }), 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(), None))), }), }) ); @@ -1440,7 +1448,7 @@ fn parse_create_table_on_commit_and_as_query() { query.unwrap().body.as_select().unwrap().projection, vec![UnnamedExpr(Expr::Value(Value::Number( "1".parse().unwrap(), - false + None )))] ); } @@ -1560,7 +1568,7 @@ fn parse_select_table_function_settings() { settings: Some(vec![ Setting { key: "s0".into(), - value: Value::Number("3".parse().unwrap(), false), + value: Value::Number("3".parse().unwrap(), None), }, Setting { key: "s1".into(), @@ -1585,7 +1593,7 @@ fn parse_select_table_function_settings() { settings: Some(vec![ Setting { key: "s0".into(), - value: Value::Number("3".parse().unwrap(), false), + value: Value::Number("3".parse().unwrap(), None), }, Setting { key: "s1".into(), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index b5b12891f..0e891a74d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1207,14 +1207,14 @@ fn parse_exponent_in_select() -> Result<(), ParserError> { SelectItem::UnnamedExpr(Expr::Value(number("10e-20"))), SelectItem::UnnamedExpr(Expr::Value(number("1e3"))), SelectItem::UnnamedExpr(Expr::Value(number("1e+3"))), - SelectItem::ExprWithAlias { - expr: Expr::Value(number("1e3")), - alias: Ident::new("a") - }, - SelectItem::ExprWithAlias { - expr: Expr::Value(number("1")), - alias: Ident::new("e") - }, + SelectItem::UnnamedExpr(Expr::Value(Value::Number( + "1e3".parse().unwrap(), + Some("a".to_string()) + ))), + SelectItem::UnnamedExpr(Expr::Value(Value::Number( + "1".parse().unwrap(), + Some("e".to_string()) + ))), SelectItem::UnnamedExpr(Expr::Value(number("0.5e2"))), ], &select.projection @@ -1290,11 +1290,11 @@ fn parse_number() { #[cfg(feature = "bigdecimal")] assert_eq!( expr, - Expr::Value(Value::Number(bigdecimal::BigDecimal::from(1), false)) + Expr::Value(Value::Number(bigdecimal::BigDecimal::from(1), None)) ); #[cfg(not(feature = "bigdecimal"))] - assert_eq!(expr, Expr::Value(Value::Number("1.0".into(), false))); + assert_eq!(expr, Expr::Value(Value::Number("1.0".into(), None))); } #[test] @@ -2427,6 +2427,78 @@ fn parse_cast() { expr_from_projection(only(&select.projection)) ); + let sql = "SELECT CAST(id AS BYTE) FROM customer"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Cast { + kind: CastKind::Cast, + expr: Box::new(Expr::Identifier(Ident::new("id"))), + data_type: DataType::Byte(None), + format: None, + }, + expr_from_projection(only(&select.projection)) + ); + + let sql = "SELECT CAST(id AS BYTE(6)) FROM customer"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Cast { + kind: CastKind::Cast, + expr: Box::new(Expr::Identifier(Ident::new("id"))), + data_type: DataType::Byte(Some(6)), + format: None, + }, + expr_from_projection(only(&select.projection)) + ); + + let sql = "SELECT CAST(id AS SHORT) FROM customer"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Cast { + kind: CastKind::Cast, + expr: Box::new(Expr::Identifier(Ident::new("id"))), + data_type: DataType::Short(None), + format: None, + }, + expr_from_projection(only(&select.projection)) + ); + + let sql = "SELECT CAST(id AS SHORT(9)) FROM customer"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Cast { + kind: CastKind::Cast, + expr: Box::new(Expr::Identifier(Ident::new("id"))), + data_type: DataType::Short(Some(9)), + format: None, + }, + expr_from_projection(only(&select.projection)) + ); + + let sql = "SELECT CAST(id AS LONG) FROM customer"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Cast { + kind: CastKind::Cast, + expr: Box::new(Expr::Identifier(Ident::new("id"))), + data_type: DataType::Long(None), + format: None, + }, + expr_from_projection(only(&select.projection)) + ); + + let sql = "SELECT CAST(id AS LONG(20)) FROM customer"; + let select = verified_only_select(sql); + assert_eq!( + &Expr::Cast { + kind: CastKind::Cast, + expr: Box::new(Expr::Identifier(Ident::new("id"))), + data_type: DataType::Long(Some(20)), + format: None, + }, + expr_from_projection(only(&select.projection)) + ); + one_statement_parses_to( "SELECT CAST(id AS MEDIUMINT) FROM customer", "SELECT CAST(id AS MEDIUMINT) FROM customer", @@ -2437,6 +2509,21 @@ fn parse_cast() { "SELECT CAST(id AS BIGINT) FROM customer", ); + one_statement_parses_to( + "SELECT CAST(id AS BYTE(6) UNSIGNED) FROM customer", + "SELECT CAST(id AS BYTE(6) UNSIGNED) FROM customer", + ); + + one_statement_parses_to( + "SELECT CAST(id AS SHORT(9) UNSIGNED) FROM customer", + "SELECT CAST(id AS SHORT(9) UNSIGNED) FROM customer", + ); + + one_statement_parses_to( + "SELECT CAST(id AS LONG(20) UNSIGNED) FROM customer", + "SELECT CAST(id AS LONG(20) UNSIGNED) FROM customer", + ); + verified_stmt("SELECT CAST(id AS NUMERIC) FROM customer"); verified_stmt("SELECT CAST(id AS DEC) FROM customer"); @@ -2657,7 +2744,7 @@ fn parse_ceil_scale() { assert_eq!( &Expr::Ceil { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(bigdecimal::BigDecimal::from(2), false)), + field: CeilFloorKind::Scale(Value::Number(bigdecimal::BigDecimal::from(2), None)), }, expr_from_projection(only(&select.projection)), ); @@ -2666,7 +2753,7 @@ fn parse_ceil_scale() { assert_eq!( &Expr::Ceil { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(2.to_string(), false)), + field: CeilFloorKind::Scale(Value::Number(2.to_string(), None)), }, expr_from_projection(only(&select.projection)), ); @@ -2681,7 +2768,7 @@ fn parse_floor_scale() { assert_eq!( &Expr::Floor { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(bigdecimal::BigDecimal::from(2), false)), + field: CeilFloorKind::Scale(Value::Number(bigdecimal::BigDecimal::from(2), None)), }, expr_from_projection(only(&select.projection)), ); @@ -2690,7 +2777,7 @@ fn parse_floor_scale() { assert_eq!( &Expr::Floor { expr: Box::new(Expr::Identifier(Ident::new("d"))), - field: CeilFloorKind::Scale(Value::Number(2.to_string(), false)), + field: CeilFloorKind::Scale(Value::Number(2.to_string(), None)), }, expr_from_projection(only(&select.projection)), ); @@ -5343,12 +5430,14 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL '1-1' YEAR TO MONTH"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1-1")))), - leading_field: Some(DateTimeField::Year), - leading_precision: None, - last_field: Some(DateTimeField::Month), - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Year), + leading_precision: None, + last_field: Some(DateTimeField::Month), + fractional_seconds_precision: None, + }, }), expr_from_projection(only(&select.projection)), ); @@ -5356,14 +5445,16 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL '01:01.01' MINUTE (5) TO SECOND (5)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( "01:01.01" )))), - leading_field: Some(DateTimeField::Minute), - leading_precision: Some(5), - last_field: Some(DateTimeField::Second), - fractional_seconds_precision: Some(5), + unit: IntervalUnit { + leading_field: Some(DateTimeField::Minute), + leading_precision: Some(5), + last_field: Some(DateTimeField::Second), + fractional_seconds_precision: Some(5), + }, }), expr_from_projection(only(&select.projection)), ); @@ -5371,12 +5462,14 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL '1' SECOND (5, 4)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("1")))), - leading_field: Some(DateTimeField::Second), - leading_precision: Some(5), - last_field: None, - fractional_seconds_precision: Some(4), + unit: IntervalUnit { + leading_field: Some(DateTimeField::Second), + leading_precision: Some(5), + last_field: None, + fractional_seconds_precision: Some(4), + }, }), expr_from_projection(only(&select.projection)), ); @@ -5384,12 +5477,14 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL '10' HOUR"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))), - leading_field: Some(DateTimeField::Hour), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Hour), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, }), expr_from_projection(only(&select.projection)), ); @@ -5397,12 +5492,14 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL 5 DAY"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(number("5"))), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, }), expr_from_projection(only(&select.projection)), ); @@ -5410,12 +5507,14 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL 5 DAYS"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(number("5"))), - leading_field: Some(DateTimeField::Days), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Days), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } }), expr_from_projection(only(&select.projection)), ); @@ -5423,12 +5522,14 @@ fn parse_interval_all() { let sql = "SELECT INTERVAL '10' HOUR (1)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from("10")))), - leading_field: Some(DateTimeField::Hour), - leading_precision: Some(1), - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Hour), + leading_precision: Some(1), + last_field: None, + fractional_seconds_precision: None, + }, }), expr_from_projection(only(&select.projection)), ); @@ -5492,14 +5593,16 @@ fn parse_interval_dont_require_unit() { let sql = "SELECT INTERVAL '1 DAY'"; let select = dialects.verified_only_select(sql); assert_eq!( - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( "1 DAY" )))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, }), expr_from_projection(only(&select.projection)), ); @@ -5518,7 +5621,7 @@ fn parse_interval_require_unit() { let err = dialects.parse_sql_statements(sql).unwrap_err(); assert_eq!( err.to_string(), - "sql parser error: INTERVAL requires a unit after the literal value" + "sql parser error: INTERVAL must have at least one valid value along with a unit if required" ) } @@ -5530,16 +5633,18 @@ fn parse_interval_require_qualifier() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::BinaryOp { left: Box::new(Expr::Value(number("1"))), op: BinaryOperator::Plus, right: Box::new(Expr::Value(number("1"))), }), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, }), ); @@ -5547,16 +5652,18 @@ fn parse_interval_require_qualifier() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::BinaryOp { left: Box::new(Expr::Value(Value::SingleQuotedString("1".to_string()))), op: BinaryOperator::Plus, right: Box::new(Expr::Value(Value::SingleQuotedString("1".to_string()))), }), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, }), ); @@ -5564,7 +5671,7 @@ fn parse_interval_require_qualifier() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::BinaryOp { left: Box::new(Expr::BinaryOp { left: Box::new(Expr::Value(Value::SingleQuotedString("1".to_string()))), @@ -5574,10 +5681,12 @@ fn parse_interval_require_qualifier() { op: BinaryOperator::Minus, right: Box::new(Expr::Value(Value::SingleQuotedString("3".to_string()))), }), - leading_field: Some(DateTimeField::Day), - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, }), ); } @@ -5590,14 +5699,16 @@ fn parse_interval_disallow_interval_expr() { let select = dialects.verified_only_select(sql); assert_eq!( expr_from_projection(only(&select.projection)), - &Expr::Interval(Interval { + &Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( "1 DAY" )))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, }), ); @@ -5613,24 +5724,28 @@ fn parse_interval_disallow_interval_expr() { assert_eq!( expr_from_projection(only(&select.projection)), &Expr::BinaryOp { - left: Box::new(Expr::Interval(Interval { + left: Box::new(Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( "1 DAY" )))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, })), op: BinaryOperator::Gt, - right: Box::new(Expr::Interval(Interval { + right: Box::new(Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString(String::from( "1 SECOND" )))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, })) } ); @@ -5643,15 +5758,17 @@ fn interval_disallow_interval_expr_gt() { assert_eq!( expr, Expr::BinaryOp { - left: Box::new(Expr::Interval(Interval { + left: Box::new(Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString( "1 second".to_string() ))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, - },)), + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, + })), op: BinaryOperator::Gt, right: Box::new(Expr::Identifier(Ident { value: "x".to_string(), @@ -5670,14 +5787,16 @@ fn interval_disallow_interval_expr_double_colon() { expr, Expr::Cast { kind: CastKind::DoubleColon, - expr: Box::new(Expr::Interval(Interval { + expr: Box::new(Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString( "1 second".to_string() ))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, })), data_type: DataType::Text, format: None, @@ -5732,14 +5851,16 @@ fn parse_interval_and_or_xor() { span: Span::empty(), })), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { + right: Box::new(Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString( "5 days".to_string(), ))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, })), }), }), @@ -5758,14 +5879,16 @@ fn parse_interval_and_or_xor() { span: Span::empty(), })), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { + right: Box::new(Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString( "3 days".to_string(), ))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, })), }), }), @@ -11463,7 +11586,7 @@ fn test_group_by_nothing() { } #[test] -fn test_extract_seconds_ok() { +fn test_extract_custom_ok() { let dialects = all_dialects_where(|d| d.allow_extract_custom()); let stmt = dialects.verified_expr("EXTRACT(SECONDS FROM '2 seconds'::INTERVAL)"); @@ -11477,7 +11600,12 @@ fn test_extract_seconds_ok() { expr: Box::new(Expr::Value(Value::SingleQuotedString( "2 seconds".to_string() ))), - data_type: DataType::Interval, + data_type: DataType::Interval(IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }), format: None, }), } @@ -11502,7 +11630,12 @@ fn test_extract_seconds_ok() { expr: Box::new(Expr::Value(Value::SingleQuotedString( "2 seconds".to_string(), ))), - data_type: DataType::Interval, + data_type: DataType::Interval(IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }), format: None, }), })], @@ -11537,15 +11670,15 @@ fn test_extract_seconds_ok() { } #[test] -fn test_extract_seconds_single_quote_ok() { +fn test_extract_custom_single_quote_ok() { let dialects = all_dialects_where(|d| d.allow_extract_custom()); - let stmt = dialects.verified_expr(r#"EXTRACT('seconds' FROM '2 seconds'::INTERVAL)"#); + let stmt = dialects.verified_expr(r#"EXTRACT('custom' FROM '2 seconds'::INTERVAL)"#); assert_eq!( stmt, Expr::Extract { field: DateTimeField::Custom(Ident { - value: "seconds".to_string(), + value: "custom".to_string(), quote_style: Some('\''), span: Span::empty(), }), @@ -11555,7 +11688,12 @@ fn test_extract_seconds_single_quote_ok() { expr: Box::new(Expr::Value(Value::SingleQuotedString( "2 seconds".to_string() ))), - data_type: DataType::Interval, + data_type: DataType::Interval(IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }), format: None, }), } @@ -11667,20 +11805,20 @@ fn parse_explain_with_option_list() { Some(vec![ UtilityOption { name: Ident::new("NUM1"), - arg: Some(Expr::Value(Value::Number("10".parse().unwrap(), false))), + arg: Some(Expr::Value(Value::Number("10".parse().unwrap(), None))), }, UtilityOption { name: Ident::new("NUM2"), arg: Some(Expr::UnaryOp { op: UnaryOperator::Plus, - expr: Box::new(Expr::Value(Value::Number("10.1".parse().unwrap(), false))), + expr: Box::new(Expr::Value(Value::Number("10.1".parse().unwrap(), None))), }), }, UtilityOption { name: Ident::new("NUM3"), arg: Some(Expr::UnaryOp { op: UnaryOperator::Minus, - expr: Box::new(Expr::Value(Value::Number("10.2".parse().unwrap(), false))), + expr: Box::new(Expr::Value(Value::Number("10.2".parse().unwrap(), None))), }), }, ]), @@ -11707,7 +11845,7 @@ fn parse_explain_with_option_list() { name: Ident::new("USER_DEF_NUM"), arg: Some(Expr::UnaryOp { op: UnaryOperator::Minus, - expr: Box::new(Expr::Value(Value::Number("100.1".parse().unwrap(), false))), + expr: Box::new(Expr::Value(Value::Number("100.1".parse().unwrap(), None))), }), }, ]; @@ -11752,15 +11890,15 @@ fn test_create_policy() { Some(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("c0"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), None))), }) ); assert_eq!( with_check, Some(Expr::BinaryOp { - left: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), false))), + left: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), None))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("1".parse().unwrap(), None))), }) ); } @@ -12334,12 +12472,12 @@ fn parse_load_data() { Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("year"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("2024".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("2024".parse().unwrap(), None))), }, Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("month"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("11".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("11".parse().unwrap(), None))), } ]), partitioned @@ -12372,12 +12510,12 @@ fn parse_load_data() { Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("year"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("2024".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("2024".parse().unwrap(), None))), }, Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("month"))), op: BinaryOperator::Eq, - right: Box::new(Expr::Value(Value::Number("11".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("11".parse().unwrap(), None))), } ]), partitioned @@ -12467,7 +12605,7 @@ fn parse_bang_not() { Box::new(Expr::Nested(Box::new(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("b"))), op: BinaryOperator::Gt, - right: Box::new(Expr::Value(Value::Number("3".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("3".parse().unwrap(), None))), }))), ] .into_iter() @@ -12683,7 +12821,7 @@ fn test_reserved_keywords_for_identifiers() { assert_eq!( dialects.parse_sql_statements(sql), Err(ParserError::ParserError( - "Expected: an expression, found: )".to_string() + "INTERVAL must have at least one valid value along with a unit if required".to_string() )) ); @@ -12809,7 +12947,7 @@ fn parse_composite_access_expr() { values: vec![ Expr::Named { name: Ident::new("a"), - expr: Box::new(Expr::Value(Number("1".parse().unwrap(), false))), + expr: Box::new(Expr::Value(Number("1".parse().unwrap(), None))), }, Expr::Named { name: Ident::new("b"), @@ -12848,11 +12986,11 @@ fn parse_create_table_with_enum_types() { vec![ EnumMember::NamedValue( "a".to_string(), - Expr::Value(Number("1".parse().unwrap(), false)) + Expr::Value(Number("1".parse().unwrap(), None)) ), EnumMember::NamedValue( "b".to_string(), - Expr::Value(Number("2".parse().unwrap(), false)) + Expr::Value(Number("2".parse().unwrap(), None)) ) ], Some(8) @@ -12866,11 +13004,11 @@ fn parse_create_table_with_enum_types() { vec![ EnumMember::NamedValue( "a".to_string(), - Expr::Value(Number("1".parse().unwrap(), false)) + Expr::Value(Number("1".parse().unwrap(), None)) ), EnumMember::NamedValue( "b".to_string(), - Expr::Value(Number("2".parse().unwrap(), false)) + Expr::Value(Number("2".parse().unwrap(), None)) ) ], Some(16) diff --git a/tests/sqlparser_custom_dialect.rs b/tests/sqlparser_custom_dialect.rs index 61874fc27..b884d624b 100644 --- a/tests/sqlparser_custom_dialect.rs +++ b/tests/sqlparser_custom_dialect.rs @@ -40,7 +40,7 @@ fn custom_prefix_parser() -> Result<(), ParserError> { } fn parse_prefix(&self, parser: &mut Parser) -> Option> { - if parser.consume_token(&Token::Number("1".to_string(), false)) { + if parser.consume_token(&Token::Number("1".to_string(), None)) { Some(Ok(Expr::Value(Value::Null))) } else { None diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index db4ffb6f6..59ad37df8 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -44,10 +44,14 @@ fn test_struct() { StructField { field_name: Some(Ident::new("v")), field_type: DataType::Varchar(None), + not_null: false, + comment: None, }, StructField { field_name: Some(Ident::new("i")), field_type: DataType::Integer(None), + not_null: false, + comment: None, }, ], StructBracketKind::Parentheses, @@ -86,6 +90,8 @@ fn test_struct() { StructField { field_name: Some(Ident::new("v")), field_type: DataType::Varchar(None), + not_null: false, + comment: None, }, StructField { field_name: Some(Ident::new("s")), @@ -94,14 +100,20 @@ fn test_struct() { StructField { field_name: Some(Ident::new("a1")), field_type: DataType::Integer(None), + not_null: false, + comment: None, }, StructField { field_name: Some(Ident::new("a2")), field_type: DataType::Varchar(None), + not_null: false, + comment: None, }, ], StructBracketKind::Parentheses, ), + not_null: false, + comment: None, }, ], StructBracketKind::Parentheses, diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 5349f1207..85bb51682 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -182,7 +182,7 @@ fn create_table_with_clustered_by() { with_fill: None, }, ]), - num_buckets: Value::Number("4".parse().unwrap(), false), + num_buckets: Value::Number("4".parse().unwrap(), None), } ) } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 567cd5382..a89f23523 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1205,7 +1205,7 @@ fn parse_mssql_declare() { local: false, hivevar: false, variables: OneOrManyWithParens::One(ObjectName(vec![Ident::new("@bar")])), - value: vec![Expr::Value(Value::Number("2".parse().unwrap(), false))], + value: vec![Expr::Value(Value::Number("2".parse().unwrap(), None))], }, Statement::Query(Box::new(Query { with: None, @@ -1226,7 +1226,7 @@ fn parse_mssql_declare() { projection: vec![SelectItem::UnnamedExpr(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("@bar"))), op: BinaryOperator::Multiply, - right: Box::new(Expr::Value(Value::Number("4".parse().unwrap(), false))), + right: Box::new(Expr::Value(Value::Number("4".parse().unwrap(), None))), })], into: None, from: vec![], diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 864fb5eb3..b622973dc 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1690,13 +1690,13 @@ fn parse_execute() { using: vec![ Expr::Cast { kind: CastKind::Cast, - expr: Box::new(Expr::Value(Value::Number("1337".parse().unwrap(), false))), + expr: Box::new(Expr::Value(Value::Number("1337".parse().unwrap(), None))), data_type: DataType::SmallInt(None), format: None }, Expr::Cast { kind: CastKind::Cast, - expr: Box::new(Expr::Value(Value::Number("7331".parse().unwrap(), false))), + expr: Box::new(Expr::Value(Value::Number("7331".parse().unwrap(), None))), data_type: DataType::SmallInt(None), format: None }, @@ -3826,7 +3826,7 @@ fn parse_drop_function() { mode: Some(ArgMode::In), name: Some("b".into()), data_type: DataType::Integer(None), - default_expr: Some(Expr::Value(Value::Number("1".parse().unwrap(), false))), + default_expr: Some(Expr::Value(Value::Number("1".parse().unwrap(), None))), } ]), }], @@ -3854,7 +3854,7 @@ fn parse_drop_function() { data_type: DataType::Integer(None), default_expr: Some(Expr::Value(Value::Number( "1".parse().unwrap(), - false + None ))), } ]), @@ -3873,7 +3873,7 @@ fn parse_drop_function() { data_type: DataType::Integer(None), default_expr: Some(Expr::Value(Value::Number( "1".parse().unwrap(), - false + None ))), } ]), @@ -3920,7 +3920,7 @@ fn parse_drop_procedure() { mode: Some(ArgMode::In), name: Some("b".into()), data_type: DataType::Integer(None), - default_expr: Some(Expr::Value(Value::Number("1".parse().unwrap(), false))), + default_expr: Some(Expr::Value(Value::Number("1".parse().unwrap(), None))), } ]), }], @@ -3948,7 +3948,7 @@ fn parse_drop_procedure() { data_type: DataType::Integer(None), default_expr: Some(Expr::Value(Value::Number( "1".parse().unwrap(), - false + None ))), } ]), @@ -3967,7 +3967,7 @@ fn parse_drop_procedure() { data_type: DataType::Integer(None), default_expr: Some(Expr::Value(Value::Number( "1".parse().unwrap(), - false + None ))), } ]), @@ -4410,7 +4410,7 @@ fn test_simple_postgres_insert_with_alias() { explicit_row: false, rows: vec![vec![ Expr::Identifier(Ident::new("DEFAULT")), - Expr::Value(Value::Number("123".to_string(), false)) + Expr::Value(Value::Number("123".to_string(), None)) ]] })), order_by: None, @@ -4482,7 +4482,7 @@ fn test_simple_postgres_insert_with_alias() { Expr::Identifier(Ident::new("DEFAULT")), Expr::Value(Value::Number( bigdecimal::BigDecimal::new(123.into(), 0), - false + None )) ]] })), @@ -4630,14 +4630,16 @@ fn parse_at_time_zone() { }), }), op: BinaryOperator::Plus, - right: Box::new(Expr::Interval(Interval { + right: Box::new(Expr::Interval(Interval::Standard { value: Box::new(Expr::Value(Value::SingleQuotedString( "23 hours".to_owned(), ))), - leading_field: None, - leading_precision: None, - last_field: None, - fractional_seconds_precision: None, + unit: IntervalUnit { + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, })), }; pretty_assertions::assert_eq!( diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 857d378bc..824fd533a 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -334,7 +334,7 @@ fn test_parse_json_path_from() { quoted: false }, JsonPathElem::Bracket { - key: Expr::Value(Value::Number("1".parse().unwrap(), false)) + key: Expr::Value(Value::Number("1".parse().unwrap(), None)) }, JsonPathElem::Dot { key: "b".to_string(), diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index edd1365f4..4036bd83d 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -473,8 +473,8 @@ fn parse_update_tuple_row_values() { ObjectName(vec![Ident::new("b"),]), ]), value: Expr::Tuple(vec![ - Expr::Value(Value::Number("1".parse().unwrap(), false)), - Expr::Value(Value::Number("2".parse().unwrap(), false)) + Expr::Value(Value::Number("1".parse().unwrap(), None)), + Expr::Value(Value::Number("2".parse().unwrap(), None)) ]) }], selection: None,