sqlboiler/templates/12_relationship_to_many_setops.tpl
Aaron L 4ae9336538 Fix bug in ToManySetOpsAdd and impure join tables
- This is a bug that manifests itself a bunch with our update code where
  you cannot actually use the update method to update a key since it
  uses the values on the struct to both update the values and find the
  object to update but in this operation the key must have two different
  values.
2017-01-04 20:31:29 -08:00

236 lines
8.2 KiB
Smarty

{{- if .Table.IsJoinTable -}}
{{- else -}}
{{- $dot := . -}}
{{- $table := .Table -}}
{{- range .Table.ToManyRelationships -}}
{{- $txt := txtsFromToMany $dot.Tables $table . -}}
{{- $varNameSingular := .Table | singular | camelCase -}}
{{- $foreignVarNameSingular := .ForeignTable | singular | camelCase}}
{{- $foreignPKeyCols := (getTable $dot.Tables .ForeignTable).PKey.Columns -}}
{{- $foreignSchemaTable := .ForeignTable | $dot.SchemaTable}}
// Add{{$txt.Function.Name}} adds the given related objects to the existing relationships
// of the {{$table.Name | singular}}, optionally inserting them as new records.
// Appends related to o.R.{{$txt.Function.Name}}.
// Sets related.R.{{$txt.Function.ForeignName}} appropriately.
func (o *{{$txt.LocalTable.NameGo}}) Add{{$txt.Function.Name}}(exec boil.Executor, insert bool, related ...*{{$txt.ForeignTable.NameGo}}) error {
var err error
for _, rel := range related {
if insert {
{{if not .ToJoinTable -}}
rel.{{$txt.Function.ForeignAssignment}} = o.{{$txt.Function.LocalAssignment}}
{{if .ForeignColumnNullable -}}
rel.{{$txt.ForeignTable.ColumnNameGo}}.Valid = true
{{end -}}
{{end -}}
if err = rel.Insert(exec); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
}{{if not .ToJoinTable}} else {
updateQuery := fmt.Sprintf(
"UPDATE {{$foreignSchemaTable}} SET %s WHERE %s",
strmangle.SetParamNames("{{$dot.LQ}}", "{{$dot.RQ}}", {{if $dot.Dialect.IndexPlaceholders}}1{{else}}0{{end}}, []string{{"{"}}"{{.ForeignColumn}}"{{"}"}}),
strmangle.WhereClause("{{$dot.LQ}}", "{{$dot.RQ}}", {{if $dot.Dialect.IndexPlaceholders}}2{{else}}0{{end}}, {{$foreignVarNameSingular}}PrimaryKeyColumns),
)
values := []interface{}{o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$foreignPKeyCols | stringMap $dot.StringFuncs.titleCase | join ", rel."}}{{"}"}}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, updateQuery)
fmt.Fprintln(boil.DebugWriter, values)
}
if _, err = exec.Exec(updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update foreign table")
}
rel.{{$txt.Function.ForeignAssignment}} = o.{{$txt.Function.LocalAssignment}}
{{if .ForeignColumnNullable -}}
rel.{{$txt.ForeignTable.ColumnNameGo}}.Valid = true
{{end -}}
}{{end -}}
}
{{if .ToJoinTable -}}
for _, rel := range related {
query := "insert into {{.JoinTable | $dot.SchemaTable}} ({{.JoinLocalColumn | $dot.Quotes}}, {{.JoinForeignColumn | $dot.Quotes}}) values {{if $dot.Dialect.IndexPlaceholders}}($1, $2){{else}}(?, ?){{end}}"
values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}, rel.{{$txt.ForeignTable.ColumnNameGo}}}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, query)
fmt.Fprintln(boil.DebugWriter, values)
}
_, err = exec.Exec(query, values...)
if err != nil {
return errors.Wrap(err, "failed to insert into join table")
}
}
{{end -}}
if o.R == nil {
o.R = &{{$varNameSingular}}R{
{{$txt.Function.Name}}: related,
}
} else {
o.R.{{$txt.Function.Name}} = append(o.R.{{$txt.Function.Name}}, related...)
}
{{if .ToJoinTable -}}
for _, rel := range related {
if rel.R == nil {
rel.R = &{{$foreignVarNameSingular}}R{
{{$txt.Function.ForeignName}}: {{$txt.LocalTable.NameGo}}Slice{{"{"}}o{{"}"}},
}
} else {
rel.R.{{$txt.Function.ForeignName}} = append(rel.R.{{$txt.Function.ForeignName}}, o)
}
}
{{else -}}
for _, rel := range related {
if rel.R == nil {
rel.R = &{{$foreignVarNameSingular}}R{
{{$txt.Function.ForeignName}}: o,
}
} else {
rel.R.{{$txt.Function.ForeignName}} = o
}
}
{{end -}}
return nil
}
{{- if (or .ForeignColumnNullable .ToJoinTable)}}
// Set{{$txt.Function.Name}} removes all previously related items of the
// {{$table.Name | singular}} replacing them completely with the passed
// in related items, optionally inserting them as new records.
// Sets o.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly.
// Replaces o.R.{{$txt.Function.Name}} with related.
// Sets related.R.{{$txt.Function.ForeignName}}'s {{$txt.Function.Name}} accordingly.
func (o *{{$txt.LocalTable.NameGo}}) Set{{$txt.Function.Name}}(exec boil.Executor, insert bool, related ...*{{$txt.ForeignTable.NameGo}}) error {
{{if .ToJoinTable -}}
query := "delete from {{.JoinTable | $dot.SchemaTable}} where {{.JoinLocalColumn | $dot.Quotes}} = {{if $dot.Dialect.IndexPlaceholders}}$1{{else}}?{{end}}"
values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}}
{{else -}}
query := "update {{.ForeignTable | $dot.SchemaTable}} set {{.ForeignColumn | $dot.Quotes}} = null where {{.ForeignColumn | $dot.Quotes}} = {{if $dot.Dialect.IndexPlaceholders}}$1{{else}}?{{end}}"
values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}}
{{end -}}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, query)
fmt.Fprintln(boil.DebugWriter, values)
}
_, err := exec.Exec(query, values...)
if err != nil {
return errors.Wrap(err, "failed to remove relationships before set")
}
{{if .ToJoinTable -}}
remove{{$txt.Function.Name}}From{{$txt.Function.ForeignName}}Slice(o, related)
o.R.{{$txt.Function.Name}} = nil
{{else -}}
if o.R != nil {
for _, rel := range o.R.{{$txt.Function.Name}} {
rel.{{$txt.ForeignTable.ColumnNameGo}}.Valid = false
if rel.R == nil {
continue
}
rel.R.{{$txt.Function.ForeignName}} = nil
}
o.R.{{$txt.Function.Name}} = nil
}
{{end -}}
return o.Add{{$txt.Function.Name}}(exec, insert, related...)
}
// Remove{{$txt.Function.Name}} relationships from objects passed in.
// Removes related items from R.{{$txt.Function.Name}} (uses pointer comparison, removal does not keep order)
// Sets related.R.{{$txt.Function.ForeignName}}.
func (o *{{$txt.LocalTable.NameGo}}) Remove{{$txt.Function.Name}}(exec boil.Executor, related ...*{{$txt.ForeignTable.NameGo}}) error {
var err error
{{if .ToJoinTable -}}
query := fmt.Sprintf(
"delete from {{.JoinTable | $dot.SchemaTable}} where {{.JoinLocalColumn | $dot.Quotes}} = {{if $dot.Dialect.IndexPlaceholders}}$1{{else}}?{{end}} and {{.JoinForeignColumn | $dot.Quotes}} in (%s)",
strmangle.Placeholders(dialect.IndexPlaceholders, len(related), 1, 1),
)
values := []interface{}{{"{"}}o.{{$txt.LocalTable.ColumnNameGo}}}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, query)
fmt.Fprintln(boil.DebugWriter, values)
}
_, err = exec.Exec(query, values...)
if err != nil {
return errors.Wrap(err, "failed to remove relationships before set")
}
{{else -}}
for _, rel := range related {
rel.{{$txt.ForeignTable.ColumnNameGo}}.Valid = false
{{if not .ToJoinTable -}}
if rel.R != nil {
rel.R.{{$txt.Function.ForeignName}} = nil
}
{{end -}}
if err = rel.Update(exec, "{{.ForeignColumn}}"); err != nil {
return err
}
}
{{end -}}
{{if .ToJoinTable -}}
remove{{$txt.Function.Name}}From{{$txt.Function.ForeignName}}Slice(o, related)
{{end -}}
if o.R == nil {
return nil
}
for _, rel := range related {
for i, ri := range o.R.{{$txt.Function.Name}} {
if rel != ri {
continue
}
ln := len(o.R.{{$txt.Function.Name}})
if ln > 1 && i < ln-1 {
o.R.{{$txt.Function.Name}}[i] = o.R.{{$txt.Function.Name}}[ln-1]
}
o.R.{{$txt.Function.Name}} = o.R.{{$txt.Function.Name}}[:ln-1]
break
}
}
return nil
}
{{if .ToJoinTable -}}
func remove{{$txt.Function.Name}}From{{$txt.Function.ForeignName}}Slice(o *{{$txt.LocalTable.NameGo}}, related []*{{$txt.ForeignTable.NameGo}}) {
for _, rel := range related {
if rel.R == nil {
continue
}
for i, ri := range rel.R.{{$txt.Function.ForeignName}} {
{{if $txt.Function.UsesBytes -}}
if 0 != bytes.Compare(o.{{$txt.Function.LocalAssignment}}, ri.{{$txt.Function.LocalAssignment}}) {
{{else -}}
if o.{{$txt.Function.LocalAssignment}} != ri.{{$txt.Function.LocalAssignment}} {
{{end -}}
continue
}
ln := len(rel.R.{{$txt.Function.ForeignName}})
if ln > 1 && i < ln-1 {
rel.R.{{$txt.Function.ForeignName}}[i] = rel.R.{{$txt.Function.ForeignName}}[ln-1]
}
rel.R.{{$txt.Function.ForeignName}} = rel.R.{{$txt.Function.ForeignName}}[:ln-1]
break
}
}
}
{{end -}}{{- /* if ToJoinTable */ -}}
{{- end -}}{{- /* if nullable foreign key */ -}}
{{- end -}}{{- /* range relationships */ -}}
{{- end -}}{{- /* if IsJoinTable */ -}}