Skip to content

Commit 6f42706

Browse files
committed
Implemet support for hours part in interval expr
1 parent bf8cfb3 commit 6f42706

2 files changed

Lines changed: 54 additions & 6 deletions

File tree

crates/gitql-parser/src/parse_interval.rs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub(crate) fn parse_interval_expression(
3737
Ok(Box::new(IntervalExpr::new(interval)))
3838
}
3939

40+
/// Parse string intl Interval expression.
41+
/// Ref: https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT
4042
fn parse_interval_literal(
4143
interval_str: &str,
4244
location: SourceLocation,
@@ -54,7 +56,8 @@ fn parse_interval_literal(
5456
let mut has_years = false;
5557
let mut has_months = false;
5658
let mut has_days = false;
57-
let mut has_hours = false;
59+
let mut has_any_time_part = false;
60+
let mut has_hours: bool = false;
5861
let mut has_minutes = false;
5962
let mut has_seconds = false;
6063

@@ -79,28 +82,38 @@ fn parse_interval_literal(
7982
}
8083

8184
// Parse the unit
82-
let maybe_unit = tokens[position];
83-
if matches!(maybe_unit, "year" | "years") {
85+
let mut maybe_unit = tokens[position];
86+
let unit_lower = &maybe_unit.to_lowercase();
87+
maybe_unit = &unit_lower.as_str();
88+
89+
if matches!(maybe_unit, "y" | "year" | "years") {
8490
check_interval_value_and_unit(&mut has_years, value, maybe_unit, location)?;
8591
interval.years = value;
8692
position += 1;
8793
continue;
8894
}
8995

90-
if matches!(maybe_unit, "mon" | "mons" | "months") {
96+
if matches!(maybe_unit, "m" | "mon" | "mons" | "months") {
9197
check_interval_value_and_unit(&mut has_months, value, maybe_unit, location)?;
9298
interval.months = value;
9399
position += 1;
94100
continue;
95101
}
96102

97-
if matches!(maybe_unit, "day" | "days") {
103+
if matches!(maybe_unit, "d" | "day" | "days") {
98104
check_interval_value_and_unit(&mut has_days, value, maybe_unit, location)?;
99105
interval.days = value;
100106
position += 1;
101107
continue;
102108
}
103109

110+
if matches!(maybe_unit, "h" | "hour" | "hours") {
111+
check_interval_value_and_unit(&mut has_any_time_part, value, maybe_unit, location)?;
112+
interval.hours = value;
113+
position += 1;
114+
continue;
115+
}
116+
104117
return Err(Diagnostic::error(&format!(
105118
"Invalid input syntax for interval unit `{maybe_unit}`",
106119
))
@@ -113,6 +126,14 @@ fn parse_interval_literal(
113126

114127
// Parse Seconds, Minutes or Hours
115128
if token.contains(':') {
129+
if has_any_time_part {
130+
return Err(
131+
Diagnostic::error("You can't have time value twice in same interval")
132+
.with_location(location)
133+
.as_boxed(),
134+
);
135+
}
136+
116137
let time_parts: Vec<&str> = token.split(':').collect();
117138
if time_parts.len() != 3 && time_parts.len() != 2 {
118139
return Err(Diagnostic::error("Invalid input syntax for type interval")
@@ -208,3 +229,30 @@ fn check_interval_value_and_unit(
208229
.with_location(location)
209230
.as_boxed())
210231
}
232+
233+
#[cfg(test)]
234+
mod tests {
235+
use super::*;
236+
237+
#[test]
238+
fn valid_hours() {
239+
let inputs = ["1 h", "1 hour", "1 hours", "1:00:00"];
240+
for input in inputs {
241+
let parse_result = parse_interval_literal(input, SourceLocation::default());
242+
assert!(parse_result.is_ok());
243+
244+
if let Ok(interval) = parse_result {
245+
assert_eq!(interval.hours, 1);
246+
}
247+
}
248+
}
249+
250+
#[test]
251+
fn invalid_time() {
252+
let inputs = ["1 h 1:00:00", "1 h 1 h"];
253+
for input in inputs {
254+
let parse_result = parse_interval_literal(input, SourceLocation::default());
255+
assert!(parse_result.is_err());
256+
}
257+
}
258+
}

crates/gitql-parser/src/token.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ impl Display for TokenKind {
356356
}
357357
}
358358

359-
#[derive(Copy, Clone)]
359+
#[derive(Default, Copy, Clone)]
360360
pub struct SourceLocation {
361361
pub line_start: u32,
362362
pub line_end: u32,

0 commit comments

Comments
 (0)