feat: Phase2影响分析 — EXPLAIN预检+大表检测(简化版)+执行策略+风险评估table_sizes支持 (1141测试)
新增: - explain_preview.py: EXPLAIN预检服务(MySQL/PG), 影响行数预估, 全表扫描检测 - ticket.executor: 三种执行策略(stop_on_error/continue/transaction) - risk_evaluator: table_sizes参数, 大表ALTER/UPDATE/DELETE自动升级high - test_phase2_features.py: 22个测试(EXPLAIN+执行策略+大表检测) 简化: - 砍掉CTX-001上下文感知规则(复杂二次扫描), 合并大表检测到risk_evaluator - 审核引擎保持纯静态分析, 不再导入_RULE_REGISTRY做二次扫描 模型: - Ticket加exec_strategy字段 - TicketSqlItem模型 - schema加sql_type/exec_strategy/risk_level/workflow_id
This commit is contained in:
1 parent
7be0bf1a7c
commit
f6f9142ce8
7 files changed
+574
-22
No files matched your search
@@ -220,29 +220,13 @@ class TicketExecutor:
|
||||
# ===== MySQL 执行 =====
|
||||
|
||||
async def _execute_mysql(self, ticket: Ticket, instance: Instance) -> ExecutionReport:
|
||||
"""执行 MySQL 工单。
|
||||
|
||||
执行流程:
|
||||
1. 语法预校验(sqlparse 解析)
|
||||
2. 建立连接(autocommit=False 显式事务)
|
||||
3. 执行前快照和数据备份
|
||||
4. 在事务中执行 SQL
|
||||
5. 执行后验证
|
||||
6. 验证通过则提交,失败则回滚
|
||||
7. 生成报告
|
||||
|
||||
Args:
|
||||
ticket: 工单对象
|
||||
instance: MySQL 实例连接信息
|
||||
|
||||
Returns:
|
||||
ExecutionReport: 执行报告
|
||||
"""
|
||||
"""执行 MySQL 工单。"""
|
||||
import aiomysql
|
||||
|
||||
report = ExecutionReport()
|
||||
start_time = time.time()
|
||||
sql_content = ticket.sql_content.strip()
|
||||
exec_strategy = getattr(ticket, "exec_strategy", "stop_on_error") or "stop_on_error"
|
||||
|
||||
# 获取超时配置(从 ticket 或选项中)
|
||||
custom_timeout = getattr(ticket, 'options', None) or {}
|
||||
@@ -279,6 +263,7 @@ class TicketExecutor:
|
||||
total_affected = 0
|
||||
output_lines: List[str] = []
|
||||
execution_failed = False
|
||||
failed_count = 0
|
||||
|
||||
statements = self._split_statements(sql_content)
|
||||
for stmt in statements:
|
||||
@@ -293,10 +278,15 @@ class TicketExecutor:
|
||||
total_affected += max(0, affected) if affected is not None else 0
|
||||
output_lines.append(f"[{stmt[:50]}...] 影响 {affected} 行")
|
||||
except Exception as stmt_error:
|
||||
failed_count += 1
|
||||
execution_failed = True
|
||||
output_lines.append(f"[{stmt[:50]}...] 执行失败: {stmt_error}")
|
||||
report.errors.append(f"语句执行失败: {stmt_error}")
|
||||
break
|
||||
|
||||
if exec_strategy == "stop_on_error":
|
||||
break
|
||||
# continue: 跳过失败继续执行
|
||||
# transaction: 任何失败都回滚(默认行为,break 后走 rollback)
|
||||
|
||||
report.affected_rows = total_affected
|
||||
report.raw_output = "\n".join(output_lines)
|
||||
@@ -311,8 +301,14 @@ class TicketExecutor:
|
||||
sql_content, report
|
||||
)
|
||||
|
||||
# 事务决策:验证通过且无执行错误则提交,否则回滚
|
||||
if not execution_failed and report.validation_passed:
|
||||
# 事务决策:根据执行策略处理
|
||||
if exec_strategy == "continue" and failed_count > 0 and failed_count < len(statements):
|
||||
# continue 模式:部分失败也算成功,提交已执行的语句
|
||||
await conn.commit()
|
||||
report.status = "partial_success"
|
||||
report.errors.insert(0, f"部分失败: {failed_count}/{len(statements)} 条语句失败")
|
||||
logger.warning("工单 %d MySQL 部分执行成功: %d 条失败", ticket.id, failed_count)
|
||||
elif not execution_failed and report.validation_passed:
|
||||
await conn.commit()
|
||||
report.status = "success"
|
||||
logger.info("工单 %d MySQL 执行成功,事务已提交", ticket.id)
|
||||
|
||||
Reference in new issue
Block a user