The second file contains a SHA-256 checksum of the SQL backup.
This allows the application to detect whether the backup file has changed or become corrupted. 2. Verify Backup button The Import/Export window now includes Verify Backup…. Select an exported SQL file, and the application calculates its checksum and compares it with the accompanying checksum file. 3. Automatic verification before import When importing an SQL file, WorkbenchReplacement checks for an accompanying .sha256 file. If one exists, its checksum must match before SQL execution begins. Third-party SQL files without checksum files can still be imported. 4. Large-file handling Checksum calculation reads files in 1 MB blocks rather than loading the entire file into memory. This reduces memory usage when verifying large backups.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
import CryptoKit
|
||||
|
||||
// MARK: - Classic Workbench Colours
|
||||
|
||||
@@ -1578,6 +1579,8 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
@State private var exportData = true
|
||||
@State private var includeDrop = false
|
||||
@State private var includeViews = true
|
||||
@State private var includeTriggers = false
|
||||
@State private var includeRoutines = false
|
||||
@State private var availableViews: [String] = []
|
||||
@State private var selectedViews: Set<String> = []
|
||||
@State private var loadingTables = false
|
||||
@@ -1635,6 +1638,10 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
.disabled(!exportStructure)
|
||||
Toggle("Include views (definitions)", isOn: $includeViews)
|
||||
.disabled(!exportStructure)
|
||||
Toggle("Include triggers", isOn: $includeTriggers)
|
||||
.disabled(!exportStructure)
|
||||
Toggle("Include stored procedures and functions", isOn: $includeRoutines)
|
||||
.disabled(!exportStructure)
|
||||
HStack {
|
||||
Text("Tables (\(selectedTables.count) selected)")
|
||||
Spacer()
|
||||
@@ -1674,7 +1681,7 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
))
|
||||
}
|
||||
}
|
||||
Text("Exports base tables and selected views. Triggers, routines, consistent snapshots and guaranteed row ordering are not yet supported.")
|
||||
Text("Triggers and routines require SHOW CREATE privileges. This exporter does not provide transaction-consistent snapshots or guaranteed row ordering.")
|
||||
.font(.caption).foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
@@ -1717,6 +1724,13 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
if !isWorking {
|
||||
Button("Verify Backup…") {
|
||||
chooseBackupToVerify()
|
||||
}
|
||||
.help("Verify an SQL file against its SHA-256 checksum sidecar")
|
||||
}
|
||||
|
||||
if isWorking {
|
||||
Button("Cancel") {
|
||||
operationTask?.cancel()
|
||||
@@ -1819,6 +1833,28 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func chooseBackupToVerify() {
|
||||
let panel = NSOpenPanel()
|
||||
panel.title = "Verify SQL Backup"
|
||||
panel.canChooseDirectories = false
|
||||
panel.canChooseFiles = true
|
||||
guard panel.runModal() == .OK, let url = panel.url else { return }
|
||||
operationTask = Task {
|
||||
isWorking = true
|
||||
progress = 0
|
||||
defer { isWorking = false; operationTask = nil }
|
||||
do {
|
||||
try await Task.detached(priority: .utility) {
|
||||
try WorkbenchBackupIntegrity.verify(url)
|
||||
}.value
|
||||
progress = 1
|
||||
status = "Verified SHA-256: \(url.lastPathComponent)"
|
||||
} catch {
|
||||
status = "Verification failed: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func chooseImportSource() {
|
||||
let panel = NSOpenPanel()
|
||||
panel.title = "Import MySQL SQL File"
|
||||
@@ -1921,7 +1957,7 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
let views = exportStructure && includeViews
|
||||
? availableViews.filter { selectedViews.contains($0) }
|
||||
: []
|
||||
guard !selectedTables.isEmpty || !views.isEmpty else { return }
|
||||
guard !selectedTables.isEmpty || !views.isEmpty || (exportStructure && (includeTriggers || includeRoutines)) else { return }
|
||||
isWorking = true
|
||||
progress = 0
|
||||
let tables = availableTables.filter { selectedTables.contains($0) }
|
||||
@@ -2010,6 +2046,59 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
try write(row[1] + ";\n")
|
||||
progress = Double(tables.count + index + 1) / Double(max(tables.count + views.count, 1))
|
||||
}
|
||||
// Routines and triggers use client-side delimiters. The importer
|
||||
// strips these directives and sends each definition as one query.
|
||||
if exportStructure && includeRoutines {
|
||||
let routines = try await execute("""
|
||||
SELECT ROUTINE_NAME, ROUTINE_TYPE
|
||||
FROM information_schema.ROUTINES
|
||||
WHERE ROUTINE_SCHEMA = \(sqlLiteral(database))
|
||||
ORDER BY ROUTINE_TYPE, ROUTINE_NAME
|
||||
""")
|
||||
for routine in routines.rows {
|
||||
try Task.checkCancellation()
|
||||
guard routine.count >= 2 else { continue }
|
||||
let name = routine[0]
|
||||
let kind = routine[1].uppercased()
|
||||
guard kind == "PROCEDURE" || kind == "FUNCTION" else { continue }
|
||||
status = "Exporting \(kind.lowercased()) \(name)"
|
||||
let qualified = "\(db).\(quoteIdentifier(name))"
|
||||
let definition = try await execute("SHOW CREATE \(kind) \(qualified)")
|
||||
// SHOW CREATE PROCEDURE/FUNCTION: Name, sql_mode, Create ..., ...
|
||||
guard let row = definition.rows.first, row.count > 2,
|
||||
!row[2].isEmpty else {
|
||||
throw WorkbenchTransferError.message("Missing CREATE \(kind) for \(name)")
|
||||
}
|
||||
try write("\n-- \(kind) \(qualified)\n")
|
||||
try write("DELIMITER $$\n")
|
||||
if includeDrop { try write("DROP \(kind) IF EXISTS \(qualified)$$\n") }
|
||||
try write(row[2] + "$$\nDELIMITER ;\n")
|
||||
}
|
||||
}
|
||||
if exportStructure && includeTriggers {
|
||||
let triggers = try await execute("""
|
||||
SELECT TRIGGER_NAME
|
||||
FROM information_schema.TRIGGERS
|
||||
WHERE TRIGGER_SCHEMA = \(sqlLiteral(database))
|
||||
ORDER BY TRIGGER_NAME
|
||||
""")
|
||||
for trigger in triggers.rows {
|
||||
try Task.checkCancellation()
|
||||
guard let name = trigger.first else { continue }
|
||||
status = "Exporting trigger \(name)"
|
||||
let qualified = "\(db).\(quoteIdentifier(name))"
|
||||
let definition = try await execute("SHOW CREATE TRIGGER \(qualified)")
|
||||
// SHOW CREATE TRIGGER: Trigger, sql_mode, SQL Original Statement, ...
|
||||
guard let row = definition.rows.first, row.count > 2,
|
||||
!row[2].isEmpty else {
|
||||
throw WorkbenchTransferError.message("Missing CREATE TRIGGER for \(name)")
|
||||
}
|
||||
try write("\n-- Trigger \(qualified)\n")
|
||||
try write("DELIMITER $$\n")
|
||||
if includeDrop { try write("DROP TRIGGER IF EXISTS \(qualified)$$\n") }
|
||||
try write(row[2] + "$$\nDELIMITER ;\n")
|
||||
}
|
||||
}
|
||||
try Task.checkCancellation()
|
||||
try handle?.close()
|
||||
handle = nil
|
||||
@@ -2018,8 +2107,16 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
} else {
|
||||
try FileManager.default.moveItem(at: tempURL, to: url)
|
||||
}
|
||||
progress = 1
|
||||
status = "Export complete: \(tables.count) tables, \(views.count) views"
|
||||
// The SQL file is complete before creating its checksum sidecar.
|
||||
// Sidecar failure is reported; it does not delete a completed export.
|
||||
do {
|
||||
try WorkbenchBackupIntegrity.createSidecar(for: url)
|
||||
progress = 1
|
||||
status = "Export complete and SHA-256 verified: \(tables.count) tables, \(views.count) views"
|
||||
} catch {
|
||||
progress = 1
|
||||
status = "SQL export complete, but checksum creation failed: \(error.localizedDescription)"
|
||||
}
|
||||
} catch is CancellationError {
|
||||
status = "Export cancelled; no final backup was written"
|
||||
} catch {
|
||||
@@ -2044,6 +2141,15 @@ struct WorkbenchDatabaseTransferView: View {
|
||||
}
|
||||
|
||||
do {
|
||||
// Refuse a corrupted backup when a checksum sidecar is present.
|
||||
// Third-party SQL files without a sidecar remain importable.
|
||||
if WorkbenchBackupIntegrity.hasSidecar(for: url) {
|
||||
status = "Verifying SQL backup checksum…"
|
||||
try await Task.detached(priority: .utility) {
|
||||
try WorkbenchBackupIntegrity.verify(url)
|
||||
}.value
|
||||
}
|
||||
try Task.checkCancellation()
|
||||
let sql = try String(
|
||||
contentsOf: url,
|
||||
encoding: .utf8
|
||||
@@ -2223,3 +2329,54 @@ enum WorkbenchSQLImportParser {
|
||||
return statements
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Backup integrity (SHA-256)
|
||||
// A checksum detects accidental changes to a completed SQL dump. It does NOT
|
||||
// establish transaction consistency, completeness or authenticity.
|
||||
private enum WorkbenchBackupIntegrity {
|
||||
static func sidecar(for sqlURL: URL) -> URL {
|
||||
URL(fileURLWithPath: sqlURL.path + ".sha256")
|
||||
}
|
||||
|
||||
static func hasSidecar(for sqlURL: URL) -> Bool {
|
||||
FileManager.default.fileExists(atPath: sidecar(for: sqlURL).path)
|
||||
}
|
||||
|
||||
static func digest(_ url: URL) throws -> String {
|
||||
let file = try FileHandle(forReadingFrom: url)
|
||||
defer { try? file.close() }
|
||||
var hasher = SHA256()
|
||||
while true {
|
||||
let chunk = try file.read(upToCount: 1024 * 1024) ?? Data()
|
||||
if chunk.isEmpty { break }
|
||||
hasher.update(data: chunk)
|
||||
}
|
||||
return hasher.finalize().map { String(format: "%02x", $0) }.joined()
|
||||
}
|
||||
|
||||
static func createSidecar(for sqlURL: URL) throws {
|
||||
let value = try digest(sqlURL)
|
||||
// Standard sha256sum-compatible format, with filename rather than path.
|
||||
let line = value + " " + sqlURL.lastPathComponent + "\n"
|
||||
try line.write(to: sidecar(for: sqlURL), atomically: true, encoding: .utf8)
|
||||
}
|
||||
|
||||
static func verify(_ sqlURL: URL) throws {
|
||||
let checksumURL = sidecar(for: sqlURL)
|
||||
guard FileManager.default.fileExists(atPath: checksumURL.path) else {
|
||||
throw WorkbenchTransferError.message("No checksum file found: \(checksumURL.lastPathComponent)")
|
||||
}
|
||||
let contents = try String(contentsOf: checksumURL, encoding: .utf8)
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let expected = String(contents.prefix(64)).lowercased()
|
||||
guard expected.count == 64,
|
||||
expected.utf8.allSatisfy({ (48...57).contains($0) || (97...102).contains($0) }) else {
|
||||
throw WorkbenchTransferError.message("Invalid SHA-256 checksum file")
|
||||
}
|
||||
let actual = try digest(sqlURL)
|
||||
guard actual == expected else {
|
||||
throw WorkbenchTransferError.message("SHA-256 mismatch: backup contents have changed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
BIN
Binary file not shown.
+1
-1
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
+200
-168
@@ -7,7 +7,7 @@
|
||||
"key": "MySQL Workbench",
|
||||
"location": {
|
||||
"startingColumn": 18,
|
||||
"startingLine": 98
|
||||
"startingLine": 99
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -15,7 +15,7 @@
|
||||
"key": "Navigator",
|
||||
"location": {
|
||||
"startingColumn": 20,
|
||||
"startingLine": 187
|
||||
"startingLine": 188
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -23,7 +23,7 @@
|
||||
"key": "Administration",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 188
|
||||
"startingLine": 189
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -31,7 +31,7 @@
|
||||
"key": "Schemas",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 189
|
||||
"startingLine": 190
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -39,7 +39,7 @@
|
||||
"key": "SCHEMAS",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 209
|
||||
"startingLine": 210
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -47,7 +47,7 @@
|
||||
"key": "Edit Connection",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 263
|
||||
"startingLine": 264
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55,7 +55,7 @@
|
||||
"key": "Set as Default Schema",
|
||||
"location": {
|
||||
"startingColumn": 24,
|
||||
"startingLine": 380
|
||||
"startingLine": 381
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -63,7 +63,7 @@
|
||||
"key": "Refresh Tables",
|
||||
"location": {
|
||||
"startingColumn": 24,
|
||||
"startingLine": 384
|
||||
"startingLine": 385
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -71,7 +71,7 @@
|
||||
"key": "Load Tables",
|
||||
"location": {
|
||||
"startingColumn": 28,
|
||||
"startingLine": 454
|
||||
"startingLine": 455
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -79,7 +79,7 @@
|
||||
"key": "Tables",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 427
|
||||
"startingLine": 428
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -87,7 +87,7 @@
|
||||
"key": "Select First 100 Rows",
|
||||
"location": {
|
||||
"startingColumn": 20,
|
||||
"startingLine": 510
|
||||
"startingLine": 511
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -95,7 +95,7 @@
|
||||
"key": "New Query Tab",
|
||||
"location": {
|
||||
"startingColumn": 19,
|
||||
"startingLine": 631
|
||||
"startingLine": 632
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -103,7 +103,7 @@
|
||||
"key": "Default Schema:",
|
||||
"location": {
|
||||
"startingColumn": 18,
|
||||
"startingLine": 656
|
||||
"startingLine": 657
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -111,7 +111,7 @@
|
||||
"key": "SQL Editor",
|
||||
"location": {
|
||||
"startingColumn": 18,
|
||||
"startingLine": 666
|
||||
"startingLine": 667
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -119,7 +119,7 @@
|
||||
"key": "Execute SQL",
|
||||
"location": {
|
||||
"startingColumn": 19,
|
||||
"startingLine": 644
|
||||
"startingLine": 645
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -127,7 +127,7 @@
|
||||
"key": "Format SQL",
|
||||
"location": {
|
||||
"startingColumn": 19,
|
||||
"startingLine": 651
|
||||
"startingLine": 652
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -135,7 +135,7 @@
|
||||
"key": "%lld",
|
||||
"location": {
|
||||
"startingColumn": 26,
|
||||
"startingLine": 706
|
||||
"startingLine": 707
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -143,7 +143,7 @@
|
||||
"key": "Result Grid",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 738
|
||||
"startingLine": 739
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -151,7 +151,7 @@
|
||||
"key": "No query results",
|
||||
"location": {
|
||||
"startingColumn": 26,
|
||||
"startingLine": 756
|
||||
"startingLine": 757
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -159,7 +159,7 @@
|
||||
"key": "Action Output",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 778
|
||||
"startingLine": 779
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -167,7 +167,7 @@
|
||||
"key": "UTF-8",
|
||||
"location": {
|
||||
"startingColumn": 18,
|
||||
"startingLine": 853
|
||||
"startingLine": 854
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -175,7 +175,7 @@
|
||||
"key": "Connection Settings",
|
||||
"location": {
|
||||
"startingColumn": 18,
|
||||
"startingLine": 1028
|
||||
"startingLine": 1029
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -183,7 +183,7 @@
|
||||
"key": "Connection Name",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1033
|
||||
"startingLine": 1034
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -191,7 +191,7 @@
|
||||
"key": "Hostname",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1038
|
||||
"startingLine": 1039
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -199,7 +199,7 @@
|
||||
"key": "Port",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1043
|
||||
"startingLine": 1044
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -207,7 +207,7 @@
|
||||
"key": "Username",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1049
|
||||
"startingLine": 1050
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -215,7 +215,7 @@
|
||||
"key": "Password (macOS Keychain)",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1054
|
||||
"startingLine": 1055
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -223,7 +223,7 @@
|
||||
"key": "Default Schema",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1059
|
||||
"startingLine": 1060
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -231,7 +231,7 @@
|
||||
"key": "Cancel",
|
||||
"location": {
|
||||
"startingColumn": 24,
|
||||
"startingLine": 1067
|
||||
"startingLine": 1068
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -239,7 +239,7 @@
|
||||
"key": "Save",
|
||||
"location": {
|
||||
"startingColumn": 24,
|
||||
"startingLine": 1071
|
||||
"startingLine": 1072
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -247,7 +247,7 @@
|
||||
"key": "The server did not return any columns.",
|
||||
"location": {
|
||||
"startingColumn": 25,
|
||||
"startingLine": 1393
|
||||
"startingLine": 1394
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -255,7 +255,7 @@
|
||||
"key": "Loading %@…",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1371
|
||||
"startingLine": 1372
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -263,7 +263,7 @@
|
||||
"key": "No Data",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1390
|
||||
"startingLine": 1391
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -271,7 +271,7 @@
|
||||
"key": "Unable to Load %@",
|
||||
"location": {
|
||||
"startingColumn": 25,
|
||||
"startingLine": 1377
|
||||
"startingLine": 1378
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -279,7 +279,7 @@
|
||||
"key": "Retry",
|
||||
"location": {
|
||||
"startingColumn": 28,
|
||||
"startingLine": 1383
|
||||
"startingLine": 1384
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -287,7 +287,7 @@
|
||||
"key": "Refresh",
|
||||
"location": {
|
||||
"startingColumn": 23,
|
||||
"startingLine": 1432
|
||||
"startingLine": 1433
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -295,7 +295,7 @@
|
||||
"key": "SQL Editor",
|
||||
"location": {
|
||||
"startingColumn": 23,
|
||||
"startingLine": 1439
|
||||
"startingLine": 1440
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -303,7 +303,7 @@
|
||||
"key": "Not loaded",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 1517
|
||||
"startingLine": 1518
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -311,7 +311,7 @@
|
||||
"key": "Updated %@",
|
||||
"location": {
|
||||
"startingColumn": 21,
|
||||
"startingLine": 1514
|
||||
"startingLine": 1515
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -319,7 +319,7 @@
|
||||
"key": "%lld rows",
|
||||
"location": {
|
||||
"startingColumn": 18,
|
||||
"startingLine": 1522
|
||||
"startingLine": 1523
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -327,7 +327,7 @@
|
||||
"key": "Import SQL into %@?",
|
||||
"location": {
|
||||
"startingColumn": 13,
|
||||
"startingLine": 1766
|
||||
"startingLine": 1780
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -335,7 +335,7 @@
|
||||
"key": "Operation",
|
||||
"location": {
|
||||
"startingColumn": 20,
|
||||
"startingLine": 1597
|
||||
"startingLine": 1600
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -343,7 +343,7 @@
|
||||
"key": "Data Export",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 1598
|
||||
"startingLine": 1601
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -351,7 +351,7 @@
|
||||
"key": "Data Import / Restore",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 1599
|
||||
"startingLine": 1602
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -359,7 +359,7 @@
|
||||
"key": "MySQL Connection",
|
||||
"location": {
|
||||
"startingColumn": 26,
|
||||
"startingLine": 1606
|
||||
"startingLine": 1609
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -367,7 +367,7 @@
|
||||
"key": "Export Options",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1630
|
||||
"startingLine": 1633
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -375,7 +375,7 @@
|
||||
"key": "Import Options",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1682
|
||||
"startingLine": 1689
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -383,7 +383,7 @@
|
||||
"key": "Progress",
|
||||
"location": {
|
||||
"startingColumn": 26,
|
||||
"startingLine": 1694
|
||||
"startingLine": 1701
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -391,7 +391,7 @@
|
||||
"key": "Connection",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 1608
|
||||
"startingLine": 1611
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -399,7 +399,7 @@
|
||||
"key": "Database",
|
||||
"location": {
|
||||
"startingColumn": 28,
|
||||
"startingLine": 1612
|
||||
"startingLine": 1615
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -407,7 +407,7 @@
|
||||
"key": "Select Database",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1613
|
||||
"startingLine": 1616
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -415,7 +415,7 @@
|
||||
"key": "Refresh Databases",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1621
|
||||
"startingLine": 1624
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -423,127 +423,143 @@
|
||||
"key": "No tables found. Select a database or refresh the list.",
|
||||
"location": {
|
||||
"startingColumn": 34,
|
||||
"startingLine": 1648
|
||||
"startingLine": 1655
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Exports base tables and selected views. Triggers, routines, consistent snapshots and guaranteed row ordering are not yet supported.",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1677
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Table structure",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1632
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Table data",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1633
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Add DROP TABLE IF EXISTS",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1634
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Include views (definitions)",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1636
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Loading tables…",
|
||||
"location": {
|
||||
"startingColumn": 57,
|
||||
"startingLine": 1646
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Tables (%lld selected)",
|
||||
"location": {
|
||||
"startingColumn": 34,
|
||||
"startingLine": 1639
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "All",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 1641
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "None",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 1642
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Refresh",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 1643
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Views (%lld selected)",
|
||||
"location": {
|
||||
"startingColumn": 38,
|
||||
"startingLine": 1662
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "All",
|
||||
"location": {
|
||||
"startingColumn": 40,
|
||||
"startingLine": 1664
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "None",
|
||||
"location": {
|
||||
"startingColumn": 40,
|
||||
"startingLine": 1665
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Import SQL statements into the selected database.",
|
||||
"key": "Triggers and routines require SHOW CREATE privileges. This exporter does not provide transaction-consistent snapshots or guaranteed row ordering.",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1684
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Table structure",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1635
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Table data",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1636
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Add DROP TABLE IF EXISTS",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1637
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Include views (definitions)",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1639
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Include triggers",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1641
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Include stored procedures and functions",
|
||||
"location": {
|
||||
"startingColumn": 32,
|
||||
"startingLine": 1643
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Loading tables…",
|
||||
"location": {
|
||||
"startingColumn": 57,
|
||||
"startingLine": 1653
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Tables (%lld selected)",
|
||||
"location": {
|
||||
"startingColumn": 34,
|
||||
"startingLine": 1646
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "All",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 1648
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "None",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 1649
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Refresh",
|
||||
"location": {
|
||||
"startingColumn": 36,
|
||||
"startingLine": 1650
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Views (%lld selected)",
|
||||
"location": {
|
||||
"startingColumn": 38,
|
||||
"startingLine": 1669
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "All",
|
||||
"location": {
|
||||
"startingColumn": 40,
|
||||
"startingLine": 1671
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "None",
|
||||
"location": {
|
||||
"startingColumn": 40,
|
||||
"startingLine": 1672
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Import SQL statements into the selected database.",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1691
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.",
|
||||
"location": {
|
||||
"startingColumn": 30,
|
||||
"startingLine": 1687
|
||||
"startingLine": 1694
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -551,7 +567,23 @@
|
||||
"key": "Close",
|
||||
"location": {
|
||||
"startingColumn": 24,
|
||||
"startingLine": 1713
|
||||
"startingLine": 1720
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Verify Backup…",
|
||||
"location": {
|
||||
"startingColumn": 28,
|
||||
"startingLine": 1728
|
||||
}
|
||||
},
|
||||
{
|
||||
"comment": "",
|
||||
"key": "Verify an SQL file against its SHA-256 checksum sidecar",
|
||||
"location": {
|
||||
"startingColumn": 27,
|
||||
"startingLine": 1731
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -559,7 +591,7 @@
|
||||
"key": "Cancel",
|
||||
"location": {
|
||||
"startingColumn": 28,
|
||||
"startingLine": 1721
|
||||
"startingLine": 1735
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -567,7 +599,7 @@
|
||||
"key": "Export SQL…",
|
||||
"location": {
|
||||
"startingColumn": 31,
|
||||
"startingLine": 1728
|
||||
"startingLine": 1742
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -575,7 +607,7 @@
|
||||
"key": "Import SQL…",
|
||||
"location": {
|
||||
"startingColumn": 31,
|
||||
"startingLine": 1729
|
||||
"startingLine": 1743
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -583,7 +615,7 @@
|
||||
"key": "Import SQL",
|
||||
"location": {
|
||||
"startingColumn": 20,
|
||||
"startingLine": 1769
|
||||
"startingLine": 1783
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -591,7 +623,7 @@
|
||||
"key": "Cancel",
|
||||
"location": {
|
||||
"startingColumn": 20,
|
||||
"startingLine": 1777
|
||||
"startingLine": 1791
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -599,7 +631,7 @@
|
||||
"key": "SQL files can modify or delete existing database objects and data. Ensure you have a backup before continuing.",
|
||||
"location": {
|
||||
"startingColumn": 17,
|
||||
"startingLine": 1782
|
||||
"startingLine": 1796
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -607,7 +639,7 @@
|
||||
"key": "Database Import / Export",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 1794
|
||||
"startingLine": 1808
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -615,7 +647,7 @@
|
||||
"key": "Built-in native MySQL database tools",
|
||||
"location": {
|
||||
"startingColumn": 22,
|
||||
"startingLine": 1797
|
||||
"startingLine": 1811
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
BIN
Binary file not shown.
+17
@@ -64,6 +64,11 @@
|
||||
"moduleName" : "CoreVideo",
|
||||
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CoreVideo.swiftmodule\/arm64e-apple-macos.swiftmodule"
|
||||
},
|
||||
{
|
||||
"isFramework" : true,
|
||||
"moduleName" : "CryptoKit",
|
||||
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/CryptoKit.swiftmodule\/arm64e-apple-macos.swiftmodule"
|
||||
},
|
||||
{
|
||||
"isFramework" : false,
|
||||
"moduleName" : "Darwin",
|
||||
@@ -99,6 +104,11 @@
|
||||
"moduleName" : "ImageIO",
|
||||
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/ImageIO.swiftmodule\/arm64e-apple-macos.swiftmodule"
|
||||
},
|
||||
{
|
||||
"isFramework" : true,
|
||||
"moduleName" : "LocalAuthentication",
|
||||
"modulePath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Toolchains\/XcodeDefault.xctoolchain\/usr\/lib\/swift\/macosx\/prebuilt-modules\/27.0\/LocalAuthentication.swiftmodule\/arm64e-apple-macos.swiftmodule"
|
||||
},
|
||||
{
|
||||
"isFramework" : false,
|
||||
"moduleName" : "Metal",
|
||||
@@ -403,6 +413,13 @@
|
||||
"isFramework" : false,
|
||||
"moduleName" : "ImageIO"
|
||||
},
|
||||
{
|
||||
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/System\/Library\/Frameworks\/LocalAuthentication.framework\/Modules\/module.modulemap",
|
||||
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/LocalAuthentication-678PKH1D7S8W88AP15VFRNL98.pcm",
|
||||
"isBridgingHeaderDependency" : false,
|
||||
"isFramework" : false,
|
||||
"moduleName" : "LocalAuthentication"
|
||||
},
|
||||
{
|
||||
"clangModuleMapPath" : "\/Applications\/Xcode.app\/Contents\/Developer\/Platforms\/MacOSX.platform\/Developer\/SDKs\/MacOSX.sdk\/usr\/include\/DarwinBasic.modulemap",
|
||||
"clangModulePath" : "\/Volumes\/Developer\/Applications\/WorkbenchReplacement\/build\/SDKExplicitPrecompiledModules\/MachO-9GGXQQ5TIOCIBWIKR2CCZVSB1.pcm",
|
||||
|
||||
+4
-4
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -10,3 +10,5 @@
|
||||
/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
|
||||
/Volumes/Developer/Applications/WorkbenchReplacement/build/Build/Products/Debug/WorkbenchReplacement.app
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+1
-1
@@ -19,7 +19,7 @@
|
||||
<dict>
|
||||
<key>cdhash</key>
|
||||
<data>
|
||||
cNkEGkXY0XL0979Pg1Y/5+wqs/g=
|
||||
RB/YriyQlwTsASbrsirLbaoN96E=
|
||||
</data>
|
||||
<key>requirement</key>
|
||||
<string>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 */</string>
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@
|
||||
<integer>13</integer>
|
||||
<key>logs</key>
|
||||
<dict>
|
||||
<key>20B4B0A0-41A0-44A0-BA02-3E5707BABE60</key>
|
||||
<key>32E9EC2D-B9B8-4CD2-8B3D-CD693A4CBD97</key>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
<string>IDECommandLineBuildLog</string>
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>domainType</key>
|
||||
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
|
||||
<key>fileName</key>
|
||||
<string>20B4B0A0-41A0-44A0-BA02-3E5707BABE60.xcactivitylog</string>
|
||||
<string>32E9EC2D-B9B8-4CD2-8B3D-CD693A4CBD97.xcactivitylog</string>
|
||||
<key>hasPrimaryLog</key>
|
||||
<true/>
|
||||
<key>primaryObservable</key>
|
||||
@@ -40,13 +40,13 @@
|
||||
<key>signature</key>
|
||||
<string>Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug</string>
|
||||
<key>timeStartedRecording</key>
|
||||
<real>813876442.84578598</real>
|
||||
<real>813876968.59599304</real>
|
||||
<key>timeStoppedRecording</key>
|
||||
<real>813876454.95874095</real>
|
||||
<real>813876981.606776</real>
|
||||
<key>title</key>
|
||||
<string>Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug</string>
|
||||
<key>uniqueIdentifier</key>
|
||||
<string>20B4B0A0-41A0-44A0-BA02-3E5707BABE60</string>
|
||||
<string>32E9EC2D-B9B8-4CD2-8B3D-CD693A4CBD97</string>
|
||||
</dict>
|
||||
<key>3389C95F-527F-43D4-BFD7-803CFE84AD6A</key>
|
||||
<dict>
|
||||
@@ -132,7 +132,7 @@
|
||||
<key>uniqueIdentifier</key>
|
||||
<string>705B952B-2A37-40D9-B731-8E2290C15462</string>
|
||||
</dict>
|
||||
<key>AF582207-87B3-4A2E-A532-DA0FD8F61FBD</key>
|
||||
<key>921D995A-6441-4831-9E4E-3283B2F38B89</key>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
<string>IDECommandLineBuildLog</string>
|
||||
@@ -141,7 +141,7 @@
|
||||
<key>domainType</key>
|
||||
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
|
||||
<key>fileName</key>
|
||||
<string>AF582207-87B3-4A2E-A532-DA0FD8F61FBD.xcactivitylog</string>
|
||||
<string>921D995A-6441-4831-9E4E-3283B2F38B89.xcactivitylog</string>
|
||||
<key>hasPrimaryLog</key>
|
||||
<true/>
|
||||
<key>primaryObservable</key>
|
||||
@@ -166,13 +166,13 @@
|
||||
<key>signature</key>
|
||||
<string>Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug</string>
|
||||
<key>timeStartedRecording</key>
|
||||
<real>813876270.55029094</real>
|
||||
<real>813877067.82482302</real>
|
||||
<key>timeStoppedRecording</key>
|
||||
<real>813876283.37291896</real>
|
||||
<real>813877081.68267095</real>
|
||||
<key>title</key>
|
||||
<string>Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug</string>
|
||||
<key>uniqueIdentifier</key>
|
||||
<string>AF582207-87B3-4A2E-A532-DA0FD8F61FBD</string>
|
||||
<string>921D995A-6441-4831-9E4E-3283B2F38B89</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
@@ -1 +1 @@
|
||||
1792184043.218933: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex")
|
||||
1792184268.211045: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex")
|
||||
|
||||
BIN
Binary file not shown.
+1
-1
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LastAccessedDate</key>
|
||||
<date>2026-10-16T20:54:03Z</date>
|
||||
<date>2026-10-16T20:57:48Z</date>
|
||||
<key>WorkspacePath</key>
|
||||
<string>/Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj</string>
|
||||
</dict>
|
||||
|
||||
Reference in New Issue
Block a user