run_scene_migration.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. 场景模块数据库迁移执行脚本
  5. 用途:执行 migrate_scene_module.sql 迁移脚本
  6. """
  7. import pymysql
  8. import yaml
  9. import os
  10. import sys
  11. def load_config():
  12. """加载数据库配置"""
  13. script_dir = os.path.dirname(os.path.abspath(__file__))
  14. # 优先使用同目录下的 config.yaml
  15. config_path = os.path.join(script_dir, 'config.yaml')
  16. if not os.path.exists(config_path):
  17. config_path = os.path.join(script_dir, 'config.example.yaml')
  18. if not os.path.exists(config_path):
  19. raise FileNotFoundError(f"配置文件不存在: {config_path}")
  20. with open(config_path, 'r', encoding='utf-8') as f:
  21. config = yaml.safe_load(f)
  22. return config['database']
  23. def execute_migration():
  24. """执行迁移脚本"""
  25. try:
  26. # 加载数据库配置
  27. db_config = load_config()
  28. print("=" * 60)
  29. print("场景模块数据库迁移工具")
  30. print("=" * 60)
  31. print(f"数据库地址: {db_config['host']}:{db_config['port']}")
  32. print(f"数据库名称: {db_config['database']}")
  33. print(f"用户名: {db_config['user']}")
  34. print("=" * 60)
  35. # 读取迁移脚本
  36. sql_file = os.path.join(os.path.dirname(__file__), 'migrate_scene_module.sql')
  37. if not os.path.exists(sql_file):
  38. print(f"错误: 迁移脚本文件不存在: {sql_file}")
  39. return False
  40. with open(sql_file, 'r', encoding='utf-8') as f:
  41. sql_content = f.read()
  42. print(f"\n读取迁移脚本: {sql_file}")
  43. print(f"脚本大小: {len(sql_content)} 字节\n")
  44. # 连接数据库
  45. print("正在连接数据库...")
  46. connection = pymysql.connect(
  47. host=db_config['host'],
  48. port=db_config['port'],
  49. user=db_config['user'],
  50. password=db_config['password'],
  51. database=db_config['database'],
  52. charset='utf8mb4'
  53. )
  54. print("数据库连接成功!\n")
  55. try:
  56. cursor = connection.cursor()
  57. # 分割SQL语句(按分号分割,但跳过注释和存储过程)
  58. statements = []
  59. current_statement = []
  60. in_delimiter_block = False
  61. for line in sql_content.split('\n'):
  62. stripped = line.strip()
  63. # 跳过空行和注释
  64. if not stripped or stripped.startswith('--'):
  65. continue
  66. # 检测DELIMITER块
  67. if 'DELIMITER' in stripped.upper():
  68. in_delimiter_block = not in_delimiter_block
  69. continue
  70. current_statement.append(line)
  71. # 如果不在DELIMITER块中,遇到分号就分割
  72. if not in_delimiter_block and stripped.endswith(';'):
  73. stmt = '\n'.join(current_statement)
  74. if stmt.strip():
  75. statements.append(stmt)
  76. current_statement = []
  77. # 添加最后一条语句
  78. if current_statement:
  79. stmt = '\n'.join(current_statement)
  80. if stmt.strip():
  81. statements.append(stmt)
  82. print(f"共解析出 {len(statements)} 条SQL语句\n")
  83. print("开始执行迁移...\n")
  84. # 执行每条语句
  85. success_count = 0
  86. error_count = 0
  87. for i, statement in enumerate(statements, 1):
  88. try:
  89. # 跳过SELECT验证语句的输出
  90. if statement.strip().upper().startswith('SELECT'):
  91. cursor.execute(statement)
  92. results = cursor.fetchall()
  93. if results and len(results) > 0:
  94. print(f"[{i}/{len(statements)}] 验证查询结果:")
  95. for row in results:
  96. print(f" {row}")
  97. else:
  98. cursor.execute(statement)
  99. print(f"[{i}/{len(statements)}] 执行成功")
  100. success_count += 1
  101. except Exception as e:
  102. error_count += 1
  103. print(f"[{i}/{len(statements)}] 执行失败: {str(e)}")
  104. # 某些语句失败可能是因为表或字段已存在,继续执行
  105. continue
  106. # 提交事务
  107. connection.commit()
  108. print("\n" + "=" * 60)
  109. print("迁移执行完成!")
  110. print("=" * 60)
  111. print(f"成功: {success_count} 条")
  112. print(f"失败: {error_count} 条")
  113. print("=" * 60)
  114. # 验证结果
  115. print("\n验证迁移结果:")
  116. print("-" * 60)
  117. # 检查 scene_template 表
  118. cursor.execute("""
  119. SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
  120. WHERE TABLE_SCHEMA = DATABASE()
  121. AND TABLE_NAME = 'scene_template'
  122. """)
  123. scene_template_exists = cursor.fetchone()[0] > 0
  124. print(f"✓ scene_template 表: {'已创建' if scene_template_exists else '未创建'}")
  125. # 检查 recognition_record 新增字段
  126. cursor.execute("""
  127. SELECT COLUMN_NAME
  128. FROM INFORMATION_SCHEMA.COLUMNS
  129. WHERE TABLE_SCHEMA = DATABASE()
  130. AND TABLE_NAME = 'recognition_record'
  131. AND COLUMN_NAME IN ('scene_type', 'hazard_count', 'current_step', 'hazard_details')
  132. """)
  133. new_columns = [row[0] for row in cursor.fetchall()]
  134. print(f"✓ recognition_record 新增字段: {', '.join(new_columns) if new_columns else '无'}")
  135. print("-" * 60)
  136. return True
  137. finally:
  138. cursor.close()
  139. connection.close()
  140. print("\n数据库连接已关闭")
  141. except FileNotFoundError as e:
  142. print(f"错误: 配置文件不存在 - {e}")
  143. return False
  144. except pymysql.Error as e:
  145. print(f"数据库错误: {e}")
  146. return False
  147. except Exception as e:
  148. print(f"执行错误: {e}")
  149. import traceback
  150. traceback.print_exc()
  151. return False
  152. if __name__ == '__main__':
  153. success = execute_migration()
  154. sys.exit(0 if success else 1)