diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs index 84f996989..9a79a5a3d 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs @@ -131,7 +131,7 @@ impl ToSqlString for ast::TriggerCmdInsert { impl ToSqlString for ast::Upsert { fn to_sql_string(&self, context: &C) -> String { format!( - "ON CONFLICT {}{}{}", + "ON CONFLICT{}{}{}", self.index.as_ref().map_or("".to_string(), |index| format!( "{} ", index.to_sql_string(context) @@ -167,10 +167,10 @@ impl ToSqlString for ast::UpsertIndex { impl ToSqlString for ast::UpsertDo { fn to_sql_string(&self, context: &C) -> String { match self { - Self::Nothing => "NOTHING".to_string(), + Self::Nothing => "DO NOTHING".to_string(), Self::Set { sets, where_clause } => { format!( - "UPDATE SET {}{}", + "DO UPDATE SET {}{}", sets.iter() .map(|set| set.to_sql_string(context)) .collect::>() diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs new file mode 100644 index 000000000..5b20d8cd4 --- /dev/null +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs @@ -0,0 +1,150 @@ +use crate::{ast, to_sql_string::ToSqlString}; + +impl ToSqlString for ast::Insert { + fn to_sql_string(&self, context: &C) -> String { + format!( + "{}INSERT {}INTO {} {}{}{}", + self.with.as_ref().map_or("".to_string(), |with| format!( + "{} ", + with.to_sql_string(context) + )), + self.or_conflict.map_or("".to_string(), |conflict| format!( + "OR {} ", + conflict.to_sql_string(context) + )), + self.tbl_name.to_sql_string(context), + self.columns + .as_ref() + .map_or("".to_string(), |col_names| format!( + "({}) ", + col_names + .iter() + .map(|name| name.0.clone()) + .collect::>() + .join(", ") + )), + self.body.to_sql_string(context), + self.returning + .as_ref() + .map_or("".to_string(), |returning| format!( + " RETURNING {}", + returning + .iter() + .map(|col| col.to_sql_string(context)) + .collect::>() + .join(", ") + )) + ) + } +} + +impl ToSqlString for ast::InsertBody { + fn to_sql_string(&self, context: &C) -> String { + match self { + Self::DefaultValues => "DEFAULT VALUES".to_string(), + Self::Select(select, upsert) => format!( + "{}{}", + select.to_sql_string(context), + upsert.as_ref().map_or("".to_string(), |upsert| format!( + " {}", + upsert.to_sql_string(context) + )), + ), + } + } +} + +#[cfg(test)] +mod tests { + use crate::to_sql_string_test; + + // Basic INSERT with all columns + to_sql_string_test!( + test_insert_basic, + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000);" + ); + + // INSERT with multiple rows + to_sql_string_test!( + test_insert_multiple_rows, + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000), (2, 'Jane Smith', 60000);" + ); + + // INSERT with specific columns + to_sql_string_test!( + test_insert_specific_columns, + "INSERT INTO employees (name, salary) VALUES ('Alice Brown', 55000);" + ); + + // INSERT with DEFAULT VALUES + to_sql_string_test!( + test_insert_default_values, + "INSERT INTO employees DEFAULT VALUES;" + ); + + // INSERT with SELECT subquery + to_sql_string_test!( + test_insert_select_subquery, + "INSERT INTO employees (id, name, salary) SELECT id, name, salary FROM temp_employees WHERE salary > 40000;" + ); + + // INSERT with ON CONFLICT IGNORE + to_sql_string_test!( + test_insert_on_conflict_ignore, + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO NOTHING;" + ); + + // INSERT with ON CONFLICT REPLACE + to_sql_string_test!( + test_insert_on_conflict_replace, + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO UPDATE SET name = excluded.name, salary = excluded.salary;" + ); + + // INSERT with RETURNING clause + to_sql_string_test!( + test_insert_with_returning, + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) RETURNING id, name;" + ); + + // INSERT with NULL values + to_sql_string_test!( + test_insert_with_null, + "INSERT INTO employees (id, name, salary, department_id) VALUES (1, 'John Doe', NULL, NULL);" + ); + + // INSERT with expression in VALUES + to_sql_string_test!( + test_insert_with_expression, + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000 * 1.1);" + ); + + // INSERT into schema-qualified table + to_sql_string_test!( + test_insert_schema_qualified, + "INSERT INTO main.employees (id, name, salary) VALUES (1, 'John Doe', 50000);" + ); + + // INSERT with subquery and JOIN + to_sql_string_test!( + test_insert_subquery_join, + "INSERT INTO employees (id, name, department_id) SELECT e.id, e.name, d.id FROM temp_employees e JOIN departments d ON e.dept_name = d.name;" + ); + + // INSERT with all columns from SELECT + to_sql_string_test!( + test_insert_all_columns_select, + "INSERT INTO employees SELECT * FROM temp_employees;" + ); + + // INSERT with ON CONFLICT and WHERE clause + to_sql_string_test!( + test_insert_on_conflict_where, + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO UPDATE SET salary = excluded.salary WHERE excluded.salary > employees.salary;" + ); + + // INSERT with quoted column names (reserved words) + to_sql_string_test!( + test_insert_quoted_columns, + "INSERT INTO employees (\"select\", \"from\") VALUES (1, 'data');" + ); +} diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs index 908a9248f..817758253 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs @@ -7,6 +7,7 @@ mod create_table; mod create_trigger; mod create_virtual_table; mod delete; +mod insert; mod select; impl ToSqlString for ast::Stmt { @@ -154,6 +155,7 @@ impl ToSqlString for ast::Stmt { if_exists.then_some("IF EXISTS ").unwrap_or(""), view_name.to_sql_string(context) ), + Self::Insert(insert) => format!("{};", insert.to_sql_string(context)), Self::Select(select) => format!("{};", select.to_sql_string(context)), _ => todo!(), }