1212from alembic import op
1313from sqlalchemy .dialects import mysql
1414
15- # Import SafeDDL helpers
16- from migrations . utils . safe_ddl import has_table , safe_alter_column
15+ from migrations . utils . safe_ddl import ( create_fk_if_not_exists , has_table ,
16+ safe_alter_column )
1717
1818# revision identifiers, used by Alembic.
19- revision : str = ' 26a927cbe516'
20- down_revision : Union [str , None ] = ' 3a42e4f129e4'
19+ revision : str = " 26a927cbe516"
20+ down_revision : Union [str , None ] = " 3a42e4f129e4"
2121branch_labels : Union [str , Sequence [str ], None ] = None
2222depends_on : Union [str , Sequence [str ], None ] = None
2323
2424
2525def upgrade () -> None :
2626 """Upgrade schema."""
27- # Check if tables exist before attempting to drop them
28- if has_table ('thread_vector_stores' ):
29- op .drop_table ('thread_vector_stores' )
27+ if has_table ("thread_vector_stores" ):
28+ op .drop_table ("thread_vector_stores" )
3029
31- if has_table (' vector_store_assistants' ):
32- op .drop_table (' vector_store_assistants' )
30+ if has_table (" vector_store_assistants" ):
31+ op .drop_table (" vector_store_assistants" )
3332
34- # Safe column alterations
3533 safe_alter_column (
36- table_name = ' messages' , column_name = ' content' , existing_type = mysql .TEXT (), nullable = False
34+ table_name = " messages" , column_name = " content" , existing_type = mysql .TEXT (), nullable = False
3735 )
38-
3936 safe_alter_column (
40- table_name = ' messages' ,
41- column_name = ' reasoning' ,
37+ table_name = " messages" ,
38+ column_name = " reasoning" ,
4239 existing_type = mysql .LONGTEXT (),
4340 type_ = sa .Text (length = 4294967295 ),
4441 existing_comment = "Stores the internal 'thinking' or reasoning tokens from the model." ,
@@ -48,53 +45,71 @@ def upgrade() -> None:
4845
4946def downgrade () -> None :
5047 """Downgrade schema."""
51- # Safe column alterations for reversion
5248 safe_alter_column (
53- table_name = ' messages' ,
54- column_name = ' reasoning' ,
49+ table_name = " messages" ,
50+ column_name = " reasoning" ,
5551 existing_type = sa .Text (length = 4294967295 ),
5652 type_ = mysql .LONGTEXT (),
5753 existing_comment = "Stores the internal 'thinking' or reasoning tokens from the model." ,
5854 existing_nullable = True ,
5955 )
60-
6156 safe_alter_column (
62- table_name = ' messages' , column_name = ' content' , existing_type = mysql .TEXT (), nullable = True
57+ table_name = " messages" , column_name = " content" , existing_type = mysql .TEXT (), nullable = True
6358 )
6459
65- # Check if tables are missing before attempting to create them
66- if not has_table ('vector_store_assistants' ):
60+ # Recreate join tables without inline FKs.
61+ # 'assistants', 'vector_stores', and 'threads' are base tables that may not
62+ # exist yet on a fresh container at this point in the downgrade chain.
63+ # FKs are added as separate deferred steps via create_fk_if_not_exists below,
64+ # which checks has_table on both source and referent before acting.
65+ if not has_table ("vector_store_assistants" ):
6766 op .create_table (
68- 'vector_store_assistants' ,
69- sa .Column ('vector_store_id' , mysql .VARCHAR (length = 64 ), nullable = False ),
70- sa .Column ('assistant_id' , mysql .VARCHAR (length = 64 ), nullable = False ),
71- sa .ForeignKeyConstraint (
72- ['assistant_id' ], ['assistants.id' ], name = op .f ('vector_store_assistants_ibfk_2' )
73- ),
74- sa .ForeignKeyConstraint (
75- ['vector_store_id' ],
76- ['vector_stores.id' ],
77- name = op .f ('vector_store_assistants_ibfk_1' ),
78- ),
79- sa .PrimaryKeyConstraint ('vector_store_id' , 'assistant_id' ),
80- mysql_collate = 'utf8mb4_0900_ai_ci' ,
81- mysql_default_charset = 'utf8mb4' ,
82- mysql_engine = 'InnoDB' ,
67+ "vector_store_assistants" ,
68+ sa .Column ("vector_store_id" , mysql .VARCHAR (length = 64 ), nullable = False ),
69+ sa .Column ("assistant_id" , mysql .VARCHAR (length = 64 ), nullable = False ),
70+ sa .PrimaryKeyConstraint ("vector_store_id" , "assistant_id" ),
71+ mysql_collate = "utf8mb4_0900_ai_ci" ,
72+ mysql_default_charset = "utf8mb4" ,
73+ mysql_engine = "InnoDB" ,
8374 )
8475
85- if not has_table ('thread_vector_stores' ):
76+ create_fk_if_not_exists (
77+ "vector_store_assistants_ibfk_1" ,
78+ "vector_store_assistants" ,
79+ "vector_stores" ,
80+ ["vector_store_id" ],
81+ ["id" ],
82+ )
83+ create_fk_if_not_exists (
84+ "vector_store_assistants_ibfk_2" ,
85+ "vector_store_assistants" ,
86+ "assistants" ,
87+ ["assistant_id" ],
88+ ["id" ],
89+ )
90+
91+ if not has_table ("thread_vector_stores" ):
8692 op .create_table (
87- 'thread_vector_stores' ,
88- sa .Column ('thread_id' , mysql .VARCHAR (length = 64 ), nullable = False ),
89- sa .Column ('vector_store_id' , mysql .VARCHAR (length = 64 ), nullable = False ),
90- sa .ForeignKeyConstraint (
91- ['thread_id' ], ['threads.id' ], name = op .f ('thread_vector_stores_ibfk_1' )
92- ),
93- sa .ForeignKeyConstraint (
94- ['vector_store_id' ], ['vector_stores.id' ], name = op .f ('thread_vector_stores_ibfk_2' )
95- ),
96- sa .PrimaryKeyConstraint ('thread_id' , 'vector_store_id' ),
97- mysql_collate = 'utf8mb4_0900_ai_ci' ,
98- mysql_default_charset = 'utf8mb4' ,
99- mysql_engine = 'InnoDB' ,
93+ "thread_vector_stores" ,
94+ sa .Column ("thread_id" , mysql .VARCHAR (length = 64 ), nullable = False ),
95+ sa .Column ("vector_store_id" , mysql .VARCHAR (length = 64 ), nullable = False ),
96+ sa .PrimaryKeyConstraint ("thread_id" , "vector_store_id" ),
97+ mysql_collate = "utf8mb4_0900_ai_ci" ,
98+ mysql_default_charset = "utf8mb4" ,
99+ mysql_engine = "InnoDB" ,
100100 )
101+
102+ create_fk_if_not_exists (
103+ "thread_vector_stores_ibfk_1" ,
104+ "thread_vector_stores" ,
105+ "threads" ,
106+ ["thread_id" ],
107+ ["id" ],
108+ )
109+ create_fk_if_not_exists (
110+ "thread_vector_stores_ibfk_2" ,
111+ "thread_vector_stores" ,
112+ "vector_stores" ,
113+ ["vector_store_id" ],
114+ ["id" ],
115+ )
0 commit comments