107 lines
2.8 KiB
Go
107 lines
2.8 KiB
Go
package mysql
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
mysqldriver "github.com/go-sql-driver/mysql"
|
|
"gorm.io/gorm"
|
|
|
|
"wucher/internal/shared/pkg/apperrors"
|
|
)
|
|
|
|
type fkReferenceRow struct {
|
|
TableName string `gorm:"column:table_name"`
|
|
ColumnName string `gorm:"column:column_name"`
|
|
}
|
|
|
|
var mysqlDeleteRefTableRegexp = regexp.MustCompile("fails \\(`[^`]+`\\.`([^`]+)`")
|
|
|
|
func ensureNoReferenceBeforeDelete(ctx context.Context, db *gorm.DB, tableName string, id []byte) error {
|
|
if db == nil || db.Dialector == nil || db.Dialector.Name() != "mysql" {
|
|
return nil
|
|
}
|
|
if len(id) != 16 || strings.TrimSpace(tableName) == "" {
|
|
return nil
|
|
}
|
|
|
|
refs := make([]fkReferenceRow, 0)
|
|
err := db.WithContext(ctx).Raw(`
|
|
SELECT kcu.TABLE_NAME AS table_name, kcu.COLUMN_NAME AS column_name
|
|
FROM information_schema.KEY_COLUMN_USAGE kcu
|
|
WHERE kcu.TABLE_SCHEMA = DATABASE()
|
|
AND kcu.REFERENCED_TABLE_SCHEMA = DATABASE()
|
|
AND kcu.REFERENCED_TABLE_NAME = ?
|
|
AND kcu.REFERENCED_COLUMN_NAME = 'id'
|
|
`, tableName).Scan(&refs).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
related := make([]string, 0)
|
|
for i := range refs {
|
|
query := fmt.Sprintf("SELECT 1 FROM `%s` WHERE `%s` = ? LIMIT 1", refs[i].TableName, refs[i].ColumnName)
|
|
var one int
|
|
rowErr := db.WithContext(ctx).Raw(query, id).Scan(&one).Error
|
|
if rowErr != nil {
|
|
return rowErr
|
|
}
|
|
if one == 1 {
|
|
related = append(related, moduleNameFromTable(refs[i].TableName))
|
|
}
|
|
}
|
|
|
|
if len(related) > 0 {
|
|
return apperrors.NewDeleteConflictError(related...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func mapDeleteConstraintError(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if apperrors.IsDeleteConflict(err) {
|
|
return err
|
|
}
|
|
var mysqlErr *mysqldriver.MySQLError
|
|
if !strings.Contains(strings.ToLower(err.Error()), "foreign key") && !strings.Contains(strings.ToLower(err.Error()), "referenced") && !strings.Contains(strings.ToLower(err.Error()), "constraint") && !strings.Contains(strings.ToLower(err.Error()), "delete") {
|
|
return err
|
|
}
|
|
if !asMySQLError(err, &mysqlErr) || mysqlErr.Number != 1451 {
|
|
return err
|
|
}
|
|
if matches := mysqlDeleteRefTableRegexp.FindStringSubmatch(mysqlErr.Message); len(matches) > 1 {
|
|
return apperrors.NewDeleteConflictError(moduleNameFromTable(matches[1]))
|
|
}
|
|
return apperrors.NewDeleteConflictError()
|
|
}
|
|
|
|
func asMySQLError(err error, target **mysqldriver.MySQLError) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
return errors.As(err, target)
|
|
}
|
|
|
|
func moduleNameFromTable(tableName string) string {
|
|
tableName = strings.TrimSpace(tableName)
|
|
if tableName == "" {
|
|
return "another module"
|
|
}
|
|
switch tableName {
|
|
case "takeover_acs":
|
|
return "takeover"
|
|
case "base_operational_shift_times":
|
|
return "base operational shift times"
|
|
case "base_contact_roles":
|
|
return "base contact roles"
|
|
case "duls":
|
|
return "dul"
|
|
}
|
|
return strings.ReplaceAll(tableName, "_", " ")
|
|
}
|