diff --git a/WorkbenchReplacement/MainView.swift b/WorkbenchReplacement/MainView.swift index 672d1c9..f571930 100644 --- a/WorkbenchReplacement/MainView.swift +++ b/WorkbenchReplacement/MainView.swift @@ -1684,7 +1684,7 @@ struct WorkbenchDatabaseTransferView: View { Text("Import SQL statements into the selected database.") .foregroundStyle(.secondary) - Text("Supports ordinary SQL statements. Dumps using custom DELIMITER directives or stored routines are not supported yet.") + Text("Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.") .font(.caption) .foregroundStyle(.secondary) } @@ -2091,145 +2091,135 @@ enum WorkbenchTransferError: LocalizedError { } // MARK: - SQL Import Parser -// -// Handles ordinary SQL statements with quoted strings, -// quoted identifiers, line comments and block comments. -// Stored routines and custom DELIMITER directives are -// deliberately rejected rather than executed incorrectly. +// Supports MySQL DELIMITER directives, quoted text, executable /*! */ +// comments, and routine bodies. Parsing does not execute SQL. enum WorkbenchSQLImportParser { static func parse(_ sql: String) throws -> [String] { + let chars = Array(sql) var statements: [String] = [] var current = "" - var quote: Character? - var escaped = false + var delimiter = ";" + var quote: Character? = nil var lineComment = false var blockComment = false + var escaped = false + var lineStart = true + var i = 0 - let characters = Array(sql) - var index = 0 + func matches(_ value: String, at index: Int) -> Bool { + let pattern = Array(value) + guard index + pattern.count <= chars.count else { return false } + return Array(chars[index..<(index + pattern.count)]) == pattern + } - while index < characters.count { - let char = characters[index] - let next: Character? = index + 1 < characters.count - ? characters[index + 1] - : nil + func emit() { + let value = current.trimmingCharacters(in: .whitespacesAndNewlines) + if !value.isEmpty { statements.append(value) } + current = "" + } + + while i < chars.count { + let ch = chars[i] + let next: Character? = i + 1 < chars.count ? chars[i + 1] : nil + + // DELIMITER is a client-side directive, never sent to MySQL. + if lineStart && quote == nil && !lineComment && !blockComment { + var j = i + while j < chars.count && (chars[j] == " " || chars[j] == "\t") { j += 1 } + if j + 9 <= chars.count && String(chars[j..= characters.count - || characters[index + 2].isWhitespace)) { + if ch == "#" || (ch == "-" && next == "-" && + (i + 2 == chars.count || chars[i + 2].isWhitespace)) { lineComment = true - index += char == "#" ? 1 : 2 + i += ch == "#" ? 1 : 2 continue } - - if char == "/", next == "*" { - // Executable MySQL comments are not safe to discard. - if index + 2 < characters.count, - characters[index + 2] == "!" { - throw WorkbenchTransferError.message( - "This SQL file contains MySQL executable comments, which are not supported by this importer yet." - ) - } - + if ch == "/" && next == "*" { + // Keep executable comments; ordinary comments may also contain + // SQL hints, so preserve all block comments verbatim. + current.append("/*") blockComment = true - index += 2 + i += 2 continue } - - if char == ";" { - let statement = current.trimmingCharacters( - in: .whitespacesAndNewlines - ) - - if !statement.isEmpty { - statements.append(statement) - } - - current = "" - index += 1 + if matches(delimiter, at: i) { + emit() + i += delimiter.count + lineStart = false continue } - - current.append(char) - index += 1 + current.append(ch) + if ch == "\n" || ch == "\r" { lineStart = true } + else if !ch.isWhitespace { lineStart = false } + i += 1 } - - if quote != nil || blockComment { - throw WorkbenchTransferError.message( - "SQL file contains an unterminated quote or comment." - ) + guard quote == nil && !blockComment else { + throw WorkbenchTransferError.message("SQL file ends inside a quoted string or block comment") } - - let remaining = current.trimmingCharacters( - in: .whitespacesAndNewlines - ) - - if !remaining.isEmpty { - statements.append(remaining) + guard delimiter == ";" else { + throw WorkbenchTransferError.message("SQL file ends without resetting DELIMITER to ;") } - - for statement in statements { - let upper = statement.uppercased() - - if upper.hasPrefix("DELIMITER ") - || upper.hasPrefix("CREATE PROCEDURE") - || upper.hasPrefix("CREATE FUNCTION") - || upper.hasPrefix("CREATE TRIGGER") - || upper.hasPrefix("CREATE EVENT") { - throw WorkbenchTransferError.message( - "Stored routines, triggers, events and DELIMITER directives require the advanced SQL importer." - ) - } - } - + emit() return statements } } 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 ebdef0a..69734c3 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 905f9e6..bece08a 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 @@ -540,7 +540,7 @@ }, { "comment": "", - "key": "Supports ordinary SQL statements. Dumps using custom DELIMITER directives or stored routines are not supported yet.", + "key": "Supports MySQL DELIMITER directives, triggers and stored routines. Import is not transactional; use a verified backup before restoring.", "location": { "startingColumn": 30, "startingLine": 1687 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 f639971..e230162 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 823ed6e..54503cd 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.swiftsourceinfo b/build/Build/Intermediates.noindex/WorkbenchReplacement.build/Debug/WorkbenchReplacement.build/Objects-normal/arm64/WorkbenchReplacement.swiftsourceinfo index 3a3b0f9..d6ec236 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 5516637..85d08d7 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 560a65a..07dbf46 100644 --- a/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt +++ b/build/Build/Intermediates.noindex/XCBuildData/registered-launchservices.txt @@ -9,3 +9,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 2e2183a..037fb35 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 eb4ebe4..8221e2f 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 2e11454..53b0c23 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 0dd69ec..fdc668c 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 - Iv3+eGn5ABjizzuIQDv3FNnVrWE= + cNkEGkXY0XL0979Pg1Y/5+wqs/g= 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 3a3b0f9..d6ec236 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/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 b/build/Index.noindex/DataStore/v5/units/MainView.o-CNVK7PE7JUX3 index cb49d78..5fc02f8 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 4dc1d83..d2d0314 100644 --- a/build/Logs/Build/LogStoreManifest.plist +++ b/build/Logs/Build/LogStoreManifest.plist @@ -6,48 +6,6 @@ 13 logs - 087CD1E0-7E94-4934-A62E-C6C070CD1577 - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - 087CD1E0-7E94-4934-A62E-C6C070CD1577.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 - 813876132.17963398 - timeStoppedRecording - 813876144.18060398 - title - Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug - uniqueIdentifier - 087CD1E0-7E94-4934-A62E-C6C070CD1577 - 20B4B0A0-41A0-44A0-BA02-3E5707BABE60 className @@ -90,6 +48,48 @@ uniqueIdentifier 20B4B0A0-41A0-44A0-BA02-3E5707BABE60 + 3389C95F-527F-43D4-BFD7-803CFE84AD6A + + className + IDECommandLineBuildLog + documentTypeString + <nil> + domainType + Xcode.IDEActivityLogDomainType.BuildLog + fileName + 3389C95F-527F-43D4-BFD7-803CFE84AD6A.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 + 813876842.83179998 + timeStoppedRecording + 813876855.72915101 + title + Building project WorkbenchReplacement with scheme WorkbenchReplacement and configuration Debug + uniqueIdentifier + 3389C95F-527F-43D4-BFD7-803CFE84AD6A + 705B952B-2A37-40D9-B731-8E2290C15462 className diff --git a/build/ModuleCache.noindex/Session.modulevalidation b/build/ModuleCache.noindex/Session.modulevalidation index 6321cee..529fc7f 100644 --- a/build/ModuleCache.noindex/Session.modulevalidation +++ b/build/ModuleCache.noindex/Session.modulevalidation @@ -1 +1 @@ -1792183792.223736: Module build session file for module cache at Path(str: "/Volumes/Developer/Applications/WorkbenchReplacement/build/ModuleCache.noindex") +1792184043.218933: 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 1d39c13..298de39 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 5776dbb..e0c8c29 100644 --- a/build/info.plist +++ b/build/info.plist @@ -3,7 +3,7 @@ LastAccessedDate - 2026-10-16T20:49:52Z + 2026-10-16T20:54:03Z WorkspacePath /Volumes/Developer/Applications/WorkbenchReplacement/WorkbenchReplacement.xcodeproj