mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-31 03:07:20 +00:00
Support VALUES clauses in FROM
VALUES clauses are not just valid in INSERT statements. They're also valid (basically) anywhere table expressions are accepted, like in FROM clauses.
This commit is contained in:
parent
a3aaa49a7e
commit
9420070a0d
4 changed files with 83 additions and 39 deletions
|
@ -20,10 +20,12 @@ mod sql_operator;
|
|||
mod sqltype;
|
||||
mod value;
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use self::ddl::{AlterTableOperation, TableConstraint};
|
||||
pub use self::query::{
|
||||
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
|
||||
SQLSelectItem, SQLSetExpr, SQLSetOperator, TableFactor,
|
||||
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableFactor,
|
||||
};
|
||||
pub use self::sqltype::SQLType;
|
||||
pub use self::value::Value;
|
||||
|
@ -31,9 +33,14 @@ pub use self::value::Value;
|
|||
pub use self::sql_operator::SQLOperator;
|
||||
|
||||
/// Like `vec.join(", ")`, but for any types implementing ToString.
|
||||
fn comma_separated_string<T: ToString>(vec: &[T]) -> String {
|
||||
vec.iter()
|
||||
.map(T::to_string)
|
||||
fn comma_separated_string<I>(iter: I) -> String
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Deref,
|
||||
<I::Item as Deref>::Target: ToString,
|
||||
{
|
||||
iter.into_iter()
|
||||
.map(|t| t.deref().to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
@ -336,7 +343,7 @@ pub enum SQLStatement {
|
|||
/// COLUMNS
|
||||
columns: Vec<SQLIdent>,
|
||||
/// VALUES (vector of rows to insert)
|
||||
values: Vec<Vec<ASTNode>>,
|
||||
values: SQLValues,
|
||||
},
|
||||
SQLCopy {
|
||||
/// TABLE
|
||||
|
@ -408,16 +415,7 @@ impl ToString for SQLStatement {
|
|||
if !columns.is_empty() {
|
||||
s += &format!(" ({})", columns.join(", "));
|
||||
}
|
||||
if !values.is_empty() {
|
||||
s += &format!(
|
||||
" VALUES({})",
|
||||
values
|
||||
.iter()
|
||||
.map(|row| comma_separated_string(row))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
s += &format!(" {}", values.to_string());
|
||||
s
|
||||
}
|
||||
SQLStatement::SQLCopy {
|
||||
|
@ -519,7 +517,7 @@ impl ToString for SQLStatement {
|
|||
"DROP {}{} {}{}",
|
||||
object_type.to_string(),
|
||||
if *if_exists { " IF EXISTS" } else { "" },
|
||||
comma_separated_string(&names),
|
||||
comma_separated_string(names),
|
||||
if *cascade { " CASCADE" } else { "" },
|
||||
),
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ pub enum SQLSetExpr {
|
|||
left: Box<SQLSetExpr>,
|
||||
right: Box<SQLSetExpr>,
|
||||
},
|
||||
// TODO: ANSI SQL supports `TABLE` and `VALUES` here.
|
||||
Values(SQLValues),
|
||||
// TODO: ANSI SQL supports `TABLE` here.
|
||||
}
|
||||
|
||||
impl ToString for SQLSetExpr {
|
||||
|
@ -66,6 +67,7 @@ impl ToString for SQLSetExpr {
|
|||
match self {
|
||||
SQLSetExpr::Select(s) => s.to_string(),
|
||||
SQLSetExpr::Query(q) => format!("({})", q.to_string()),
|
||||
SQLSetExpr::Values(v) => v.to_string(),
|
||||
SQLSetExpr::SetOperation {
|
||||
left,
|
||||
right,
|
||||
|
@ -364,3 +366,16 @@ impl ToString for Fetch {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SQLValues(pub Vec<Vec<ASTNode>>);
|
||||
|
||||
impl ToString for SQLValues {
|
||||
fn to_string(&self) -> String {
|
||||
let rows = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|row| format!("({})", comma_separated_string(row)));
|
||||
format!("VALUES {}", comma_separated_string(rows))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1352,6 +1352,8 @@ impl Parser {
|
|||
let subquery = self.parse_query()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
SQLSetExpr::Query(Box::new(subquery))
|
||||
} else if self.parse_keyword("VALUES") {
|
||||
SQLSetExpr::Values(self.parse_values()?)
|
||||
} else {
|
||||
return self.expected("SELECT or a subquery in the query body", self.peek_token());
|
||||
};
|
||||
|
@ -1564,13 +1566,11 @@ impl Parser {
|
|||
let table_name = self.parse_object_name()?;
|
||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
||||
self.expect_keyword("VALUES")?;
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let values = self.parse_expr_list()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
let values = self.parse_values()?;
|
||||
Ok(SQLStatement::SQLInsert {
|
||||
table_name,
|
||||
columns,
|
||||
values: vec![values],
|
||||
values,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1688,6 +1688,20 @@ impl Parser {
|
|||
quantity,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_values(&mut self) -> Result<SQLValues, ParserError> {
|
||||
let mut values = vec![];
|
||||
loop {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
values.push(self.parse_expr_list()?);
|
||||
self.expect_token(&Token::RParen)?;
|
||||
match self.peek_token() {
|
||||
Some(Token::Comma) => self.next_token(),
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
Ok(SQLValues(values))
|
||||
}
|
||||
}
|
||||
|
||||
impl SQLWord {
|
||||
|
|
|
@ -14,23 +14,40 @@ use sqlparser::test_utils::{all_dialects, expr_from_projection, only};
|
|||
|
||||
#[test]
|
||||
fn parse_insert_values() {
|
||||
let sql = "INSERT INTO customer VALUES(1, 2, 3)";
|
||||
check_one(sql, "customer", vec![]);
|
||||
let row = vec![
|
||||
ASTNode::SQLValue(Value::Long(1)),
|
||||
ASTNode::SQLValue(Value::Long(2)),
|
||||
ASTNode::SQLValue(Value::Long(3)),
|
||||
];
|
||||
let rows1 = vec![row.clone()];
|
||||
let rows2 = vec![row.clone(), row];
|
||||
|
||||
let sql = "INSERT INTO public.customer VALUES(1, 2, 3)";
|
||||
check_one(sql, "public.customer", vec![]);
|
||||
let sql = "INSERT INTO customer VALUES (1, 2, 3)";
|
||||
check_one(sql, "customer", &[], &rows1);
|
||||
|
||||
let sql = "INSERT INTO db.public.customer VALUES(1, 2, 3)";
|
||||
check_one(sql, "db.public.customer", vec![]);
|
||||
let sql = "INSERT INTO customer VALUES (1, 2, 3), (1, 2, 3)";
|
||||
check_one(sql, "customer", &[], &rows2);
|
||||
|
||||
let sql = "INSERT INTO public.customer (id, name, active) VALUES(1, 2, 3)";
|
||||
let sql = "INSERT INTO public.customer VALUES (1, 2, 3)";
|
||||
check_one(sql, "public.customer", &[], &rows1);
|
||||
|
||||
let sql = "INSERT INTO db.public.customer VALUES (1, 2, 3)";
|
||||
check_one(sql, "db.public.customer", &[], &rows1);
|
||||
|
||||
let sql = "INSERT INTO public.customer (id, name, active) VALUES (1, 2, 3)";
|
||||
check_one(
|
||||
sql,
|
||||
"public.customer",
|
||||
vec!["id".to_string(), "name".to_string(), "active".to_string()],
|
||||
&["id".to_string(), "name".to_string(), "active".to_string()],
|
||||
&rows1,
|
||||
);
|
||||
|
||||
fn check_one(sql: &str, expected_table_name: &str, expected_columns: Vec<String>) {
|
||||
fn check_one(
|
||||
sql: &str,
|
||||
expected_table_name: &str,
|
||||
expected_columns: &[String],
|
||||
expected_rows: &[Vec<ASTNode>],
|
||||
) {
|
||||
match verified_stmt(sql) {
|
||||
SQLStatement::SQLInsert {
|
||||
table_name,
|
||||
|
@ -40,14 +57,7 @@ fn parse_insert_values() {
|
|||
} => {
|
||||
assert_eq!(table_name.to_string(), expected_table_name);
|
||||
assert_eq!(columns, expected_columns);
|
||||
assert_eq!(
|
||||
vec![vec![
|
||||
ASTNode::SQLValue(Value::Long(1)),
|
||||
ASTNode::SQLValue(Value::Long(2)),
|
||||
ASTNode::SQLValue(Value::Long(3))
|
||||
]],
|
||||
values
|
||||
);
|
||||
assert_eq!(values.0.as_slice(), expected_rows);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -1383,6 +1393,13 @@ fn parse_union() {
|
|||
verified_stmt("SELECT foo FROM tab UNION SELECT bar FROM TAB");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_values() {
|
||||
verified_stmt("SELECT * FROM (VALUES (1), (2), (3))");
|
||||
verified_stmt("SELECT * FROM (VALUES (1), (2), (3)), (VALUES (1, 2, 3))");
|
||||
verified_stmt("SELECT * FROM (VALUES (1)) UNION VALUES (1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_statements() {
|
||||
fn test_with(sql1: &str, sql2_kw: &str, sql2_rest: &str) {
|
||||
|
@ -1416,7 +1433,7 @@ fn parse_multiple_statements() {
|
|||
" cte AS (SELECT 1 AS s) SELECT bar",
|
||||
);
|
||||
test_with("DELETE FROM foo", "SELECT", " bar");
|
||||
test_with("INSERT INTO foo VALUES(1)", "SELECT", " bar");
|
||||
test_with("INSERT INTO foo VALUES (1)", "SELECT", " bar");
|
||||
test_with("CREATE TABLE foo (baz int)", "SELECT", " bar");
|
||||
// Make sure that empty statements do not cause an error:
|
||||
let res = parse_sql_statements(";;");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue