model merging

This commit is contained in:
Alex Grintsvayg 2017-05-10 15:29:02 -04:00
parent b160e5c1f4
commit 31fe8b6e1d
3 changed files with 144 additions and 1 deletions

View file

@ -179,11 +179,16 @@ func newImporter() importer {
} }
imp.Singleton = mapImports{ imp.Singleton = mapImports{
"boil_queries": { "boil_queries": imports{
standard: importList{
`"database/sql"`,
`"strings"`,
},
thirdParty: importList{ thirdParty: importList{
`"github.com/lbryio/sqlboiler/boil"`, `"github.com/lbryio/sqlboiler/boil"`,
`"github.com/lbryio/sqlboiler/queries"`, `"github.com/lbryio/sqlboiler/queries"`,
`"github.com/lbryio/sqlboiler/queries/qm"`, `"github.com/lbryio/sqlboiler/queries/qm"`,
`"github.com/pkg/errors"`,
}, },
}, },
"boil_types": { "boil_types": {

84
templates/23_merge.tpl Normal file
View 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 */}}

View file

@ -19,3 +19,57 @@ func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *queries.Query {
return q 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
}