#!/usr/bin/env python3 """ Production Migration Script for llm -> llm_catalog_rel. This script handles: 1. Creating the new relationship table if it doesn't exist. 2. Migrating existing single-type associations to the new table. 3. Dropping the old column (optional but recommended). Usage: cd /home/hermesai/repos/sage ./py3/bin/python migrate_llm_catelog_prod.py """ import asyncio import sys sys.path.insert(0, '.') from sqlor.dbpools import DBPools from appPublic.jsonConfig import getConfig from appPublic.uniqueID import getID async def migrate(): config = getConfig('.') db = DBPools(config.databases) # Sage 数据库 dbname = 'sage' async with db.sqlorContext(dbname) as sor: # 1. 检查列是否存在 check_col_sql = "select count(*) as cnt from information_schema.columns where table_schema = database() and table_name = 'llm' and column_name = 'llmcatelogid'" res = await sor.sqlExe(check_col_sql, {}) has_column = res[0].cnt > 0 if res else False if not has_column: print("[SKIP] Column 'llmcatelogid' does not exist in 'llm' table. Migration not needed.") return True # 2. 创建表 (如果不存在) print("[STEP 1] Ensuring table 'llm_catalog_rel' exists...") create_sql = """ CREATE TABLE IF NOT EXISTS llm_catalog_rel ( id VARCHAR(32) NOT NULL PRIMARY KEY, llmid VARCHAR(32) NOT NULL, llmcatelogid VARCHAR(32) NOT NULL, UNIQUE KEY uq_llm_catelog (llmid, llmcatelogid), INDEX idx_llm (llmid), INDEX idx_catelog (llmcatelogid) ) """ try: await sor.sqlExe(create_sql, {}) print(" -> Table created or exists.") except Exception as e: print(f" -> Table creation warning: {e}") # 2.1 确保唯一索引存在 print("[STEP 1.1] Ensuring unique index 'uq_llm_catelog' exists...") try: await sor.sqlExe("ALTER TABLE llm_catalog_rel ADD UNIQUE INDEX uq_llm_catelog (llmid, llmcatelogid)", {}) print(" -> Unique index added.") except Exception as e: if "Duplicate key name" in str(e) or "Multiple primary key defined" in str(e): print(" -> Unique index already exists.") else: print(f" -> Index creation warning (may already exist): {e}") # 3. 迁移数据 print("[STEP 2] Migrating data...") sql = "select id, llmcatelogid from llm where llmcatelogid is not null and llmcatelogid != ''" rows = await sor.sqlExe(sql, {}) print(f" -> Found {len(rows)} records to migrate.") migrated_count = 0 error_count = 0 for r in rows: # Check if already migrated to avoid duplicates if re-running check_rel_sql = "select count(*) as cnt from llm_catalog_rel where llmid = ${llmid}$ and llmcatelogid = ${catelogid}$" rel_res = await sor.sqlExe(check_rel_sql, {'llmid': r['id'], 'catelogid': r['llmcatelogid']}) if rel_res and rel_res[0].cnt > 0: continue new_id = getID() data = { 'id': new_id, 'llmid': r['id'], 'llmcatelogid': r['llmcatelogid'] } try: await sor.C('llm_catalog_rel', data) migrated_count += 1 except Exception as e: print(f" -> Error migrating {r['id']}: {e}") error_count += 1 print(f" -> Migration complete. Inserted: {migrated_count}, Errors: {error_count}") # 4. 删除旧列 (生产环境谨慎执行,建议确认迁移成功后手动执行) # print("[STEP 3] Dropping old column 'llmcatelogid'...") # try: # await sor.sqlExe("alter table llm drop column llmcatelogid", {}) # print(" -> Column dropped.") # except Exception as e: # print(f" -> Drop column error: {e}") print("\n[MIGRATION DONE]") print("Please verify data in 'llm_catalog_rel'.") print("If correct, you may manually run: alter table llm drop column llmcatelogid;") return True if __name__ == '__main__': print("Starting production migration...") try: success = asyncio.get_event_loop().run_until_complete(migrate()) if success: sys.exit(0) else: sys.exit(1) except Exception as e: print(f"FATAL ERROR: {e}") import traceback traceback.print_exc() sys.exit(1)