From 267821100f28a44c9421e8952cc2274710d18264 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 6 Feb 2022 15:47:47 +0800 Subject: [PATCH] Insert Default (#223) * feat: insert default * test: insert default with returning * fix!: insert statement skip empty values row Breaking Changes: - `InsertStatement::exprs` method skip pushing empty values row into vector * Update test cases --- src/backend/mysql/query.rs | 4 ++ src/backend/query_builder.rs | 63 ++++++++++++++++------------ src/query/insert.rs | 20 +++++---- tests/mysql/query.rs | 21 ++++++++++ tests/postgres/query.rs | 79 +++++++++++++++++++++++------------- tests/sqlite/query.rs | 21 ++++++++++ 6 files changed, 143 insertions(+), 65 deletions(-) diff --git a/src/backend/mysql/query.rs b/src/backend/mysql/query.rs index 5adc07fd34..b0a1eff9c3 100644 --- a/src/backend/mysql/query.rs +++ b/src/backend/mysql/query.rs @@ -56,4 +56,8 @@ impl QueryBuilder for MysqlQueryBuilder { ) { // MySQL doesn't support declaring materialization in SQL for with query. } + + fn insert_default_keyword(&self) -> &str { + "VALUES (DEFAULT)" + } } diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 550f8e3ff1..b253588428 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -22,39 +22,43 @@ pub trait QueryBuilder: QuotedBuilder { write!(sql, " ").unwrap(); } - write!(sql, "(").unwrap(); - insert.columns.iter().fold(true, |first, col| { - if !first { - write!(sql, ", ").unwrap() - } - col.prepare(sql, self.quote()); - false - }); - write!(sql, ")").unwrap(); + if insert.columns.is_empty() && insert.source.is_none() { + write!(sql, "{}", self.insert_default_keyword()).unwrap(); + } else { + write!(sql, "(").unwrap(); + insert.columns.iter().fold(true, |first, col| { + if !first { + write!(sql, ", ").unwrap() + } + col.prepare(sql, self.quote()); + false + }); + write!(sql, ")").unwrap(); - if let Some(source) = &insert.source { - write!(sql, " ").unwrap(); - match source { - InsertValueSource::Values(values) => { - write!(sql, "VALUES ").unwrap(); - values.iter().fold(true, |first, row| { - if !first { - write!(sql, ", ").unwrap() - } - write!(sql, "(").unwrap(); - row.iter().fold(true, |first, col| { + if let Some(source) = &insert.source { + write!(sql, " ").unwrap(); + match source { + InsertValueSource::Values(values) => { + write!(sql, "VALUES ").unwrap(); + values.iter().fold(true, |first, row| { if !first { write!(sql, ", ").unwrap() } - self.prepare_simple_expr(col, sql, collector); + write!(sql, "(").unwrap(); + row.iter().fold(true, |first, col| { + if !first { + write!(sql, ", ").unwrap() + } + self.prepare_simple_expr(col, sql, collector); + false + }); + write!(sql, ")").unwrap(); false }); - write!(sql, ")").unwrap(); - false - }); - } - InsertValueSource::Select(select_query) => { - self.prepare_select_statement(select_query.deref(), sql, collector); + } + InsertValueSource::Select(select_query) => { + self.prepare_select_statement(select_query.deref(), sql, collector); + } } } } @@ -1164,6 +1168,11 @@ pub trait QueryBuilder: QuotedBuilder { fn char_length_function(&self) -> &str { "CHAR_LENGTH" } + + /// The keywords for insert default statement (insert without explicit values) + fn insert_default_keyword(&self) -> &str { + "DEFAULT VALUES" + } } impl SubQueryStatement { diff --git a/src/query/insert.rs b/src/query/insert.rs index fb7d5bb6a5..33675b76e3 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -231,17 +231,19 @@ impl InsertStatement { val_len: values.len(), }); } - let values_source = if let Some(InsertValueSource::Values(values)) = &mut self.source { - values - } else { - self.source = Some(InsertValueSource::Values(Default::default())); - if let Some(InsertValueSource::Values(values)) = &mut self.source { + if !values.is_empty() { + let values_source = if let Some(InsertValueSource::Values(values)) = &mut self.source { values } else { - unreachable!(); - } - }; - values_source.push(values); + self.source = Some(InsertValueSource::Values(Default::default())); + if let Some(InsertValueSource::Values(values)) = &mut self.source { + values + } else { + unreachable!(); + } + }; + values_source.push(values); + } Ok(self) } diff --git a/tests/mysql/query.rs b/tests/mysql/query.rs index 413a113af9..640b4fd097 100644 --- a/tests/mysql/query.rs +++ b/tests/mysql/query.rs @@ -967,6 +967,27 @@ fn insert_5() { ); } +#[test] +fn insert_6() { + assert_eq!( + Query::insert() + .into_table(Glyph::Table) + .to_string(MysqlQueryBuilder), + "INSERT INTO `glyph` VALUES (DEFAULT)" + ); +} + +#[test] +fn insert_7() { + assert_eq!( + Query::insert() + .into_table(Glyph::Table) + .returning_col(Glyph::Id) + .to_string(MysqlQueryBuilder), + "INSERT INTO `glyph` VALUES (DEFAULT)" + ); +} + #[test] fn insert_from_select() { assert_eq!( diff --git a/tests/postgres/query.rs b/tests/postgres/query.rs index dfa0d34185..3a4085dec2 100644 --- a/tests/postgres/query.rs +++ b/tests/postgres/query.rs @@ -936,35 +936,6 @@ fn insert_5() { ); } -#[test] -fn insert_from_select() { - assert_eq!( - Query::insert() - .into_table(Glyph::Table) - .columns(vec![Glyph::Aspect, Glyph::Image]) - .select_from( - Query::select() - .column(Glyph::Aspect) - .column(Glyph::Image) - .from(Glyph::Table) - .conditions( - true, - |x| { - x.and_where(Expr::col(Glyph::Image).like("%")); - }, - |x| { - x.and_where(Expr::col(Glyph::Id).eq(6)); - }, - ) - .to_owned() - ) - .unwrap() - .to_owned() - .to_string(PostgresQueryBuilder), - r#"INSERT INTO "glyph" ("aspect", "image") SELECT "aspect", "image" FROM "glyph" WHERE "image" LIKE '%'"# - ); -} - #[test] fn insert_6() -> sea_query::error::Result<()> { let select = SelectStatement::new() @@ -999,6 +970,56 @@ fn insert_6() -> sea_query::error::Result<()> { Ok(()) } +#[test] +fn insert_7() { + assert_eq!( + Query::insert() + .into_table(Glyph::Table) + .to_string(PostgresQueryBuilder), + "INSERT INTO \"glyph\" DEFAULT VALUES" + ); +} + +#[test] +fn insert_8() { + assert_eq!( + Query::insert() + .into_table(Glyph::Table) + .returning_col(Glyph::Id) + .to_string(PostgresQueryBuilder), + "INSERT INTO \"glyph\" DEFAULT VALUES RETURNING \"id\"" + ); +} + +#[test] +fn insert_from_select() { + assert_eq!( + Query::insert() + .into_table(Glyph::Table) + .columns(vec![Glyph::Aspect, Glyph::Image]) + .select_from( + Query::select() + .column(Glyph::Aspect) + .column(Glyph::Image) + .from(Glyph::Table) + .conditions( + true, + |x| { + x.and_where(Expr::col(Glyph::Image).like("%")); + }, + |x| { + x.and_where(Expr::col(Glyph::Id).eq(6)); + }, + ) + .to_owned() + ) + .unwrap() + .to_owned() + .to_string(PostgresQueryBuilder), + r#"INSERT INTO "glyph" ("aspect", "image") SELECT "aspect", "image" FROM "glyph" WHERE "image" LIKE '%'"# + ); +} + #[test] fn update_1() { assert_eq!( diff --git a/tests/sqlite/query.rs b/tests/sqlite/query.rs index 11d7668649..c78d8d2b6e 100644 --- a/tests/sqlite/query.rs +++ b/tests/sqlite/query.rs @@ -965,6 +965,27 @@ fn insert_5() { ); } +#[test] +fn insert_6() { + assert_eq!( + Query::insert() + .into_table(Glyph::Table) + .to_string(SqliteQueryBuilder), + r#"INSERT INTO "glyph" DEFAULT VALUES"# + ); +} + +#[test] +fn insert_7() { + assert_eq!( + Query::insert() + .into_table(Glyph::Table) + .returning_col(Glyph::Id) + .to_string(SqliteQueryBuilder), + r#"INSERT INTO "glyph" DEFAULT VALUES RETURNING "id""# + ); +} + #[test] fn update_1() { assert_eq!(