Files
data-lite-core/Sources/DataLiteCore/Extensions/String+SQL.swift

732 lines
26 KiB
Swift

import Foundation
extension String {
/// Removes SQL comments from the string while preserving string literals.
///
/// This function preserves escaped single quotes inside string literals
/// and removes both single-line (`-- ...`) and multi-line (`/* ... */`) comments.
///
/// The implementation of this function is generated using
/// [`re2swift`](https://re2c.org/manual/manual_swift.html)
/// from the following parsing template:
///
/// ```swift
/// @inline(__always)
/// func removingComments() -> String {
/// withCString {
/// let yyinput = $0
/// let yylimit = strlen($0)
/// var yyoutput = [CChar]()
/// var yycursor = 0
/// loop: while yycursor < yylimit {
/// var llmarker = yycursor/*!re2c
/// re2c:define:YYCTYPE = "CChar";
/// re2c:yyfill:enable = 0;
///
/// "'" ([^'] | "''")* "'" {
/// while llmarker < yycursor {
/// yyoutput.append(yyinput[llmarker])
/// llmarker += 1
/// }
/// continue loop }
///
/// "--" [^\r\n\x00]* {
/// continue loop }
///
/// "/*" ([^*] | "*"[^/])* "*/" {
/// continue loop }
///
/// [^] {
/// yyoutput.append(yyinput[llmarker])
/// continue loop }
/// */}
/// yyoutput.append(0)
/// return String(
/// cString: yyoutput,
/// encoding: .utf8
/// ) ?? ""
/// }
/// }
/// ```
@inline(__always)
func removingComments() -> String {
withCString {
let yyinput = $0
let yylimit = strlen($0)
var yyoutput = [CChar]()
var yycursor = 0
loop: while yycursor < yylimit {
var llmarker = yycursor
var yych: CChar = 0
var yystate: UInt = 0
yyl: while true {
switch yystate {
case 0:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 3
continue yyl
case 0x2D:
yystate = 4
continue yyl
case 0x2F:
yystate = 5
continue yyl
default:
yystate = 1
continue yyl
}
case 1:
yystate = 2
continue yyl
case 2:
yyoutput.append(yyinput[llmarker])
continue loop
case 3:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 6
continue yyl
default:
yystate = 3
continue yyl
}
case 4:
yych = yyinput[yycursor]
switch yych {
case 0x2D:
yycursor += 1
yystate = 8
continue yyl
default:
yystate = 2
continue yyl
}
case 5:
yych = yyinput[yycursor]
switch yych {
case 0x2A:
yycursor += 1
yystate = 10
continue yyl
default:
yystate = 2
continue yyl
}
case 6:
yych = yyinput[yycursor]
switch yych {
case 0x27:
yycursor += 1
yystate = 3
continue yyl
default:
yystate = 7
continue yyl
}
case 7:
while llmarker < yycursor {
yyoutput.append(yyinput[llmarker])
llmarker += 1
}
continue loop
case 8:
yych = yyinput[yycursor]
switch yych {
case 0x00:
fallthrough
case 0x0A:
fallthrough
case 0x0D:
yystate = 9
continue yyl
default:
yycursor += 1
yystate = 8
continue yyl
}
case 9:
continue loop
case 10:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x2A:
yystate = 11
continue yyl
default:
yystate = 10
continue yyl
}
case 11:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x2F:
yystate = 12
continue yyl
default:
yystate = 10
continue yyl
}
case 12:
continue loop
default: fatalError("internal lexer error")
}
}
}
yyoutput.append(0)
return String(cString: yyoutput, encoding: .utf8) ?? ""
}
}
/// Trims empty lines and trailing whitespace outside string literals.
///
/// This function preserves line breaks and whitespace inside string literals,
/// removing only redundant empty lines and trailing whitespace outside literals.
///
/// The implementation of this function is generated using
/// [`re2swift`](https://re2c.org/manual/manual_swift.html)
/// from the following parsing template:
///
/// ```swift
/// @inline(__always)
/// func trimmingLines() -> String {
/// withCString {
/// let yyinput = $0
/// let yylimit = strlen($0)
/// var yyoutput = [CChar]()
/// var yycursor = 0
/// var yymarker = 0
/// loop: while yycursor < yylimit {
/// var llmarker = yycursor/*!re2c
/// re2c:define:YYCTYPE = "CChar";
/// re2c:yyfill:enable = 0;
///
/// "'" ([^'] | "''")* "'" {
/// while llmarker < yycursor {
/// yyoutput.append(yyinput[llmarker])
/// llmarker += 1
/// }
/// continue loop }
///
/// [ \t]* "\x00" {
/// continue loop }
///
/// [ \t]* "\n\x00" {
/// continue loop }
///
/// [ \t\n]* "\n"+ {
/// if llmarker > 0 && yycursor < yylimit {
/// yyoutput.append(0x0A)
/// }
/// continue loop }
///
/// [^] {
/// yyoutput.append(yyinput[llmarker])
/// continue loop }
/// */}
/// yyoutput.append(0)
/// return String(
/// cString: yyoutput,
/// encoding: .utf8
/// ) ?? ""
/// }
/// }
/// ```
@inline(__always)
func trimmingLines() -> String {
withCString {
let yyinput = $0
let yylimit = strlen($0)
var yyoutput = [CChar]()
var yycursor = 0
var yymarker = 0
loop: while yycursor < yylimit {
var llmarker = yycursor
var yych: CChar = 0
var yyaccept: UInt = 0
var yystate: UInt = 0
yyl: while true {
switch yystate {
case 0:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x00:
yystate = 1
continue yyl
case 0x09:
fallthrough
case 0x20:
yystate = 4
continue yyl
case 0x0A:
yystate = 5
continue yyl
case 0x27:
yystate = 7
continue yyl
default:
yystate = 2
continue yyl
}
case 1:
continue loop
case 2:
yystate = 3
continue yyl
case 3:
yyoutput.append(yyinput[llmarker])
continue loop
case 4:
yyaccept = 0
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x00:
fallthrough
case 0x09...0x0A:
fallthrough
case 0x20:
yystate = 9
continue yyl
default:
yystate = 3
continue yyl
}
case 5:
yyaccept = 1
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x00:
yycursor += 1
yystate = 11
continue yyl
case 0x09...0x0A:
fallthrough
case 0x20:
yystate = 13
continue yyl
default:
yystate = 6
continue yyl
}
case 6:
if llmarker > 0 && yycursor < yylimit {
yyoutput.append(0x0A)
}
continue loop
case 7:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 15
continue yyl
default:
yystate = 7
continue yyl
}
case 8:
yych = yyinput[yycursor]
yystate = 9
continue yyl
case 9:
switch yych {
case 0x00:
yycursor += 1
yystate = 1
continue yyl
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 8
continue yyl
case 0x0A:
yycursor += 1
yystate = 5
continue yyl
default:
yystate = 10
continue yyl
}
case 10:
yycursor = yymarker
if yyaccept == 0 {
yystate = 3
continue yyl
} else {
yystate = 6
continue yyl
}
case 11:
continue loop
case 12:
yych = yyinput[yycursor]
yystate = 13
continue yyl
case 13:
switch yych {
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 12
continue yyl
case 0x0A:
yycursor += 1
yystate = 14
continue yyl
default:
yystate = 10
continue yyl
}
case 14:
yyaccept = 1
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 12
continue yyl
case 0x0A:
yycursor += 1
yystate = 14
continue yyl
default:
yystate = 6
continue yyl
}
case 15:
yych = yyinput[yycursor]
switch yych {
case 0x27:
yycursor += 1
yystate = 7
continue yyl
default:
yystate = 16
continue yyl
}
case 16:
while llmarker < yycursor {
yyoutput.append(yyinput[llmarker])
llmarker += 1
}
continue loop
default: fatalError("internal lexer error")
}
}
}
yyoutput.append(0)
return String(
cString: yyoutput,
encoding: .utf8
) ?? ""
}
}
/// Splits the SQL script into individual statements by semicolons.
///
/// This function preserves string literals (enclosed in single quotes),
/// and treats `BEGIN...END` blocks as single nested statements, preventing
/// splitting inside these blocks. Statements are split only at semicolons
/// outside string literals and `BEGIN...END` blocks.
///
/// The implementation of this function is generated using
/// [`re2swift`](https://re2c.org/manual/manual_swift.html)
/// from the following parsing template:
///
/// ```swift
/// @inline(__always)
/// func splitStatements() -> [String] {
/// withCString {
/// let yyinput = $0
/// let yylimit = strlen($0)
/// var yyranges = [Range<Int>]()
/// var yycursor = 0
/// var yymarker = 0
/// var yynesting = 0
/// var yystart = 0
/// var yyend = 0
/// loop: while yycursor < yylimit {/*!re2c
/// re2c:define:YYCTYPE = "CChar";
/// re2c:yyfill:enable = 0;
///
/// "'" ( [^'] | "''" )* "'" {
/// yyend = yycursor
/// continue loop }
///
/// 'BEGIN' {
/// yynesting += 1
/// yyend = yycursor
/// continue loop }
///
/// 'END' {
/// if yynesting > 0 {
/// yynesting -= 1
/// }
/// yyend = yycursor
/// continue loop }
///
/// ";" [ \t]* "\n"* {
/// if yynesting == 0 {
/// if yystart < yyend {
/// yyranges.append(yystart..<yyend)
/// }
/// yystart = yycursor
/// continue loop
/// } else {
/// continue loop
/// }}
///
/// [^] {
/// yyend = yycursor
/// continue loop }
/// */}
/// if yystart < yyend {
/// yyranges.append(yystart..<yyend)
/// }
/// return yyranges.map { range in
/// let buffer = UnsafeBufferPointer<CChar>(
/// start: yyinput.advanced(by: range.lowerBound),
/// count: range.count
/// )
/// let array = Array(buffer) + [0]
/// return String(cString: array, encoding: .utf8) ?? ""
/// }
/// }
/// }
/// ```
@inline(__always)
func splitStatements() -> [String] {
withCString {
let yyinput = $0
let yylimit = strlen($0)
var yyranges = [Range<Int>]()
var yycursor = 0
var yymarker = 0
var yynesting = 0
var yystart = 0
var yyend = 0
loop: while yycursor < yylimit {
var yych: CChar = 0
var yystate: UInt = 0
yyl: while true {
switch yystate {
case 0:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 3
continue yyl
case 0x3B:
yystate = 4
continue yyl
case 0x42:
fallthrough
case 0x62:
yystate = 6
continue yyl
case 0x45:
fallthrough
case 0x65:
yystate = 7
continue yyl
default:
yystate = 1
continue yyl
}
case 1:
yystate = 2
continue yyl
case 2:
yyend = yycursor
continue loop
case 3:
yych = yyinput[yycursor]
yycursor += 1
switch yych {
case 0x27:
yystate = 8
continue yyl
default:
yystate = 3
continue yyl
}
case 4:
yych = yyinput[yycursor]
switch yych {
case 0x09:
fallthrough
case 0x20:
yycursor += 1
yystate = 4
continue yyl
case 0x0A:
yycursor += 1
yystate = 10
continue yyl
default:
yystate = 5
continue yyl
}
case 5:
if yynesting == 0 {
if yystart < yyend {
yyranges.append(yystart..<yyend)
}
yystart = yycursor
continue loop
} else {
continue loop
}
case 6:
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x45:
fallthrough
case 0x65:
yycursor += 1
yystate = 11
continue yyl
default:
yystate = 2
continue yyl
}
case 7:
yymarker = yycursor
yych = yyinput[yycursor]
switch yych {
case 0x4E:
fallthrough
case 0x6E:
yycursor += 1
yystate = 13
continue yyl
default:
yystate = 2
continue yyl
}
case 8:
yych = yyinput[yycursor]
switch yych {
case 0x27:
yycursor += 1
yystate = 3
continue yyl
default:
yystate = 9
continue yyl
}
case 9:
yyend = yycursor
continue loop
case 10:
yych = yyinput[yycursor]
switch yych {
case 0x0A:
yycursor += 1
yystate = 10
continue yyl
default:
yystate = 5
continue yyl
}
case 11:
yych = yyinput[yycursor]
switch yych {
case 0x47:
fallthrough
case 0x67:
yycursor += 1
yystate = 14
continue yyl
default:
yystate = 12
continue yyl
}
case 12:
yycursor = yymarker
yystate = 2
continue yyl
case 13:
yych = yyinput[yycursor]
switch yych {
case 0x44:
fallthrough
case 0x64:
yycursor += 1
yystate = 15
continue yyl
default:
yystate = 12
continue yyl
}
case 14:
yych = yyinput[yycursor]
switch yych {
case 0x49:
fallthrough
case 0x69:
yycursor += 1
yystate = 16
continue yyl
default:
yystate = 12
continue yyl
}
case 15:
if yynesting > 0 {
yynesting -= 1
}
yyend = yycursor
continue loop
case 16:
yych = yyinput[yycursor]
switch yych {
case 0x4E:
fallthrough
case 0x6E:
yycursor += 1
yystate = 17
continue yyl
default:
yystate = 12
continue yyl
}
case 17:
yynesting += 1
yyend = yycursor
continue loop
default: fatalError("internal lexer error")
}
}
}
if yystart < yyend {
yyranges.append(yystart..<yyend)
}
return yyranges.map { range in
let buffer = UnsafeBufferPointer<CChar>(
start: yyinput.advanced(by: range.lowerBound),
count: range.count
)
let array = Array(buffer) + [0]
return String(cString: array, encoding: .utf8) ?? ""
}
}
}
}