package migrate import "strings" // splitSQL splits a SQL string into individual statements. // Handles $$ ... $$ dollar-quoted blocks, $tag$ ... $tag$ tagged quotes, // -- line comments, /* ... */ block comments, and '...' string literals // so that semicolons inside any of these constructs are not treated as // statement boundaries. func SplitSQL(sql string) []string { var statements []string var current strings.Builder inDollarQuote := false dollarTag := "" i := 0 for i < len(sql) { // Handle line comments (-- to end of line) if !inDollarQuote && i+1 < len(sql) && sql[i] == '-' && sql[i+1] == '-' { for i < len(sql) && sql[i] != '\n' { current.WriteByte(sql[i]) i++ } continue } // Handle block comments (/* ... */) if !inDollarQuote && i+1 < len(sql) && sql[i] == '/' && sql[i+1] == '*' { end := strings.Index(sql[i+2:], "*/") if end >= 0 { current.WriteString(sql[i : i+end+4]) i += end + 4 continue } } // Handle single-quoted string literals ('...') if !inDollarQuote && sql[i] == '\'' { j := i + 1 for j < len(sql) { if sql[j] == '\'' { if j+1 < len(sql) && sql[j+1] == '\'' { j += 2 // skip doubled quote '' continue } break } j++ } current.WriteString(sql[i : j+1]) i = j + 1 continue } // Check for dollar-quote start/end if !inDollarQuote && sql[i] == '$' { j := i + 1 for j < len(sql) && (sql[j] == '_' || (sql[j] >= 'a' && sql[j] <= 'z') || (sql[j] >= 'A' && sql[j] <= 'Z') || (sql[j] >= '0' && sql[j] <= '9')) { j++ } if j < len(sql) && sql[j] == '$' { dollarTag = sql[i : j+1] current.WriteString(dollarTag) inDollarQuote = true i = j + 1 continue } } if inDollarQuote && strings.HasPrefix(sql[i:], dollarTag) { current.WriteString(dollarTag) i += len(dollarTag) inDollarQuote = false dollarTag = "" continue } if !inDollarQuote && sql[i] == ';' { statements = append(statements, current.String()) current.Reset() i++ continue } current.WriteByte(sql[i]) i++ } if strings.TrimSpace(current.String()) != "" { statements = append(statements, current.String()) } return statements }