Skip to content

Commit 5729338

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

File tree

4 files changed

+139
-18
lines changed

4 files changed

+139
-18
lines changed

src/ast/value.rs

+20
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

+6
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

+25
Original file line numberDiff line numberDiff line change
@@ -2276,7 +2276,9 @@ impl<'a> Parser<'a> {
22762276
match &next_token.token {
22772277
Token::Word(w) => match w.keyword {
22782278
Keyword::YEAR => Ok(DateTimeField::Year),
2279+
Keyword::YEARS => Ok(DateTimeField::Years),
22792280
Keyword::MONTH => Ok(DateTimeField::Month),
2281+
Keyword::MONTHS => Ok(DateTimeField::Months),
22802282
Keyword::WEEK => {
22812283
let week_day = if dialect_of!(self is BigQueryDialect | GenericDialect)
22822284
&& self.consume_token(&Token::LParen)
@@ -2289,14 +2291,30 @@ impl<'a> Parser<'a> {
22892291
};
22902292
Ok(DateTimeField::Week(week_day))
22912293
}
2294+
Keyword::WEEKS => {
2295+
let week_day = if dialect_of!(self is BigQueryDialect | GenericDialect)
2296+
&& self.consume_token(&Token::LParen)
2297+
{
2298+
let week_day = self.parse_identifier(false)?;
2299+
self.expect_token(&Token::RParen)?;
2300+
Some(week_day)
2301+
} else {
2302+
None
2303+
};
2304+
Ok(DateTimeField::Weeks(week_day))
2305+
}
22922306
Keyword::DAY => Ok(DateTimeField::Day),
22932307
Keyword::DAYOFWEEK => Ok(DateTimeField::DayOfWeek),
22942308
Keyword::DAYOFYEAR => Ok(DateTimeField::DayOfYear),
2309+
Keyword::DAYS => Ok(DateTimeField::Days),
22952310
Keyword::DATE => Ok(DateTimeField::Date),
22962311
Keyword::DATETIME => Ok(DateTimeField::Datetime),
22972312
Keyword::HOUR => Ok(DateTimeField::Hour),
2313+
Keyword::HOURS => Ok(DateTimeField::Hours),
22982314
Keyword::MINUTE => Ok(DateTimeField::Minute),
2315+
Keyword::MINUTES => Ok(DateTimeField::Minutes),
22992316
Keyword::SECOND => Ok(DateTimeField::Second),
2317+
Keyword::SECONDS => Ok(DateTimeField::Seconds),
23002318
Keyword::CENTURY => Ok(DateTimeField::Century),
23012319
Keyword::DECADE => Ok(DateTimeField::Decade),
23022320
Keyword::DOY => Ok(DateTimeField::Doy),
@@ -2523,12 +2541,19 @@ impl<'a> Parser<'a> {
25232541
matches!(
25242542
word.keyword,
25252543
Keyword::YEAR
2544+
| Keyword::YEARS
25262545
| Keyword::MONTH
2546+
| Keyword::MONTHS
25272547
| Keyword::WEEK
2548+
| Keyword::WEEKS
25282549
| Keyword::DAY
2550+
| Keyword::DAYS
25292551
| Keyword::HOUR
2552+
| Keyword::HOURS
25302553
| Keyword::MINUTE
2554+
| Keyword::MINUTES
25312555
| Keyword::SECOND
2556+
| Keyword::SECONDS
25322557
| Keyword::CENTURY
25332558
| Keyword::DECADE
25342559
| Keyword::DOW

tests/sqlparser_common.rs

+88-18
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;
@@ -5227,6 +5228,19 @@ fn parse_interval_all() {
52275228
expr_from_projection(only(&select.projection)),
52285229
);
52295230

5231+
let sql = "SELECT INTERVAL 5 DAYS";
5232+
let select = verified_only_select(sql);
5233+
assert_eq!(
5234+
&Expr::Interval(Interval {
5235+
value: Box::new(Expr::Value(number("5"))),
5236+
leading_field: Some(DateTimeField::Days),
5237+
leading_precision: None,
5238+
last_field: None,
5239+
fractional_seconds_precision: None,
5240+
}),
5241+
expr_from_projection(only(&select.projection)),
5242+
);
5243+
52305244
let sql = "SELECT INTERVAL '10' HOUR (1)";
52315245
let select = verified_only_select(sql);
52325246
assert_eq!(
@@ -5254,10 +5268,18 @@ fn parse_interval_all() {
52545268

52555269
verified_only_select("SELECT INTERVAL '1' YEAR");
52565270
verified_only_select("SELECT INTERVAL '1' MONTH");
5271+
verified_only_select("SELECT INTERVAL '1' WEEK");
52575272
verified_only_select("SELECT INTERVAL '1' DAY");
52585273
verified_only_select("SELECT INTERVAL '1' HOUR");
52595274
verified_only_select("SELECT INTERVAL '1' MINUTE");
52605275
verified_only_select("SELECT INTERVAL '1' SECOND");
5276+
verified_only_select("SELECT INTERVAL '1' YEARS");
5277+
verified_only_select("SELECT INTERVAL '1' MONTHS");
5278+
verified_only_select("SELECT INTERVAL '1' WEEKS");
5279+
verified_only_select("SELECT INTERVAL '1' DAYS");
5280+
verified_only_select("SELECT INTERVAL '1' HOURS");
5281+
verified_only_select("SELECT INTERVAL '1' MINUTES");
5282+
verified_only_select("SELECT INTERVAL '1' SECONDS");
52615283
verified_only_select("SELECT INTERVAL '1' YEAR TO MONTH");
52625284
verified_only_select("SELECT INTERVAL '1' DAY TO HOUR");
52635285
verified_only_select("SELECT INTERVAL '1' DAY TO MINUTE");
@@ -5267,10 +5289,21 @@ fn parse_interval_all() {
52675289
verified_only_select("SELECT INTERVAL '1' MINUTE TO SECOND");
52685290
verified_only_select("SELECT INTERVAL 1 YEAR");
52695291
verified_only_select("SELECT INTERVAL 1 MONTH");
5292+
verified_only_select("SELECT INTERVAL 1 WEEK");
52705293
verified_only_select("SELECT INTERVAL 1 DAY");
52715294
verified_only_select("SELECT INTERVAL 1 HOUR");
52725295
verified_only_select("SELECT INTERVAL 1 MINUTE");
52735296
verified_only_select("SELECT INTERVAL 1 SECOND");
5297+
verified_only_select("SELECT INTERVAL 1 YEARS");
5298+
verified_only_select("SELECT INTERVAL 1 MONTHS");
5299+
verified_only_select("SELECT INTERVAL 1 WEEKS");
5300+
verified_only_select("SELECT INTERVAL 1 DAYS");
5301+
verified_only_select("SELECT INTERVAL 1 HOURS");
5302+
verified_only_select("SELECT INTERVAL 1 MINUTES");
5303+
verified_only_select("SELECT INTERVAL 1 SECONDS");
5304+
verified_only_select(
5305+
"SELECT '2 years 15 months 100 weeks 99 hours 123456789 milliseconds'::INTERVAL",
5306+
);
52745307
}
52755308

52765309
#[test]
@@ -11175,16 +11208,12 @@ fn test_group_by_nothing() {
1117511208
#[test]
1117611209
fn test_extract_seconds_ok() {
1117711210
let dialects = all_dialects_where(|d| d.allow_extract_custom());
11178-
let stmt = dialects.verified_expr("EXTRACT(seconds FROM '2 seconds'::INTERVAL)");
11211+
let stmt = dialects.verified_expr("EXTRACT(SECONDS FROM '2 seconds'::INTERVAL)");
1117911212

1118011213
assert_eq!(
1118111214
stmt,
1118211215
Expr::Extract {
11183-
field: DateTimeField::Custom(Ident {
11184-
value: "seconds".to_string(),
11185-
quote_style: None,
11186-
span: Span::empty(),
11187-
}),
11216+
field: Seconds,
1118811217
syntax: ExtractSyntax::From,
1118911218
expr: Box::new(Expr::Cast {
1119011219
kind: CastKind::DoubleColon,
@@ -11195,7 +11224,59 @@ fn test_extract_seconds_ok() {
1119511224
format: None,
1119611225
}),
1119711226
}
11198-
)
11227+
);
11228+
11229+
let actual_ast = dialects
11230+
.parse_sql_statements("SELECT EXTRACT(seconds FROM '2 seconds'::INTERVAL)")
11231+
.unwrap();
11232+
11233+
let expected_ast = vec![Statement::Query(Box::new(Query {
11234+
with: None,
11235+
body: Box::new(SetExpr::Select(Box::new(Select {
11236+
select_token: AttachedToken::empty(),
11237+
distinct: None,
11238+
top: None,
11239+
top_before_distinct: false,
11240+
projection: vec![UnnamedExpr(Expr::Extract {
11241+
field: Seconds,
11242+
syntax: ExtractSyntax::From,
11243+
expr: Box::new(Expr::Cast {
11244+
kind: CastKind::DoubleColon,
11245+
expr: Box::new(Expr::Value(Value::SingleQuotedString(
11246+
"2 seconds".to_string(),
11247+
))),
11248+
data_type: DataType::Interval,
11249+
format: None,
11250+
}),
11251+
})],
11252+
into: None,
11253+
from: vec![],
11254+
lateral_views: vec![],
11255+
prewhere: None,
11256+
selection: None,
11257+
group_by: GroupByExpr::Expressions(vec![], vec![]),
11258+
cluster_by: vec![],
11259+
distribute_by: vec![],
11260+
sort_by: vec![],
11261+
having: None,
11262+
named_window: vec![],
11263+
qualify: None,
11264+
window_before_qualify: false,
11265+
value_table_mode: None,
11266+
connect_by: None,
11267+
}))),
11268+
order_by: None,
11269+
limit: None,
11270+
limit_by: vec![],
11271+
offset: None,
11272+
fetch: None,
11273+
locks: vec![],
11274+
for_clause: None,
11275+
settings: None,
11276+
format_clause: None,
11277+
}))];
11278+
11279+
assert_eq!(actual_ast, expected_ast);
1119911280
}
1120011281

1120111282
#[test]
@@ -11224,17 +11305,6 @@ fn test_extract_seconds_single_quote_ok() {
1122411305
)
1122511306
}
1122611307

11227-
#[test]
11228-
fn test_extract_seconds_err() {
11229-
let sql = "SELECT EXTRACT(seconds FROM '2 seconds'::INTERVAL)";
11230-
let dialects = all_dialects_except(|d| d.allow_extract_custom());
11231-
let err = dialects.parse_sql_statements(sql).unwrap_err();
11232-
assert_eq!(
11233-
err.to_string(),
11234-
"sql parser error: Expected: date/time field, found: seconds"
11235-
);
11236-
}
11237-
1123811308
#[test]
1123911309
fn test_extract_seconds_single_quote_err() {
1124011310
let sql = r#"SELECT EXTRACT('seconds' FROM '2 seconds'::INTERVAL)"#;

0 commit comments

Comments
 (0)