"""Parsing del piano EXPLAIN testuale restituito dall'rpc `explain_query`. Il transport diretto usa `EXPLAIN (FORMAT JSON)`; via REST l'rpc ritorna il piano come righe di testo (formato default di EXPLAIN). Questo modulo le riconduce a `PlanSummary`, così la logica a valle (`plan_warnings`, display) non cambia. """ import re from tht.execute import ExecutionError, PlanSummary # es. "(cost=0.00..3712.97 rows=13706 width=0)" _COST_RE = re.compile(r"\(cost=[\d.]+\.\.([\d.]+)\s+rows=(\d+)\s+width=\d+\)") def parse_text_plan(lines: list[str]) -> PlanSummary: node_types: list[str] = [] total_cost = 0.0 plan_rows = 0 root_seen = False for line in lines: m = _COST_RE.search(line) if m is None: # riga di dettaglio (Filter, Index Cond, …): non è un nodo del piano continue label = line.split("(cost=")[0].replace("->", " ").strip() if label: node_types.append(label) if not root_seen: total_cost = float(m.group(1)) plan_rows = int(m.group(2)) root_seen = True if not root_seen: raise ExecutionError("piano EXPLAIN vuoto o non interpretabile") return PlanSummary(total_cost=total_cost, plan_rows=plan_rows, node_types=node_types)