4 changed files with 153 additions and 268 deletions
-
133weed/command/sql.go
-
132weed/server/postgres/protocol.go
-
142weed/util/sqlutil/splitter.go
-
14weed/util/sqlutil/splitter_test.go
@ -0,0 +1,142 @@ |
|||||
|
package sqlutil |
||||
|
|
||||
|
import ( |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// SplitStatements splits a query string into individual SQL statements.
|
||||
|
// This robust implementation handles SQL comments, quoted strings, and escaped characters.
|
||||
|
//
|
||||
|
// Features:
|
||||
|
// - Handles single-line comments (-- comment)
|
||||
|
// - Handles multi-line comments (/* comment */)
|
||||
|
// - Properly escapes single quotes in strings ('don”t')
|
||||
|
// - Properly escapes double quotes in identifiers ("column""name")
|
||||
|
// - Ignores semicolons within quoted strings and comments
|
||||
|
// - Returns clean, trimmed statements with empty statements filtered out
|
||||
|
func SplitStatements(query string) []string { |
||||
|
var statements []string |
||||
|
var current strings.Builder |
||||
|
|
||||
|
query = strings.TrimSpace(query) |
||||
|
if query == "" { |
||||
|
return []string{} |
||||
|
} |
||||
|
|
||||
|
runes := []rune(query) |
||||
|
i := 0 |
||||
|
|
||||
|
for i < len(runes) { |
||||
|
char := runes[i] |
||||
|
|
||||
|
// Handle single-line comments (-- comment)
|
||||
|
if char == '-' && i+1 < len(runes) && runes[i+1] == '-' { |
||||
|
// Skip the entire comment without including it in any statement
|
||||
|
for i < len(runes) && runes[i] != '\n' && runes[i] != '\r' { |
||||
|
i++ |
||||
|
} |
||||
|
// Skip the newline if present
|
||||
|
if i < len(runes) { |
||||
|
i++ |
||||
|
} |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// Handle multi-line comments (/* comment */)
|
||||
|
if char == '/' && i+1 < len(runes) && runes[i+1] == '*' { |
||||
|
// Skip the /* opening
|
||||
|
i++ |
||||
|
i++ |
||||
|
|
||||
|
// Skip to end of comment or end of input without including content
|
||||
|
for i < len(runes) { |
||||
|
if runes[i] == '*' && i+1 < len(runes) && runes[i+1] == '/' { |
||||
|
i++ // Skip the *
|
||||
|
i++ // Skip the /
|
||||
|
break |
||||
|
} |
||||
|
i++ |
||||
|
} |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// Handle single-quoted strings
|
||||
|
if char == '\'' { |
||||
|
current.WriteRune(char) |
||||
|
i++ |
||||
|
|
||||
|
for i < len(runes) { |
||||
|
char = runes[i] |
||||
|
current.WriteRune(char) |
||||
|
|
||||
|
if char == '\'' { |
||||
|
// Check if it's an escaped quote
|
||||
|
if i+1 < len(runes) && runes[i+1] == '\'' { |
||||
|
i++ // Skip the next quote (it's escaped)
|
||||
|
if i < len(runes) { |
||||
|
current.WriteRune(runes[i]) |
||||
|
} |
||||
|
} else { |
||||
|
break // End of string
|
||||
|
} |
||||
|
} |
||||
|
i++ |
||||
|
} |
||||
|
i++ |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// Handle double-quoted identifiers
|
||||
|
if char == '"' { |
||||
|
current.WriteRune(char) |
||||
|
i++ |
||||
|
|
||||
|
for i < len(runes) { |
||||
|
char = runes[i] |
||||
|
current.WriteRune(char) |
||||
|
|
||||
|
if char == '"' { |
||||
|
// Check if it's an escaped quote
|
||||
|
if i+1 < len(runes) && runes[i+1] == '"' { |
||||
|
i++ // Skip the next quote (it's escaped)
|
||||
|
if i < len(runes) { |
||||
|
current.WriteRune(runes[i]) |
||||
|
} |
||||
|
} else { |
||||
|
break // End of identifier
|
||||
|
} |
||||
|
} |
||||
|
i++ |
||||
|
} |
||||
|
i++ |
||||
|
continue |
||||
|
} |
||||
|
|
||||
|
// Handle semicolon (statement separator)
|
||||
|
if char == ';' { |
||||
|
stmt := strings.TrimSpace(current.String()) |
||||
|
if stmt != "" { |
||||
|
statements = append(statements, stmt) |
||||
|
} |
||||
|
current.Reset() |
||||
|
} else { |
||||
|
current.WriteRune(char) |
||||
|
} |
||||
|
i++ |
||||
|
} |
||||
|
|
||||
|
// Add any remaining statement
|
||||
|
if current.Len() > 0 { |
||||
|
stmt := strings.TrimSpace(current.String()) |
||||
|
if stmt != "" { |
||||
|
statements = append(statements, stmt) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// If no statements found, return the original query as a single statement
|
||||
|
if len(statements) == 0 { |
||||
|
return []string{strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(query), ";"))} |
||||
|
} |
||||
|
|
||||
|
return statements |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue