rosetta-sql 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,247 @@
1
+ """
2
+ Handler for the 'result' subcommand — list / show / export historical runs.
3
+ """
4
+
5
+ import json
6
+ import os
7
+ import re
8
+ from typing import TYPE_CHECKING, List, Dict, Any, Optional
9
+
10
+ from .result import CommandResult
11
+
12
+ if TYPE_CHECKING:
13
+ from .output import OutputFormatter
14
+
15
+ # ---------------------------------------------------------------------------
16
+ # Pattern for run directory names produced by rosetta
17
+ # bench_json_mv_ddl_20260326_141650
18
+ # array_index_20260313_144633
19
+ # ---------------------------------------------------------------------------
20
+ _TIMESTAMP_RE = re.compile(r"(\d{8}_\d{6})$")
21
+
22
+
23
+ def handle_result(args, output: "OutputFormatter") -> CommandResult:
24
+ """Dispatch result sub-actions."""
25
+ action = getattr(args, "result_action", None)
26
+
27
+ # ``rosetta result`` with no sub-action → default to list
28
+ if not action:
29
+ action = "list"
30
+
31
+ if action == "list":
32
+ return _handle_list(args, output)
33
+ elif action == "show":
34
+ return _handle_show(args, output)
35
+ else:
36
+ return CommandResult.failure(f"Unknown result action: {action}")
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Helpers
41
+ # ---------------------------------------------------------------------------
42
+
43
+ def _scan_runs(output_dir: str) -> List[Dict[str, Any]]:
44
+ """Scan the output directory and return a list of run metadata dicts,
45
+ sorted newest-first."""
46
+ runs: List[Dict[str, Any]] = []
47
+ if not os.path.isdir(output_dir):
48
+ return runs
49
+
50
+ for name in os.listdir(output_dir):
51
+ full = os.path.join(output_dir, name)
52
+ if not os.path.isdir(full) or name in ("latest", "__pycache__"):
53
+ continue
54
+
55
+ run: Dict[str, Any] = {
56
+ "id": name,
57
+ "path": full,
58
+ "type": "unknown",
59
+ "timestamp": "",
60
+ "workload": "",
61
+ }
62
+
63
+ # Extract timestamp from directory name
64
+ m = _TIMESTAMP_RE.search(name)
65
+ if m:
66
+ raw = m.group(1) # e.g. 20260326_141650
67
+ run["timestamp"] = (
68
+ f"{raw[:4]}-{raw[4:6]}-{raw[6:8]} "
69
+ f"{raw[9:11]}:{raw[11:13]}:{raw[13:15]}"
70
+ )
71
+ # Workload = everything before the timestamp part
72
+ prefix = name[: m.start()].rstrip("_")
73
+ run["workload"] = prefix
74
+
75
+ # Detect run type
76
+ if os.path.isfile(os.path.join(full, "bench_result.json")):
77
+ run["type"] = "bench"
78
+ else:
79
+ result_files = [f for f in os.listdir(full) if f.endswith(".result")]
80
+ if result_files:
81
+ run["type"] = "mtr"
82
+
83
+ # Extra metadata for bench
84
+ if run["type"] == "bench":
85
+ try:
86
+ with open(os.path.join(full, "bench_result.json"), "r") as f:
87
+ bdata = json.load(f)
88
+ run["mode"] = bdata.get("mode", "")
89
+ run["dbms_targets"] = [
90
+ d.get("dbms_name", "") for d in bdata.get("dbms_results", [])
91
+ ]
92
+ except Exception:
93
+ pass
94
+
95
+ # For MTR, list result files
96
+ if run["type"] == "mtr":
97
+ run["result_files"] = sorted(
98
+ f for f in os.listdir(full) if f.endswith(".result")
99
+ )
100
+ # Infer dbms targets from .result filenames (e.g. test.mysql.result)
101
+ targets = []
102
+ for rf in run.get("result_files", []):
103
+ parts = rf.rsplit(".", 2)
104
+ if len(parts) == 3:
105
+ targets.append(parts[1])
106
+ run["dbms_targets"] = targets
107
+
108
+ # Count report files
109
+ report_files = [
110
+ f for f in os.listdir(full)
111
+ if f.endswith((".html", ".report.txt", ".json", ".diff"))
112
+ ]
113
+ run["report_files"] = sorted(report_files)
114
+
115
+ runs.append(run)
116
+
117
+ # Sort newest first
118
+ runs.sort(key=lambda r: r.get("timestamp", ""), reverse=True)
119
+ return runs
120
+
121
+
122
+ def _resolve_run(run_id: Optional[str], output_dir: str) -> Optional[Dict[str, Any]]:
123
+ """Resolve a run_id (exact, prefix, or 'latest') to a run metadata dict."""
124
+ runs = _scan_runs(output_dir)
125
+ if not runs:
126
+ return None
127
+
128
+ if not run_id:
129
+ # Default: latest
130
+ return runs[0] if runs else None
131
+
132
+ # Exact match
133
+ for r in runs:
134
+ if r["id"] == run_id:
135
+ return r
136
+
137
+ # Direct path
138
+ if os.path.isdir(run_id):
139
+ return {"id": os.path.basename(run_id), "path": run_id, "type": "unknown"}
140
+
141
+ # Prefix match
142
+ candidates = [r for r in runs if r["id"].startswith(run_id)]
143
+ if len(candidates) == 1:
144
+ return candidates[0]
145
+
146
+ return None
147
+
148
+
149
+ # ---------------------------------------------------------------------------
150
+ # Sub-actions
151
+ # ---------------------------------------------------------------------------
152
+
153
+ def _handle_list(args, output: "OutputFormatter") -> CommandResult:
154
+ """List historical runs with pagination."""
155
+ output_dir = getattr(args, "output_dir", "results")
156
+ limit = getattr(args, "limit", 20)
157
+ page = max(1, getattr(args, "page", 1))
158
+ type_filter = getattr(args, "type", "all")
159
+
160
+ runs = _scan_runs(output_dir)
161
+
162
+ if type_filter != "all":
163
+ runs = [r for r in runs if r["type"] == type_filter]
164
+
165
+ total = len(runs)
166
+ total_pages = max(1, (total + limit - 1) // limit)
167
+ page = min(page, total_pages)
168
+
169
+ start = (page - 1) * limit
170
+ display_runs = runs[start:start + limit]
171
+
172
+ # Slim down for output
173
+ rows = []
174
+ for i, r in enumerate(display_runs, start + 1):
175
+ rows.append({
176
+ "idx": i,
177
+ "id": r["id"],
178
+ "type": r["type"],
179
+ "workload": r.get("workload", ""),
180
+ "timestamp": r.get("timestamp", ""),
181
+ "dbms": ", ".join(r.get("dbms_targets", [])),
182
+ })
183
+
184
+ return CommandResult.success(
185
+ "result list",
186
+ {
187
+ "total": total,
188
+ "page": page,
189
+ "total_pages": total_pages,
190
+ "per_page": limit,
191
+ "showing": len(rows),
192
+ "output_dir": output_dir,
193
+ "runs": rows,
194
+ },
195
+ )
196
+
197
+
198
+ def _handle_show(args, output: "OutputFormatter") -> CommandResult:
199
+ """Show details of a specific run."""
200
+ output_dir = getattr(args, "output_dir", "results")
201
+ run_id = getattr(args, "run_id", None)
202
+
203
+ run = _resolve_run(run_id, output_dir)
204
+ if not run:
205
+ if run_id:
206
+ return CommandResult.failure(f"Run not found: {run_id}")
207
+ return CommandResult.failure("No runs found in results directory")
208
+
209
+ abs_path = os.path.abspath(run.get("path", ""))
210
+
211
+ data: Dict[str, Any] = {
212
+ "run_id": run["id"],
213
+ "type": run.get("type", "unknown"),
214
+ "timestamp": run.get("timestamp", ""),
215
+ "workload": run.get("workload", ""),
216
+ "path": abs_path,
217
+ "dbms": run.get("dbms_targets", []),
218
+ "report_files": [
219
+ os.path.join(abs_path, f) for f in run.get("report_files", [])
220
+ ],
221
+ }
222
+
223
+ # Bench: include summary stats
224
+ if run.get("type") == "bench":
225
+ bench_json = os.path.join(run["path"], "bench_result.json")
226
+ if os.path.isfile(bench_json):
227
+ try:
228
+ with open(bench_json, "r", encoding="utf-8") as f:
229
+ bdata = json.load(f)
230
+ data["mode"] = bdata.get("mode", "")
231
+ data["bench_summary"] = []
232
+ for dr in bdata.get("dbms_results", []):
233
+ data["bench_summary"].append({
234
+ "dbms": dr.get("dbms_name", ""),
235
+ "qps": round(dr.get("overall_qps", 0), 2),
236
+ "duration_s": round(dr.get("total_duration_s", 0), 2),
237
+ "queries": dr.get("total_queries", 0),
238
+ "errors": dr.get("total_errors", 0),
239
+ })
240
+ except Exception:
241
+ pass
242
+
243
+ # MTR: list result files
244
+ if run.get("type") == "mtr":
245
+ data["result_files"] = run.get("result_files", [])
246
+
247
+ return CommandResult.success("result show", data)