11use super :: { Schema , TopologicalSort } ;
22use crate :: { ConnectionTrait , DbBackend , DbErr , EntityTrait , Statement } ;
33use sea_query:: {
4- ForeignKeyCreateStatement , Index , IndexCreateStatement , IntoIden , TableAlterStatement ,
5- TableCreateStatement , TableName , TableRef , extension:: postgres:: TypeCreateStatement ,
4+ ForeignKeyCreateStatement , IndexCreateStatement , TableAlterStatement , TableCreateStatement ,
5+ TableName , TableRef , extension:: postgres:: TypeCreateStatement ,
66} ;
77
88/// A schema builder that can take a registry of Entities and synchronize it with database.
@@ -322,7 +322,32 @@ impl EntitySchemaInfo {
322322 break ;
323323 }
324324 }
325+
325326 if !column_exists {
327+ #[ cfg( feature = "rusqlite" ) ]
328+ {
329+ // TODO: sea_scheme has a bug. this is a quick fix
330+ // On SQLite, PRAGMA table_info does not return generated/virtual columns.
331+ if column_def. get_column_spec ( ) . extra . is_some ( ) {
332+ let tbl_str = table_name. 1 . to_string ( ) ;
333+ let col_name = column_def. get_column_name ( ) ;
334+ // hidden IN (2, 3) - filters to only hidden generated columns:
335+ // 2 = Hidden generated column (computed, not stored)
336+ // 3 = Hidden stored generated column (computed and stored)
337+ let xinfo_sql = format ! (
338+ "SELECT name FROM pragma_table_xinfo('{}') WHERE name = '{}' AND hidden IN (2, 3)" ,
339+ tbl_str. replace( '\'' , "''" ) ,
340+ col_name. replace( '\'' , "''" )
341+ ) ;
342+ let rows = db
343+ . query_all_raw ( Statement :: from_string ( DbBackend :: Sqlite , xinfo_sql) )
344+ . await ?;
345+ if !rows. is_empty ( ) {
346+ continue ; // Column exists actually
347+ }
348+ }
349+ }
350+
326351 let mut renamed_from = "" ;
327352 if let Some ( comment) = & column_def. get_column_spec ( ) . comment {
328353 if let Some ( ( _, suffix) ) = comment. rsplit_once ( "renamed_from \" " ) {
@@ -351,6 +376,7 @@ impl EntitySchemaInfo {
351376 }
352377 }
353378 }
379+
354380 if db. get_database_backend ( ) != DbBackend :: Sqlite {
355381 for foreign_key in self . table . get_foreign_key_create_stmts ( ) . iter ( ) {
356382 let mut key_exists = false ;
@@ -387,46 +413,6 @@ impl EntitySchemaInfo {
387413 db. execute ( & stmt) . await ?;
388414 }
389415 }
390- if let Some ( existing_table) = existing_table {
391- // For columns with a column-level UNIQUE constraint (#[sea_orm(unique)]) that
392- // already exist in the table but do not yet have a unique index, create one.
393- for column_def in self . table . get_columns ( ) {
394- if column_def. get_column_spec ( ) . unique {
395- let col_name = column_def. get_column_name ( ) ;
396- let col_exists = existing_table
397- . get_columns ( )
398- . iter ( )
399- . any ( |c| c. get_column_name ( ) == col_name) ;
400- if !col_exists {
401- // Column is being added in this sync pass; the ALTER TABLE ADD COLUMN
402- // will include the UNIQUE inline, so no separate index needed.
403- continue ;
404- }
405- let already_unique = existing_table. get_indexes ( ) . iter ( ) . any ( |idx| {
406- if !idx. is_unique_key ( ) {
407- return false ;
408- }
409- let cols = idx. get_index_spec ( ) . get_column_names ( ) ;
410- cols. len ( ) == 1 && cols[ 0 ] == col_name
411- } ) ;
412- if !already_unique {
413- let table_name =
414- self . table . get_table_name ( ) . expect ( "table must have a name" ) ;
415- let tbl_str = table_name. sea_orm_table ( ) . to_string ( ) ;
416- let table_ref = table_name. clone ( ) ;
417- db. execute (
418- Index :: create ( )
419- . name ( format ! ( "idx-{tbl_str}-{col_name}" ) )
420- . table ( table_ref)
421- . col ( col_name. into_iden ( ) )
422- . unique ( )
423- . if_not_exists ( ) ,
424- )
425- . await ?;
426- }
427- }
428- }
429- }
430416 if let Some ( existing_table) = existing_table {
431417 // find all unique keys from existing table
432418 // if it no longer exist in new schema, drop it
@@ -441,23 +427,6 @@ impl EntitySchemaInfo {
441427 break ;
442428 }
443429 }
444- // Also check if the unique index corresponds to a column-level UNIQUE
445- // constraint (from #[sea_orm(unique)]). These are embedded in the CREATE
446- // TABLE column definition and not tracked in self.indexes, so we must not
447- // try to drop them during sync.
448- if !has_index {
449- let index_cols = existing_index. get_index_spec ( ) . get_column_names ( ) ;
450- if index_cols. len ( ) == 1 {
451- for column_def in self . table . get_columns ( ) {
452- if column_def. get_column_name ( ) == index_cols[ 0 ]
453- && column_def. get_column_spec ( ) . unique
454- {
455- has_index = true ;
456- break ;
457- }
458- }
459- }
460- }
461430 if !has_index {
462431 if let Some ( drop_existing) = existing_index. get_index_spec ( ) . get_name ( ) {
463432 db. execute ( sea_query:: Index :: drop ( ) . name ( drop_existing) )
0 commit comments