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:
Mark Gregory
2026-10-16 21:58:41 +01:00
parent 2b6eb249ce
commit d75515ffbc
27 changed files with 399 additions and 191 deletions
@@ -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
}
}
]
@@ -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",
@@ -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
@@ -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>