Skip to content

Commit e4bf262

Browse files
authored
Merge pull request apache#8 from MaterializeInc/dates-and-intervals
Dates and intervals
2 parents aa9b649 + 6b489dd commit e4bf262

File tree

7 files changed

+1372
-143
lines changed

7 files changed

+1372
-143
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ Given that the parser produces a typed AST, any changes to the AST will technica
88
## [Unreleased]
99
Nothing here yet! Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented changes.
1010

11+
### Changed
12+
13+
- Now supports parsing date and time types
14+
- ast::Value::Interval changed its representation to include an inner
15+
IntervalValue that includes a `ParsedDateTime` and some useful methods.
16+
- ast::Value::Date changed its representation to include an inner
17+
`ParsedDate`
18+
1119
## [0.4.0] - 2019-07-02
1220
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.
1321

src/ast/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ pub use self::query::{
4949
Cte, Fetch, Join, JoinConstraint, JoinOperator, OrderByExpr, Query, Select, SelectItem,
5050
SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Values,
5151
};
52-
pub use self::value::{DateTimeField, Value};
52+
pub use self::value::{
53+
DateTimeField, Interval, IntervalValue, ParsedDate, ParsedDateTime, ParsedTimestamp, Value,
54+
};
5355

5456
struct DisplaySeparated<'a, T>
5557
where

src/ast/value.rs

+65-68
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@
1313
use bigdecimal::BigDecimal;
1414
use std::fmt;
1515

16+
mod datetime;
17+
pub use datetime::{
18+
DateTimeField, Interval, IntervalValue, ParsedDate, ParsedDateTime, ParsedTimestamp,
19+
};
20+
21+
#[derive(Debug)]
22+
pub struct ValueError(String);
23+
24+
impl std::error::Error for ValueError {}
25+
26+
impl fmt::Display for ValueError {
27+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28+
write!(f, "{}", self.0)
29+
}
30+
}
31+
1632
/// Primitive SQL values such as number and string
1733
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1834
pub enum Value {
@@ -29,14 +45,14 @@ pub enum Value {
2945
/// Boolean value true or false
3046
Boolean(bool),
3147
/// `DATE '...'` literals
32-
Date(String),
48+
Date(String, ParsedDate),
3349
/// `TIME '...'` literals
3450
Time(String),
3551
/// `TIMESTAMP '...'` literals
36-
Timestamp(String),
52+
Timestamp(String, ParsedTimestamp),
3753
/// INTERVAL literals, roughly in the following format:
3854
///
39-
/// ```ignore
55+
/// ```text
4056
/// INTERVAL '<value>' <leading_field> [ (<leading_precision>) ]
4157
/// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]
4258
/// ```
@@ -45,47 +61,13 @@ pub enum Value {
4561
/// The parser does not validate the `<value>`, nor does it ensure
4662
/// that the `<leading_field>` units >= the units in `<last_field>`,
4763
/// so the user will have to reject intervals like `HOUR TO YEAR`.
48-
Interval {
49-
/// The raw [value] that was present in `INTERVAL '[value]'`
50-
value: String,
51-
/// The unit of the first field in the interval. `INTERVAL 'T' MINUTE`
52-
/// means `T` is in minutes
53-
leading_field: DateTimeField,
54-
/// How many digits the leading field is allowed to occupy.
55-
///
56-
/// The interval `INTERVAL '1234' MINUTE(3)` is **illegal**, but `INTERVAL
57-
/// '123' MINUTE(3)` is fine.
58-
///
59-
/// This parser does not do any validation that fields fit.
60-
leading_precision: Option<u64>,
61-
/// How much precision to keep track of
62-
///
63-
/// If this is ommitted, then you are supposed to ignore all of the
64-
/// non-lead fields. If it is less precise than the final field, you
65-
/// are supposed to ignore the final field.
66-
///
67-
/// For the following specifications:
68-
///
69-
/// * `INTERVAL '1:1:1' HOURS TO SECONDS` the `last_field` gets
70-
/// `Some(DateTimeField::Second)` and interpreters should generate an
71-
/// interval equivalent to `3661` seconds.
72-
/// * In `INTERVAL '1:1:1' HOURS` the `last_field` gets `None` and
73-
/// interpreters should generate an interval equivalent to `3600`
74-
/// seconds.
75-
/// * In `INTERVAL '1:1:1' HOURS TO MINUTES` the interval should be
76-
/// equivalent to `3660` seconds.
77-
last_field: Option<DateTimeField>,
78-
/// The seconds precision can be specified in SQL source as
79-
/// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field`
80-
/// will be `Second` and the `last_field` will be `None`),
81-
/// or as `__ TO SECOND(x)`.
82-
fractional_seconds_precision: Option<u64>,
83-
},
64+
Interval(IntervalValue),
8465
/// `NULL` value
8566
Null,
8667
}
8768

8869
impl fmt::Display for Value {
70+
#[allow(clippy::unneeded_field_pattern)] // want to be warned if we add another field
8971
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
9072
match self {
9173
Value::Long(v) => write!(f, "{}", v),
@@ -94,16 +76,17 @@ impl fmt::Display for Value {
9476
Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
9577
Value::HexStringLiteral(v) => write!(f, "X'{}'", v),
9678
Value::Boolean(v) => write!(f, "{}", v),
97-
Value::Date(v) => write!(f, "DATE '{}'", escape_single_quote_string(v)),
79+
Value::Date(v, _) => write!(f, "DATE '{}'", escape_single_quote_string(v)),
9880
Value::Time(v) => write!(f, "TIME '{}'", escape_single_quote_string(v)),
99-
Value::Timestamp(v) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)),
100-
Value::Interval {
81+
Value::Timestamp(v, _) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)),
82+
Value::Interval(IntervalValue {
83+
parsed: _,
10184
value,
10285
leading_field: DateTimeField::Second,
10386
leading_precision: Some(leading_precision),
10487
last_field,
10588
fractional_seconds_precision: Some(fractional_seconds_precision),
106-
} => {
89+
}) => {
10790
// When the leading field is SECOND, the parser guarantees that
10891
// the last field is None.
10992
assert!(last_field.is_none());
@@ -115,13 +98,14 @@ impl fmt::Display for Value {
11598
fractional_seconds_precision
11699
)
117100
}
118-
Value::Interval {
101+
Value::Interval(IntervalValue {
102+
parsed: _,
119103
value,
120104
leading_field,
121105
leading_precision,
122106
last_field,
123107
fractional_seconds_precision,
124-
} => {
108+
}) => {
125109
write!(
126110
f,
127111
"INTERVAL '{}' {}",
@@ -144,29 +128,6 @@ impl fmt::Display for Value {
144128
}
145129
}
146130

147-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
148-
pub enum DateTimeField {
149-
Year,
150-
Month,
151-
Day,
152-
Hour,
153-
Minute,
154-
Second,
155-
}
156-
157-
impl fmt::Display for DateTimeField {
158-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159-
f.write_str(match self {
160-
DateTimeField::Year => "YEAR",
161-
DateTimeField::Month => "MONTH",
162-
DateTimeField::Day => "DAY",
163-
DateTimeField::Hour => "HOUR",
164-
DateTimeField::Minute => "MINUTE",
165-
DateTimeField::Second => "SECOND",
166-
})
167-
}
168-
}
169-
170131
pub struct EscapeSingleQuoteString<'a>(&'a str);
171132

172133
impl<'a> fmt::Display for EscapeSingleQuoteString<'a> {
@@ -185,3 +146,39 @@ impl<'a> fmt::Display for EscapeSingleQuoteString<'a> {
185146
pub fn escape_single_quote_string(s: &str) -> EscapeSingleQuoteString<'_> {
186147
EscapeSingleQuoteString(s)
187148
}
149+
150+
#[cfg(test)]
151+
mod test {
152+
use super::*;
153+
154+
/// An extremely default interval value
155+
fn ivalue() -> IntervalValue {
156+
IntervalValue {
157+
value: "".into(),
158+
parsed: ParsedDateTime::default(),
159+
leading_field: DateTimeField::Year,
160+
leading_precision: None,
161+
last_field: None,
162+
fractional_seconds_precision: None,
163+
}
164+
}
165+
166+
#[test]
167+
fn interval_values() {
168+
let mut iv = ivalue();
169+
iv.parsed.year = None;
170+
match iv.computed_permissive() {
171+
Err(ValueError { .. }) => {}
172+
Ok(why) => panic!("should not be okay: {:?}", why),
173+
}
174+
}
175+
176+
#[test]
177+
fn iterate_datetimefield() {
178+
use DateTimeField::*;
179+
assert_eq!(
180+
Year.into_iter().take(10).collect::<Vec<_>>(),
181+
vec![Month, Day, Hour, Minute, Second]
182+
)
183+
}
184+
}

0 commit comments

Comments
 (0)