Skip to content

Commit f8b32e6

Browse files
committed
add time unit SECONDS/MINUTES/HOURS/DAYS/WEEKS/MONTHS/YEARS for INTERVAL type
1 parent fe36020 commit f8b32e6

File tree

4 files changed

+139
-18
lines changed

4 files changed

+139
-18
lines changed

src/ast/value.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ impl fmt::Display for DollarQuotedString {
155155
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
156156
pub enum DateTimeField {
157157
Year,
158+
Years,
158159
Month,
160+
Months,
159161
/// Week optionally followed by a WEEKDAY.
160162
///
161163
/// ```sql
@@ -164,14 +166,19 @@ pub enum DateTimeField {
164166
///
165167
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions#extract)
166168
Week(Option<Ident>),
169+
Weeks(Option<Ident>),
167170
Day,
168171
DayOfWeek,
169172
DayOfYear,
173+
Days,
170174
Date,
171175
Datetime,
172176
Hour,
177+
Hours,
173178
Minute,
179+
Minutes,
174180
Second,
181+
Seconds,
175182
Century,
176183
Decade,
177184
Dow,
@@ -210,22 +217,35 @@ impl fmt::Display for DateTimeField {
210217
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211218
match self {
212219
DateTimeField::Year => write!(f, "YEAR"),
220+
DateTimeField::Years => write!(f, "YEARS"),
213221
DateTimeField::Month => write!(f, "MONTH"),
222+
DateTimeField::Months => write!(f, "MONTHS"),
214223
DateTimeField::Week(week_day) => {
215224
write!(f, "WEEK")?;
216225
if let Some(week_day) = week_day {
217226
write!(f, "({week_day})")?
218227
}
219228
Ok(())
220229
}
230+
DateTimeField::Weeks(week_day) => {
231+
write!(f, "WEEKS")?;
232+
if let Some(week_day) = week_day {
233+
write!(f, "({week_day})")?
234+
}
235+
Ok(())
236+
}
221237
DateTimeField::Day => write!(f, "DAY"),
222238
DateTimeField::DayOfWeek => write!(f, "DAYOFWEEK"),
223239
DateTimeField::DayOfYear => write!(f, "DAYOFYEAR"),
240+
DateTimeField::Days => write!(f, "DAYS"),
224241
DateTimeField::Date => write!(f, "DATE"),
225242
DateTimeField::Datetime => write!(f, "DATETIME"),
226243
DateTimeField::Hour => write!(f, "HOUR"),
244+
DateTimeField::Hours => write!(f, "HOURS"),
227245
DateTimeField::Minute => write!(f, "MINUTE"),
246+
DateTimeField::Minutes => write!(f, "MINUTES"),
228247
DateTimeField::Second => write!(f, "SECOND"),
248+
DateTimeField::Seconds => write!(f, "SECONDS"),
229249
DateTimeField::Century => write!(f, "CENTURY"),
230250
DateTimeField::Decade => write!(f, "DECADE"),
231251
DateTimeField::Dow => write!(f, "DOW"),

src/keywords.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ define_keywords!(
234234
DAY,
235235
DAYOFWEEK,
236236
DAYOFYEAR,
237+
DAYS,
237238
DEALLOCATE,
238239
DEC,
239240
DECADE,
@@ -497,13 +498,15 @@ define_keywords!(
497498
MILLISECONDS,
498499
MIN,
499500
MINUTE,
501+
MINUTES,
500502
MINVALUE,
501503
MOD,
502504
MODE,
503505
MODIFIES,
504506
MODIFY,
505507
MODULE,
506508
MONTH,
509+
MONTHS,
507510
MSCK,
508511
MULTISET,
509512
MUTATION,
@@ -693,6 +696,7 @@ define_keywords!(
693696
SEARCH,
694697
SECOND,
695698
SECONDARY,
699+
SECONDS,
696700
SECRET,
697701
SECURITY,
698702
SEED,
@@ -861,6 +865,7 @@ define_keywords!(
861865
VOLATILE,
862866
WAREHOUSE,
863867
WEEK,
868+
WEEKS,
864869
WHEN,
865870
WHENEVER,
866871
WHERE,
@@ -875,6 +880,7 @@ define_keywords!(
875880
XML,
876881
XOR,
877882
YEAR,
883+
YEARS,
878884
ZONE,
879885
ZORDER
880886
);

src/parser/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2340,7 +2340,9 @@ impl<'a> Parser<'a> {
23402340
match &next_token.token {
23412341
Token::Word(w) => match w.keyword {
23422342
Keyword::YEAR => Ok(DateTimeField::Year),
2343+
Keyword::YEARS => Ok(DateTimeField::Years),
23432344
Keyword::MONTH => Ok(DateTimeField::Month),
2345+
Keyword::MONTHS => Ok(DateTimeField::Months),
23442346
Keyword::WEEK => {
23452347
let week_day = if dialect_of!(self is BigQueryDialect | GenericDialect)
23462348
&& self.consume_token(&Token::LParen)
@@ -2353,14 +2355,30 @@ impl<'a> Parser<'a> {
23532355
};
23542356
Ok(DateTimeField::Week(week_day))
23552357
}
2358+
Keyword::WEEKS => {
2359+
let week_day = if dialect_of!(self is BigQueryDialect | GenericDialect)
2360+
&& self.consume_token(&Token::LParen)
2361+
{
2362+
let week_day = self.parse_identifier()?;
2363+
self.expect_token(&Token::RParen)?;
2364+
Some(week_day)
2365+
} else {
2366+
None
2367+
};
2368+
Ok(DateTimeField::Weeks(week_day))
2369+
}
23562370
Keyword::DAY => Ok(DateTimeField::Day),
23572371
Keyword::DAYOFWEEK => Ok(DateTimeField::DayOfWeek),
23582372
Keyword::DAYOFYEAR => Ok(DateTimeField::DayOfYear),
2373+
Keyword::DAYS => Ok(DateTimeField::Days),
23592374
Keyword::DATE => Ok(DateTimeField::Date),
23602375
Keyword::DATETIME => Ok(DateTimeField::Datetime),
23612376
Keyword::HOUR => Ok(DateTimeField::Hour),
2377+
Keyword::HOURS => Ok(DateTimeField::Hours),
23622378
Keyword::MINUTE => Ok(DateTimeField::Minute),
2379+
Keyword::MINUTES => Ok(DateTimeField::Minutes),
23632380
Keyword::SECOND => Ok(DateTimeField::Second),
2381+
Keyword::SECONDS => Ok(DateTimeField::Seconds),
23642382
Keyword::CENTURY => Ok(DateTimeField::Century),
23652383
Keyword::DECADE => Ok(DateTimeField::Decade),
23662384
Keyword::DOY => Ok(DateTimeField::Doy),
@@ -2587,12 +2605,19 @@ impl<'a> Parser<'a> {
25872605
matches!(
25882606
word.keyword,
25892607
Keyword::YEAR
2608+
| Keyword::YEARS
25902609
| Keyword::MONTH
2610+
| Keyword::MONTHS
25912611
| Keyword::WEEK
2612+
| Keyword::WEEKS
25922613
| Keyword::DAY
2614+
| Keyword::DAYS
25932615
| Keyword::HOUR
2616+
| Keyword::HOURS
25942617
| Keyword::MINUTE
2618+
| Keyword::MINUTES
25952619
| Keyword::SECOND
2620+
| Keyword::SECONDS
25962621
| Keyword::CENTURY
25972622
| Keyword::DECADE
25982623
| Keyword::DOW

tests/sqlparser_common.rs

Lines changed: 88 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ mod test_utils;
5050
#[cfg(test)]
5151
use pretty_assertions::assert_eq;
5252
use sqlparser::ast::ColumnOption::Comment;
53+
use sqlparser::ast::DateTimeField::Seconds;
5354
use sqlparser::ast::Expr::{Identifier, UnaryOp};
5455
use sqlparser::ast::Value::Number;
5556
use sqlparser::test_utils::all_dialects_except;
@@ -5334,6 +5335,19 @@ fn parse_interval_all() {
53345335
expr_from_projection(only(&select.projection)),
53355336
);
53365337

5338+
let sql = "SELECT INTERVAL 5 DAYS";
5339+
let select = verified_only_select(sql);
5340+
assert_eq!(
5341+
&Expr::Interval(Interval {
5342+
value: Box::new(Expr::Value(number("5"))),
5343+
leading_field: Some(DateTimeField::Days),
5344+
leading_precision: None,
5345+
last_field: None,
5346+
fractional_seconds_precision: None,
5347+
}),
5348+
expr_from_projection(only(&select.projection)),
5349+
);
5350+
53375351
let sql = "SELECT INTERVAL '10' HOUR (1)";
53385352
let select = verified_only_select(sql);
53395353
assert_eq!(
@@ -5361,10 +5375,18 @@ fn parse_interval_all() {
53615375

53625376
verified_only_select("SELECT INTERVAL '1' YEAR");
53635377
verified_only_select("SELECT INTERVAL '1' MONTH");
5378+
verified_only_select("SELECT INTERVAL '1' WEEK");
53645379
verified_only_select("SELECT INTERVAL '1' DAY");
53655380
verified_only_select("SELECT INTERVAL '1' HOUR");
53665381
verified_only_select("SELECT INTERVAL '1' MINUTE");
53675382
verified_only_select("SELECT INTERVAL '1' SECOND");
5383+
verified_only_select("SELECT INTERVAL '1' YEARS");
5384+
verified_only_select("SELECT INTERVAL '1' MONTHS");
5385+
verified_only_select("SELECT INTERVAL '1' WEEKS");
5386+
verified_only_select("SELECT INTERVAL '1' DAYS");
5387+
verified_only_select("SELECT INTERVAL '1' HOURS");
5388+
verified_only_select("SELECT INTERVAL '1' MINUTES");
5389+
verified_only_select("SELECT INTERVAL '1' SECONDS");
53685390
verified_only_select("SELECT INTERVAL '1' YEAR TO MONTH");
53695391
verified_only_select("SELECT INTERVAL '1' DAY TO HOUR");
53705392
verified_only_select("SELECT INTERVAL '1' DAY TO MINUTE");
@@ -5374,10 +5396,21 @@ fn parse_interval_all() {
53745396
verified_only_select("SELECT INTERVAL '1' MINUTE TO SECOND");
53755397
verified_only_select("SELECT INTERVAL 1 YEAR");
53765398
verified_only_select("SELECT INTERVAL 1 MONTH");
5399+
verified_only_select("SELECT INTERVAL 1 WEEK");
53775400
verified_only_select("SELECT INTERVAL 1 DAY");
53785401
verified_only_select("SELECT INTERVAL 1 HOUR");
53795402
verified_only_select("SELECT INTERVAL 1 MINUTE");
53805403
verified_only_select("SELECT INTERVAL 1 SECOND");
5404+
verified_only_select("SELECT INTERVAL 1 YEARS");
5405+
verified_only_select("SELECT INTERVAL 1 MONTHS");
5406+
verified_only_select("SELECT INTERVAL 1 WEEKS");
5407+
verified_only_select("SELECT INTERVAL 1 DAYS");
5408+
verified_only_select("SELECT INTERVAL 1 HOURS");
5409+
verified_only_select("SELECT INTERVAL 1 MINUTES");
5410+
verified_only_select("SELECT INTERVAL 1 SECONDS");
5411+
verified_only_select(
5412+
"SELECT '2 years 15 months 100 weeks 99 hours 123456789 milliseconds'::INTERVAL",
5413+
);
53815414
}
53825415

53835416
#[test]
@@ -11282,16 +11315,12 @@ fn test_group_by_nothing() {
1128211315
#[test]
1128311316
fn test_extract_seconds_ok() {
1128411317
let dialects = all_dialects_where(|d| d.allow_extract_custom());
11285-
let stmt = dialects.verified_expr("EXTRACT(seconds FROM '2 seconds'::INTERVAL)");
11318+
let stmt = dialects.verified_expr("EXTRACT(SECONDS FROM '2 seconds'::INTERVAL)");
1128611319

1128711320
assert_eq!(
1128811321
stmt,
1128911322
Expr::Extract {
11290-
field: DateTimeField::Custom(Ident {
11291-
value: "seconds".to_string(),
11292-
quote_style: None,
11293-
span: Span::empty(),
11294-
}),
11323+
field: Seconds,
1129511324
syntax: ExtractSyntax::From,
1129611325
expr: Box::new(Expr::Cast {
1129711326
kind: CastKind::DoubleColon,
@@ -11302,7 +11331,59 @@ fn test_extract_seconds_ok() {
1130211331
format: None,
1130311332
}),
1130411333
}
11305-
)
11334+
);
11335+
11336+
let actual_ast = dialects
11337+
.parse_sql_statements("SELECT EXTRACT(seconds FROM '2 seconds'::INTERVAL)")
11338+
.unwrap();
11339+
11340+
let expected_ast = vec![Statement::Query(Box::new(Query {
11341+
with: None,
11342+
body: Box::new(SetExpr::Select(Box::new(Select {
11343+
select_token: AttachedToken::empty(),
11344+
distinct: None,
11345+
top: None,
11346+
top_before_distinct: false,
11347+
projection: vec![UnnamedExpr(Expr::Extract {
11348+
field: Seconds,
11349+
syntax: ExtractSyntax::From,
11350+
expr: Box::new(Expr::Cast {
11351+
kind: CastKind::DoubleColon,
11352+
expr: Box::new(Expr::Value(Value::SingleQuotedString(
11353+
"2 seconds".to_string(),
11354+
))),
11355+
data_type: DataType::Interval,
11356+
format: None,
11357+
}),
11358+
})],
11359+
into: None,
11360+
from: vec![],
11361+
lateral_views: vec![],
11362+
prewhere: None,
11363+
selection: None,
11364+
group_by: GroupByExpr::Expressions(vec![], vec![]),
11365+
cluster_by: vec![],
11366+
distribute_by: vec![],
11367+
sort_by: vec![],
11368+
having: None,
11369+
named_window: vec![],
11370+
qualify: None,
11371+
window_before_qualify: false,
11372+
value_table_mode: None,
11373+
connect_by: None,
11374+
}))),
11375+
order_by: None,
11376+
limit: None,
11377+
limit_by: vec![],
11378+
offset: None,
11379+
fetch: None,
11380+
locks: vec![],
11381+
for_clause: None,
11382+
settings: None,
11383+
format_clause: None,
11384+
}))];
11385+
11386+
assert_eq!(actual_ast, expected_ast);
1130611387
}
1130711388

1130811389
#[test]
@@ -11331,17 +11412,6 @@ fn test_extract_seconds_single_quote_ok() {
1133111412
)
1133211413
}
1133311414

11334-
#[test]
11335-
fn test_extract_seconds_err() {
11336-
let sql = "SELECT EXTRACT(seconds FROM '2 seconds'::INTERVAL)";
11337-
let dialects = all_dialects_except(|d| d.allow_extract_custom());
11338-
let err = dialects.parse_sql_statements(sql).unwrap_err();
11339-
assert_eq!(
11340-
err.to_string(),
11341-
"sql parser error: Expected: date/time field, found: seconds"
11342-
);
11343-
}
11344-
1134511415
#[test]
1134611416
fn test_extract_seconds_single_quote_err() {
1134711417
let sql = r#"SELECT EXTRACT('seconds' FROM '2 seconds'::INTERVAL)"#;

0 commit comments

Comments
 (0)