aboutsummaryrefslogtreecommitdiff
path: root/weed/util/sqlutil/splitter.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/util/sqlutil/splitter.go')
-rw-r--r--weed/util/sqlutil/splitter.go142
1 files changed, 142 insertions, 0 deletions
diff --git a/weed/util/sqlutil/splitter.go b/weed/util/sqlutil/splitter.go
new file mode 100644
index 000000000..098a7ecb3
--- /dev/null
+++ b/weed/util/sqlutil/splitter.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
+}