Skip to main content

vespertide_query/sql/
data_migration.rs

1use vespertide_core::DataMigrationSql;
2
3use super::types::{BuiltQuery, RawSql};
4
5/// Emit a `DataMigration` action's SQL **verbatim**, per backend.
6///
7/// The statement is passed through untouched: no case folding, no cast
8/// rewriting, no reformatting. Every backend-normalising helper in this crate
9/// (`convert_default_for_backend`, `normalize_fill_with`, …) is deliberately
10/// bypassed — the user wrote executable SQL and gets exactly that SQL back.
11pub fn build_data_migration(sql: &DataMigrationSql) -> Vec<BuiltQuery> {
12    vec![BuiltQuery::Raw(RawSql::per_backend(
13        sql.postgres().to_string(),
14        sql.mysql().to_string(),
15        sql.sqlite().to_string(),
16    ))]
17}
18
19#[cfg(test)]
20mod tests {
21    use super::*;
22    use crate::sql::types::DatabaseBackend;
23    use insta::{assert_snapshot, with_settings};
24    use rstest::rstest;
25
26    /// SQL deliberately loaded with everything the other emitters rewrite:
27    /// mixed-case keywords, a `::` cast, a quoted identifier, a string
28    /// literal, and multi-line formatting.
29    const HOSTILE_SQL: &str =
30        "UpDaTe \"User\"\n  SET meta = '{\"a\": 1}'::jsonb, n = N + 1\n  WHERE Kind = 'Legacy';";
31
32    #[rstest]
33    #[case::postgres(DatabaseBackend::Postgres)]
34    #[case::mysql(DatabaseBackend::MySql)]
35    #[case::sqlite(DatabaseBackend::Sqlite)]
36    fn uniform_sql_is_emitted_byte_for_byte(#[case] backend: DatabaseBackend) {
37        let queries = build_data_migration(&DataMigrationSql::Uniform(HOSTILE_SQL.to_string()));
38
39        assert_eq!(queries.len(), 1);
40        assert_eq!(
41            queries[0].build(backend),
42            HOSTILE_SQL,
43            "data_migration SQL must survive emission unchanged"
44        );
45
46        with_settings!({ snapshot_suffix => format!("data_migration_uniform_{backend:?}") }, {
47            assert_snapshot!(queries[0].build(backend));
48        });
49    }
50
51    #[rstest]
52    #[case::postgres(
53        DatabaseBackend::Postgres,
54        "UPDATE t SET j = jsonb_build_object('ko', c)"
55    )]
56    #[case::mysql(DatabaseBackend::MySql, "UPDATE t SET j = JSON_OBJECT('ko', c)")]
57    #[case::sqlite(DatabaseBackend::Sqlite, "UPDATE t SET j = json_object('ko', c)")]
58    fn per_backend_sql_selects_the_matching_statement(
59        #[case] backend: DatabaseBackend,
60        #[case] expected: &str,
61    ) {
62        let queries = build_data_migration(&DataMigrationSql::PerBackend {
63            postgres: "UPDATE t SET j = jsonb_build_object('ko', c)".to_string(),
64            mysql: "UPDATE t SET j = JSON_OBJECT('ko', c)".to_string(),
65            sqlite: "UPDATE t SET j = json_object('ko', c)".to_string(),
66        });
67
68        assert_eq!(queries.len(), 1);
69        assert_eq!(queries[0].build(backend), expected);
70
71        with_settings!({ snapshot_suffix => format!("data_migration_per_backend_{backend:?}") }, {
72            assert_snapshot!(queries[0].build(backend));
73        });
74    }
75}