model merging
This commit is contained in:
parent
b160e5c1f4
commit
31fe8b6e1d
3 changed files with 144 additions and 1 deletions
|
@ -179,11 +179,16 @@ func newImporter() importer {
|
|||
}
|
||||
|
||||
imp.Singleton = mapImports{
|
||||
"boil_queries": {
|
||||
"boil_queries": imports{
|
||||
standard: importList{
|
||||
`"database/sql"`,
|
||||
`"strings"`,
|
||||
},
|
||||
thirdParty: importList{
|
||||
`"github.com/lbryio/sqlboiler/boil"`,
|
||||
`"github.com/lbryio/sqlboiler/queries"`,
|
||||
`"github.com/lbryio/sqlboiler/queries/qm"`,
|
||||
`"github.com/pkg/errors"`,
|
||||
},
|
||||
},
|
||||
"boil_types": {
|
||||
|
|
84
templates/23_merge.tpl
Normal file
84
templates/23_merge.tpl
Normal file
|
@ -0,0 +1,84 @@
|
|||
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
|
||||
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
|
||||
{{- if .Table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- $dot := . }}
|
||||
// Merge combines two {{$tableNamePlural}} into one. The primary record will be kept, and the secondary will be deleted.
|
||||
func Merge{{$tableNamePlural}}(exec boil.Executor, primaryID uint64, secondaryID uint64) error {
|
||||
txdb, ok := exec.(boil.Beginner)
|
||||
if !ok {
|
||||
return errors.New("database does not support transactions")
|
||||
}
|
||||
|
||||
tx, txErr := txdb.Begin()
|
||||
if txErr != nil {
|
||||
return txErr
|
||||
}
|
||||
|
||||
primary, err := Find{{$tableNameSingular}}(tx, primaryID)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if primary == nil {
|
||||
return errors.New("Primary {{$tableNameSingular}} not found")
|
||||
}
|
||||
|
||||
secondary, err := Find{{$tableNameSingular}}(tx, secondaryID)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if secondary == nil {
|
||||
return errors.New("Secondary {{$tableNameSingular}} not found")
|
||||
}
|
||||
|
||||
relatedFields := map[string]string{
|
||||
{{- range .Tables -}}
|
||||
{{- range .FKeys -}}
|
||||
{{- if eq $dot.Table.Name .ForeignTable }}
|
||||
"{{.Table }}": "{{ .Column}}",
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
}
|
||||
err = mergeModels(tx, primaryID, secondaryID, relatedFields)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
pr := reflect.ValueOf(primary)
|
||||
sr := reflect.ValueOf(secondary)
|
||||
// for any column thats null on the primary and not null on the secondary, copy from secondary to primary
|
||||
for i := 0; i < sr.Elem().NumField(); i++ {
|
||||
pf := pr.Elem().Field(i)
|
||||
sf := sr.Elem().Field(i)
|
||||
if sf.IsValid() {
|
||||
if nullable, ok := sf.Interface().(Nullable); ok && !nullable.IsZero() && pf.Interface().(Nullable).IsZero() {
|
||||
pf.Set(sf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = primary.Update(tx)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
err = secondary.Delete(tx)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge combines two {{$tableNamePlural}} into one. The primary record will be kept, and the secondary will be deleted.
|
||||
func Merge{{$tableNamePlural}}G(primaryID uint64, secondaryID uint64) error {
|
||||
return Merge{{$tableNamePlural}}(boil.GetDB(), primaryID, secondaryID)
|
||||
}
|
||||
{{- end -}}{{/* join table */}}
|
|
@ -19,3 +19,57 @@ func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *queries.Query {
|
|||
|
||||
return q
|
||||
}
|
||||
|
||||
func mergeModels(tx *sql.Tx, primaryID uint64, secondaryID uint64, relatedFields map[string]string) error {
|
||||
if len(relatedFields) < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for table, column := range relatedFields {
|
||||
// TODO: use NewQuery here, not plain sql
|
||||
query := "UPDATE " + table + " SET " + column + " = ? WHERE " + column + " = ?"
|
||||
_, err := tx.Exec(query, primaryID, secondaryID)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
return checkMerge(tx, relatedFields)
|
||||
}
|
||||
|
||||
func checkMerge(tx *sql.Tx, fields map[string]string) error {
|
||||
columns := []interface{}{}
|
||||
seenColumns := map[string]bool{}
|
||||
placeholders := []string{}
|
||||
for _, column := range fields {
|
||||
if _, ok := seenColumns[column]; !ok {
|
||||
columns = append(columns, column)
|
||||
seenColumns[column] = true
|
||||
placeholders = append(placeholders, "?")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
placeholder := strings.Join(placeholders, ", ")
|
||||
|
||||
q := `SELECT table_name, column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND column_name IN (` + placeholder + `)`
|
||||
rows, err := tx.Query(q, columns...)
|
||||
defer rows.Close()
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var tableName string
|
||||
var columnName string
|
||||
err = rows.Scan(&tableName, &columnName)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if _, exists := fields[tableName]; !exists {
|
||||
return errors.New("Missing merge for " + tableName + "." + columnName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue