added typeaheead and introspetion
This commit is contained in:
@@ -2851,6 +2851,70 @@ struct WorkbenchAdministrationView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 69: advisory diagnostics from the already loaded server snapshot.
|
||||
// No additional queries, writes, or persistent SQL text are required.
|
||||
private var diagnosticFindings: [String] {
|
||||
func cell(_ row: [String], _ name: String) -> String {
|
||||
guard let index = result.columns.firstIndex(where: { $0.caseInsensitiveCompare(name) == .orderedSame }),
|
||||
index < row.count else { return "" }
|
||||
return row[index]
|
||||
}
|
||||
func number(_ row: [String], _ name: String) -> Double {
|
||||
Double(cell(row, name)) ?? 0
|
||||
}
|
||||
switch page {
|
||||
case .slowQueries:
|
||||
return result.rows.prefix(100).enumerated().compactMap { position, row in
|
||||
let calls = number(row, "COUNT_STAR")
|
||||
let noIndex = number(row, "SUM_NO_INDEX_USED")
|
||||
let examined = number(row, "SUM_ROWS_EXAMINED")
|
||||
let sent = number(row, "SUM_ROWS_SENT")
|
||||
let digest = cell(row, "DIGEST_TEXT")
|
||||
let label = digest.isEmpty ? "Digest #\(position + 1)" : String(digest.prefix(110))
|
||||
if calls > 0 && noIndex > 0 {
|
||||
return "\(label): \(Int(noIndex)) of \(Int(calls)) executions reported no index usage. Inspect EXPLAIN and predicates."
|
||||
}
|
||||
if calls > 0 && examined > 10000 && sent < examined / 100 {
|
||||
return "\(label): high examined-to-returned row ratio. Check filters and index selectivity."
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case .tableIO:
|
||||
return result.rows.prefix(20).compactMap { row in
|
||||
let reads = number(row, "COUNT_READ")
|
||||
let writes = number(row, "COUNT_WRITE")
|
||||
guard reads + writes > 10000 else { return nil }
|
||||
let table = "\(cell(row, "OBJECT_SCHEMA")).\(cell(row, "OBJECT_NAME"))"
|
||||
return "\(table): \(Int(reads)) reads, \(Int(writes)) writes since counters were reset. Review workload before tuning."
|
||||
}
|
||||
case .innodbLocks:
|
||||
guard !result.rows.isEmpty else { return [] }
|
||||
return ["\(result.rows.count) lock-wait relationship(s) visible. Inspect active transactions before taking action."]
|
||||
case .connectionStatistics:
|
||||
let values = Dictionary(uniqueKeysWithValues: result.rows.compactMap { row -> (String, Double)? in
|
||||
guard row.count >= 2, let value = Double(row[1]) else { return nil }
|
||||
return (row[0], value)
|
||||
})
|
||||
var findings: [String] = []
|
||||
if (values["Aborted_connects"] ?? 0) > 0 {
|
||||
findings.append("Aborted connections recorded: \(Int(values["Aborted_connects"] ?? 0)). Check credentials, network failures and connection limits.")
|
||||
}
|
||||
if (values["Created_tmp_disk_tables"] ?? 0) > 0 {
|
||||
findings.append("Disk temporary tables recorded: \(Int(values["Created_tmp_disk_tables"] ?? 0)). Review heavy grouping and sorting queries.")
|
||||
}
|
||||
return findings
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private var diagnosticReportText: String {
|
||||
(["DBSyn Database Diagnostics", page.rawValue, "Snapshot: \(Date().formatted())", "",
|
||||
"Advisory findings (cumulative counters; not proof of a defect):"] +
|
||||
(diagnosticFindings.isEmpty ? ["No threshold-based findings for this snapshot."] : diagnosticFindings))
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
|
||||
private var filteredRows: [[String]] {
|
||||
guard !searchText.isEmpty else { return result.rows }
|
||||
return result.rows.filter { row in
|
||||
@@ -2907,6 +2971,35 @@ struct WorkbenchAdministrationView: View {
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
||||
if !diagnosticFindings.isEmpty && errorMessage == nil {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
HStack {
|
||||
Label("Diagnostic Findings", systemImage: "stethoscope")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
Button("Copy Report") {
|
||||
NSPasteboard.general.clearContents()
|
||||
NSPasteboard.general.setString(diagnosticReportText, forType: .string)
|
||||
}
|
||||
}
|
||||
ForEach(Array(diagnosticFindings.prefix(5).enumerated()), id: \.offset) { _, finding in
|
||||
Text("• " + finding)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
if diagnosticFindings.count > 5 {
|
||||
Text("\(diagnosticFindings.count - 5) additional findings available in Copy Report")
|
||||
.font(.caption2).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.padding(12)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(.quaternary.opacity(0.35), in: RoundedRectangle(cornerRadius: 8))
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 6)
|
||||
}
|
||||
|
||||
if supportsTrends && !diagnosticSamples.isEmpty && page != .dashboard && page != .serverStatus {
|
||||
HStack(spacing: 12) {
|
||||
Label("\(diagnosticSamples.count) samples", systemImage: "chart.xyaxis.line")
|
||||
|
||||
Reference in New Issue
Block a user