mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-27 07:59:11 +00:00
Add parsing of timestamp values
This commit is contained in:
parent
9ab5c1358d
commit
7803063ece
2 changed files with 114 additions and 23 deletions
|
@ -17,6 +17,11 @@
|
||||||
use chrono::{NaiveDate,
|
use chrono::{NaiveDate,
|
||||||
NaiveDateTime,
|
NaiveDateTime,
|
||||||
NaiveTime,
|
NaiveTime,
|
||||||
|
offset::{FixedOffset,
|
||||||
|
TimeZone,
|
||||||
|
},
|
||||||
|
DateTime,
|
||||||
|
Utc,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// SQL Abstract Syntax Tree (AST)
|
/// SQL Abstract Syntax Tree (AST)
|
||||||
|
@ -133,8 +138,10 @@ pub enum Value{
|
||||||
Date(NaiveDate),
|
Date(NaiveDate),
|
||||||
// Time
|
// Time
|
||||||
Time(NaiveTime),
|
Time(NaiveTime),
|
||||||
/// Timestamp
|
/// Date and time
|
||||||
DateTime(NaiveDateTime),
|
DateTime(NaiveDateTime),
|
||||||
|
/// Timstamp with time zone
|
||||||
|
Timestamp(DateTime<FixedOffset>),
|
||||||
/// NULL value in insert statements,
|
/// NULL value in insert statements,
|
||||||
Null,
|
Null,
|
||||||
}
|
}
|
||||||
|
|
128
src/sqlparser.rs
128
src/sqlparser.rs
|
@ -20,6 +20,11 @@ use super::sqltokenizer::*;
|
||||||
use chrono::{NaiveDate,
|
use chrono::{NaiveDate,
|
||||||
NaiveDateTime,
|
NaiveDateTime,
|
||||||
NaiveTime,
|
NaiveTime,
|
||||||
|
offset::{FixedOffset,
|
||||||
|
TimeZone,
|
||||||
|
},
|
||||||
|
DateTime,
|
||||||
|
Utc,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -509,7 +514,16 @@ impl Parser {
|
||||||
Token::Number(ref n) => match n.parse::<i64>() {
|
Token::Number(ref n) => match n.parse::<i64>() {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
if let Some(Token::Minus) = self.peek_token(){
|
if let Some(Token::Minus) = self.peek_token(){
|
||||||
self.parse_date_or_timestamp(n)
|
if let Ok(timestamp) = self.parse_timestamp_with_timezone(){
|
||||||
|
Ok(Value::Timestamp(timestamp))
|
||||||
|
}else{
|
||||||
|
println!("unable to parse timestamp with year, trying with time zone :{:?}", self.peek_token());
|
||||||
|
if let Ok(date_time) = self.parse_timestamp_with_year(n){
|
||||||
|
Ok(Value::DateTime(date_time))
|
||||||
|
}else{
|
||||||
|
parser_err!(format!("expecting timestamp, but found {:?}", self.peek_token()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
Ok(Value::Long(n))
|
Ok(Value::Long(n))
|
||||||
}
|
}
|
||||||
|
@ -535,6 +549,16 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a literal integer/long
|
||||||
|
pub fn parse_literal_double(&mut self) -> Result<f64, ParserError> {
|
||||||
|
match self.next_token() {
|
||||||
|
Some(Token::Number(s)) => s.parse::<f64>().map_err(|e| {
|
||||||
|
ParserError::ParserError(format!("Could not parse '{}' as i64: {}", s, e))
|
||||||
|
}),
|
||||||
|
other => parser_err!(format!("Expected literal int, found {:?}", other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a literal string
|
/// Parse a literal string
|
||||||
pub fn parse_literal_string(&mut self) -> Result<String, ParserError> {
|
pub fn parse_literal_string(&mut self) -> Result<String, ParserError> {
|
||||||
match self.next_token() {
|
match self.next_token() {
|
||||||
|
@ -543,23 +567,56 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_date_or_timestamp(&mut self, year: i64) -> Result<Value, ParserError> {
|
pub fn parse_timestamp(&mut self) -> Result<NaiveDateTime, ParserError> {
|
||||||
|
let year = self.parse_literal_int()?;
|
||||||
|
self.parse_timestamp_with_year(year)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_timestamp_with_timezone(&mut self) -> Result<DateTime<FixedOffset>, ParserError> {
|
||||||
|
let timestamp = self.parse_timestamp()?;
|
||||||
|
let tz_offset = self.parse_timezone_offset()?;
|
||||||
|
let offset = FixedOffset::east(tz_offset as i32 * 3600);
|
||||||
|
Ok(DateTime::from_utc(timestamp, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_timezone_offset(&mut self) -> Result<i8, ParserError> {
|
||||||
|
match self.next_token(){
|
||||||
|
Some(Token::Plus) => {
|
||||||
|
let n = self.parse_literal_int()?;
|
||||||
|
Ok(n as i8)
|
||||||
|
}
|
||||||
|
Some(Token::Minus) => {
|
||||||
|
let n = self.parse_literal_int()?;
|
||||||
|
Ok(-n as i8)
|
||||||
|
}
|
||||||
|
other => parser_err!(format!("Expecting `+` or `-` in timezone, but found {:?}", other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn parse_timestamp_with_year(&mut self, year: i64) -> Result<NaiveDateTime, ParserError> {
|
||||||
|
let date = self.parse_date(year)?;
|
||||||
|
if let Ok(time) = self.parse_time(){
|
||||||
|
Ok(NaiveDateTime::new(date, time))
|
||||||
|
}else{
|
||||||
|
parser_err!(format!("Expecting time after date, but found {:?}", self.peek_token()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_date(&mut self, year: i64) -> Result<NaiveDate, ParserError> {
|
||||||
if let Ok(true) = self.consume_token(&Token::Minus){
|
if let Ok(true) = self.consume_token(&Token::Minus){
|
||||||
let month = self.parse_literal_int()?;
|
let month = self.parse_literal_int()?;
|
||||||
if let Ok(true) = self.consume_token(&Token::Minus){
|
if let Ok(true) = self.consume_token(&Token::Minus){
|
||||||
let day = self.parse_literal_int()?;
|
let day = self.parse_literal_int()?;
|
||||||
let date = NaiveDate::from_ymd(year as i32, month as u32, day as u32);
|
let date = NaiveDate::from_ymd(year as i32, month as u32, day as u32);
|
||||||
if let Ok(time) = self.parse_time(){
|
Ok(date)
|
||||||
Ok(Value::DateTime(NaiveDateTime::new(date, time)))
|
|
||||||
}else{
|
|
||||||
Ok(Value::Date(date))
|
|
||||||
}
|
|
||||||
}else{
|
}else{
|
||||||
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token()))
|
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token()))
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token()))
|
parser_err!(format!("Expecting `-` for date separator, found {:?}", self.peek_token()))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_time(&mut self) -> Result<NaiveTime, ParserError> {
|
pub fn parse_time(&mut self) -> Result<NaiveTime, ParserError> {
|
||||||
|
@ -567,8 +624,17 @@ impl Parser {
|
||||||
self.consume_token(&Token::Colon)?;
|
self.consume_token(&Token::Colon)?;
|
||||||
let min = self.parse_literal_int()?;
|
let min = self.parse_literal_int()?;
|
||||||
self.consume_token(&Token::Colon)?;
|
self.consume_token(&Token::Colon)?;
|
||||||
let sec = self.parse_literal_int()?;
|
let sec = self.parse_literal_double()?;
|
||||||
Ok(NaiveTime::from_hms(hour as u32, min as u32, sec as u32))
|
println!("sec: {}", sec);
|
||||||
|
let ms = (sec.fract() * 1000.0).round();
|
||||||
|
println!("ms: {:?}", ms);
|
||||||
|
if let Ok(true) = self.consume_token(&Token::Period){
|
||||||
|
println!("milliseconds here");
|
||||||
|
let ms = self.parse_literal_int()?;
|
||||||
|
Ok(NaiveTime::from_hms_milli(hour as u32, min as u32, sec as u32, ms as u32))
|
||||||
|
}else{
|
||||||
|
Ok(NaiveTime::from_hms(hour as u32, min as u32, sec as u32))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a SQL datatype (in the context of a CREATE TABLE statement for example)
|
/// Parse a SQL datatype (in the context of a CREATE TABLE statement for example)
|
||||||
|
@ -1405,19 +1471,19 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_copy_example(){
|
fn parse_copy_example(){
|
||||||
let sql = String::from("
|
let sql = String::from("
|
||||||
COPY public.actor (actor_id, first_name, last_name, last_update) FROM stdin;
|
COPY public.actor (actor_id, first_name, last_name, last_update, value) FROM stdin;
|
||||||
1 PENELOPE GUINESS 2006-02-15 09:34:33
|
1 PENELOPE GUINESS 2006-02-15 09:34:33 0.11111
|
||||||
2 NICK WAHLBERG 2006-02-15 09:34:33
|
2 NICK WAHLBERG 2006-02-15 09:34:33 0.22222
|
||||||
3 ED CHASE 2006-02-15 09:34:33
|
3 ED CHASE 2006-02-15 09:34:33 0.312323
|
||||||
4 JENNIFER DAVIS 2006-02-15 09:34:33
|
4 JENNIFER DAVIS 2006-02-15 09:34:33 0.3232
|
||||||
5 JOHNNY LOLLOBRIGIDA 2006-02-15 09:34:33
|
5 JOHNNY LOLLOBRIGIDA 2006-02-15 09:34:33 1.343
|
||||||
6 BETTE NICHOLSON 2006-02-15 09:34:33
|
6 BETTE NICHOLSON 2006-02-15 09:34:33 5.0
|
||||||
7 GRACE MOSTEL 2006-02-15 09:34:33
|
7 GRACE MOSTEL 2006-02-15 09:34:33 6.0
|
||||||
8 MATTHEW JOHANSSON 2006-02-15 09:34:33
|
8 MATTHEW JOHANSSON 2006-02-15 09:34:33 7.0
|
||||||
9 JOE SWANK 2006-02-15 09:34:33
|
9 JOE SWANK 2006-02-15 09:34:33 8.0
|
||||||
10 CHRISTIAN GABLE 2006-02-15 09:34:33
|
10 CHRISTIAN GABLE 2006-02-15 09:34:33 9.1
|
||||||
11 ZERO CAGE 2006-02-15 09:34:33
|
11 ZERO CAGE 2006-02-15 09:34:33 10.001
|
||||||
12 KARL BERRY 2006-02-15 09:34:33
|
12 KARL BERRY 2017-11-02 19:15:42.308637+08 11.001
|
||||||
\\.
|
\\.
|
||||||
");
|
");
|
||||||
let mut parser = parser(&sql);
|
let mut parser = parser(&sql);
|
||||||
|
@ -1426,6 +1492,24 @@ mod tests {
|
||||||
assert!(ast.is_ok());
|
assert!(ast.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_timestamps_example(){
|
||||||
|
let sql = "2016-02-15 09:43:33";
|
||||||
|
let mut parser = parser(&sql);
|
||||||
|
let ast = parser.parse_timestamp();
|
||||||
|
println!("ast: {:?}", ast);
|
||||||
|
assert!(ast.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_timestamps_with_millis_example(){
|
||||||
|
let sql = "2017-11-02 19:15:42.308637";
|
||||||
|
let mut parser = parser(&sql);
|
||||||
|
let ast = parser.parse_timestamp();
|
||||||
|
println!("ast: {:?}", ast);
|
||||||
|
assert!(ast.is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_scalar_function_in_projection() {
|
fn parse_scalar_function_in_projection() {
|
||||||
let sql = String::from("SELECT sqrt(id) FROM foo");
|
let sql = String::from("SELECT sqrt(id) FROM foo");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue