@@ -4,7 +4,8 @@ use vespertide_core::{ColumnDef, TableDef};
44
55use super :: helpers:: {
66 build_create_enum_type_sql, build_sea_column_def_with_table, build_sqlite_temp_table_create,
7- normalize_enum_default, normalize_fill_with, recreate_indexes_after_rebuild,
7+ convert_default_for_backend, normalize_enum_default, normalize_fill_with,
8+ recreate_indexes_after_rebuild,
89} ;
910use super :: rename_table:: build_rename_table;
1011use super :: types:: { BuiltQuery , DatabaseBackend } ;
@@ -66,9 +67,11 @@ pub fn build_add_column(
6667 }
6768 let normalized_fill = normalize_fill_with ( fill_with) ;
6869 let fill_expr = if let Some ( fill) = normalized_fill. as_deref ( ) {
69- Expr :: cust ( normalize_enum_default ( & column. r#type , fill) )
70+ let converted = convert_default_for_backend ( fill, backend) ;
71+ Expr :: cust ( normalize_enum_default ( & column. r#type , & converted) )
7072 } else if let Some ( def) = & column. default {
71- Expr :: cust ( normalize_enum_default ( & column. r#type , & def. to_sql ( ) ) )
73+ let converted = convert_default_for_backend ( & def. to_sql ( ) , backend) ;
74+ Expr :: cust ( normalize_enum_default ( & column. r#type , & converted) )
7275 } else {
7376 Expr :: cust ( "NULL" )
7477 } ;
@@ -124,6 +127,7 @@ pub fn build_add_column(
124127
125128 // Backfill with provided value
126129 if let Some ( fill) = normalize_fill_with ( fill_with) {
130+ let fill = convert_default_for_backend ( & fill, backend) ;
127131 let update_stmt = Query :: update ( )
128132 . table ( Alias :: new ( table) )
129133 . value ( Alias :: new ( & column. name ) , Expr :: cust ( fill) )
@@ -604,6 +608,70 @@ mod tests {
604608 } ) ;
605609 }
606610
611+ /// Test adding NOT NULL column with '[]'::json default on SQLite
612+ /// SQLite should strip the ::json cast, MySQL should use CAST(... AS JSON)
613+ #[ rstest]
614+ #[ case:: postgres( DatabaseBackend :: Postgres ) ]
615+ #[ case:: mysql( DatabaseBackend :: MySql ) ]
616+ #[ case:: sqlite( DatabaseBackend :: Sqlite ) ]
617+ fn test_add_column_with_pg_type_cast_default ( #[ case] backend : DatabaseBackend ) {
618+ let column = ColumnDef {
619+ name : "story_index" . into ( ) ,
620+ r#type : ColumnType :: Simple ( SimpleColumnType :: Json ) ,
621+ nullable : false ,
622+ default : Some ( "'[]'::json" . into ( ) ) ,
623+ comment : None ,
624+ primary_key : None ,
625+ unique : None ,
626+ index : None ,
627+ foreign_key : None ,
628+ } ;
629+ let current_schema = vec ! [ TableDef {
630+ name: "project" . into( ) ,
631+ description: None ,
632+ columns: vec![ ColumnDef {
633+ name: "id" . into( ) ,
634+ r#type: ColumnType :: Simple ( SimpleColumnType :: Integer ) ,
635+ nullable: false ,
636+ default : None ,
637+ comment: None ,
638+ primary_key: None ,
639+ unique: None ,
640+ index: None ,
641+ foreign_key: None ,
642+ } ] ,
643+ constraints: vec![ ] ,
644+ } ] ;
645+ let result = build_add_column ( & backend, "project" , & column, None , & current_schema) . unwrap ( ) ;
646+ let sql = result
647+ . iter ( )
648+ . map ( |q| q. build ( backend) )
649+ . collect :: < Vec < String > > ( )
650+ . join ( "\n " ) ;
651+
652+ // SQLite must NOT contain ::json syntax
653+ if backend == DatabaseBackend :: Sqlite {
654+ assert ! (
655+ !sql. contains( "::json" ) ,
656+ "SQLite SQL should not contain ::json cast, got: {}" ,
657+ sql
658+ ) ;
659+ }
660+
661+ // MySQL should use CAST syntax
662+ if backend == DatabaseBackend :: MySql {
663+ assert ! (
664+ !sql. contains( "::json" ) ,
665+ "MySQL SQL should not contain ::json cast, got: {}" ,
666+ sql
667+ ) ;
668+ }
669+
670+ with_settings ! ( { snapshot_suffix => format!( "pg_type_cast_default_{:?}" , backend) } , {
671+ assert_snapshot!( sql) ;
672+ } ) ;
673+ }
674+
607675 #[ rstest]
608676 #[ case:: postgres( DatabaseBackend :: Postgres ) ]
609677 #[ case:: mysql( DatabaseBackend :: MySql ) ]
0 commit comments