Files
orbit/internal/storage/mysql/sqlsplit.go

100 lines
1.9 KiB
Go

package mysql
import (
"errors"
"strings"
)
// splitSQL supports ordinary migration statements, quoted values and comments.
// Stored routines and DELIMITER scripts are intentionally unsupported.
func splitSQL(src string) ([]string, error) {
var out []string
var b strings.Builder
var quote byte
lineComment, blockComment := false, false
flush := func() error {
s := strings.TrimSpace(b.String())
b.Reset()
if s == "" {
return nil
}
if strings.HasPrefix(strings.ToUpper(s), "DELIMITER") {
return errors.New("DELIMITER scripts are unsupported")
}
out = append(out, s)
return nil
}
for i := 0; i < len(src); i++ {
c := src[i]
if lineComment {
if c == '\n' {
lineComment = false
b.WriteByte(' ')
}
continue
}
if blockComment {
if c == '*' && i+1 < len(src) && src[i+1] == '/' {
blockComment = false
i++
b.WriteByte(' ')
}
continue
}
if quote != 0 {
b.WriteByte(c)
if c == '\\' && i+1 < len(src) {
i++
b.WriteByte(src[i])
continue
}
if c == quote {
if i+1 < len(src) && src[i+1] == quote {
i++
b.WriteByte(src[i])
} else {
quote = 0
}
}
continue
}
if c == '#' || c == '-' && i+2 < len(src) && src[i+1] == '-' && src[i+2] <= ' ' {
lineComment = true
if c == '-' {
i++
}
continue
}
if c == '/' && i+1 < len(src) && src[i+1] == '*' {
if i+2 < len(src) && src[i+2] == '!' {
return nil, errors.New("executable SQL comments are unsupported")
}
blockComment = true
i++
continue
}
if c == '\'' || c == '"' || c == '`' {
quote = c
b.WriteByte(c)
continue
}
if c == ';' {
if err := flush(); err != nil {
return nil, err
}
continue
}
b.WriteByte(c)
}
if quote != 0 || blockComment {
return nil, errors.New("unterminated SQL quote or comment")
}
if err := flush(); err != nil {
return nil, err
}
if len(out) == 0 {
return nil, errors.New("empty migration")
}
return out, nil
}