diff --git a/CHANGELOG.md b/CHANGELOG.md index 413ea14..e0405f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ Given that the parser produces a typed AST, any changes to the AST will technica ## [Unreleased] Nothing here yet! Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented changes. +### Changed + +- Now supports parsing date and time types + - ast::Value::Interval changed its representation to include an inner + IntervalValue that includes a `ParsedDateTime` and some useful methods. + - ast::Value::Date changed its representation to include an inner + `ParsedDate` + ## [0.4.0] - 2019-07-02 This release brings us closer to SQL-92 support, mainly thanks to the improvements contributed back from @MaterializeInc's fork and other work by @benesch. diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 24a7fd1..79cc82e 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -49,7 +49,9 @@ pub use self::query::{ Cte, Fetch, Join, JoinConstraint, JoinOperator, OrderByExpr, Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Values, }; -pub use self::value::{DateTimeField, Value}; +pub use self::value::{ + DateTimeField, Interval, IntervalValue, ParsedDate, ParsedDateTime, ParsedTimestamp, Value, +}; struct DisplaySeparated<'a, T> where diff --git a/src/ast/value.rs b/src/ast/value.rs index 3d10824..795dced 100644 --- a/src/ast/value.rs +++ b/src/ast/value.rs @@ -13,6 +13,22 @@ use bigdecimal::BigDecimal; use std::fmt; +mod datetime; +pub use datetime::{ + DateTimeField, Interval, IntervalValue, ParsedDate, ParsedDateTime, ParsedTimestamp, +}; + +#[derive(Debug)] +pub struct ValueError(String); + +impl std::error::Error for ValueError {} + +impl fmt::Display for ValueError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + /// Primitive SQL values such as number and string #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Value { @@ -29,14 +45,14 @@ pub enum Value { /// Boolean value true or false Boolean(bool), /// `DATE '...'` literals - Date(String), + Date(String, ParsedDate), /// `TIME '...'` literals Time(String), /// `TIMESTAMP '...'` literals - Timestamp(String), + Timestamp(String, ParsedTimestamp), /// INTERVAL literals, roughly in the following format: /// - /// ```ignore + /// ```text /// INTERVAL '' [ () ] /// [ TO [ () ] ] /// ``` @@ -45,47 +61,13 @@ pub enum Value { /// The parser does not validate the ``, nor does it ensure /// that the `` units >= the units in ``, /// so the user will have to reject intervals like `HOUR TO YEAR`. - Interval { - /// The raw [value] that was present in `INTERVAL '[value]'` - value: String, - /// The unit of the first field in the interval. `INTERVAL 'T' MINUTE` - /// means `T` is in minutes - leading_field: DateTimeField, - /// How many digits the leading field is allowed to occupy. - /// - /// The interval `INTERVAL '1234' MINUTE(3)` is **illegal**, but `INTERVAL - /// '123' MINUTE(3)` is fine. - /// - /// This parser does not do any validation that fields fit. - leading_precision: Option, - /// How much precision to keep track of - /// - /// If this is ommitted, then you are supposed to ignore all of the - /// non-lead fields. If it is less precise than the final field, you - /// are supposed to ignore the final field. - /// - /// For the following specifications: - /// - /// * `INTERVAL '1:1:1' HOURS TO SECONDS` the `last_field` gets - /// `Some(DateTimeField::Second)` and interpreters should generate an - /// interval equivalent to `3661` seconds. - /// * In `INTERVAL '1:1:1' HOURS` the `last_field` gets `None` and - /// interpreters should generate an interval equivalent to `3600` - /// seconds. - /// * In `INTERVAL '1:1:1' HOURS TO MINUTES` the interval should be - /// equivalent to `3660` seconds. - last_field: Option, - /// The seconds precision can be specified in SQL source as - /// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field` - /// will be `Second` and the `last_field` will be `None`), - /// or as `__ TO SECOND(x)`. - fractional_seconds_precision: Option, - }, + Interval(IntervalValue), /// `NULL` value Null, } impl fmt::Display for Value { + #[allow(clippy::unneeded_field_pattern)] // want to be warned if we add another field fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Value::Long(v) => write!(f, "{}", v), @@ -94,16 +76,17 @@ impl fmt::Display for Value { Value::NationalStringLiteral(v) => write!(f, "N'{}'", v), Value::HexStringLiteral(v) => write!(f, "X'{}'", v), Value::Boolean(v) => write!(f, "{}", v), - Value::Date(v) => write!(f, "DATE '{}'", escape_single_quote_string(v)), + Value::Date(v, _) => write!(f, "DATE '{}'", escape_single_quote_string(v)), Value::Time(v) => write!(f, "TIME '{}'", escape_single_quote_string(v)), - Value::Timestamp(v) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)), - Value::Interval { + Value::Timestamp(v, _) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)), + Value::Interval(IntervalValue { + parsed: _, value, leading_field: DateTimeField::Second, leading_precision: Some(leading_precision), last_field, fractional_seconds_precision: Some(fractional_seconds_precision), - } => { + }) => { // When the leading field is SECOND, the parser guarantees that // the last field is None. assert!(last_field.is_none()); @@ -115,13 +98,14 @@ impl fmt::Display for Value { fractional_seconds_precision ) } - Value::Interval { + Value::Interval(IntervalValue { + parsed: _, value, leading_field, leading_precision, last_field, fractional_seconds_precision, - } => { + }) => { write!( f, "INTERVAL '{}' {}", @@ -144,29 +128,6 @@ impl fmt::Display for Value { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum DateTimeField { - Year, - Month, - Day, - Hour, - Minute, - Second, -} - -impl fmt::Display for DateTimeField { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match self { - DateTimeField::Year => "YEAR", - DateTimeField::Month => "MONTH", - DateTimeField::Day => "DAY", - DateTimeField::Hour => "HOUR", - DateTimeField::Minute => "MINUTE", - DateTimeField::Second => "SECOND", - }) - } -} - pub struct EscapeSingleQuoteString<'a>(&'a str); impl<'a> fmt::Display for EscapeSingleQuoteString<'a> { @@ -185,3 +146,39 @@ impl<'a> fmt::Display for EscapeSingleQuoteString<'a> { pub fn escape_single_quote_string(s: &str) -> EscapeSingleQuoteString<'_> { EscapeSingleQuoteString(s) } + +#[cfg(test)] +mod test { + use super::*; + + /// An extremely default interval value + fn ivalue() -> IntervalValue { + IntervalValue { + value: "".into(), + parsed: ParsedDateTime::default(), + leading_field: DateTimeField::Year, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + } + } + + #[test] + fn interval_values() { + let mut iv = ivalue(); + iv.parsed.year = None; + match iv.computed_permissive() { + Err(ValueError { .. }) => {} + Ok(why) => panic!("should not be okay: {:?}", why), + } + } + + #[test] + fn iterate_datetimefield() { + use DateTimeField::*; + assert_eq!( + Year.into_iter().take(10).collect::>(), + vec![Month, Day, Hour, Minute, Second] + ) + } +} diff --git a/src/ast/value/datetime.rs b/src/ast/value/datetime.rs new file mode 100644 index 0000000..bb6b7ac --- /dev/null +++ b/src/ast/value/datetime.rs @@ -0,0 +1,388 @@ +use std::fmt; +use std::time::Duration; + +use super::ValueError; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct IntervalValue { + /// The raw `[value]` that was present in `INTERVAL '[value]'` + pub value: String, + /// the fully parsed date time + pub parsed: ParsedDateTime, + /// The unit of the first field in the interval. `INTERVAL 'T' MINUTE` + /// means `T` is in minutes + pub leading_field: DateTimeField, + /// How many digits the leading field is allowed to occupy. + /// + /// The interval `INTERVAL '1234' MINUTE(3)` is **illegal**, but `INTERVAL + /// '123' MINUTE(3)` is fine. + /// + /// This parser does not do any validation that fields fit. + pub leading_precision: Option, + /// How much precision to keep track of + /// + /// If this is ommitted, then you are supposed to ignore all of the + /// non-lead fields. If it is less precise than the final field, you + /// are supposed to ignore the final field. + /// + /// For the following specifications: + /// + /// * `INTERVAL '1:1:1' HOURS TO SECONDS` the `last_field` gets + /// `Some(DateTimeField::Second)` and interpreters should generate an + /// interval equivalent to `3661` seconds. + /// * In `INTERVAL '1:1:1' HOURS` the `last_field` gets `None` and + /// interpreters should generate an interval equivalent to `3600` + /// seconds. + /// * In `INTERVAL '1:1:1' HOURS TO MINUTES` the interval should be + /// equivalent to `3660` seconds. + pub last_field: Option, + /// The seconds precision can be specified in SQL source as + /// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field` + /// will be `Second` and the `last_field` will be `None`), + /// or as `__ TO SECOND(x)`. + pub fractional_seconds_precision: Option, +} + +impl IntervalValue { + /// Get Either the number of Months or the Duration specified by this interval + /// + /// This computes the fiels permissively: it assumes that the leading field + /// (i.e. the lead in `INTERVAL 'str' LEAD [TO LAST]`) is valid and parses + /// all field in the `str` starting at the leading field, ignoring the + /// truncation that should be specified by `LAST`. + /// + /// See also the related [`fields_match_precision`] function that will give + /// an error if the interval string does not exactly match the `FROM TO + /// LAST` spec. + /// + /// # Errors + /// + /// If a required field is missing (i.e. there is no value) or the `TO + /// LAST` field is larger than the `LEAD`. + pub fn computed_permissive(&self) -> Result { + use DateTimeField::*; + match &self.leading_field { + Year => match &self.last_field { + Some(Month) => Ok(Interval::Months( + self.parsed.positivity() * self.parsed.year.unwrap_or(0) as i64 * 12 + + self.parsed.month.unwrap_or(0) as i64, + )), + Some(Year) | None => self + .parsed + .year + .ok_or_else(|| ValueError("No YEAR provided".into())) + .map(|year| Interval::Months(self.parsed.positivity() * year as i64 * 12)), + Some(invalid) => Err(ValueError(format!( + "Invalid specifier for YEAR precision: {}", + &invalid + ))), + }, + Month => match &self.last_field { + Some(Month) | None => self + .parsed + .month + .ok_or_else(|| ValueError("No MONTH provided".into())) + .map(|m| Interval::Months(self.parsed.positivity() * m as i64)), + Some(invalid) => Err(ValueError(format!( + "Invalid specifier for MONTH precision: {}", + &invalid + ))), + }, + durationlike_field => { + let mut seconds = 0u64; + match self.units_of(&durationlike_field) { + Some(time) => seconds += time * seconds_multiplier(&durationlike_field), + None => { + return Err(ValueError(format!( + "No {} provided in value string for {}", + durationlike_field, self.value + ))) + } + } + let min_field = &self + .last_field + .clone() + .unwrap_or_else(|| durationlike_field.clone()); + for field in durationlike_field + .clone() + .into_iter() + .take_while(|f| f <= min_field) + { + if let Some(time) = self.units_of(&field) { + seconds += time * seconds_multiplier(&field); + } + } + let duration = match (min_field, self.parsed.nano) { + (DateTimeField::Second, Some(nanos)) => Duration::new(seconds, nanos), + (_, _) => Duration::from_secs(seconds), + }; + Ok(Interval::Duration { + is_positive: self.parsed.is_positive, + duration, + }) + } + } + } + + /// Retrieve the number that we parsed out of the literal string for the `field` + fn units_of(&self, field: &DateTimeField) -> Option { + match field { + DateTimeField::Year => self.parsed.year, + DateTimeField::Month => self.parsed.month, + DateTimeField::Day => self.parsed.day, + DateTimeField::Hour => self.parsed.hour, + DateTimeField::Minute => self.parsed.minute, + DateTimeField::Second => self.parsed.second, + } + } + + /// Verify that the fields in me make sense + /// + /// Returns Ok if the fields are fully specified, otherwise an error + /// + /// # Examples + /// + /// ```sql + /// INTERVAL '1 5' DAY TO HOUR -- Ok + /// INTERVAL '1 5' DAY -- Err + /// INTERVAL '1:2:3' HOUR TO SECOND -- Ok + /// INTERVAL '1:2:3' HOUR TO MINUTE -- Err + /// INTERVAL '1:2:3' MINUTE TO SECOND -- Err + /// INTERVAL '1:2:3' DAY TO SECOND -- Err + /// ``` + pub fn fields_match_precision(&self) -> Result<(), ValueError> { + let mut errors = vec![]; + let last_field = self + .last_field + .as_ref() + .unwrap_or_else(|| &self.leading_field); + let mut extra_leading_fields = vec![]; + let mut extra_trailing_fields = vec![]; + // check for more data in the input string than was requested in TO + for field in std::iter::once(DateTimeField::Year).chain(DateTimeField::Year.into_iter()) { + if self.units_of(&field).is_none() { + continue; + } + + if field < self.leading_field { + extra_leading_fields.push(field.clone()); + } + if &field > last_field { + extra_trailing_fields.push(field.clone()); + } + } + + if !extra_leading_fields.is_empty() { + errors.push(format!( + "The interval string '{}' specifies {}s but the significance requested is {}", + self.value, + fields_msg(extra_leading_fields.into_iter()), + self.leading_field + )); + } + if !extra_trailing_fields.is_empty() { + errors.push(format!( + "The interval string '{}' specifies {}s but the requested precision would truncate to {}", + self.value, fields_msg(extra_trailing_fields.into_iter()), last_field + )); + } + + // check for data requested by the TO that does not exist in the data + let missing_fields = match ( + self.units_of(&self.leading_field), + self.units_of(&last_field), + ) { + (Some(_), Some(_)) => vec![], + (None, Some(_)) => vec![&self.leading_field], + (Some(_), None) => vec![last_field], + (None, None) => vec![&self.leading_field, last_field], + }; + + if !missing_fields.is_empty() { + errors.push(format!( + "The interval string '{}' provides {} - which does not include the requested field(s) {}", + self.value, self.present_fields(), fields_msg(missing_fields.into_iter().cloned()))); + } + + if !errors.is_empty() { + Err(ValueError(errors.join("; "))) + } else { + Ok(()) + } + } + + fn present_fields(&self) -> String { + fields_msg( + std::iter::once(DateTimeField::Year) + .chain(DateTimeField::Year.into_iter()) + .filter(|field| self.units_of(&field).is_some()), + ) + } +} + +fn fields_msg(fields: impl Iterator) -> String { + fields + .map(|field: DateTimeField| field.to_string()) + .collect::>() + .join(", ") +} + +fn seconds_multiplier(field: &DateTimeField) -> u64 { + match field { + DateTimeField::Day => 60 * 60 * 24, + DateTimeField::Hour => 60 * 60, + DateTimeField::Minute => 60, + DateTimeField::Second => 1, + _other => unreachable!("Do not call with a non-duration field"), + } +} + +/// The result of parsing an `INTERVAL '' [TO ]` +/// +/// Units of type `YEAR` or `MONTH` are semantically some multiple of months, +/// which are not well defined, and this parser normalizes them to some number +/// of months. +/// +/// Intervals of unit [`DateTimeField::Day`] or smaller are semantically a +/// multiple of seconds. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Interval { + /// A possibly negative number of months for field types like `YEAR` + Months(i64), + /// An actual timespan, possibly negative, because why not + Duration { + is_positive: bool, + duration: Duration, + }, +} + +/// The fields of a Date +/// +/// This is not guaranteed to be a valid date +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParsedDate { + pub year: i64, + pub month: u8, + pub day: u8, +} + +/// The fields in a `Timestamp` +/// +/// Similar to a [`ParsedDateTime`], except that all the fields are required. +/// +/// This is not guaranteed to be a valid date +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParsedTimestamp { + pub year: i64, + pub month: u8, + pub day: u8, + pub hour: u8, + pub minute: u8, + pub second: u8, + pub nano: u32, +} + +/// All of the fields that can appear in a literal `DATE`, `TIMESTAMP` or `INTERVAL` string +/// +/// This is only used in an `Interval`, which can have any contiguous set of +/// fields set, otherwise you are probably looking for [`ParsedDate`] or +/// [`ParsedTimestamp`]. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParsedDateTime { + pub is_positive: bool, + pub year: Option, + pub month: Option, + pub day: Option, + pub hour: Option, + pub minute: Option, + pub second: Option, + pub nano: Option, +} + +impl ParsedDateTime { + /// `1` if is_positive, else `-1` + pub(crate) fn positivity(&self) -> i64 { + match self.is_positive { + true => 1, + false => -1, + } + } +} + +impl Default for ParsedDateTime { + fn default() -> ParsedDateTime { + ParsedDateTime { + is_positive: true, + year: None, + month: None, + day: None, + hour: None, + minute: None, + second: None, + nano: None, + } + } +} + +#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub enum DateTimeField { + Year, + Month, + Day, + Hour, + Minute, + Second, +} + +impl fmt::Display for DateTimeField { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + DateTimeField::Year => "YEAR", + DateTimeField::Month => "MONTH", + DateTimeField::Day => "DAY", + DateTimeField::Hour => "HOUR", + DateTimeField::Minute => "MINUTE", + DateTimeField::Second => "SECOND", + }) + } +} + +/// Iterate over `DateTimeField`s in descending significance +impl IntoIterator for DateTimeField { + type Item = DateTimeField; + type IntoIter = DateTimeFieldIterator; + fn into_iter(self) -> DateTimeFieldIterator { + DateTimeFieldIterator(Some(self)) + } +} + +/// An iterator over DateTimeFields +/// +/// Always starts with the value smaller than the current one. +/// +/// ``` +/// use sqlparser::ast::DateTimeField::*; +/// let mut itr = Hour.into_iter(); +/// assert_eq!(itr.next(), Some(Minute)); +/// assert_eq!(itr.next(), Some(Second)); +/// assert_eq!(itr.next(), None); +/// ``` +pub struct DateTimeFieldIterator(Option); + +/// Go through fields in descending significance order +impl Iterator for DateTimeFieldIterator { + type Item = DateTimeField; + fn next(&mut self) -> Option { + use DateTimeField::*; + self.0 = match self.0 { + Some(Year) => Some(Month), + Some(Month) => Some(Day), + Some(Day) => Some(Hour), + Some(Hour) => Some(Minute), + Some(Minute) => Some(Second), + Some(Second) => None, + None => None, + }; + self.0.clone() + } +} diff --git a/src/parser.rs b/src/parser.rs index 7a313d5..5647036 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -22,17 +22,24 @@ use super::tokenizer::*; use std::error::Error; use std::fmt; -#[derive(Debug, Clone, PartialEq)] -pub enum ParserError { - TokenizerError(String), - ParserError(String), -} +use crate::ast::{ParsedDate, ParsedTimestamp}; // Use `Parser::expected` instead, if possible macro_rules! parser_err { ($MSG:expr) => { Err(ParserError::ParserError($MSG.to_string())) }; + ($($arg:tt)*) => { + Err(ParserError::ParserError(format!($($arg)*))) + }; +} + +mod datetime; + +#[derive(Debug, Clone, PartialEq)] +pub enum ParserError { + TokenizerError(String), + ParserError(String), } #[derive(PartialEq)] @@ -196,7 +203,7 @@ impl Parser { } "CASE" => self.parse_case_expr(), "CAST" => self.parse_cast_expr(), - "DATE" => Ok(Expr::Value(Value::Date(self.parse_literal_string()?))), + "DATE" => Ok(Expr::Value(self.parse_date()?)), "EXISTS" => self.parse_exists_expr(), "EXTRACT" => self.parse_extract_expr(), "INTERVAL" => self.parse_literal_interval(), @@ -205,7 +212,7 @@ impl Parser { expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), }), "TIME" => Ok(Expr::Value(Value::Time(self.parse_literal_string()?))), - "TIMESTAMP" => Ok(Expr::Value(Value::Timestamp(self.parse_literal_string()?))), + "TIMESTAMP" => Ok(Expr::Value(self.parse_timestamp()?)), // Here `w` is a word, check if it's a part of a multi-part // identifier, a function call, or a simple identifier: _ => match self.peek_token() { @@ -453,6 +460,117 @@ impl Parser { } } + fn parse_date(&mut self) -> Result { + use std::convert::TryInto; + + let value = self.parse_literal_string()?; + let pdt = Self::parse_interval_string(&value, &DateTimeField::Year)?; + + match (pdt.year, pdt.month, pdt.day, pdt.hour) { + (Some(year), Some(month), Some(day), None) => { + let p_err = |e: std::num::TryFromIntError, field: &str| { + ParserError::ParserError(format!( + "{} in date '{}' is invalid: {}", + field, value, e + )) + }; + + // type inference with try_into() fails so we need to mutate it negative + let mut year: i64 = year.try_into().map_err(|e| p_err(e, "Year"))?; + year *= pdt.positivity(); + if month > 12 || month == 0 { + return parser_err!( + "Month in date '{}' must be a number between 1 and 12, got: {}", + value, + month + ); + } + let month: u8 = month.try_into().expect("invalid month"); + let day: u8 = day.try_into().map_err(|e| p_err(e, "Day"))?; + if day == 0 { + parser_err!("Day in date '{}' cannot be zero: {}", value, day)?; + } + Ok(Value::Date(value, ParsedDate { year, month, day })) + } + (Some(_), Some(_), Some(_), Some(hours)) => parser_err!( + "Hours cannot be supplied for DATE, got {} in '{}'", + hours, + value + ), + (_, _, _, _) => Err(ParserError::ParserError(format!( + "year, day and month are all required, got: '{}'", + value + ))), + } + } + + fn parse_timestamp(&mut self) -> Result { + use std::convert::TryInto; + + let value = self.parse_literal_string()?; + let pdt = Self::parse_interval_string(&value, &DateTimeField::Year)?; + + match ( + pdt.year, pdt.month, pdt.day, pdt.hour, pdt.minute, pdt.second, pdt.nano, + ) { + (Some(year), Some(month), Some(day), Some(hour), Some(minute), Some(second), nano) => { + let p_err = |e: std::num::TryFromIntError, field: &str| { + ParserError::ParserError(format!( + "{} in date '{}' is invalid: {}", + field, value, e + )) + }; + + // type inference with try_into() fails so we need to mutate it negative + let mut year: i64 = year.try_into().map_err(|e| p_err(e, "Year"))?; + year *= pdt.positivity(); + if month > 12 || month == 0 { + return parser_err!( + "Month in date '{}' must be a number between 1 and 12, got: {}", + value, + month + ); + } + let month: u8 = month.try_into().expect("invalid month"); + if month == 0 { + parser_err!("Month in timestamp '{}' cannot be zero: {}", value, day)?; + } + let day: u8 = day.try_into().map_err(|e| p_err(e, "Day"))?; + if day == 0 { + parser_err!("Day in timestamp '{}' cannot be zero: {}", value, day)?; + } + let hour: u8 = hour.try_into().map_err(|e| p_err(e, "Hour"))?; + if hour > 23 { + parser_err!("Hour in timestamp '{}' cannot be > 23: {}", value, hour)?; + } + let minute: u8 = minute.try_into().map_err(|e| p_err(e, "Minute"))?; + if minute > 59 { + parser_err!("Minute in timestamp '{}' cannot be > 59: {}", value, minute)?; + } + let second: u8 = second.try_into().map_err(|e| p_err(e, "Minute"))?; + if second > 60 { + parser_err!("Second in timestamp '{}' cannot be > 60: {}", value, second)?; + } + Ok(Value::Timestamp( + value, + ParsedTimestamp { + year, + month, + day, + hour, + minute, + second, + nano: nano.unwrap_or(0), + }, + )) + } + _ => Err(ParserError::ParserError(format!( + "timestamp is missing fields, year through second are all required, got: '{}'", + value + ))), + } + } + /// Parse an INTERVAL literal. /// /// Some syntactically valid intervals: @@ -466,14 +584,14 @@ impl Parser { /// /// Note that we do not currently attempt to parse the quoted value. pub fn parse_literal_interval(&mut self) -> Result { - // The SQL standard allows an optional sign before the value string, but + // The SQL standard allows an optional sign before the raw_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.) + // inside the raw_value string.) // The first token in an interval is a string literal which specifies // the duration of the interval. - let value = self.parse_literal_string()?; + let raw_value = self.parse_literal_string()?; // Following the string literal is a qualifier which indicates the units // of the duration specified in the string literal. @@ -507,13 +625,16 @@ impl Parser { } }; - Ok(Expr::Value(Value::Interval { - value, + let value = Self::parse_interval_string(&raw_value, &leading_field)?; + + Ok(Expr::Value(Value::Interval(IntervalValue { + value: raw_value, + parsed: value, leading_field, leading_precision, last_field, fractional_seconds_precision: fsec_precision, - })) + }))) } /// Parse an operator following an expression @@ -588,6 +709,38 @@ impl Parser { } } + /// parse + /// + /// ```text + /// ::= + /// [ ] { | } + /// ::= + /// [ ] + /// | + /// ::= + /// + /// |