diff --git a/PHASE24-NOTES.md b/PHASE24-NOTES.md new file mode 100644 index 0000000..cadcf3b --- /dev/null +++ b/PHASE24-NOTES.md @@ -0,0 +1,12 @@ +# Phase 24 — Query Performance Diagnostics + +Builds on Phase 23. + +- Adds a read-only index diagnostic section to the EXPLAIN inspector. Flags full scans with/without candidate indexes, and optimizer steps without a selected index. +- Adds per-step tabular EXPLAIN baseline comparisons for access type, selected index, possible indexes, estimated rows, filtered percentage, and Extra information. +- Adds **Copy Diagnostics** to copy SQL, plan observations, index analysis, and baseline differences. +- Preserves the Phase 23 query plan history and existing database management features. + +## Important + +The application has not been compiled in Xcode or tested against MySQL in this environment. The analysis uses optimizer estimates, not measured timings. It intentionally does not generate or apply CREATE INDEX statements. Compare plans only for equivalent schema/data conditions. diff --git a/WorkbenchReplacement/MainView.swift b/WorkbenchReplacement/MainView.swift index 6410ecc..82bd9e2 100644 --- a/WorkbenchReplacement/MainView.swift +++ b/WorkbenchReplacement/MainView.swift @@ -1432,6 +1432,86 @@ private struct WorkbenchExplainView: View { return message + " Estimates are not measured query timings." } + // Phase 24: Compare tabular plan steps by position and table, not only + // by aggregate row estimates. EXPLAIN estimates are not runtime metrics. + private var stepComparison: [String] { + guard let baseline, !useJSON, baseline.format == "Table", !plan.columns.isEmpty else { return [] } + func value(_ columns: [String], _ row: [String], _ field: String) -> String { + guard let index = columns.firstIndex(where: { $0.caseInsensitiveCompare(field) == .orderedSame }), + index < row.count else { return "—" } + return row[index] + } + var details: [String] = [] + let count = max(baseline.resultRows.count, plan.rows.count) + for index in 0.. String { + guard let index = names.firstIndex(of: name), index < row.count else { return "" } + return row[index] + } + return plan.rows.compactMap { row -> String? in + let table = value(row, "table") + let access = value(row, "type").uppercased() + let selected = value(row, "key") + let possible = value(row, "possible_keys") + let label = table.isEmpty ? "Query step" : "Table \(table)" + if access == "ALL" && possible.uppercased() != "NULL" && !possible.isEmpty { + return "\(label): full scan despite candidate indexes [\(possible)]. Review predicate selectivity and statistics." + } + if access == "ALL" && (possible.isEmpty || possible.uppercased() == "NULL") { + return "\(label): full scan with no candidate index listed. Inspect WHERE/JOIN predicates before considering an index." + } + if selected.isEmpty || selected.uppercased() == "NULL" { + return "\(label): optimizer did not select an index. This may be optimal for small tables." + } + return nil + } + } + + private var diagnosticReport: String { + let sections = [ + "SQL:", normalizedStatement, + "", "Plan observations:", + planInsights.isEmpty ? "None" : planInsights.joined(separator: "\n"), + "", "Index analysis:", + indexDiagnostics.isEmpty ? "None (or JSON format selected)" : indexDiagnostics.joined(separator: "\n"), + "", "Baseline comparison:", + comparisonText ?? "No baseline selected", + "", "Changed plan steps:", + stepComparison.isEmpty ? "None" : stepComparison.joined(separator: "\n"), + "", "These are optimizer estimates, not measured query performance." + ] + return sections.joined(separator: "\n") + } + private var normalizedStatement: String { var value = statement.trimmingCharacters(in: .whitespacesAndNewlines) if value.hasSuffix(";") { value.removeLast() } @@ -1592,6 +1672,47 @@ private struct WorkbenchExplainView: View { .font(.callout) .textSelection(.enabled) } + if !plan.columns.isEmpty { + HStack { + Label("Index analysis", systemImage: "magnifyingglass") + .font(.headline) + Spacer() + Button("Copy Diagnostics") { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(diagnosticReport, forType: .string) + } + } + if !indexDiagnostics.isEmpty { + ScrollView { + VStack(alignment: .leading, spacing: 6) { + ForEach(Array(indexDiagnostics.enumerated()), id: \.offset) { _, note in + Text(note) + .frame(maxWidth: .infinity, alignment: .leading) + .textSelection(.enabled) + } + } + } + .frame(maxHeight: 100) + } else { + Text("No additional index warnings for this plan format.") + .font(.caption) + .foregroundStyle(.secondary) + } + } + if !stepComparison.isEmpty { + DisclosureGroup("Changed plan steps (\(stepComparison.count))") { + ScrollView { + VStack(alignment: .leading, spacing: 6) { + ForEach(Array(stepComparison.enumerated()), id: \.offset) { _, change in + Text(change) + .frame(maxWidth: .infinity, alignment: .leading) + .textSelection(.enabled) + } + } + } + .frame(maxHeight: 130) + } + } if !planInsights.isEmpty { DisclosureGroup("Plan observations (\(planInsights.count))") { ScrollView { diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o index 7cf6ba5..730c3b2 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.o differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata index b4a64fb..fbc7baa 100644 --- a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata +++ b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.stringsdata @@ -447,7 +447,7 @@ "key": "Uses MySQL EXPLAIN to inspect the optimizer's estimated plan without executing the SELECT statement itself.", "location": { "startingColumn": 18, - "startingLine": 1502 + "startingLine": 1582 } }, { @@ -455,7 +455,15 @@ "key": "Select a SELECT or WITH query in the SQL editor before opening EXPLAIN.", "location": { "startingColumn": 124, - "startingLine": 1543 + "startingLine": 1623 + } + }, + { + "comment": "", + "key": "No additional index warnings for this plan format.", + "location": { + "startingColumn": 26, + "startingLine": 1697 } }, { @@ -463,7 +471,7 @@ "key": "EXPLAIN shows estimates, not actual query execution times. Observations are heuristics, not index recommendations. Confirm with representative data before changing production indexes.", "location": { "startingColumn": 18, - "startingLine": 1610 + "startingLine": 1731 } }, { @@ -471,7 +479,7 @@ "key": "SELECT query required", "location": { "startingColumn": 40, - "startingLine": 1543 + "startingLine": 1623 } }, { @@ -479,7 +487,7 @@ "key": "No plan loaded", "location": { "startingColumn": 40, - "startingLine": 1561 + "startingLine": 1641 } }, { @@ -487,7 +495,15 @@ "key": "Saved plans for this query (%lld)", "location": { "startingColumn": 33, - "startingLine": 1564 + "startingLine": 1644 + } + }, + { + "comment": "", + "key": "Changed plan steps (%lld)", + "location": { + "startingColumn": 33, + "startingLine": 1703 } }, { @@ -495,7 +511,7 @@ "key": "Plan observations (%lld)", "location": { "startingColumn": 33, - "startingLine": 1596 + "startingLine": 1717 } }, { @@ -503,7 +519,7 @@ "key": "Query Execution Plan", "location": { "startingColumn": 23, - "startingLine": 1497 + "startingLine": 1577 } }, { @@ -511,7 +527,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 1500 + "startingLine": 1580 } }, { @@ -519,7 +535,7 @@ "key": "Comparing with %@", "location": { "startingColumn": 26, - "startingLine": 1525 + "startingLine": 1605 } }, { @@ -527,7 +543,7 @@ "key": "Plan format", "location": { "startingColumn": 24, - "startingLine": 1513 + "startingLine": 1593 } }, { @@ -535,7 +551,7 @@ "key": "Run EXPLAIN", "location": { "startingColumn": 24, - "startingLine": 1519 + "startingLine": 1599 } }, { @@ -543,7 +559,7 @@ "key": "Save Snapshot", "location": { "startingColumn": 24, - "startingLine": 1521 + "startingLine": 1601 } }, { @@ -551,7 +567,7 @@ "key": "History", "location": { "startingColumn": 24, - "startingLine": 1523 + "startingLine": 1603 } }, { @@ -559,7 +575,7 @@ "key": "Clear Baseline", "location": { "startingColumn": 28, - "startingLine": 1528 + "startingLine": 1608 } }, { @@ -567,7 +583,7 @@ "key": "Copy Plan", "location": { "startingColumn": 24, - "startingLine": 1530 + "startingLine": 1610 } }, { @@ -575,7 +591,7 @@ "key": "Table", "location": { "startingColumn": 26, - "startingLine": 1514 + "startingLine": 1594 } }, { @@ -583,7 +599,7 @@ "key": "JSON", "location": { "startingColumn": 26, - "startingLine": 1515 + "startingLine": 1595 } }, { @@ -591,7 +607,7 @@ "key": "No saved plans for this connection, schema and SQL statement.", "location": { "startingColumn": 30, - "startingLine": 1566 + "startingLine": 1646 } }, { @@ -599,7 +615,7 @@ "key": "Compare", "location": { "startingColumn": 48, - "startingLine": 1576 + "startingLine": 1656 } }, { @@ -607,7 +623,23 @@ "key": "Delete", "location": { "startingColumn": 48, - "startingLine": 1577 + "startingLine": 1657 + } + }, + { + "comment": "", + "key": "Index analysis", + "location": { + "startingColumn": 27, + "startingLine": 1677 + } + }, + { + "comment": "", + "key": "Copy Diagnostics", + "location": { + "startingColumn": 28, + "startingLine": 1680 } }, { @@ -615,7 +647,7 @@ "key": "Connection Settings", "location": { "startingColumn": 18, - "startingLine": 1678 + "startingLine": 1799 } }, { @@ -623,7 +655,7 @@ "key": "Connection Name", "location": { "startingColumn": 21, - "startingLine": 1683 + "startingLine": 1804 } }, { @@ -631,7 +663,7 @@ "key": "Hostname", "location": { "startingColumn": 21, - "startingLine": 1688 + "startingLine": 1809 } }, { @@ -639,7 +671,7 @@ "key": "Port", "location": { "startingColumn": 21, - "startingLine": 1693 + "startingLine": 1814 } }, { @@ -647,7 +679,7 @@ "key": "Username", "location": { "startingColumn": 21, - "startingLine": 1699 + "startingLine": 1820 } }, { @@ -655,7 +687,7 @@ "key": "Password (macOS Keychain)", "location": { "startingColumn": 21, - "startingLine": 1704 + "startingLine": 1825 } }, { @@ -663,7 +695,7 @@ "key": "Default Schema", "location": { "startingColumn": 21, - "startingLine": 1709 + "startingLine": 1830 } }, { @@ -671,7 +703,7 @@ "key": "Cancel", "location": { "startingColumn": 24, - "startingLine": 1717 + "startingLine": 1838 } }, { @@ -679,7 +711,7 @@ "key": "Save", "location": { "startingColumn": 24, - "startingLine": 1721 + "startingLine": 1842 } }, { @@ -687,7 +719,7 @@ "key": "Terminate MySQL session?", "location": { "startingColumn": 13, - "startingLine": 2216 + "startingLine": 2337 } }, { @@ -695,7 +727,7 @@ "key": "The server did not return any columns.", "location": { "startingColumn": 25, - "startingLine": 2149 + "startingLine": 2270 } }, { @@ -703,7 +735,7 @@ "key": "Loading %@…", "location": { "startingColumn": 30, - "startingLine": 2127 + "startingLine": 2248 } }, { @@ -711,7 +743,7 @@ "key": "No Data", "location": { "startingColumn": 21, - "startingLine": 2146 + "startingLine": 2267 } }, { @@ -719,7 +751,7 @@ "key": "Unable to Load %@", "location": { "startingColumn": 25, - "startingLine": 2133 + "startingLine": 2254 } }, { @@ -727,7 +759,7 @@ "key": "Retry", "location": { "startingColumn": 28, - "startingLine": 2139 + "startingLine": 2260 } }, { @@ -735,7 +767,7 @@ "key": "Changes require confirmation", "location": { "startingColumn": 26, - "startingLine": 2193 + "startingLine": 2314 } }, { @@ -743,7 +775,7 @@ "key": "Account", "location": { "startingColumn": 28, - "startingLine": 2172 + "startingLine": 2293 } }, { @@ -751,7 +783,7 @@ "key": "Show Grants", "location": { "startingColumn": 28, - "startingLine": 2183 + "startingLine": 2304 } }, { @@ -759,7 +791,7 @@ "key": "Manage Accounts…", "location": { "startingColumn": 28, - "startingLine": 2187 + "startingLine": 2308 } }, { @@ -767,7 +799,7 @@ "key": "Select account", "location": { "startingColumn": 30, - "startingLine": 2173 + "startingLine": 2294 } }, { @@ -775,7 +807,7 @@ "key": "%@@%@", "location": { "startingColumn": 38, - "startingLine": 2177 + "startingLine": 2298 } }, { @@ -783,7 +815,7 @@ "key": "%@ session %@", "location": { "startingColumn": 24, - "startingLine": 2224 + "startingLine": 2345 } }, { @@ -791,7 +823,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 2229 + "startingLine": 2350 } }, { @@ -799,7 +831,7 @@ "key": "This operation affects a live MySQL session. Terminating a connection can interrupt transactions and roll back uncommitted work. Verify the session ID before proceeding.", "location": { "startingColumn": 18, - "startingLine": 2231 + "startingLine": 2352 } }, { @@ -807,7 +839,7 @@ "key": "Grants: %@", "location": { "startingColumn": 22, - "startingLine": 2245 + "startingLine": 2366 } }, { @@ -815,7 +847,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 2257 + "startingLine": 2378 } }, { @@ -823,7 +855,7 @@ "key": "Requires PROCESS privilege to see other users' sessions and appropriate privileges to terminate them.", "location": { "startingColumn": 18, - "startingLine": 2287 + "startingLine": 2408 } }, { @@ -831,7 +863,7 @@ "key": "Session", "location": { "startingColumn": 24, - "startingLine": 2267 + "startingLine": 2388 } }, { @@ -839,7 +871,7 @@ "key": "Stop Query", "location": { "startingColumn": 24, - "startingLine": 2274 + "startingLine": 2395 } }, { @@ -847,7 +879,7 @@ "key": "Disconnect Session", "location": { "startingColumn": 24, - "startingLine": 2276 + "startingLine": 2397 } }, { @@ -855,7 +887,7 @@ "key": "Select a session", "location": { "startingColumn": 26, - "startingLine": 2268 + "startingLine": 2389 } }, { @@ -863,7 +895,7 @@ "key": "ID %@", "location": { "startingColumn": 30, - "startingLine": 2270 + "startingLine": 2391 } }, { @@ -871,7 +903,7 @@ "key": "Filter results", "location": { "startingColumn": 27, - "startingLine": 2352 + "startingLine": 2473 } }, { @@ -879,7 +911,7 @@ "key": "Auto-refresh", "location": { "startingColumn": 20, - "startingLine": 2357 + "startingLine": 2478 } }, { @@ -887,7 +919,7 @@ "key": "Refresh every five seconds", "location": { "startingColumn": 23, - "startingLine": 2359 + "startingLine": 2480 } }, { @@ -895,7 +927,7 @@ "key": "Refresh", "location": { "startingColumn": 23, - "startingLine": 2364 + "startingLine": 2485 } }, { @@ -903,7 +935,7 @@ "key": "SQL Editor", "location": { "startingColumn": 23, - "startingLine": 2371 + "startingLine": 2492 } }, { @@ -911,7 +943,7 @@ "key": "Not loaded", "location": { "startingColumn": 22, - "startingLine": 2449 + "startingLine": 2570 } }, { @@ -919,7 +951,7 @@ "key": "Updated %@", "location": { "startingColumn": 21, - "startingLine": 2446 + "startingLine": 2567 } }, { @@ -927,7 +959,7 @@ "key": "%lld rows", "location": { "startingColumn": 18, - "startingLine": 2454 + "startingLine": 2575 } }, { @@ -935,7 +967,7 @@ "key": "Import SQL into %@?", "location": { "startingColumn": 13, - "startingLine": 2761 + "startingLine": 2882 } }, { @@ -943,7 +975,7 @@ "key": "Operation", "location": { "startingColumn": 20, - "startingLine": 2564 + "startingLine": 2685 } }, { @@ -951,7 +983,7 @@ "key": "Data Export", "location": { "startingColumn": 22, - "startingLine": 2565 + "startingLine": 2686 } }, { @@ -959,7 +991,7 @@ "key": "Data Import / Restore", "location": { "startingColumn": 22, - "startingLine": 2566 + "startingLine": 2687 } }, { @@ -967,7 +999,7 @@ "key": "MySQL Connection", "location": { "startingColumn": 26, - "startingLine": 2573 + "startingLine": 2694 } }, { @@ -975,7 +1007,7 @@ "key": "Export Options", "location": { "startingColumn": 30, - "startingLine": 2597 + "startingLine": 2718 } }, { @@ -983,7 +1015,7 @@ "key": "Import Options", "location": { "startingColumn": 30, - "startingLine": 2653 + "startingLine": 2774 } }, { @@ -991,7 +1023,7 @@ "key": "Progress", "location": { "startingColumn": 26, - "startingLine": 2665 + "startingLine": 2786 } }, { @@ -999,7 +1031,7 @@ "key": "Connection", "location": { "startingColumn": 36, - "startingLine": 2575 + "startingLine": 2696 } }, { @@ -1007,7 +1039,7 @@ "key": "Database", "location": { "startingColumn": 28, - "startingLine": 2579 + "startingLine": 2700 } }, { @@ -1015,7 +1047,7 @@ "key": "Select Database", "location": { "startingColumn": 30, - "startingLine": 2580 + "startingLine": 2701 } }, { @@ -1023,7 +1055,7 @@ "key": "Refresh Databases", "location": { "startingColumn": 32, - "startingLine": 2588 + "startingLine": 2709 } }, { @@ -1031,7 +1063,7 @@ "key": "No tables found. Select a database or refresh the list.", "location": { "startingColumn": 34, - "startingLine": 2619 + "startingLine": 2740 } }, { @@ -1039,7 +1071,7 @@ "key": "InnoDB data is exported using a consistent snapshot and a single streamed query per table. Tables without unique indexes are supported.", "location": { "startingColumn": 30, - "startingLine": 2648 + "startingLine": 2769 } }, { @@ -1047,7 +1079,7 @@ "key": "Table structure", "location": { "startingColumn": 32, - "startingLine": 2599 + "startingLine": 2720 } }, { @@ -1055,7 +1087,7 @@ "key": "Table data", "location": { "startingColumn": 32, - "startingLine": 2600 + "startingLine": 2721 } }, { @@ -1063,7 +1095,7 @@ "key": "Add DROP TABLE IF EXISTS", "location": { "startingColumn": 32, - "startingLine": 2601 + "startingLine": 2722 } }, { @@ -1071,7 +1103,7 @@ "key": "Include views (definitions)", "location": { "startingColumn": 32, - "startingLine": 2603 + "startingLine": 2724 } }, { @@ -1079,7 +1111,7 @@ "key": "Include triggers", "location": { "startingColumn": 32, - "startingLine": 2605 + "startingLine": 2726 } }, { @@ -1087,7 +1119,7 @@ "key": "Include stored procedures and functions", "location": { "startingColumn": 32, - "startingLine": 2607 + "startingLine": 2728 } }, { @@ -1095,7 +1127,7 @@ "key": "Loading tables…", "location": { "startingColumn": 57, - "startingLine": 2617 + "startingLine": 2738 } }, { @@ -1103,7 +1135,7 @@ "key": "Tables (%lld selected)", "location": { "startingColumn": 34, - "startingLine": 2610 + "startingLine": 2731 } }, { @@ -1111,7 +1143,7 @@ "key": "All", "location": { "startingColumn": 36, - "startingLine": 2612 + "startingLine": 2733 } }, { @@ -1119,7 +1151,7 @@ "key": "None", "location": { "startingColumn": 36, - "startingLine": 2613 + "startingLine": 2734 } }, { @@ -1127,7 +1159,7 @@ "key": "Refresh", "location": { "startingColumn": 36, - "startingLine": 2614 + "startingLine": 2735 } }, { @@ -1135,7 +1167,7 @@ "key": "Views (%lld selected)", "location": { "startingColumn": 38, - "startingLine": 2633 + "startingLine": 2754 } }, { @@ -1143,7 +1175,7 @@ "key": "All", "location": { "startingColumn": 40, - "startingLine": 2635 + "startingLine": 2756 } }, { @@ -1151,7 +1183,7 @@ "key": "None", "location": { "startingColumn": 40, - "startingLine": 2636 + "startingLine": 2757 } }, { @@ -1159,7 +1191,7 @@ "key": "Import SQL statements into the selected database.", "location": { "startingColumn": 30, - "startingLine": 2655 + "startingLine": 2776 } }, { @@ -1167,7 +1199,7 @@ "key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.", "location": { "startingColumn": 30, - "startingLine": 2658 + "startingLine": 2779 } }, { @@ -1175,7 +1207,7 @@ "key": "Current table: %@ / %@ rows · Total exported: %@", "location": { "startingColumn": 30, - "startingLine": 2682 + "startingLine": 2803 } }, { @@ -1183,7 +1215,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 2696 + "startingLine": 2817 } }, { @@ -1191,7 +1223,7 @@ "key": "Verify Backup…", "location": { "startingColumn": 28, - "startingLine": 2704 + "startingLine": 2825 } }, { @@ -1199,7 +1231,7 @@ "key": "Verify an SQL file against its SHA-256 checksum sidecar", "location": { "startingColumn": 27, - "startingLine": 2707 + "startingLine": 2828 } }, { @@ -1207,7 +1239,7 @@ "key": "Verify Restored Database…", "location": { "startingColumn": 28, - "startingLine": 2708 + "startingLine": 2829 } }, { @@ -1215,7 +1247,7 @@ "key": "Compare restored table row counts and object names with the export manifest", "location": { "startingColumn": 27, - "startingLine": 2712 + "startingLine": 2833 } }, { @@ -1223,7 +1255,7 @@ "key": "Cancel", "location": { "startingColumn": 28, - "startingLine": 2716 + "startingLine": 2837 } }, { @@ -1231,7 +1263,7 @@ "key": "Export SQL…", "location": { "startingColumn": 31, - "startingLine": 2723 + "startingLine": 2844 } }, { @@ -1239,7 +1271,7 @@ "key": "Import SQL…", "location": { "startingColumn": 31, - "startingLine": 2724 + "startingLine": 2845 } }, { @@ -1247,7 +1279,7 @@ "key": "Import SQL", "location": { "startingColumn": 20, - "startingLine": 2764 + "startingLine": 2885 } }, { @@ -1255,7 +1287,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 2772 + "startingLine": 2893 } }, { @@ -1263,7 +1295,7 @@ "key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.", "location": { "startingColumn": 17, - "startingLine": 2777 + "startingLine": 2898 } }, { @@ -1271,7 +1303,7 @@ "key": "Database Import / Export", "location": { "startingColumn": 22, - "startingLine": 2789 + "startingLine": 2910 } }, { @@ -1279,7 +1311,7 @@ "key": "Built-in native MySQL database tools", "location": { "startingColumn": 22, - "startingLine": 2792 + "startingLine": 2913 } }, { @@ -1287,7 +1319,7 @@ "key": "Confirm %@", "location": { "startingColumn": 16, - "startingLine": 3843 + "startingLine": 3964 } }, { @@ -1295,7 +1327,7 @@ "key": "Manage MySQL Accounts", "location": { "startingColumn": 18, - "startingLine": 3793 + "startingLine": 3914 } }, { @@ -1303,7 +1335,7 @@ "key": "Account changes run on %@. Review the SQL before applying it.", "location": { "startingColumn": 18, - "startingLine": 3794 + "startingLine": 3915 } }, { @@ -1311,7 +1343,7 @@ "key": "Operation", "location": { "startingColumn": 24, - "startingLine": 3797 + "startingLine": 3918 } }, { @@ -1319,7 +1351,7 @@ "key": "Username", "location": { "startingColumn": 27, - "startingLine": 3802 + "startingLine": 3923 } }, { @@ -1327,7 +1359,7 @@ "key": "Host", "location": { "startingColumn": 27, - "startingLine": 3804 + "startingLine": 3925 } }, { @@ -1335,7 +1367,7 @@ "key": "Password", "location": { "startingColumn": 33, - "startingLine": 3806 + "startingLine": 3927 } }, { @@ -1343,7 +1375,7 @@ "key": "Database", "location": { "startingColumn": 31, - "startingLine": 3809 + "startingLine": 3930 } }, { @@ -1351,7 +1383,7 @@ "key": "Privilege", "location": { "startingColumn": 28, - "startingLine": 3810 + "startingLine": 3931 } }, { @@ -1359,7 +1391,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 3825 + "startingLine": 3946 } }, { @@ -1367,7 +1399,7 @@ "key": "Review SQL…", "location": { "startingColumn": 24, - "startingLine": 3827 + "startingLine": 3948 } }, { @@ -1375,7 +1407,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 3844 + "startingLine": 3965 } }, { @@ -1383,7 +1415,7 @@ "key": "Apply Change", "location": { "startingColumn": 20, - "startingLine": 3845 + "startingLine": 3966 } }, { @@ -1391,7 +1423,7 @@ "key": "Run this operation against %@?\n\n%@", "location": { "startingColumn": 18, - "startingLine": 3850 + "startingLine": 3971 } } ] diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps index 2d00f7a..7904cfd 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/MainView.swiftdeps differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors index 1943904..4e23019 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement-primary.priors differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule index bb888bf..5690d6b 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftmodule differ diff --git a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo index 8738761..0b56cdc 100644 Binary files a/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo and b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo differ diff --git a/build/Build/Intermediates.noindex/XCBuildData/build.db b/build/Build/Intermediates.noindex/XCBuildData/build.db index 53fc312..ae994cc 100644 Binary files a/build/Build/Intermediates.noindex/XCBuildData/build.db and b/build/Build/Intermediates.noindex/XCBuildData/build.db differ diff --git a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt index c53346e..1dd82f4 100644 --- a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt +++ b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt @@ -29,3 +29,4 @@ /Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app /Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app /Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app +/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement index 14fe12f..cf38929 100755 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement and b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib index c98db95..e3f65ca 100755 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib and b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/WorkbenchReplacement.debug.dylib differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib index f1c34e7..8560fd8 100755 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib and b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/MacOS/__preview.dylib differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources index 2414e8d..9e990a2 100644 --- a/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources +++ b/build/Build/Products/Debug/WorkbenchReplacement.app/Contents/_CodeSignature/CodeResources @@ -19,7 +19,7 @@ cdhash - 4cHefiu8TlGXWe220IW6MEbRNgU= + s+RLc8/qmsHJTbP/vbm0kuQhxbA= requirement identifier "WorkbenchReplacement.debug" and anchor apple generic and certificate leaf[subject.CN] = "Apple Development: Mark Gregory (FLBRJ3BYDD)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo index 8738761..0b56cdc 100644 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo and b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo differ diff --git a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule index bb888bf..5690d6b 100644 Binary files a/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule and b/build/Build/Products/Debug/WorkbenchReplacement.swiftmodule/arm64-apple-macos.swiftmodule differ diff --git a/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 b/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 index 36fd591..c229536 100644 Binary files a/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 and b/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 differ diff --git a/build/Logs/Build/LogStoreManifest.plist b/build/Logs/Build/LogStoreManifest.plist index 1adb7e7..facb46a 100644 --- a/build/Logs/Build/LogStoreManifest.plist +++ b/build/Logs/Build/LogStoreManifest.plist @@ -6,48 +6,6 @@ 13 logs - 0C437F7E-D2AF-4660-9194-7E7E610BB7CE - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - 0C437F7E-D2AF-4660-9194-7E7E610BB7CE.xcactivitylog - hasPrimaryLog - - primaryObservable - - highLevelStatus - S - totalNumberOfAnalyzerIssues - 0 - totalNumberOfErrors - 0 - totalNumberOfTestFailures - 0 - totalNumberOfWarnings - 0 - - schemeIdentifier-containerName - WorkbenchReplacement project - schemeIdentifier-schemeName - WorkbenchReplacement - schemeIdentifier-sharedScheme - 1 - signature - Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug - timeStartedRecording - 813879584.86891794 - timeStoppedRecording - 813879600.88160503 - title - Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug - uniqueIdentifier - 0C437F7E-D2AF-4660-9194-7E7E610BB7CE - 0D84D8B7-4EBD-46B6-9E1E-5318CA005DB1 className @@ -90,7 +48,7 @@ uniqueIdentifier 0D84D8B7-4EBD-46B6-9E1E-5318CA005DB1 - 27B7943C-710F-4B49-802F-4D4346522832 + 64DBE2F7-41EB-44D3-A0AB-CCC5468F4E72 className IDECommandLineBuildLog @@ -99,7 +57,7 @@ domainType Xcode.IDEActivityLogDomainType.BuildLog fileName - 27B7943C-710F-4B49-802F-4D4346522832.xcactivitylog + 64DBE2F7-41EB-44D3-A0AB-CCC5468F4E72.xcactivitylog hasPrimaryLog primaryObservable @@ -124,13 +82,13 @@ signature Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug timeStartedRecording - 813879486.90431201 + 813880254.37309003 timeStoppedRecording - 813879487.48329401 + 813880270.59995699 title Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug uniqueIdentifier - 27B7943C-710F-4B49-802F-4D4346522832 + 64DBE2F7-41EB-44D3-A0AB-CCC5468F4E72 9B404BEF-D5CC-4B4B-93CF-3A0C95C748D2 diff --git a/build/ModuleCache.noindex/Session.modulevalidation b/build/ModuleCache.noindex/Session.modulevalidation index 305518f..529aeff 100644 --- a/build/ModuleCache.noindex/Session.modulevalidation +++ b/build/ModuleCache.noindex/Session.modulevalidation @@ -1 +1 @@ -1792187270.412821: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") +1792187454.770874: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") diff --git a/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache b/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache index 8999d90..9b8e1f6 100644 Binary files a/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache and b/build/SDKStatCaches.noindex/macosx27.0-26A425-e829b21a6f8c9b70876ba0858053c0447231f6df4def33e022ae2eb8f782df1a.sdkstatcache differ diff --git a/build/info.plist b/build/info.plist index cc19de9..90f17fa 100644 --- a/build/info.plist +++ b/build/info.plist @@ -3,7 +3,7 @@ LastAccessedDate - 2026-10-16T21:47:50Z + 2026-10-16T21:50:54Z WorkspacePath /Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj