mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-20 06:00:15 +00:00
Add support for FROM <filename>
, DELIMITER
, and CSV HEADER
options for COPY
command (#409)
* add additional options for copy command Signed-off-by: password <rbalajis25@gmail.com> * cargo fmt Signed-off-by: password <rbalajis25@gmail.com>
This commit is contained in:
parent
33d4f27bfc
commit
2614576dbf
4 changed files with 118 additions and 5 deletions
|
@ -670,6 +670,12 @@ pub enum Statement {
|
||||||
columns: Vec<Ident>,
|
columns: Vec<Ident>,
|
||||||
/// VALUES a vector of values to be copied
|
/// VALUES a vector of values to be copied
|
||||||
values: Vec<Option<String>>,
|
values: Vec<Option<String>>,
|
||||||
|
/// file name of the data to be copied from
|
||||||
|
filename: Option<Ident>,
|
||||||
|
/// delimiter character
|
||||||
|
delimiter: Option<Ident>,
|
||||||
|
/// CSV HEADER
|
||||||
|
csv_header: bool,
|
||||||
},
|
},
|
||||||
/// UPDATE
|
/// UPDATE
|
||||||
Update {
|
Update {
|
||||||
|
@ -1043,13 +1049,28 @@ impl fmt::Display for Statement {
|
||||||
table_name,
|
table_name,
|
||||||
columns,
|
columns,
|
||||||
values,
|
values,
|
||||||
|
delimiter,
|
||||||
|
filename,
|
||||||
|
csv_header,
|
||||||
} => {
|
} => {
|
||||||
write!(f, "COPY {}", table_name)?;
|
write!(f, "COPY {}", table_name)?;
|
||||||
if !columns.is_empty() {
|
if !columns.is_empty() {
|
||||||
write!(f, " ({})", display_comma_separated(columns))?;
|
write!(f, " ({})", display_comma_separated(columns))?;
|
||||||
}
|
}
|
||||||
write!(f, " FROM stdin; ")?;
|
|
||||||
|
if let Some(name) = filename {
|
||||||
|
write!(f, " FROM {}", name)?;
|
||||||
|
} else {
|
||||||
|
write!(f, " FROM stdin ")?;
|
||||||
|
}
|
||||||
|
if let Some(delimiter) = delimiter {
|
||||||
|
write!(f, " DELIMITER {}", delimiter)?;
|
||||||
|
}
|
||||||
|
if *csv_header {
|
||||||
|
write!(f, " CSV HEADER")?;
|
||||||
|
}
|
||||||
if !values.is_empty() {
|
if !values.is_empty() {
|
||||||
|
write!(f, ";")?;
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
let mut delim = "";
|
let mut delim = "";
|
||||||
for v in values {
|
for v in values {
|
||||||
|
@ -1062,7 +1083,10 @@ impl fmt::Display for Statement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(f, "\n\\.")
|
if filename.is_none() {
|
||||||
|
write!(f, "\n\\.")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Statement::Update {
|
Statement::Update {
|
||||||
table,
|
table,
|
||||||
|
|
|
@ -173,6 +173,7 @@ define_keywords!(
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
DELETE,
|
DELETE,
|
||||||
DELIMITED,
|
DELIMITED,
|
||||||
|
DELIMITER,
|
||||||
DENSE_RANK,
|
DENSE_RANK,
|
||||||
DEREF,
|
DEREF,
|
||||||
DESC,
|
DESC,
|
||||||
|
|
|
@ -2087,13 +2087,44 @@ impl<'a> Parser<'a> {
|
||||||
pub fn parse_copy(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_copy(&mut self) -> Result<Statement, ParserError> {
|
||||||
let table_name = self.parse_object_name()?;
|
let table_name = self.parse_object_name()?;
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let columns = self.parse_parenthesized_column_list(Optional)?;
|
||||||
self.expect_keywords(&[Keyword::FROM, Keyword::STDIN])?;
|
self.expect_keywords(&[Keyword::FROM])?;
|
||||||
self.expect_token(&Token::SemiColon)?;
|
let mut filename = None;
|
||||||
let values = self.parse_tsv();
|
// check whether data has to be copied form table or std in.
|
||||||
|
if !self.parse_keyword(Keyword::STDIN) {
|
||||||
|
filename = Some(self.parse_identifier()?)
|
||||||
|
}
|
||||||
|
// parse copy options.
|
||||||
|
let mut delimiter = None;
|
||||||
|
let mut csv_header = false;
|
||||||
|
loop {
|
||||||
|
if let Some(keyword) = self.parse_one_of_keywords(&[Keyword::DELIMITER, Keyword::CSV]) {
|
||||||
|
match keyword {
|
||||||
|
Keyword::DELIMITER => {
|
||||||
|
delimiter = Some(self.parse_identifier()?);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Keyword::CSV => {
|
||||||
|
self.expect_keyword(Keyword::HEADER)?;
|
||||||
|
csv_header = true
|
||||||
|
}
|
||||||
|
_ => unreachable!("something wrong while parsing copy statment :("),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// copy the values from stdin if there is no file to be copied from.
|
||||||
|
let mut values = vec![];
|
||||||
|
if filename.is_none() {
|
||||||
|
self.expect_token(&Token::SemiColon)?;
|
||||||
|
values = self.parse_tsv();
|
||||||
|
}
|
||||||
Ok(Statement::Copy {
|
Ok(Statement::Copy {
|
||||||
table_name,
|
table_name,
|
||||||
columns,
|
columns,
|
||||||
values,
|
values,
|
||||||
|
filename,
|
||||||
|
delimiter,
|
||||||
|
csv_header,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -404,6 +404,63 @@ PHP ₱ USD $
|
||||||
//assert_eq!(sql, ast.to_string());
|
//assert_eq!(sql, ast.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_copy() {
|
||||||
|
let stmt = pg().verified_stmt("COPY users FROM 'data.csv'");
|
||||||
|
assert_eq!(
|
||||||
|
stmt,
|
||||||
|
Statement::Copy {
|
||||||
|
table_name: ObjectName(vec!["users".into()]),
|
||||||
|
columns: vec![],
|
||||||
|
filename: Some(Ident {
|
||||||
|
value: "data.csv".to_string(),
|
||||||
|
quote_style: Some('\'')
|
||||||
|
}),
|
||||||
|
values: vec![],
|
||||||
|
delimiter: None,
|
||||||
|
csv_header: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let stmt = pg().verified_stmt("COPY users FROM 'data.csv' DELIMITER ','");
|
||||||
|
assert_eq!(
|
||||||
|
stmt,
|
||||||
|
Statement::Copy {
|
||||||
|
table_name: ObjectName(vec!["users".into()]),
|
||||||
|
columns: vec![],
|
||||||
|
filename: Some(Ident {
|
||||||
|
value: "data.csv".to_string(),
|
||||||
|
quote_style: Some('\'')
|
||||||
|
}),
|
||||||
|
values: vec![],
|
||||||
|
delimiter: Some(Ident {
|
||||||
|
value: ",".to_string(),
|
||||||
|
quote_style: Some('\'')
|
||||||
|
}),
|
||||||
|
csv_header: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let stmt = pg().verified_stmt("COPY users FROM 'data.csv' DELIMITER ',' CSV HEADER");
|
||||||
|
assert_eq!(
|
||||||
|
stmt,
|
||||||
|
Statement::Copy {
|
||||||
|
table_name: ObjectName(vec!["users".into()]),
|
||||||
|
columns: vec![],
|
||||||
|
filename: Some(Ident {
|
||||||
|
value: "data.csv".to_string(),
|
||||||
|
quote_style: Some('\'')
|
||||||
|
}),
|
||||||
|
values: vec![],
|
||||||
|
delimiter: Some(Ident {
|
||||||
|
value: ",".to_string(),
|
||||||
|
quote_style: Some('\'')
|
||||||
|
}),
|
||||||
|
csv_header: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_set() {
|
fn parse_set() {
|
||||||
let stmt = pg_and_generic().verified_stmt("SET a = b");
|
let stmt = pg_and_generic().verified_stmt("SET a = b");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue