Files

54 lines
1.4 KiB
Go
Raw Permalink Normal View History

package main
import (
"fmt"
"regexp"
"strings"
)
// SQL identifiers in this migrator are always static allowlisted names or
// programmer-supplied column paths — never end-user free text. Still quote
// and validate before interpolating into DDL/DML to fail closed on mistakes.
var sqlIdentSegment = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)
func quoteMySQLIdent(ident string) (string, error) {
if !sqlIdentSegment.MatchString(ident) {
return "", fmt.Errorf("invalid MySQL identifier %q", ident)
}
return "`" + strings.ReplaceAll(ident, "`", "``") + "`", nil
}
// quoteMySQLIdentPath quotes dotted paths such as company_id or cf.company_id.
func quoteMySQLIdentPath(path string) (string, error) {
path = strings.TrimSpace(path)
if path == "" {
return "", fmt.Errorf("empty MySQL identifier path")
}
parts := strings.Split(path, ".")
out := make([]string, len(parts))
for i, part := range parts {
q, err := quoteMySQLIdent(part)
if err != nil {
return "", err
}
out[i] = q
}
return strings.Join(out, "."), nil
}
func mustQuoteMySQLIdent(ident string) string {
q, err := quoteMySQLIdent(ident)
if err != nil {
panic(err)
}
return q
}
func quotePGIdent(ident string) (string, error) {
if !sqlIdentSegment.MatchString(ident) {
return "", fmt.Errorf("invalid Postgres identifier %q", ident)
}
return `"` + strings.ReplaceAll(ident, `"`, `""`) + `"`, nil
}