mod alter_table; mod create_table; mod create_trigger; mod create_virtual_table; mod delete; mod insert; mod select; mod update; #[cfg(test)] mod tests { use crate::to_sql_string::ToSqlContext; #[macro_export] /// Create a test that first parses then input, the converts the parsed ast back to a string and compares with original input macro_rules! to_sql_string_test { ($test_name:ident, $input:expr) => { #[test] fn $test_name() { use $crate::parser::ast::fmt::ToTokens; let context = $crate::to_sql_string::stmt::tests::TestContext; let input = $input.split_whitespace().collect::>().join(" "); let mut parser = $crate::lexer::sql::Parser::new(input.as_bytes()); let cmd = fallible_iterator::FallibleIterator::next(&mut parser) .unwrap() .unwrap(); assert_eq!( input, cmd.stmt().format_with_context(&context).unwrap().replace('\n', " "), ); } }; ($test_name:ident, $input:expr, $($attribute:meta),*) => { #[test] $(#[$attribute])* fn $test_name() { use $crate::parser::ast::fmt::ToTokens; let context = $crate::to_sql_string::stmt::tests::TestContext; let input = $input.split_whitespace().collect::>().join(" "); let mut parser = $crate::lexer::sql::Parser::new(input.as_bytes()); let cmd = fallible_iterator::FallibleIterator::next(&mut parser) .unwrap() .unwrap(); assert_eq!( input, cmd.stmt().format_with_context(&context).unwrap().replace('\n', " "), ); } } } pub(crate) struct TestContext; // Placeholders for compilation // Context only necessary parsing inside turso_core or in the simulator impl ToSqlContext for TestContext { fn get_column_name( &self, _table_id: crate::ast::TableInternalId, _col_idx: usize, ) -> String { todo!() } fn get_table_name(&self, _id: crate::ast::TableInternalId) -> &str { todo!() } } to_sql_string_test!(test_analyze, "ANALYZE"); to_sql_string_test!( test_analyze_table, "ANALYZE table", ignore = "parser can't parse table name" ); to_sql_string_test!( test_analyze_schema_table, "ANALYZE schema.table", ignore = "parser can't parse schema.table name" ); to_sql_string_test!(test_attach, "ATTACH './test.db' AS test_db"); to_sql_string_test!(test_transaction, "BEGIN"); to_sql_string_test!(test_transaction_deferred, "BEGIN DEFERRED"); to_sql_string_test!(test_transaction_immediate, "BEGIN IMMEDIATE"); to_sql_string_test!(test_transaction_exclusive, "BEGIN EXCLUSIVE"); to_sql_string_test!(test_commit, "COMMIT"); // Test a simple index on a single column to_sql_string_test!( test_create_index_simple, "CREATE INDEX idx_name ON employees (last_name)" ); // Test a unique index to enforce uniqueness on a column to_sql_string_test!( test_create_unique_index, "CREATE UNIQUE INDEX idx_unique_email ON users (email)" ); // Test a multi-column index to_sql_string_test!( test_create_index_multi_column, "CREATE INDEX idx_name_salary ON employees (last_name, salary)" ); // Test a partial index with a WHERE clause to_sql_string_test!( test_create_partial_index, "CREATE INDEX idx_active_users ON users (username) WHERE active = true" ); // Test an index on an expression to_sql_string_test!( test_create_index_on_expression, "CREATE INDEX idx_upper_name ON employees (UPPER (last_name))" ); // Test an index with descending order to_sql_string_test!( test_create_index_descending, "CREATE INDEX idx_salary_desc ON employees (salary DESC)" ); // Test an index with mixed ascending and descending orders on multiple columns to_sql_string_test!( test_create_index_mixed_order, "CREATE INDEX idx_name_asc_salary_desc ON employees (last_name ASC, salary DESC)" ); // Test 1: View with DISTINCT keyword to_sql_string_test!( test_create_view_distinct, "CREATE VIEW view_distinct AS SELECT DISTINCT name FROM employees" ); // Test 2: View with LIMIT clause to_sql_string_test!( test_create_view_limit, "CREATE VIEW view_limit AS SELECT id, name FROM employees LIMIT 10" ); // Test 3: View with CASE expression to_sql_string_test!( test_create_view_case, "CREATE VIEW view_case AS SELECT name, CASE WHEN salary > 70000 THEN 'High' ELSE 'Low' END AS salary_level FROM employees" ); // Test 4: View with LEFT JOIN to_sql_string_test!( test_create_view_left_join, "CREATE VIEW view_left_join AS SELECT e.name, d.name AS department FROM employees e LEFT OUTER JOIN departments d ON e.department_id = d.id" ); // Test 5: View with HAVING clause to_sql_string_test!( test_create_view_having, "CREATE VIEW view_having AS SELECT department_id, AVG (salary) AS avg_salary FROM employees GROUP BY department_id HAVING AVG (salary) > 55000" ); // Test 6: View with CTE (Common Table Expression) to_sql_string_test!( test_create_view_cte, "CREATE VIEW view_cte AS WITH high_earners AS (SELECT * FROM employees WHERE salary > 80000) SELECT id, name FROM high_earners" ); // Test 7: View with multiple conditions in WHERE to_sql_string_test!( test_create_view_multi_where, "CREATE VIEW view_multi_where AS SELECT id, name FROM employees WHERE salary > 50000 AND department_id = 3" ); // Test 8: View with NULL handling to_sql_string_test!( test_create_view_null, "CREATE VIEW view_null AS SELECT name, COALESCE (salary, 0) AS salary FROM employees" ); // Test 9: View with subquery in WHERE clause to_sql_string_test!( test_create_view_subquery_where, "CREATE VIEW view_subquery_where AS SELECT name FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales')" ); // Test 10: View with arithmetic expression to_sql_string_test!( test_create_view_arithmetic, "CREATE VIEW view_arithmetic AS SELECT name, salary * 1.1 AS adjusted_salary FROM employees" ); to_sql_string_test!(test_detach, "DETACH 'x.db'"); to_sql_string_test!(test_drop_index, "DROP INDEX schema_name.test_index"); to_sql_string_test!(test_drop_table, "DROP TABLE schema_name.test_table"); to_sql_string_test!(test_drop_trigger, "DROP TRIGGER schema_name.test_trigger"); to_sql_string_test!(test_drop_view, "DROP VIEW schema_name.test_view"); to_sql_string_test!(test_pragma_equals, "PRAGMA schema_name.Pragma_name = 1"); to_sql_string_test!(test_pragma_call, "PRAGMA schema_name.Pragma_name_2 (1)"); to_sql_string_test!(test_reindex, "REINDEX schema_name.test_table"); to_sql_string_test!(test_reindex_2, "REINDEX"); to_sql_string_test!(test_release, "RELEASE savepoint_name"); to_sql_string_test!(test_rollback, "ROLLBACK"); to_sql_string_test!(test_rollback_2, "ROLLBACK TO savepoint_name"); to_sql_string_test!(test_savepoint, "SAVEPOINT savepoint_name"); to_sql_string_test!(test_vacuum, "VACUUM"); to_sql_string_test!(test_vacuum_2, "VACUUM schema_name"); to_sql_string_test!(test_vacuum_3, "VACUUM schema_name INTO test.db"); }