Skip to content

Support INSERT INTO ... DEFAULT VALUES ... #1036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ pub enum Statement {
/// Overwrite (Hive)
overwrite: bool,
/// A SQL query that specifies what to insert
source: Box<Query>,
source: Option<Box<Query>>,
/// partitioned insert (Hive)
partitioned: Option<Vec<Expr>>,
/// Columns defined after PARTITION
Expand Down Expand Up @@ -2241,7 +2241,14 @@ impl fmt::Display for Statement {
if !after_columns.is_empty() {
write!(f, "({}) ", display_comma_separated(after_columns))?;
}
write!(f, "{source}")?;

if let Some(source) = source {
write!(f, "{source}")?;
}

if source.is_none() && columns.is_empty() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if source.is_none() && columns.is_empty() {
if source.is_none() {

I think we should never print INSERT INTO my_table(my_column);, which is syntactically invalid.

INSERT INTO my_table(my_column) DEFAULT VALUES; should successfully round-trip through sqlparser, since it's a logical error, but not a syntax error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review @lovasoa.

INSERT INTO my_table(my_column) DEFAULT VALUES; should successfully round-trip through sqlparser, since it's a logical error, but not a syntax error.

I thought about this a bit and double checked the SQL grammar and implementation in Postgres and both seem to make INSERT INTO my_table(my_column) DEFAULT VALUES; invalid at the syntax level. I think it'd make sense for sqlparser to make this scenario a syntax error as well, but I could be missing something.

I also tested a statement out with Postgres in a DB fiddle and confirmed that it raises a syntax error:

-- error: syntax error at or near "DEFAULT"
INSERT INTO test_table (test_column) DEFAULT VALUES;

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right about postgres, but it's not a syntax error in sqlite.

And insert into t(x); is a syntax error everywhere anyway, so I think we should never emit that.

Copy link
Contributor Author

@CDThomas CDThomas Nov 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah interesting, I was not aware of the behaviour in SQLite.

I added some coverage for syntax errors with DEFAULT VALUES here. Do you think that behaviour looks reasonable?

write!(f, "DEFAULT VALUES")?;
}

if let Some(on) = on {
write!(f, "{on}")?;
Expand Down
37 changes: 25 additions & 12 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7090,21 +7090,23 @@ impl<'a> Parser<'a> {
let table = self.parse_keyword(Keyword::TABLE);
let table_name = self.parse_object_name()?;
let is_mysql = dialect_of!(self is MySqlDialect);
let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?;

let partitioned = if self.parse_keyword(Keyword::PARTITION) {
self.expect_token(&Token::LParen)?;
let r = Some(self.parse_comma_separated(Parser::parse_expr)?);
self.expect_token(&Token::RParen)?;
r
} else {
None
};
let (columns, partitioned, after_columns, source) =
if self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) {
(vec![], None, vec![], None)
} else {
let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?;

// Hive allows you to specify columns after partitions as well if you want.
let after_columns = self.parse_parenthesized_column_list(Optional, false)?;
let partitioned = self.parse_insert_partition()?;

// Hive allows you to specify columns after partitions as well if you want.
let after_columns = self.parse_parenthesized_column_list(Optional, false)?;

let source = Some(Box::new(self.parse_query()?));

(columns, partitioned, after_columns, source)
};

let source = Box::new(self.parse_query()?);
let on = if self.parse_keyword(Keyword::ON) {
if self.parse_keyword(Keyword::CONFLICT) {
let conflict_target =
Expand Down Expand Up @@ -7175,6 +7177,17 @@ impl<'a> Parser<'a> {
}
}

pub fn parse_insert_partition(&mut self) -> Result<Option<Vec<Expr>>, ParserError> {
if self.parse_keyword(Keyword::PARTITION) {
self.expect_token(&Token::LParen)?;
let partition_cols = Some(self.parse_comma_separated(Parser::parse_expr)?);
self.expect_token(&Token::RParen)?;
Ok(partition_cols)
} else {
Ok(None)
}
}

pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
let table = self.parse_table_and_joins()?;
self.expect_keyword(Keyword::SET)?;
Expand Down
109 changes: 107 additions & 2 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ fn parse_insert_values() {
Statement::Insert {
table_name,
columns,
source,
source: Some(source),
..
} => {
assert_eq!(table_name.to_string(), expected_table_name);
assert_eq!(columns.len(), expected_columns.len());
for (index, column) in columns.iter().enumerate() {
assert_eq!(column, &Ident::new(expected_columns[index].clone()));
}
match &*source.body {
match *source.body {
SetExpr::Values(Values { rows, .. }) => {
assert_eq!(rows.as_slice(), expected_rows)
}
Expand All @@ -107,6 +107,111 @@ fn parse_insert_values() {
verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1)");
}

#[test]
fn parse_insert_default_values() {
let insert_with_default_values = verified_stmt("INSERT INTO test_table DEFAULT VALUES");

match insert_with_default_values {
Statement::Insert {
after_columns,
columns,
on,
partitioned,
returning,
source,
table_name,
..
} => {
assert_eq!(columns, vec![]);
assert_eq!(after_columns, vec![]);
assert_eq!(on, None);
assert_eq!(partitioned, None);
assert_eq!(returning, None);
assert_eq!(source, None);
assert_eq!(table_name, ObjectName(vec!["test_table".into()]));
}
_ => unreachable!(),
}

let insert_with_default_values_and_returning =
verified_stmt("INSERT INTO test_table DEFAULT VALUES RETURNING test_column");

match insert_with_default_values_and_returning {
Statement::Insert {
after_columns,
columns,
on,
partitioned,
returning,
source,
table_name,
..
} => {
assert_eq!(after_columns, vec![]);
assert_eq!(columns, vec![]);
assert_eq!(on, None);
assert_eq!(partitioned, None);
assert!(returning.is_some());
assert_eq!(source, None);
assert_eq!(table_name, ObjectName(vec!["test_table".into()]));
}
_ => unreachable!(),
}

let insert_with_default_values_and_on_conflict =
verified_stmt("INSERT INTO test_table DEFAULT VALUES ON CONFLICT DO NOTHING");

match insert_with_default_values_and_on_conflict {
Statement::Insert {
after_columns,
columns,
on,
partitioned,
returning,
source,
table_name,
..
} => {
assert_eq!(after_columns, vec![]);
assert_eq!(columns, vec![]);
assert!(on.is_some());
assert_eq!(partitioned, None);
assert_eq!(returning, None);
assert_eq!(source, None);
assert_eq!(table_name, ObjectName(vec!["test_table".into()]));
}
_ => unreachable!(),
}

let insert_with_columns_and_default_values = "INSERT INTO test_table (test_col) DEFAULT VALUES";
assert_eq!(
ParserError::ParserError(
"Expected SELECT, VALUES, or a subquery in the query body, found: DEFAULT".to_string()
),
parse_sql_statements(insert_with_columns_and_default_values).unwrap_err()
);

let insert_with_default_values_and_hive_after_columns =
"INSERT INTO test_table DEFAULT VALUES (some_column)";
assert_eq!(
ParserError::ParserError("Expected end of statement, found: (".to_string()),
parse_sql_statements(insert_with_default_values_and_hive_after_columns).unwrap_err()
);

let insert_with_default_values_and_hive_partition =
"INSERT INTO test_table DEFAULT VALUES PARTITION (some_column)";
assert_eq!(
ParserError::ParserError("Expected end of statement, found: PARTITION".to_string()),
parse_sql_statements(insert_with_default_values_and_hive_partition).unwrap_err()
);

let insert_with_default_values_and_values_list = "INSERT INTO test_table DEFAULT VALUES (1)";
assert_eq!(
ParserError::ParserError("Expected end of statement, found: (".to_string()),
parse_sql_statements(insert_with_default_values_and_values_list).unwrap_err()
);
}

#[test]
fn parse_insert_sqlite() {
let dialect = SQLiteDialect {};
Expand Down
16 changes: 8 additions & 8 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,7 @@ fn parse_simple_insert() {
assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns);
assert!(on.is_none());
assert_eq!(
Box::new(Query {
Some(Box::new(Query {
with: None,
body: Box::new(SetExpr::Values(Values {
explicit_row: false,
Expand All @@ -964,7 +964,7 @@ fn parse_simple_insert() {
offset: None,
fetch: None,
locks: vec![],
}),
})),
source
);
}
Expand All @@ -990,7 +990,7 @@ fn parse_ignore_insert() {
assert!(on.is_none());
assert!(ignore);
assert_eq!(
Box::new(Query {
Some(Box::new(Query {
with: None,
body: Box::new(SetExpr::Values(Values {
explicit_row: false,
Expand All @@ -1005,7 +1005,7 @@ fn parse_ignore_insert() {
offset: None,
fetch: None,
locks: vec![]
}),
})),
source
);
}
Expand All @@ -1029,7 +1029,7 @@ fn parse_empty_row_insert() {
assert!(columns.is_empty());
assert!(on.is_none());
assert_eq!(
Box::new(Query {
Some(Box::new(Query {
with: None,
body: Box::new(SetExpr::Values(Values {
explicit_row: false,
Expand All @@ -1041,7 +1041,7 @@ fn parse_empty_row_insert() {
offset: None,
fetch: None,
locks: vec![],
}),
})),
source
);
}
Expand Down Expand Up @@ -1077,7 +1077,7 @@ fn parse_insert_with_on_duplicate_update() {
columns
);
assert_eq!(
Box::new(Query {
Some(Box::new(Query {
with: None,
body: Box::new(SetExpr::Values(Values {
explicit_row: false,
Expand All @@ -1100,7 +1100,7 @@ fn parse_insert_with_on_duplicate_update() {
offset: None,
fetch: None,
locks: vec![],
}),
})),
source
);
assert_eq!(
Expand Down
2 changes: 1 addition & 1 deletion tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,7 @@ fn parse_prepare() {
Statement::Insert {
table_name,
columns,
source,
source: Some(source),
..
} => {
assert_eq!(table_name.to_string(), "customers");
Expand Down