diff --git a/PHASE14-NOTES.md b/PHASE14-NOTES.md
deleted file mode 100644
index 92ecf40..0000000
--- a/PHASE14-NOTES.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Phase 14 — MySQL Account Management
-
-Adds a **Manage Accounts…** sheet under Administration → Users and Privileges.
-
-Operations: create account, change password, lock/unlock account, grant/revoke an individual database privilege. Every operation displays a SQL preview and requires an explicit confirmation. Passwords are masked in previews, never persisted by the account management form, and cleared after execution. Privileges are chosen from an allowlist. User, host, and database inputs use conservative character allowlists.
-
-The existing account list and Show Grants remain available. Auto-refresh is switched off when account management opens, and the account list refreshes on dismissal.
-
-**Limitations:** Requires sufficient MySQL privileges. Only basic ASCII account/host/database names supported. No account deletion, global privileges, roles, or complex grant options. SQL mode NO_BACKSLASH_ESCAPES is rejected for password operations. Password values are sent using the native query protocol, which may expose them in server-side SQL logs; parameterized MySQL account DDL isn't implemented. No automatic rollback for account changes.
-
-**Validation:** Static/source inspection and ZIP integrity only. This environment cannot compile the macOS Xcode target or verify changes against a MySQL server. Test on a disposable MySQL instance before production use.
diff --git a/PHASE16-NOTES.md b/PHASE16-NOTES.md
deleted file mode 100644
index d407e02..0000000
--- a/PHASE16-NOTES.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Phase 16 — Native MySQL Diagnostics
-
-Adds five read-only Administration → Performance screens:
-
-- **Slow Query Analysis:** `performance_schema.events_statements_summary_by_digest` sorted by cumulative execution time, with counts, rows examined, and first/last seen timestamps. Times are raw picoseconds, **not** milliseconds.
-- **Table I/O Hotspots:** `performance_schema.table_io_waits_summary_by_table` with read/write counts and timings.
-- **InnoDB Metrics:** selected `SHOW GLOBAL STATUS` buffer-pool, I/O, lock, and row counters.
-- **InnoDB Lock Waits:** current edges in `performance_schema.data_lock_waits`.
-- **Connection Statistics:** connection, abort, thread, and query counters.
-
-All screens reuse the existing searchable grid, manual refresh, and optional five-second auto-refresh. SQL is read-only. Some screens require MySQL 8.0+ and enabled Performance Schema instrumentation/privileges; errors are displayed in the existing administration error view. The digest and table I/O summaries are cumulative and may be reset by server restarts or instrumentation changes. This phase does **not** enable slow-query logging or change server configuration.
-
-## Validation
-
-Source inspection and ZIP integrity check performed. **Not compiled with Xcode or tested on a live MySQL server.** Test on a nonproduction MySQL 8.0+ instance first.
diff --git a/PHASE17-NOTES.md b/PHASE17-NOTES.md
deleted file mode 100644
index bb4af1d..0000000
--- a/PHASE17-NOTES.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Phase 17 — SQL Editor Productivity
-
-- Added persisted query execution history (latest 200 statements), including timestamp, connection, schema, success/failure, and error details. Schema-list refreshes are not recorded.
-- Added History panel with filtering, reopen in a new tab, and clear history.
-- Added a persistent saved SQL query library, with save, filter, open, and delete.
-- Added AppKit SQL keyword completion via NSTextViewDelegate's standard completion interface (typically Esc or Option-Esc depending on macOS key bindings). Completions are keyword-based, not live schema-aware.
-- Preserved the existing SQL syntax highlighting and query tab handling.
-
-## Caveats
-
-History and saved SQL are stored in local UserDefaults and may contain sensitive SQL text; avoid executing statements containing literal secrets if local history retention is a concern. Results remain subject to the existing protocol row cap. This phase has not been built in Xcode or tested against a live server.
diff --git a/PHASE18-NOTES.md b/PHASE18-NOTES.md
deleted file mode 100644
index fd0df25..0000000
--- a/PHASE18-NOTES.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Phase 18 — SQL Editor and Result Grid
-
-Built from the Phase 17 project archive and includes the correction for the Phase 17 `localizedCaseInsensitiveHasPrefix` compilation error.
-
-## Changes
-- Execute highlighted SQL independently of the complete editor buffer (Run Selection).
-- SQL completion includes cached table and view names loaded in the Schemas navigator, alongside SQL keywords. Column-aware suggestions are not yet implemented.
-- Case-insensitive local filtering of query result rows.
-- Local result sorting by chosen column, ascending or descending.
-- CSV export of the currently displayed rows, with RFC-style doubled quotes, UTF-8 BOM and CRLF line endings.
-- Query elapsed wall-clock time, shown after execution completes (includes connection overhead).
-
-## Limitations
-- Result grid filters/sorts operate only on rows returned by the existing query engine, not the full database table.
-- The CSV writer constructs the file in memory and should not be used for huge result sets.
-- SQL autocomplete suggestions depend on schemas already loaded; no column metadata is fetched.
-- No new SQL error underlining was added.
-- Not compiled on macOS or tested against a MySQL server; run xcodebuild on the Mac.
diff --git a/WorkbenchReplacement/MainView.swift b/WorkbenchReplacement/MainView.swift
index 36bda32..9a4c4a9 100644
--- a/WorkbenchReplacement/MainView.swift
+++ b/WorkbenchReplacement/MainView.swift
@@ -750,7 +750,20 @@ struct MainView: View {
}
private var sqlEditor: some View {
- HStack(spacing: 0) {
+ VStack(spacing: 0) {
+ if model.status == "Error" && !model.result.message.isEmpty {
+ HStack(alignment: .top, spacing: 8) {
+ Image(systemName: "exclamationmark.triangle.fill")
+ .foregroundStyle(.red)
+ Text(model.result.message)
+ .font(.system(size: 11, design: .monospaced))
+ .textSelection(.enabled)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ }
+ .padding(8)
+ .background(Color.red.opacity(0.09))
+ }
+ HStack(spacing: 0) {
if UserDefaults.standard.object(
forKey: "preferences.showLineNumbers"
) as? Bool ?? true {
@@ -767,7 +780,8 @@ struct MainView: View {
SQLTextView(text: $model.sql, selectedSQL: $selectedSQL, completions: Array(Set(model.schemaTables.values.flatMap { $0 } + model.schemaViews.values.flatMap { $0 } + (model.schemaColumns[model.selectedDatabase] ?? []))).sorted())
}
- .background(Color(nsColor: .textBackgroundColor))
+ .background(Color(nsColor: .textBackgroundColor))
+ }
}
private var lineNumbers: some View {
@@ -1349,6 +1363,50 @@ private struct WorkbenchExplainView: View {
return value.trimmingCharacters(in: .whitespacesAndNewlines)
}
+ // Advisory analysis of the tabular EXPLAIN result. These are optimizer
+ // estimates, not measured execution statistics or automatic index DDL.
+ private var planInsights: [String] {
+ guard !useJSON, !plan.columns.isEmpty else { return [] }
+ let headings = plan.columns.map { $0.lowercased() }
+ func field(_ row: [String], _ name: String) -> String {
+ guard let index = headings.firstIndex(of: name), index < row.count else { return "" }
+ return row[index]
+ }
+ var messages: [String] = []
+ for row in plan.rows {
+ let table = field(row, "table")
+ let label = table.isEmpty ? "A query step" : "Table \(table)"
+ let access = field(row, "type").uppercased()
+ let key = field(row, "key")
+ let estimatedRows = field(row, "rows")
+ let extra = field(row, "extra").lowercased()
+ if access == "ALL" {
+ messages.append("\(label): full table scan (type=ALL). Check selectivity, predicates and available indexes before considering a new index.")
+ } else if access == "INDEX" {
+ messages.append("\(label): full index scan (type=index). This is not necessarily a problem for small tables or covering indexes.")
+ }
+ if key.isEmpty || key.uppercased() == "NULL" {
+ messages.append("\(label): no index selected for this step. An index is not always beneficial.")
+ }
+ if extra.contains("using temporary") {
+ messages.append("\(label): temporary table may be used for grouping, sorting or other operations.")
+ }
+ if extra.contains("using filesort") {
+ messages.append("\(label): additional sort operation indicated (Using filesort).")
+ }
+ if !estimatedRows.isEmpty, estimatedRows.uppercased() != "NULL" {
+ messages.append("\(label): optimizer estimates \(estimatedRows) rows examined for this step.")
+ }
+ }
+ return messages
+ }
+
+ private var copyablePlan: String {
+ if useJSON { return plan.rows.first?.first ?? "" }
+ return ([plan.columns.joined(separator: "\t")] + plan.rows.map { $0.joined(separator: "\t") })
+ .joined(separator: "\n")
+ }
+
private var eligible: Bool {
// Deliberately restrict this inspector to SELECT queries. EXPLAIN for
// UPDATE/DELETE is supported by MySQL but not offered here to keep
@@ -1385,6 +1443,11 @@ private struct WorkbenchExplainView: View {
.frame(width: 240)
Button("Run EXPLAIN") { Task { await loadPlan() } }
.disabled(isLoading || !eligible || model.selected == nil)
+ Button("Copy Plan") {
+ NSPasteboard.general.clearContents()
+ NSPasteboard.general.setString(copyablePlan, forType: .string)
+ }
+ .disabled(plan.columns.isEmpty)
if isLoading { ProgressView().controlSize(.small) }
if let duration {
Text(String(format: "%.3f s", duration))
@@ -1413,7 +1476,22 @@ private struct WorkbenchExplainView: View {
} else {
ContentUnavailableView("No plan loaded", systemImage: "chart.bar.doc.horizontal")
}
- Text("EXPLAIN shows estimates, not actual query execution times. The plan depends on the server's current statistics and configuration.")
+ if !planInsights.isEmpty {
+ DisclosureGroup("Plan observations (\(planInsights.count))") {
+ ScrollView {
+ VStack(alignment: .leading, spacing: 6) {
+ ForEach(Array(planInsights.enumerated()), id: \.offset) { _, observation in
+ Label(observation, systemImage: "info.circle")
+ .frame(maxWidth: .infinity, alignment: .leading)
+ }
+ }
+ .font(.callout)
+ .padding(.vertical, 6)
+ }
+ .frame(maxHeight: 160)
+ }
+ }
+ Text("EXPLAIN shows estimates, not actual query execution times. Observations are heuristics, not index recommendations. Confirm with representative data before changing production indexes.")
.font(.caption)
.foregroundStyle(.secondary)
}
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 946163a..650a97c 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 d2b1ced..1d0b408 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
@@ -247,7 +247,7 @@
"key": "%lld",
"location": {
"startingColumn": 26,
- "startingLine": 782
+ "startingLine": 796
}
},
{
@@ -255,7 +255,7 @@
"key": "Result Grid",
"location": {
"startingColumn": 22,
- "startingLine": 814
+ "startingLine": 828
}
},
{
@@ -263,7 +263,7 @@
"key": "Filter rows",
"location": {
"startingColumn": 31,
- "startingLine": 820
+ "startingLine": 834
}
},
{
@@ -271,7 +271,7 @@
"key": "Sort",
"location": {
"startingColumn": 28,
- "startingLine": 823
+ "startingLine": 837
}
},
{
@@ -279,7 +279,7 @@
"key": "Ascending",
"location": {
"startingColumn": 50,
- "startingLine": 829
+ "startingLine": 843
}
},
{
@@ -287,7 +287,7 @@
"key": "Descending",
"location": {
"startingColumn": 64,
- "startingLine": 829
+ "startingLine": 843
}
},
{
@@ -295,7 +295,7 @@
"key": "Columns",
"location": {
"startingColumn": 26,
- "startingLine": 832
+ "startingLine": 846
}
},
{
@@ -303,7 +303,7 @@
"key": "Export CSV",
"location": {
"startingColumn": 28,
- "startingLine": 847
+ "startingLine": 861
}
},
{
@@ -311,7 +311,7 @@
"key": "Show All Columns",
"location": {
"startingColumn": 32,
- "startingLine": 833
+ "startingLine": 847
}
},
{
@@ -319,7 +319,7 @@
"key": "No query results",
"location": {
"startingColumn": 26,
- "startingLine": 864
+ "startingLine": 878
}
},
{
@@ -327,7 +327,7 @@
"key": "%lld matching rows · showing %lld–%lld",
"location": {
"startingColumn": 26,
- "startingLine": 871
+ "startingLine": 885
}
},
{
@@ -335,7 +335,7 @@
"key": "Page %lld / %lld",
"location": {
"startingColumn": 26,
- "startingLine": 876
+ "startingLine": 890
}
},
{
@@ -343,7 +343,7 @@
"key": "Previous",
"location": {
"startingColumn": 28,
- "startingLine": 874
+ "startingLine": 888
}
},
{
@@ -351,7 +351,7 @@
"key": "Next",
"location": {
"startingColumn": 28,
- "startingLine": 877
+ "startingLine": 891
}
},
{
@@ -359,7 +359,7 @@
"key": "Action Output",
"location": {
"startingColumn": 22,
- "startingLine": 962
+ "startingLine": 976
}
},
{
@@ -367,7 +367,7 @@
"key": "UTF-8",
"location": {
"startingColumn": 18,
- "startingLine": 1037
+ "startingLine": 1051
}
},
{
@@ -375,7 +375,7 @@
"key": "Filter SQL or connection",
"location": {
"startingColumn": 23,
- "startingLine": 1088
+ "startingLine": 1102
}
},
{
@@ -383,7 +383,7 @@
"key": "Query History",
"location": {
"startingColumn": 22,
- "startingLine": 1082
+ "startingLine": 1096
}
},
{
@@ -391,7 +391,7 @@
"key": "Clear History",
"location": {
"startingColumn": 24,
- "startingLine": 1084
+ "startingLine": 1098
}
},
{
@@ -399,7 +399,7 @@
"key": "Done",
"location": {
"startingColumn": 24,
- "startingLine": 1086
+ "startingLine": 1100
}
},
{
@@ -407,7 +407,7 @@
"key": "Open in New Tab",
"location": {
"startingColumn": 32,
- "startingLine": 1100
+ "startingLine": 1114
}
},
{
@@ -415,7 +415,7 @@
"key": "Filter saved queries",
"location": {
"startingColumn": 23,
- "startingLine": 1130
+ "startingLine": 1144
}
},
{
@@ -423,7 +423,7 @@
"key": "Saved SQL Queries",
"location": {
"startingColumn": 22,
- "startingLine": 1126
+ "startingLine": 1140
}
},
{
@@ -431,7 +431,7 @@
"key": "Done",
"location": {
"startingColumn": 24,
- "startingLine": 1128
+ "startingLine": 1142
}
},
{
@@ -439,7 +439,7 @@
"key": "Open",
"location": {
"startingColumn": 28,
- "startingLine": 1143
+ "startingLine": 1157
}
},
{
@@ -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": 1369
+ "startingLine": 1427
}
},
{
@@ -455,15 +455,15 @@
"key": "Select a SELECT or WITH query in the SQL editor before opening EXPLAIN.",
"location": {
"startingColumn": 124,
- "startingLine": 1396
+ "startingLine": 1459
}
},
{
"comment": "",
- "key": "EXPLAIN shows estimates, not actual query execution times. The plan depends on the server's current statistics and configuration.",
+ "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": 1416
+ "startingLine": 1494
}
},
{
@@ -471,7 +471,7 @@
"key": "SELECT query required",
"location": {
"startingColumn": 40,
- "startingLine": 1396
+ "startingLine": 1459
}
},
{
@@ -479,7 +479,15 @@
"key": "No plan loaded",
"location": {
"startingColumn": 40,
- "startingLine": 1414
+ "startingLine": 1477
+ }
+ },
+ {
+ "comment": "",
+ "key": "Plan observations (%lld)",
+ "location": {
+ "startingColumn": 33,
+ "startingLine": 1480
}
},
{
@@ -487,7 +495,7 @@
"key": "Query Execution Plan",
"location": {
"startingColumn": 23,
- "startingLine": 1364
+ "startingLine": 1422
}
},
{
@@ -495,7 +503,7 @@
"key": "Close",
"location": {
"startingColumn": 24,
- "startingLine": 1367
+ "startingLine": 1425
}
},
{
@@ -503,7 +511,7 @@
"key": "Plan format",
"location": {
"startingColumn": 24,
- "startingLine": 1380
+ "startingLine": 1438
}
},
{
@@ -511,7 +519,15 @@
"key": "Run EXPLAIN",
"location": {
"startingColumn": 24,
- "startingLine": 1386
+ "startingLine": 1444
+ }
+ },
+ {
+ "comment": "",
+ "key": "Copy Plan",
+ "location": {
+ "startingColumn": 24,
+ "startingLine": 1446
}
},
{
@@ -519,7 +535,7 @@
"key": "Table",
"location": {
"startingColumn": 26,
- "startingLine": 1381
+ "startingLine": 1439
}
},
{
@@ -527,7 +543,7 @@
"key": "JSON",
"location": {
"startingColumn": 26,
- "startingLine": 1382
+ "startingLine": 1440
}
},
{
@@ -535,7 +551,7 @@
"key": "Connection Settings",
"location": {
"startingColumn": 18,
- "startingLine": 1466
+ "startingLine": 1544
}
},
{
@@ -543,7 +559,7 @@
"key": "Connection Name",
"location": {
"startingColumn": 21,
- "startingLine": 1471
+ "startingLine": 1549
}
},
{
@@ -551,7 +567,7 @@
"key": "Hostname",
"location": {
"startingColumn": 21,
- "startingLine": 1476
+ "startingLine": 1554
}
},
{
@@ -559,7 +575,7 @@
"key": "Port",
"location": {
"startingColumn": 21,
- "startingLine": 1481
+ "startingLine": 1559
}
},
{
@@ -567,7 +583,7 @@
"key": "Username",
"location": {
"startingColumn": 21,
- "startingLine": 1487
+ "startingLine": 1565
}
},
{
@@ -575,7 +591,7 @@
"key": "Password (macOS Keychain)",
"location": {
"startingColumn": 21,
- "startingLine": 1492
+ "startingLine": 1570
}
},
{
@@ -583,7 +599,7 @@
"key": "Default Schema",
"location": {
"startingColumn": 21,
- "startingLine": 1497
+ "startingLine": 1575
}
},
{
@@ -591,7 +607,7 @@
"key": "Cancel",
"location": {
"startingColumn": 24,
- "startingLine": 1505
+ "startingLine": 1583
}
},
{
@@ -599,7 +615,7 @@
"key": "Save",
"location": {
"startingColumn": 24,
- "startingLine": 1509
+ "startingLine": 1587
}
},
{
@@ -607,7 +623,7 @@
"key": "Terminate MySQL session?",
"location": {
"startingColumn": 13,
- "startingLine": 2004
+ "startingLine": 2082
}
},
{
@@ -615,7 +631,7 @@
"key": "The server did not return any columns.",
"location": {
"startingColumn": 25,
- "startingLine": 1937
+ "startingLine": 2015
}
},
{
@@ -623,7 +639,7 @@
"key": "Loading %@…",
"location": {
"startingColumn": 30,
- "startingLine": 1915
+ "startingLine": 1993
}
},
{
@@ -631,7 +647,7 @@
"key": "No Data",
"location": {
"startingColumn": 21,
- "startingLine": 1934
+ "startingLine": 2012
}
},
{
@@ -639,7 +655,7 @@
"key": "Unable to Load %@",
"location": {
"startingColumn": 25,
- "startingLine": 1921
+ "startingLine": 1999
}
},
{
@@ -647,7 +663,7 @@
"key": "Retry",
"location": {
"startingColumn": 28,
- "startingLine": 1927
+ "startingLine": 2005
}
},
{
@@ -655,7 +671,7 @@
"key": "Changes require confirmation",
"location": {
"startingColumn": 26,
- "startingLine": 1981
+ "startingLine": 2059
}
},
{
@@ -663,7 +679,7 @@
"key": "Account",
"location": {
"startingColumn": 28,
- "startingLine": 1960
+ "startingLine": 2038
}
},
{
@@ -671,7 +687,7 @@
"key": "Show Grants",
"location": {
"startingColumn": 28,
- "startingLine": 1971
+ "startingLine": 2049
}
},
{
@@ -679,7 +695,7 @@
"key": "Manage Accounts…",
"location": {
"startingColumn": 28,
- "startingLine": 1975
+ "startingLine": 2053
}
},
{
@@ -687,7 +703,7 @@
"key": "Select account",
"location": {
"startingColumn": 30,
- "startingLine": 1961
+ "startingLine": 2039
}
},
{
@@ -695,7 +711,7 @@
"key": "%@@%@",
"location": {
"startingColumn": 38,
- "startingLine": 1965
+ "startingLine": 2043
}
},
{
@@ -703,7 +719,7 @@
"key": "%@ session %@",
"location": {
"startingColumn": 24,
- "startingLine": 2012
+ "startingLine": 2090
}
},
{
@@ -711,7 +727,7 @@
"key": "Cancel",
"location": {
"startingColumn": 20,
- "startingLine": 2017
+ "startingLine": 2095
}
},
{
@@ -719,7 +735,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": 2019
+ "startingLine": 2097
}
},
{
@@ -727,7 +743,7 @@
"key": "Grants: %@",
"location": {
"startingColumn": 22,
- "startingLine": 2033
+ "startingLine": 2111
}
},
{
@@ -735,7 +751,7 @@
"key": "Close",
"location": {
"startingColumn": 24,
- "startingLine": 2045
+ "startingLine": 2123
}
},
{
@@ -743,7 +759,7 @@
"key": "Requires PROCESS privilege to see other users' sessions and appropriate privileges to terminate them.",
"location": {
"startingColumn": 18,
- "startingLine": 2075
+ "startingLine": 2153
}
},
{
@@ -751,7 +767,7 @@
"key": "Session",
"location": {
"startingColumn": 24,
- "startingLine": 2055
+ "startingLine": 2133
}
},
{
@@ -759,7 +775,7 @@
"key": "Stop Query",
"location": {
"startingColumn": 24,
- "startingLine": 2062
+ "startingLine": 2140
}
},
{
@@ -767,7 +783,7 @@
"key": "Disconnect Session",
"location": {
"startingColumn": 24,
- "startingLine": 2064
+ "startingLine": 2142
}
},
{
@@ -775,7 +791,7 @@
"key": "Select a session",
"location": {
"startingColumn": 26,
- "startingLine": 2056
+ "startingLine": 2134
}
},
{
@@ -783,7 +799,7 @@
"key": "ID %@",
"location": {
"startingColumn": 30,
- "startingLine": 2058
+ "startingLine": 2136
}
},
{
@@ -791,7 +807,7 @@
"key": "Filter results",
"location": {
"startingColumn": 27,
- "startingLine": 2140
+ "startingLine": 2218
}
},
{
@@ -799,7 +815,7 @@
"key": "Auto-refresh",
"location": {
"startingColumn": 20,
- "startingLine": 2145
+ "startingLine": 2223
}
},
{
@@ -807,7 +823,7 @@
"key": "Refresh every five seconds",
"location": {
"startingColumn": 23,
- "startingLine": 2147
+ "startingLine": 2225
}
},
{
@@ -815,7 +831,7 @@
"key": "Refresh",
"location": {
"startingColumn": 23,
- "startingLine": 2152
+ "startingLine": 2230
}
},
{
@@ -823,7 +839,7 @@
"key": "SQL Editor",
"location": {
"startingColumn": 23,
- "startingLine": 2159
+ "startingLine": 2237
}
},
{
@@ -831,7 +847,7 @@
"key": "Not loaded",
"location": {
"startingColumn": 22,
- "startingLine": 2237
+ "startingLine": 2315
}
},
{
@@ -839,7 +855,7 @@
"key": "Updated %@",
"location": {
"startingColumn": 21,
- "startingLine": 2234
+ "startingLine": 2312
}
},
{
@@ -847,7 +863,7 @@
"key": "%lld rows",
"location": {
"startingColumn": 18,
- "startingLine": 2242
+ "startingLine": 2320
}
},
{
@@ -855,7 +871,7 @@
"key": "Import SQL into %@?",
"location": {
"startingColumn": 13,
- "startingLine": 2549
+ "startingLine": 2627
}
},
{
@@ -863,7 +879,7 @@
"key": "Operation",
"location": {
"startingColumn": 20,
- "startingLine": 2352
+ "startingLine": 2430
}
},
{
@@ -871,7 +887,7 @@
"key": "Data Export",
"location": {
"startingColumn": 22,
- "startingLine": 2353
+ "startingLine": 2431
}
},
{
@@ -879,7 +895,7 @@
"key": "Data Import / Restore",
"location": {
"startingColumn": 22,
- "startingLine": 2354
+ "startingLine": 2432
}
},
{
@@ -887,7 +903,7 @@
"key": "MySQL Connection",
"location": {
"startingColumn": 26,
- "startingLine": 2361
+ "startingLine": 2439
}
},
{
@@ -895,7 +911,7 @@
"key": "Export Options",
"location": {
"startingColumn": 30,
- "startingLine": 2385
+ "startingLine": 2463
}
},
{
@@ -903,7 +919,7 @@
"key": "Import Options",
"location": {
"startingColumn": 30,
- "startingLine": 2441
+ "startingLine": 2519
}
},
{
@@ -911,7 +927,7 @@
"key": "Progress",
"location": {
"startingColumn": 26,
- "startingLine": 2453
+ "startingLine": 2531
}
},
{
@@ -919,7 +935,7 @@
"key": "Connection",
"location": {
"startingColumn": 36,
- "startingLine": 2363
+ "startingLine": 2441
}
},
{
@@ -927,7 +943,7 @@
"key": "Database",
"location": {
"startingColumn": 28,
- "startingLine": 2367
+ "startingLine": 2445
}
},
{
@@ -935,7 +951,7 @@
"key": "Select Database",
"location": {
"startingColumn": 30,
- "startingLine": 2368
+ "startingLine": 2446
}
},
{
@@ -943,7 +959,7 @@
"key": "Refresh Databases",
"location": {
"startingColumn": 32,
- "startingLine": 2376
+ "startingLine": 2454
}
},
{
@@ -951,7 +967,7 @@
"key": "No tables found. Select a database or refresh the list.",
"location": {
"startingColumn": 34,
- "startingLine": 2407
+ "startingLine": 2485
}
},
{
@@ -959,7 +975,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": 2436
+ "startingLine": 2514
}
},
{
@@ -967,7 +983,7 @@
"key": "Table structure",
"location": {
"startingColumn": 32,
- "startingLine": 2387
+ "startingLine": 2465
}
},
{
@@ -975,7 +991,7 @@
"key": "Table data",
"location": {
"startingColumn": 32,
- "startingLine": 2388
+ "startingLine": 2466
}
},
{
@@ -983,7 +999,7 @@
"key": "Add DROP TABLE IF EXISTS",
"location": {
"startingColumn": 32,
- "startingLine": 2389
+ "startingLine": 2467
}
},
{
@@ -991,7 +1007,7 @@
"key": "Include views (definitions)",
"location": {
"startingColumn": 32,
- "startingLine": 2391
+ "startingLine": 2469
}
},
{
@@ -999,7 +1015,7 @@
"key": "Include triggers",
"location": {
"startingColumn": 32,
- "startingLine": 2393
+ "startingLine": 2471
}
},
{
@@ -1007,7 +1023,7 @@
"key": "Include stored procedures and functions",
"location": {
"startingColumn": 32,
- "startingLine": 2395
+ "startingLine": 2473
}
},
{
@@ -1015,7 +1031,7 @@
"key": "Loading tables…",
"location": {
"startingColumn": 57,
- "startingLine": 2405
+ "startingLine": 2483
}
},
{
@@ -1023,7 +1039,7 @@
"key": "Tables (%lld selected)",
"location": {
"startingColumn": 34,
- "startingLine": 2398
+ "startingLine": 2476
}
},
{
@@ -1031,7 +1047,7 @@
"key": "All",
"location": {
"startingColumn": 36,
- "startingLine": 2400
+ "startingLine": 2478
}
},
{
@@ -1039,7 +1055,7 @@
"key": "None",
"location": {
"startingColumn": 36,
- "startingLine": 2401
+ "startingLine": 2479
}
},
{
@@ -1047,7 +1063,7 @@
"key": "Refresh",
"location": {
"startingColumn": 36,
- "startingLine": 2402
+ "startingLine": 2480
}
},
{
@@ -1055,7 +1071,7 @@
"key": "Views (%lld selected)",
"location": {
"startingColumn": 38,
- "startingLine": 2421
+ "startingLine": 2499
}
},
{
@@ -1063,7 +1079,7 @@
"key": "All",
"location": {
"startingColumn": 40,
- "startingLine": 2423
+ "startingLine": 2501
}
},
{
@@ -1071,7 +1087,7 @@
"key": "None",
"location": {
"startingColumn": 40,
- "startingLine": 2424
+ "startingLine": 2502
}
},
{
@@ -1079,7 +1095,7 @@
"key": "Import SQL statements into the selected database.",
"location": {
"startingColumn": 30,
- "startingLine": 2443
+ "startingLine": 2521
}
},
{
@@ -1087,7 +1103,7 @@
"key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.",
"location": {
"startingColumn": 30,
- "startingLine": 2446
+ "startingLine": 2524
}
},
{
@@ -1095,7 +1111,7 @@
"key": "Current table: %@ / %@ rows · Total exported: %@",
"location": {
"startingColumn": 30,
- "startingLine": 2470
+ "startingLine": 2548
}
},
{
@@ -1103,7 +1119,7 @@
"key": "Close",
"location": {
"startingColumn": 24,
- "startingLine": 2484
+ "startingLine": 2562
}
},
{
@@ -1111,7 +1127,7 @@
"key": "Verify Backup…",
"location": {
"startingColumn": 28,
- "startingLine": 2492
+ "startingLine": 2570
}
},
{
@@ -1119,7 +1135,7 @@
"key": "Verify an SQL file against its SHA-256 checksum sidecar",
"location": {
"startingColumn": 27,
- "startingLine": 2495
+ "startingLine": 2573
}
},
{
@@ -1127,7 +1143,7 @@
"key": "Verify Restored Database…",
"location": {
"startingColumn": 28,
- "startingLine": 2496
+ "startingLine": 2574
}
},
{
@@ -1135,7 +1151,7 @@
"key": "Compare restored table row counts and object names with the export manifest",
"location": {
"startingColumn": 27,
- "startingLine": 2500
+ "startingLine": 2578
}
},
{
@@ -1143,7 +1159,7 @@
"key": "Cancel",
"location": {
"startingColumn": 28,
- "startingLine": 2504
+ "startingLine": 2582
}
},
{
@@ -1151,7 +1167,7 @@
"key": "Export SQL…",
"location": {
"startingColumn": 31,
- "startingLine": 2511
+ "startingLine": 2589
}
},
{
@@ -1159,7 +1175,7 @@
"key": "Import SQL…",
"location": {
"startingColumn": 31,
- "startingLine": 2512
+ "startingLine": 2590
}
},
{
@@ -1167,7 +1183,7 @@
"key": "Import SQL",
"location": {
"startingColumn": 20,
- "startingLine": 2552
+ "startingLine": 2630
}
},
{
@@ -1175,7 +1191,7 @@
"key": "Cancel",
"location": {
"startingColumn": 20,
- "startingLine": 2560
+ "startingLine": 2638
}
},
{
@@ -1183,7 +1199,7 @@
"key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.",
"location": {
"startingColumn": 17,
- "startingLine": 2565
+ "startingLine": 2643
}
},
{
@@ -1191,7 +1207,7 @@
"key": "Database Import / Export",
"location": {
"startingColumn": 22,
- "startingLine": 2577
+ "startingLine": 2655
}
},
{
@@ -1199,7 +1215,7 @@
"key": "Built-in native MySQL database tools",
"location": {
"startingColumn": 22,
- "startingLine": 2580
+ "startingLine": 2658
}
},
{
@@ -1207,7 +1223,7 @@
"key": "Confirm %@",
"location": {
"startingColumn": 16,
- "startingLine": 3631
+ "startingLine": 3709
}
},
{
@@ -1215,7 +1231,7 @@
"key": "Manage MySQL Accounts",
"location": {
"startingColumn": 18,
- "startingLine": 3581
+ "startingLine": 3659
}
},
{
@@ -1223,7 +1239,7 @@
"key": "Account changes run on %@. Review the SQL before applying it.",
"location": {
"startingColumn": 18,
- "startingLine": 3582
+ "startingLine": 3660
}
},
{
@@ -1231,7 +1247,7 @@
"key": "Operation",
"location": {
"startingColumn": 24,
- "startingLine": 3585
+ "startingLine": 3663
}
},
{
@@ -1239,7 +1255,7 @@
"key": "Username",
"location": {
"startingColumn": 27,
- "startingLine": 3590
+ "startingLine": 3668
}
},
{
@@ -1247,7 +1263,7 @@
"key": "Host",
"location": {
"startingColumn": 27,
- "startingLine": 3592
+ "startingLine": 3670
}
},
{
@@ -1255,7 +1271,7 @@
"key": "Password",
"location": {
"startingColumn": 33,
- "startingLine": 3594
+ "startingLine": 3672
}
},
{
@@ -1263,7 +1279,7 @@
"key": "Database",
"location": {
"startingColumn": 31,
- "startingLine": 3597
+ "startingLine": 3675
}
},
{
@@ -1271,7 +1287,7 @@
"key": "Privilege",
"location": {
"startingColumn": 28,
- "startingLine": 3598
+ "startingLine": 3676
}
},
{
@@ -1279,7 +1295,7 @@
"key": "Close",
"location": {
"startingColumn": 24,
- "startingLine": 3613
+ "startingLine": 3691
}
},
{
@@ -1287,7 +1303,7 @@
"key": "Review SQL…",
"location": {
"startingColumn": 24,
- "startingLine": 3615
+ "startingLine": 3693
}
},
{
@@ -1295,7 +1311,7 @@
"key": "Cancel",
"location": {
"startingColumn": 20,
- "startingLine": 3632
+ "startingLine": 3710
}
},
{
@@ -1303,7 +1319,7 @@
"key": "Apply Change",
"location": {
"startingColumn": 20,
- "startingLine": 3633
+ "startingLine": 3711
}
},
{
@@ -1311,7 +1327,7 @@
"key": "Run this operation against %@?\n\n%@",
"location": {
"startingColumn": 18,
- "startingLine": 3638
+ "startingLine": 3716
}
}
]
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 31f9b51..68c6b91 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 955ed4f..5188bd7 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 066cc3e..bdd390d 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 265e392..3f7f3ac 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 2a7e471..4331b1c 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 1e9cdf9..4fd065f 100644
--- a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt
+++ b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt
@@ -27,3 +27,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 4252617..8bff9e1 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 eb1109b..8dd6e16 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 0c0fede..7dc74f5 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 70bd4df..f9bdf12 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
- gxPm9V32PbdbRR8yOyYXPS9q9hU=
+ PVnMv6+daaZqstZMQ+mKbqwrMxo=
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 265e392..3f7f3ac 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 066cc3e..bdd390d 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 14f87b5..747b123 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 fe286c4..de8d537 100644
--- a/build/Logs/Build/LogStoreManifest.plist
+++ b/build/Logs/Build/LogStoreManifest.plist
@@ -90,90 +90,6 @@
uniqueIdentifier
27B7943C-710F-4B49-802F-4D4346522832
- 546596EB-C774-4242-A9D1-C86D46C7CAE8
-
- className
- IDECommandLineBuildLog
- documentTypeString
- <nil>
- domainType
- Xcode.IDEActivityLogDomainType.BuildLog
- fileName
- 546596EB-C774-4242-A9D1-C86D46C7CAE8.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
- 813879115.19696796
- timeStoppedRecording
- 813879130.83529198
- title
- Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug
- uniqueIdentifier
- 546596EB-C774-4242-A9D1-C86D46C7CAE8
-
- 67C568D7-BB93-4240-AB48-EF7CBFC85D9F
-
- className
- IDECommandLineBuildLog
- documentTypeString
- <nil>
- domainType
- Xcode.IDEActivityLogDomainType.BuildLog
- fileName
- 67C568D7-BB93-4240-AB48-EF7CBFC85D9F.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
- 813879245.19600403
- timeStoppedRecording
- 813879260.71434796
- title
- Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug
- uniqueIdentifier
- 67C568D7-BB93-4240-AB48-EF7CBFC85D9F
-
9B404BEF-D5CC-4B4B-93CF-3A0C95C748D2
className
@@ -258,6 +174,48 @@
uniqueIdentifier
C0104C63-BBE9-4E40-811C-E7DC2E1277F6
+ FE752EBA-0994-486D-9A0C-1DA5C49E3451
+
+ className
+ IDECommandLineBuildLog
+ documentTypeString
+ <nil>
+ domainType
+ Xcode.IDEActivityLogDomainType.BuildLog
+ fileName
+ FE752EBA-0994-486D-9A0C-1DA5C49E3451.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
+ 813879926.65620303
+ timeStoppedRecording
+ 813879942.82261503
+ title
+ Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug
+ uniqueIdentifier
+ FE752EBA-0994-486D-9A0C-1DA5C49E3451
+
diff --git a/build/ModuleCache.noindex/Session.modulevalidation b/build/ModuleCache.noindex/Session.modulevalidation
index ef88f85..2486e5a 100644
--- a/build/ModuleCache.noindex/Session.modulevalidation
+++ b/build/ModuleCache.noindex/Session.modulevalidation
@@ -1 +1 @@
-1792186902.93557: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex")
+1792187127.0273461: 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 2ac346d..462f1c3 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 e27536f..13cd219 100644
--- a/build/info.plist
+++ b/build/info.plist
@@ -3,7 +3,7 @@
LastAccessedDate
- 2026-10-16T21:41:42Z
+ 2026-10-16T21:45:26Z
WorkspacePath
/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj
diff --git a/PHASE19-NOTES.md b/docs/PHASE19-NOTES.md
similarity index 100%
rename from PHASE19-NOTES.md
rename to docs/PHASE19-NOTES.md
diff --git a/PHASE20-NOTES.md b/docs/PHASE20-NOTES.md
similarity index 100%
rename from PHASE20-NOTES.md
rename to docs/PHASE20-NOTES.md
diff --git a/PHASE21-NOTES.md b/docs/PHASE21-NOTES.md
similarity index 100%
rename from PHASE21-NOTES.md
rename to docs/PHASE21-NOTES.md
diff --git a/docs/PHASE22-NOTES.md b/docs/PHASE22-NOTES.md
new file mode 100644
index 0000000..0c4af9c
--- /dev/null
+++ b/docs/PHASE22-NOTES.md
@@ -0,0 +1,9 @@
+# Phase 22 — SQL diagnostics and plan observations
+
+- Adds read-only interpretation of tabular MySQL EXPLAIN output: full scans, index scans, missing selected index, temporary tables, filesort and estimated row counts.
+- Adds Copy Plan for tabular and JSON EXPLAIN output.
+- Displays MySQL execution errors directly above the SQL editor, with selectable error text. No inaccurate error-line highlighting is attempted.
+- Index observations are explicitly advisory and do not generate or execute DDL.
+- Preserves Phase 21 and earlier project files.
+
+Build and runtime validation on macOS / a MySQL server are still required.