Skip to content

Commit 5324acb

Browse files
committed
Support interval literals
1 parent 1f87083 commit 5324acb

File tree

5 files changed

+276
-41
lines changed

5 files changed

+276
-41
lines changed

src/sqlast/mod.rs

+1-24
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub use self::query::{
2828
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableAlias, TableFactor,
2929
};
3030
pub use self::sqltype::SQLType;
31-
pub use self::value::Value;
31+
pub use self::value::{SQLDateTimeField, SQLIntervalQualifier, Value};
3232

3333
pub use self::sql_operator::SQLOperator;
3434

@@ -635,29 +635,6 @@ impl ToString for SQLFunction {
635635
}
636636
}
637637

638-
#[derive(Debug, Clone, PartialEq, Hash)]
639-
pub enum SQLDateTimeField {
640-
Year,
641-
Month,
642-
Day,
643-
Hour,
644-
Minute,
645-
Second,
646-
}
647-
648-
impl ToString for SQLDateTimeField {
649-
fn to_string(&self) -> String {
650-
match self {
651-
SQLDateTimeField::Year => "YEAR".to_string(),
652-
SQLDateTimeField::Month => "MONTH".to_string(),
653-
SQLDateTimeField::Day => "DAY".to_string(),
654-
SQLDateTimeField::Hour => "HOUR".to_string(),
655-
SQLDateTimeField::Minute => "MINUTE".to_string(),
656-
SQLDateTimeField::Second => "SECOND".to_string(),
657-
}
658-
}
659-
}
660-
661638
/// External table's available file format
662639
#[derive(Debug, Clone, PartialEq, Hash)]
663640
pub enum FileFormat {

src/sqlast/sqltype.rs

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ pub enum SQLType {
3939
Time,
4040
/// Timestamp
4141
Timestamp,
42+
/// Interval
43+
Interval,
4244
/// Regclass used in postgresql serial
4345
Regclass,
4446
/// Text
@@ -78,6 +80,7 @@ impl ToString for SQLType {
7880
SQLType::Date => "date".to_string(),
7981
SQLType::Time => "time".to_string(),
8082
SQLType::Timestamp => "timestamp".to_string(),
83+
SQLType::Interval => "interval".to_string(),
8184
SQLType::Regclass => "regclass".to_string(),
8285
SQLType::Text => "text".to_string(),
8386
SQLType::Bytea => "bytea".to_string(),

src/sqlast/value.rs

+87
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ pub enum Value {
2121
Time(String),
2222
/// Timestamp literals, which include both a date and time
2323
Timestamp(String),
24+
/// INTERVAL literals, e.g. INTERVAL '12:34.56' MINUTE TO SECOND (2)
25+
Interval {
26+
value: String,
27+
leading_field: SQLDateTimeField,
28+
leading_precision: Option<u64>,
29+
last_field: Option<SQLDateTimeField>,
30+
fractional_seconds_precision: Option<u64>,
31+
},
2432
/// NULL value in insert statements,
2533
Null,
2634
}
@@ -37,11 +45,90 @@ impl ToString for Value {
3745
Value::Date(v) => format!("DATE '{}'", escape_single_quote_string(v)),
3846
Value::Time(v) => format!("TIME '{}'", escape_single_quote_string(v)),
3947
Value::Timestamp(v) => format!("TIMESTAMP '{}'", escape_single_quote_string(v)),
48+
Value::Interval {
49+
value,
50+
leading_field: SQLDateTimeField::Second,
51+
leading_precision: Some(leading_precision),
52+
last_field,
53+
fractional_seconds_precision: Some(fractional_seconds_precision),
54+
} => {
55+
// When the leading field is SECOND, the parser guarantees that
56+
// the last field is None.
57+
assert!(last_field.is_none());
58+
format!(
59+
"INTERVAL '{}' SECOND ({}, {})",
60+
escape_single_quote_string(value),
61+
leading_precision,
62+
fractional_seconds_precision
63+
)
64+
}
65+
Value::Interval {
66+
value,
67+
leading_field,
68+
leading_precision,
69+
last_field,
70+
fractional_seconds_precision,
71+
} => {
72+
let mut s = format!(
73+
"INTERVAL '{}' {}",
74+
escape_single_quote_string(value),
75+
leading_field.to_string()
76+
);
77+
if let Some(leading_precision) = leading_precision {
78+
s += &format!(" ({})", leading_precision);
79+
}
80+
if let Some(last_field) = last_field {
81+
s += &format!(" TO {}", last_field.to_string());
82+
}
83+
if let Some(fractional_seconds_precision) = fractional_seconds_precision {
84+
s += &format!(" ({})", fractional_seconds_precision);
85+
}
86+
s
87+
}
4088
Value::Null => "NULL".to_string(),
4189
}
4290
}
4391
}
4492

93+
#[derive(Debug, Clone, PartialEq, Hash)]
94+
pub struct SQLIntervalQualifier {
95+
pub field: SQLDateTimeField,
96+
pub precision: Option<u64>,
97+
}
98+
99+
impl ToString for SQLIntervalQualifier {
100+
fn to_string(&self) -> String {
101+
let mut s = self.field.to_string();
102+
if let Some(precision) = self.precision {
103+
s += &format!(" ({})", precision);
104+
}
105+
s
106+
}
107+
}
108+
109+
#[derive(Debug, Clone, PartialEq, Hash)]
110+
pub enum SQLDateTimeField {
111+
Year,
112+
Month,
113+
Day,
114+
Hour,
115+
Minute,
116+
Second,
117+
}
118+
119+
impl ToString for SQLDateTimeField {
120+
fn to_string(&self) -> String {
121+
match self {
122+
SQLDateTimeField::Year => "YEAR".to_string(),
123+
SQLDateTimeField::Month => "MONTH".to_string(),
124+
SQLDateTimeField::Day => "DAY".to_string(),
125+
SQLDateTimeField::Hour => "HOUR".to_string(),
126+
SQLDateTimeField::Minute => "MINUTE".to_string(),
127+
SQLDateTimeField::Second => "SECOND".to_string(),
128+
}
129+
}
130+
}
131+
45132
fn escape_single_quote_string(s: &str) -> String {
46133
let mut escaped = String::new();
47134
for c in s.chars() {

src/sqlparser.rs

+90-14
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ impl Parser {
195195
"DATE" => Ok(ASTNode::SQLValue(Value::Date(self.parse_literal_string()?))),
196196
"EXISTS" => self.parse_exists_expression(),
197197
"EXTRACT" => self.parse_extract_expression(),
198+
"INTERVAL" => self.parse_literal_interval(),
198199
"NOT" => Ok(ASTNode::SQLUnary {
199200
operator: SQLOperator::Not,
200201
expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?),
@@ -425,20 +426,7 @@ impl Parser {
425426

426427
pub fn parse_extract_expression(&mut self) -> Result<ASTNode, ParserError> {
427428
self.expect_token(&Token::LParen)?;
428-
let tok = self.next_token();
429-
let field = if let Some(Token::SQLWord(ref k)) = tok {
430-
match k.keyword.as_ref() {
431-
"YEAR" => SQLDateTimeField::Year,
432-
"MONTH" => SQLDateTimeField::Month,
433-
"DAY" => SQLDateTimeField::Day,
434-
"HOUR" => SQLDateTimeField::Hour,
435-
"MINUTE" => SQLDateTimeField::Minute,
436-
"SECOND" => SQLDateTimeField::Second,
437-
_ => self.expected("Date/time field inside of EXTRACT function", tok)?,
438-
}
439-
} else {
440-
self.expected("Date/time field inside of EXTRACT function", tok)?
441-
};
429+
let field = self.parse_date_time_field()?;
442430
self.expect_keyword("FROM")?;
443431
let expr = self.parse_expr()?;
444432
self.expect_token(&Token::RParen)?;
@@ -448,6 +436,90 @@ impl Parser {
448436
})
449437
}
450438

439+
// This function parses date/time fields for both the EXTRACT function-like
440+
// operator and interval qualifiers. EXTRACT supports a wider set of
441+
// date/time fields than interval qualifiers, so this function may need to
442+
// be split in two.
443+
pub fn parse_date_time_field(&mut self) -> Result<SQLDateTimeField, ParserError> {
444+
let tok = self.next_token();
445+
if let Some(Token::SQLWord(ref k)) = tok {
446+
match k.keyword.as_ref() {
447+
"YEAR" => Ok(SQLDateTimeField::Year),
448+
"MONTH" => Ok(SQLDateTimeField::Month),
449+
"DAY" => Ok(SQLDateTimeField::Day),
450+
"HOUR" => Ok(SQLDateTimeField::Hour),
451+
"MINUTE" => Ok(SQLDateTimeField::Minute),
452+
"SECOND" => Ok(SQLDateTimeField::Second),
453+
_ => self.expected("date/time field", tok)?,
454+
}
455+
} else {
456+
self.expected("date/time field", tok)?
457+
}
458+
}
459+
460+
/// Parse an INTERVAL literal.
461+
///
462+
/// Some syntactically valid intervals:
463+
///
464+
/// 1. `INTERVAL '1' DAY`
465+
/// 2. `INTERVAL '1-1' YEAR TO MONTH`
466+
/// 3. `INTERVAL '1' SECOND`
467+
/// 4. `INTERVAL '1:1:1.1' HOUR (5) TO SECOND (5)`
468+
/// 5. `INTERVAL '1.1' SECOND (2, 2)`
469+
/// 6. `INTERVAL '1:1' HOUR (5) TO MINUTE (5)`
470+
///
471+
/// Note that we do not currently attempt to parse the quoted value.
472+
pub fn parse_literal_interval(&mut self) -> Result<ASTNode, ParserError> {
473+
// The SQL standard allows an optional sign before the value string, but
474+
// it is not clear if any implementations support that syntax, so we
475+
// don't currently try to parse it. (The sign can instead be included
476+
// inside the value string.)
477+
478+
// The first token in an interval is a string literal which specifies
479+
// the duration of the interval.
480+
let value = self.parse_literal_string()?;
481+
482+
// Following the string literal is a qualifier which indicates the units
483+
// of the duration specified in the string literal.
484+
//
485+
// Note that PostgreSQL allows omitting the qualifier, but we currently
486+
// require at least the leading field, in accordance with the ANSI spec.
487+
let leading_field = self.parse_date_time_field()?;
488+
489+
let (leading_precision, last_field, fsec_precision) =
490+
if leading_field == SQLDateTimeField::Second {
491+
// SQL mandates special syntax for `SECOND TO SECOND` literals.
492+
// Instead of
493+
// `SECOND [(<leading precision>)] TO SECOND[(<fractional seconds precision>)]`
494+
// one must use the special format:
495+
// `SECOND [( <leading precision> [ , <fractional seconds precision>] )]`
496+
let last_field = None;
497+
let (leading_precision, fsec_precision) = self.parse_optional_precision_scale()?;
498+
(leading_precision, last_field, fsec_precision)
499+
} else {
500+
let leading_precision = self.parse_optional_precision()?;
501+
if self.parse_keyword("TO") {
502+
let last_field = Some(self.parse_date_time_field()?);
503+
let fsec_precision = if last_field == Some(SQLDateTimeField::Second) {
504+
self.parse_optional_precision()?
505+
} else {
506+
None
507+
};
508+
(leading_precision, last_field, fsec_precision)
509+
} else {
510+
(leading_precision, None, None)
511+
}
512+
};
513+
514+
Ok(ASTNode::SQLValue(Value::Interval {
515+
value,
516+
leading_field,
517+
leading_precision,
518+
last_field,
519+
fractional_seconds_precision: fsec_precision,
520+
}))
521+
}
522+
451523
/// Parse an operator following an expression
452524
pub fn parse_infix(&mut self, expr: ASTNode, precedence: u8) -> Result<ASTNode, ParserError> {
453525
debug!("parsing infix");
@@ -1182,6 +1254,10 @@ impl Parser {
11821254
}
11831255
Ok(SQLType::Time)
11841256
}
1257+
// Interval types can be followed by a complicated interval
1258+
// qualifier that we don't currently support. See
1259+
// parse_interval_literal for a taste.
1260+
"INTERVAL" => Ok(SQLType::Interval),
11851261
"REGCLASS" => Ok(SQLType::Regclass),
11861262
"TEXT" => {
11871263
if self.consume_token(&Token::LBracket) {

0 commit comments

Comments
 (0)