diff --git a/WorkbenchReplacement/MainView.swift b/WorkbenchReplacement/MainView.swift index 8839189..4b15db5 100644 --- a/WorkbenchReplacement/MainView.swift +++ b/WorkbenchReplacement/MainView.swift @@ -292,6 +292,11 @@ struct MainView: View { pages: [ .dashboard, .performanceReports, + .slowQueries, + .tableIO, + .innodbMetrics, + .innodbLocks, + .connectionStatistics, .performanceSchema ] ) @@ -1274,11 +1279,21 @@ enum WorkbenchAdminPage: String, CaseIterable, Identifiable { case performanceReports = "Performance Reports" case performanceSchema = "Performance Schema Setup" case serverLogs = "Server Error Log" + case slowQueries = "Slow Query Analysis" + case tableIO = "Table I/O Hotspots" + case innodbMetrics = "InnoDB Metrics" + case innodbLocks = "InnoDB Lock Waits" + case connectionStatistics = "Connection Statistics" var id: String { rawValue } var icon: String { switch self { + case .slowQueries: return "tortoise" + case .tableIO: return "externaldrive" + case .innodbMetrics: return "internaldrive" + case .innodbLocks: return "lock.shield" + case .connectionStatistics: return "network" case .serverLogs: return "text.alignleft" case .serverStatus: return "server.rack" case .clientConnections: return "person.2" @@ -1292,6 +1307,53 @@ enum WorkbenchAdminPage: String, CaseIterable, Identifiable { var statement: String { switch self { + case .slowQueries: + return """ + SELECT DIGEST_TEXT, COUNT_STAR, SUM_TIMER_WAIT, AVG_TIMER_WAIT, + MAX_TIMER_WAIT, SUM_ROWS_EXAMINED, SUM_ROWS_SENT, + SUM_NO_INDEX_USED, FIRST_SEEN, LAST_SEEN + FROM performance_schema.events_statements_summary_by_digest + WHERE DIGEST_TEXT IS NOT NULL + ORDER BY SUM_TIMER_WAIT DESC LIMIT 100 + """ + case .tableIO: + return """ + SELECT OBJECT_SCHEMA, OBJECT_NAME, COUNT_READ, COUNT_WRITE, + SUM_TIMER_READ, SUM_TIMER_WRITE + FROM performance_schema.table_io_waits_summary_by_table + WHERE OBJECT_SCHEMA IS NOT NULL + ORDER BY SUM_TIMER_WAIT DESC LIMIT 100 + """ + case .innodbMetrics: + return """ + SHOW GLOBAL STATUS WHERE Variable_name IN ( + 'Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads', + 'Innodb_buffer_pool_pages_total', 'Innodb_buffer_pool_pages_free', + 'Innodb_buffer_pool_pages_dirty', 'Innodb_data_reads', + 'Innodb_data_writes', 'Innodb_log_waits', + 'Innodb_row_lock_waits', 'Innodb_row_lock_time', + 'Innodb_rows_read', 'Innodb_rows_inserted', + 'Innodb_rows_updated', 'Innodb_rows_deleted' + ) + """ + case .innodbLocks: + return """ + SELECT REQUESTING_ENGINE_TRANSACTION_ID, BLOCKING_ENGINE_TRANSACTION_ID, + REQUESTING_ENGINE_LOCK_ID, BLOCKING_ENGINE_LOCK_ID + FROM performance_schema.data_lock_waits + LIMIT 100 + """ + case .connectionStatistics: + return """ + SHOW GLOBAL STATUS WHERE Variable_name IN ( + 'Uptime', 'Connections', 'Max_used_connections', + 'Threads_connected', 'Threads_running', 'Threads_cached', + 'Threads_created', 'Aborted_clients', 'Aborted_connects', + 'Connection_errors_max_connections', 'Slow_queries', + 'Questions', 'Queries', 'Created_tmp_disk_tables', + 'Select_full_join', 'Sort_merge_passes' + ) + """ case .serverLogs: return "SELECT LOGGED, PRIO, ERROR_CODE, SUBSYSTEM, DATA FROM performance_schema.error_log ORDER BY LOGGED DESC LIMIT 500" case .serverStatus: @@ -1382,6 +1444,23 @@ struct WorkbenchAdministrationView: View { @State private var processActionStatus: String? = nil @State private var terminatingProcess = false + private var diagnosticGuidance: String? { + switch page { + case .slowQueries: + return "Aggregated statement digests since Performance Schema statistics were last reset. Times are in picoseconds; this is not the server slow-query log. SQL text may be truncated." + case .tableIO: + return "Cumulative Performance Schema table I/O counters since last reset. Times are in picoseconds; refresh to observe changes." + case .innodbMetrics: + return "Cumulative InnoDB status counters; calculate rates from measurements at two different times." + case .innodbLocks: + return "Current InnoDB lock-wait relationships only. An empty result means no waits were visible at the time of the query." + case .connectionStatistics: + return "Server-wide counters since startup. Connections is cumulative, whereas Threads_connected is the current count." + default: + return nil + } + } + private var filteredRows: [[String]] { guard !searchText.isEmpty else { return result.rows } return result.rows.filter { row in @@ -1428,6 +1507,15 @@ struct WorkbenchAdministrationView: View { resultContent } + if let guidance = diagnosticGuidance { + Text(guidance) + .font(.caption) + .foregroundStyle(.secondary) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 16) + .padding(.vertical, 8) + } + if page == .clientConnections { processControls } 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 51e29fa..2e532ca 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 ad77631..2bdf14f 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 @@ -55,7 +55,7 @@ "key": "Set as Default Schema", "location": { "startingColumn": 24, - "startingLine": 382 + "startingLine": 387 } }, { @@ -63,7 +63,7 @@ "key": "Refresh Tables", "location": { "startingColumn": 24, - "startingLine": 386 + "startingLine": 391 } }, { @@ -71,7 +71,7 @@ "key": "Load Tables", "location": { "startingColumn": 28, - "startingLine": 456 + "startingLine": 461 } }, { @@ -79,7 +79,7 @@ "key": "Tables", "location": { "startingColumn": 22, - "startingLine": 429 + "startingLine": 434 } }, { @@ -87,7 +87,7 @@ "key": "Select First 100 Rows", "location": { "startingColumn": 20, - "startingLine": 512 + "startingLine": 517 } }, { @@ -95,7 +95,7 @@ "key": "New Query Tab", "location": { "startingColumn": 19, - "startingLine": 633 + "startingLine": 638 } }, { @@ -103,7 +103,7 @@ "key": "Default Schema:", "location": { "startingColumn": 18, - "startingLine": 658 + "startingLine": 663 } }, { @@ -111,7 +111,7 @@ "key": "SQL Editor", "location": { "startingColumn": 18, - "startingLine": 668 + "startingLine": 673 } }, { @@ -119,7 +119,7 @@ "key": "Execute SQL", "location": { "startingColumn": 19, - "startingLine": 646 + "startingLine": 651 } }, { @@ -127,7 +127,7 @@ "key": "Format SQL", "location": { "startingColumn": 19, - "startingLine": 653 + "startingLine": 658 } }, { @@ -135,7 +135,7 @@ "key": "%lld", "location": { "startingColumn": 26, - "startingLine": 708 + "startingLine": 713 } }, { @@ -143,7 +143,7 @@ "key": "Result Grid", "location": { "startingColumn": 22, - "startingLine": 740 + "startingLine": 745 } }, { @@ -151,7 +151,7 @@ "key": "No query results", "location": { "startingColumn": 26, - "startingLine": 758 + "startingLine": 763 } }, { @@ -159,7 +159,7 @@ "key": "Action Output", "location": { "startingColumn": 22, - "startingLine": 780 + "startingLine": 785 } }, { @@ -167,7 +167,7 @@ "key": "UTF-8", "location": { "startingColumn": 18, - "startingLine": 855 + "startingLine": 860 } }, { @@ -175,7 +175,7 @@ "key": "Connection Settings", "location": { "startingColumn": 18, - "startingLine": 1030 + "startingLine": 1035 } }, { @@ -183,7 +183,7 @@ "key": "Connection Name", "location": { "startingColumn": 21, - "startingLine": 1035 + "startingLine": 1040 } }, { @@ -191,7 +191,7 @@ "key": "Hostname", "location": { "startingColumn": 21, - "startingLine": 1040 + "startingLine": 1045 } }, { @@ -199,20 +199,12 @@ "key": "Port", "location": { "startingColumn": 21, - "startingLine": 1045 + "startingLine": 1050 } }, { "comment": "", "key": "Username", - "location": { - "startingColumn": 21, - "startingLine": 1051 - } - }, - { - "comment": "", - "key": "Password (macOS Keychain)", "location": { "startingColumn": 21, "startingLine": 1056 @@ -220,18 +212,26 @@ }, { "comment": "", - "key": "Default Schema", + "key": "Password (macOS Keychain)", "location": { "startingColumn": 21, "startingLine": 1061 } }, + { + "comment": "", + "key": "Default Schema", + "location": { + "startingColumn": 21, + "startingLine": 1066 + } + }, { "comment": "", "key": "Cancel", "location": { "startingColumn": 24, - "startingLine": 1069 + "startingLine": 1074 } }, { @@ -239,7 +239,7 @@ "key": "Save", "location": { "startingColumn": 24, - "startingLine": 1073 + "startingLine": 1078 } }, { @@ -247,7 +247,7 @@ "key": "Terminate MySQL session?", "location": { "startingColumn": 13, - "startingLine": 1481 + "startingLine": 1569 } }, { @@ -255,7 +255,7 @@ "key": "The server did not return any columns.", "location": { "startingColumn": 25, - "startingLine": 1423 + "startingLine": 1502 } }, { @@ -263,7 +263,7 @@ "key": "Loading %@…", "location": { "startingColumn": 30, - "startingLine": 1401 + "startingLine": 1480 } }, { @@ -271,7 +271,7 @@ "key": "No Data", "location": { "startingColumn": 21, - "startingLine": 1420 + "startingLine": 1499 } }, { @@ -279,7 +279,7 @@ "key": "Unable to Load %@", "location": { "startingColumn": 25, - "startingLine": 1407 + "startingLine": 1486 } }, { @@ -287,7 +287,7 @@ "key": "Retry", "location": { "startingColumn": 28, - "startingLine": 1413 + "startingLine": 1492 } }, { @@ -295,7 +295,7 @@ "key": "Changes require confirmation", "location": { "startingColumn": 26, - "startingLine": 1458 + "startingLine": 1546 } }, { @@ -303,7 +303,7 @@ "key": "Account", "location": { "startingColumn": 28, - "startingLine": 1437 + "startingLine": 1525 } }, { @@ -311,7 +311,7 @@ "key": "Show Grants", "location": { "startingColumn": 28, - "startingLine": 1448 + "startingLine": 1536 } }, { @@ -319,7 +319,7 @@ "key": "Manage Accounts…", "location": { "startingColumn": 28, - "startingLine": 1452 + "startingLine": 1540 } }, { @@ -327,7 +327,7 @@ "key": "Select account", "location": { "startingColumn": 30, - "startingLine": 1438 + "startingLine": 1526 } }, { @@ -335,7 +335,7 @@ "key": "%@@%@", "location": { "startingColumn": 38, - "startingLine": 1442 + "startingLine": 1530 } }, { @@ -343,7 +343,7 @@ "key": "%@ session %@", "location": { "startingColumn": 24, - "startingLine": 1489 + "startingLine": 1577 } }, { @@ -351,7 +351,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 1494 + "startingLine": 1582 } }, { @@ -359,7 +359,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": 1496 + "startingLine": 1584 } }, { @@ -367,7 +367,7 @@ "key": "Grants: %@", "location": { "startingColumn": 22, - "startingLine": 1510 + "startingLine": 1598 } }, { @@ -375,7 +375,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 1522 + "startingLine": 1610 } }, { @@ -383,7 +383,7 @@ "key": "Requires PROCESS privilege to see other users' sessions and appropriate privileges to terminate them.", "location": { "startingColumn": 18, - "startingLine": 1552 + "startingLine": 1640 } }, { @@ -391,7 +391,7 @@ "key": "Session", "location": { "startingColumn": 24, - "startingLine": 1532 + "startingLine": 1620 } }, { @@ -399,7 +399,7 @@ "key": "Stop Query", "location": { "startingColumn": 24, - "startingLine": 1539 + "startingLine": 1627 } }, { @@ -407,7 +407,7 @@ "key": "Disconnect Session", "location": { "startingColumn": 24, - "startingLine": 1541 + "startingLine": 1629 } }, { @@ -415,7 +415,7 @@ "key": "Select a session", "location": { "startingColumn": 26, - "startingLine": 1533 + "startingLine": 1621 } }, { @@ -423,7 +423,7 @@ "key": "ID %@", "location": { "startingColumn": 30, - "startingLine": 1535 + "startingLine": 1623 } }, { @@ -431,7 +431,7 @@ "key": "Filter results", "location": { "startingColumn": 27, - "startingLine": 1617 + "startingLine": 1705 } }, { @@ -439,7 +439,7 @@ "key": "Auto-refresh", "location": { "startingColumn": 20, - "startingLine": 1622 + "startingLine": 1710 } }, { @@ -447,7 +447,7 @@ "key": "Refresh every five seconds", "location": { "startingColumn": 23, - "startingLine": 1624 + "startingLine": 1712 } }, { @@ -455,7 +455,7 @@ "key": "Refresh", "location": { "startingColumn": 23, - "startingLine": 1629 + "startingLine": 1717 } }, { @@ -463,7 +463,7 @@ "key": "SQL Editor", "location": { "startingColumn": 23, - "startingLine": 1636 + "startingLine": 1724 } }, { @@ -471,7 +471,7 @@ "key": "Not loaded", "location": { "startingColumn": 22, - "startingLine": 1714 + "startingLine": 1802 } }, { @@ -479,7 +479,7 @@ "key": "Updated %@", "location": { "startingColumn": 21, - "startingLine": 1711 + "startingLine": 1799 } }, { @@ -487,7 +487,7 @@ "key": "%lld rows", "location": { "startingColumn": 18, - "startingLine": 1719 + "startingLine": 1807 } }, { @@ -495,7 +495,7 @@ "key": "Import SQL into %@?", "location": { "startingColumn": 13, - "startingLine": 2026 + "startingLine": 2114 } }, { @@ -503,7 +503,7 @@ "key": "Operation", "location": { "startingColumn": 20, - "startingLine": 1829 + "startingLine": 1917 } }, { @@ -511,7 +511,7 @@ "key": "Data Export", "location": { "startingColumn": 22, - "startingLine": 1830 + "startingLine": 1918 } }, { @@ -519,7 +519,7 @@ "key": "Data Import / Restore", "location": { "startingColumn": 22, - "startingLine": 1831 + "startingLine": 1919 } }, { @@ -527,7 +527,7 @@ "key": "MySQL Connection", "location": { "startingColumn": 26, - "startingLine": 1838 + "startingLine": 1926 } }, { @@ -535,7 +535,7 @@ "key": "Export Options", "location": { "startingColumn": 30, - "startingLine": 1862 + "startingLine": 1950 } }, { @@ -543,7 +543,7 @@ "key": "Import Options", "location": { "startingColumn": 30, - "startingLine": 1918 + "startingLine": 2006 } }, { @@ -551,7 +551,7 @@ "key": "Progress", "location": { "startingColumn": 26, - "startingLine": 1930 + "startingLine": 2018 } }, { @@ -559,7 +559,7 @@ "key": "Connection", "location": { "startingColumn": 36, - "startingLine": 1840 + "startingLine": 1928 } }, { @@ -567,7 +567,7 @@ "key": "Database", "location": { "startingColumn": 28, - "startingLine": 1844 + "startingLine": 1932 } }, { @@ -575,7 +575,7 @@ "key": "Select Database", "location": { "startingColumn": 30, - "startingLine": 1845 + "startingLine": 1933 } }, { @@ -583,7 +583,7 @@ "key": "Refresh Databases", "location": { "startingColumn": 32, - "startingLine": 1853 + "startingLine": 1941 } }, { @@ -591,7 +591,7 @@ "key": "No tables found. Select a database or refresh the list.", "location": { "startingColumn": 34, - "startingLine": 1884 + "startingLine": 1972 } }, { @@ -599,7 +599,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": 1913 + "startingLine": 2001 } }, { @@ -607,7 +607,7 @@ "key": "Table structure", "location": { "startingColumn": 32, - "startingLine": 1864 + "startingLine": 1952 } }, { @@ -615,7 +615,7 @@ "key": "Table data", "location": { "startingColumn": 32, - "startingLine": 1865 + "startingLine": 1953 } }, { @@ -623,7 +623,7 @@ "key": "Add DROP TABLE IF EXISTS", "location": { "startingColumn": 32, - "startingLine": 1866 + "startingLine": 1954 } }, { @@ -631,7 +631,7 @@ "key": "Include views (definitions)", "location": { "startingColumn": 32, - "startingLine": 1868 + "startingLine": 1956 } }, { @@ -639,7 +639,7 @@ "key": "Include triggers", "location": { "startingColumn": 32, - "startingLine": 1870 + "startingLine": 1958 } }, { @@ -647,7 +647,7 @@ "key": "Include stored procedures and functions", "location": { "startingColumn": 32, - "startingLine": 1872 + "startingLine": 1960 } }, { @@ -655,7 +655,7 @@ "key": "Loading tables…", "location": { "startingColumn": 57, - "startingLine": 1882 + "startingLine": 1970 } }, { @@ -663,7 +663,7 @@ "key": "Tables (%lld selected)", "location": { "startingColumn": 34, - "startingLine": 1875 + "startingLine": 1963 } }, { @@ -671,7 +671,7 @@ "key": "All", "location": { "startingColumn": 36, - "startingLine": 1877 + "startingLine": 1965 } }, { @@ -679,7 +679,7 @@ "key": "None", "location": { "startingColumn": 36, - "startingLine": 1878 + "startingLine": 1966 } }, { @@ -687,7 +687,7 @@ "key": "Refresh", "location": { "startingColumn": 36, - "startingLine": 1879 + "startingLine": 1967 } }, { @@ -695,7 +695,7 @@ "key": "Views (%lld selected)", "location": { "startingColumn": 38, - "startingLine": 1898 + "startingLine": 1986 } }, { @@ -703,7 +703,7 @@ "key": "All", "location": { "startingColumn": 40, - "startingLine": 1900 + "startingLine": 1988 } }, { @@ -711,7 +711,7 @@ "key": "None", "location": { "startingColumn": 40, - "startingLine": 1901 + "startingLine": 1989 } }, { @@ -719,7 +719,7 @@ "key": "Import SQL statements into the selected database.", "location": { "startingColumn": 30, - "startingLine": 1920 + "startingLine": 2008 } }, { @@ -727,7 +727,7 @@ "key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.", "location": { "startingColumn": 30, - "startingLine": 1923 + "startingLine": 2011 } }, { @@ -735,7 +735,7 @@ "key": "Current table: %@ / %@ rows · Total exported: %@", "location": { "startingColumn": 30, - "startingLine": 1947 + "startingLine": 2035 } }, { @@ -743,7 +743,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 1961 + "startingLine": 2049 } }, { @@ -751,7 +751,7 @@ "key": "Verify Backup…", "location": { "startingColumn": 28, - "startingLine": 1969 + "startingLine": 2057 } }, { @@ -759,7 +759,7 @@ "key": "Verify an SQL file against its SHA-256 checksum sidecar", "location": { "startingColumn": 27, - "startingLine": 1972 + "startingLine": 2060 } }, { @@ -767,7 +767,7 @@ "key": "Verify Restored Database…", "location": { "startingColumn": 28, - "startingLine": 1973 + "startingLine": 2061 } }, { @@ -775,7 +775,7 @@ "key": "Compare restored table row counts and object names with the export manifest", "location": { "startingColumn": 27, - "startingLine": 1977 + "startingLine": 2065 } }, { @@ -783,7 +783,7 @@ "key": "Cancel", "location": { "startingColumn": 28, - "startingLine": 1981 + "startingLine": 2069 } }, { @@ -791,7 +791,7 @@ "key": "Export SQL…", "location": { "startingColumn": 31, - "startingLine": 1988 + "startingLine": 2076 } }, { @@ -799,7 +799,7 @@ "key": "Import SQL…", "location": { "startingColumn": 31, - "startingLine": 1989 + "startingLine": 2077 } }, { @@ -807,7 +807,7 @@ "key": "Import SQL", "location": { "startingColumn": 20, - "startingLine": 2029 + "startingLine": 2117 } }, { @@ -815,7 +815,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 2037 + "startingLine": 2125 } }, { @@ -823,7 +823,7 @@ "key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.", "location": { "startingColumn": 17, - "startingLine": 2042 + "startingLine": 2130 } }, { @@ -831,7 +831,7 @@ "key": "Database Import / Export", "location": { "startingColumn": 22, - "startingLine": 2054 + "startingLine": 2142 } }, { @@ -839,7 +839,7 @@ "key": "Built-in native MySQL database tools", "location": { "startingColumn": 22, - "startingLine": 2057 + "startingLine": 2145 } }, { @@ -847,7 +847,7 @@ "key": "Confirm %@", "location": { "startingColumn": 16, - "startingLine": 3108 + "startingLine": 3196 } }, { @@ -855,7 +855,7 @@ "key": "Manage MySQL Accounts", "location": { "startingColumn": 18, - "startingLine": 3058 + "startingLine": 3146 } }, { @@ -863,7 +863,7 @@ "key": "Account changes run on %@. Review the SQL before applying it.", "location": { "startingColumn": 18, - "startingLine": 3059 + "startingLine": 3147 } }, { @@ -871,7 +871,7 @@ "key": "Operation", "location": { "startingColumn": 24, - "startingLine": 3062 + "startingLine": 3150 } }, { @@ -879,7 +879,7 @@ "key": "Username", "location": { "startingColumn": 27, - "startingLine": 3067 + "startingLine": 3155 } }, { @@ -887,7 +887,7 @@ "key": "Host", "location": { "startingColumn": 27, - "startingLine": 3069 + "startingLine": 3157 } }, { @@ -895,7 +895,7 @@ "key": "Password", "location": { "startingColumn": 33, - "startingLine": 3071 + "startingLine": 3159 } }, { @@ -903,7 +903,7 @@ "key": "Database", "location": { "startingColumn": 31, - "startingLine": 3074 + "startingLine": 3162 } }, { @@ -911,7 +911,7 @@ "key": "Privilege", "location": { "startingColumn": 28, - "startingLine": 3075 + "startingLine": 3163 } }, { @@ -919,7 +919,7 @@ "key": "Close", "location": { "startingColumn": 24, - "startingLine": 3090 + "startingLine": 3178 } }, { @@ -927,7 +927,7 @@ "key": "Review SQL…", "location": { "startingColumn": 24, - "startingLine": 3092 + "startingLine": 3180 } }, { @@ -935,7 +935,7 @@ "key": "Cancel", "location": { "startingColumn": 20, - "startingLine": 3109 + "startingLine": 3197 } }, { @@ -943,7 +943,7 @@ "key": "Apply Change", "location": { "startingColumn": 20, - "startingLine": 3110 + "startingLine": 3198 } }, { @@ -951,7 +951,7 @@ "key": "Run this operation against %@?\n\n%@", "location": { "startingColumn": 18, - "startingLine": 3115 + "startingLine": 3203 } } ] 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 a1080b8..686273b 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 5d91ed8..7aaec99 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 6665436..645bcd1 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 4f50f50..0cca635 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 67cc01a..4210473 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 ed4ed8c..65dfee8 100644 --- a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt +++ b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt @@ -21,3 +21,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 1b00f8c..73e48df 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 80bd7b7..cc634a2 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 eeca561..da1912e 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 1562649..c366b89 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 - gthWJHd1NQPpD+yBFdYymYDGvKo= + /SGo73prsJFq4QMPB62FM4xJVmQ= 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 4f50f50..0cca635 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 6665436..645bcd1 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 85809b3..9b5388d 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 358e975..a2e6479 100644 --- a/build/Logs/Build/LogStoreManifest.plist +++ b/build/Logs/Build/LogStoreManifest.plist @@ -6,6 +6,48 @@ 13 logs + 0BE3AE4B-CC13-4C5D-9DA7-73BB03242CE6 + + className + IDECommandLineBuildLog + documentTypeString + <nil> + domainType + Xcode.IDEActivityLogDomainType.BuildLog + fileName + 0BE3AE4B-CC13-4C5D-9DA7-73BB03242CE6.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 + 813878832.27419496 + timeStoppedRecording + 813878846.83622098 + title + Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug + uniqueIdentifier + 0BE3AE4B-CC13-4C5D-9DA7-73BB03242CE6 + A5DCBFE5-D4F5-45BE-8C98-8AC35E432D50 className @@ -48,48 +90,6 @@ uniqueIdentifier A5DCBFE5-D4F5-45BE-8C98-8AC35E432D50 - B8940B11-E198-4118-908A-6DCC06A473A1 - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - B8940B11-E198-4118-908A-6DCC06A473A1.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 - 813878192.18681705 - timeStoppedRecording - 813878206.57349706 - title - Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug - uniqueIdentifier - B8940B11-E198-4118-908A-6DCC06A473A1 - BE675D45-226B-48D5-9BED-8FCC12B530FF className diff --git a/build/ModuleCache.noindex/Session.modulevalidation b/build/ModuleCache.noindex/Session.modulevalidation index 5b0a868..620f4cc 100644 --- a/build/ModuleCache.noindex/Session.modulevalidation +++ b/build/ModuleCache.noindex/Session.modulevalidation @@ -1 +1 @@ -1792185899.0886679: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") +1792186032.67269: 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 b01f2c2..9b3a06d 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 9bb07cd..4af30d2 100644 --- a/build/info.plist +++ b/build/info.plist @@ -3,7 +3,7 @@ LastAccessedDate - 2026-10-16T21:24:58Z + 2026-10-16T21:27:12Z WorkspacePath /Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj diff --git a/PHASE13-NOTES.md b/docs/PHASE13-NOTES.md similarity index 100% rename from PHASE13-NOTES.md rename to docs/PHASE13-NOTES.md diff --git a/PHASE14-NOTES.md b/docs/PHASE14-NOTES.md similarity index 100% rename from PHASE14-NOTES.md rename to docs/PHASE14-NOTES.md diff --git a/PHASE15-NOTES.md b/docs/PHASE15-NOTES.md similarity index 100% rename from PHASE15-NOTES.md rename to docs/PHASE15-NOTES.md diff --git a/docs/PHASE16-NOTES.md b/docs/PHASE16-NOTES.md new file mode 100644 index 0000000..d407e02 --- /dev/null +++ b/docs/PHASE16-NOTES.md @@ -0,0 +1,15 @@ +# 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.