Merge branch 'sets'
This commit is contained in:
commit
9553f462c7
20 changed files with 1104 additions and 136 deletions
|
@ -100,7 +100,7 @@ users, err := models.Users(db, Load("FavoriteMovies")).All()
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(len(users.Loaded.FavoriteMovies))
|
||||
fmt.Println(len(users.R.FavoriteMovies))
|
||||
```
|
||||
|
||||
## How to boil your database
|
||||
|
|
|
@ -59,7 +59,7 @@ func (m *MockDriver) Columns(tableName string) ([]bdb.Column, error) {
|
|||
func (m *MockDriver) ForeignKeyInfo(tableName string) ([]bdb.ForeignKey, error) {
|
||||
return map[string][]bdb.ForeignKey{
|
||||
"jets": {
|
||||
{Table: "jets", Name: "jets_pilot_id_fk", Column: "pilot_id", ForeignTable: "pilots", ForeignColumn: "id"},
|
||||
{Table: "jets", Name: "jets_pilot_id_fk", Column: "pilot_id", ForeignTable: "pilots", ForeignColumn: "id", ForeignColumnUnique: true},
|
||||
{Table: "jets", Name: "jets_airport_id_fk", Column: "airport_id", ForeignTable: "airports", ForeignColumn: "id"},
|
||||
},
|
||||
"licenses": {
|
||||
|
|
|
@ -1,98 +1,140 @@
|
|||
package bdb
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/vattle/sqlboiler/strmangle"
|
||||
)
|
||||
|
||||
type testInterface struct{}
|
||||
type mockDriver struct{}
|
||||
|
||||
func (t testInterface) TableNames(exclude []string) ([]string, error) {
|
||||
return []string{"table1", "table2"}, nil
|
||||
func (m mockDriver) TranslateColumnType(c Column) Column { return c }
|
||||
func (m mockDriver) UseLastInsertID() bool { return false }
|
||||
func (m mockDriver) Open() error { return nil }
|
||||
func (m mockDriver) Close() {}
|
||||
|
||||
func (m mockDriver) TableNames(exclude []string) ([]string, error) {
|
||||
tables := []string{"pilots", "jets", "airports", "licenses", "hangars", "languages", "pilot_languages"}
|
||||
return strmangle.SetComplement(tables, exclude), nil
|
||||
}
|
||||
|
||||
var testCols = []Column{
|
||||
{Name: "col1", Type: "character varying"},
|
||||
{Name: "col2", Type: "character varying", Nullable: true},
|
||||
// Columns returns a list of mock columns
|
||||
func (m mockDriver) Columns(tableName string) ([]Column, error) {
|
||||
return map[string][]Column{
|
||||
"pilots": {
|
||||
{Name: "id", Type: "int", DBType: "integer"},
|
||||
{Name: "name", Type: "string", DBType: "character"},
|
||||
},
|
||||
"airports": {
|
||||
{Name: "id", Type: "int", DBType: "integer"},
|
||||
{Name: "size", Type: "null.Int", DBType: "integer", Nullable: true},
|
||||
},
|
||||
"jets": {
|
||||
{Name: "id", Type: "int", DBType: "integer"},
|
||||
{Name: "pilot_id", Type: "int", DBType: "integer", Nullable: true, Unique: true},
|
||||
{Name: "airport_id", Type: "int", DBType: "integer"},
|
||||
{Name: "name", Type: "string", DBType: "character", Nullable: false},
|
||||
{Name: "color", Type: "null.String", DBType: "character", Nullable: true},
|
||||
{Name: "uuid", Type: "string", DBType: "uuid", Nullable: true},
|
||||
{Name: "identifier", Type: "string", DBType: "uuid", Nullable: false},
|
||||
{Name: "cargo", Type: "[]byte", DBType: "bytea", Nullable: false},
|
||||
{Name: "manifest", Type: "[]byte", DBType: "bytea", Nullable: true, Unique: true},
|
||||
},
|
||||
"licenses": {
|
||||
{Name: "id", Type: "int", DBType: "integer"},
|
||||
{Name: "pilot_id", Type: "int", DBType: "integer"},
|
||||
},
|
||||
"hangars": {
|
||||
{Name: "id", Type: "int", DBType: "integer"},
|
||||
{Name: "name", Type: "string", DBType: "character", Nullable: true, Unique: true},
|
||||
{Name: "hangar_id", Type: "int", DBType: "integer", Nullable: true},
|
||||
},
|
||||
"languages": {
|
||||
{Name: "id", Type: "int", DBType: "integer"},
|
||||
{Name: "language", Type: "string", DBType: "character", Nullable: false, Unique: true},
|
||||
},
|
||||
"pilot_languages": {
|
||||
{Name: "pilot_id", Type: "int", DBType: "integer"},
|
||||
{Name: "language_id", Type: "int", DBType: "integer"},
|
||||
},
|
||||
}[tableName], nil
|
||||
}
|
||||
|
||||
func (t testInterface) Columns(tableName string) ([]Column, error) {
|
||||
return testCols, nil
|
||||
// ForeignKeyInfo returns a list of mock foreignkeys
|
||||
func (m mockDriver) ForeignKeyInfo(tableName string) ([]ForeignKey, error) {
|
||||
return map[string][]ForeignKey{
|
||||
"jets": {
|
||||
{Table: "jets", Name: "jets_pilot_id_fk", Column: "pilot_id", ForeignTable: "pilots", ForeignColumn: "id", ForeignColumnUnique: true},
|
||||
{Table: "jets", Name: "jets_airport_id_fk", Column: "airport_id", ForeignTable: "airports", ForeignColumn: "id"},
|
||||
},
|
||||
"licenses": {
|
||||
{Table: "licenses", Name: "licenses_pilot_id_fk", Column: "pilot_id", ForeignTable: "pilots", ForeignColumn: "id"},
|
||||
},
|
||||
"pilot_languages": {
|
||||
{Table: "pilot_languages", Name: "pilot_id_fk", Column: "pilot_id", ForeignTable: "pilots", ForeignColumn: "id"},
|
||||
{Table: "pilot_languages", Name: "jet_id_fk", Column: "language_id", ForeignTable: "languages", ForeignColumn: "id"},
|
||||
},
|
||||
"hangars": {
|
||||
{Table: "hangars", Name: "hangar_fk_id", Column: "hangar_id", ForeignTable: "hangars", ForeignColumn: "id"},
|
||||
},
|
||||
}[tableName], nil
|
||||
}
|
||||
|
||||
func (t testInterface) UseLastInsertID() bool {
|
||||
return false
|
||||
// PrimaryKeyInfo returns mock primary key info for the passed in table name
|
||||
func (m mockDriver) PrimaryKeyInfo(tableName string) (*PrimaryKey, error) {
|
||||
return map[string]*PrimaryKey{
|
||||
"pilots": {Name: "pilot_id_pkey", Columns: []string{"id"}},
|
||||
"airports": {Name: "airport_id_pkey", Columns: []string{"id"}},
|
||||
"jets": {Name: "jet_id_pkey", Columns: []string{"id"}},
|
||||
"licenses": {Name: "license_id_pkey", Columns: []string{"id"}},
|
||||
"hangars": {Name: "hangar_id_pkey", Columns: []string{"id"}},
|
||||
"languages": {Name: "language_id_pkey", Columns: []string{"id"}},
|
||||
"pilot_languages": {Name: "pilot_languages_pkey", Columns: []string{"pilot_id", "language_id"}},
|
||||
}[tableName], nil
|
||||
}
|
||||
|
||||
var testPkey = &PrimaryKey{Name: "pkey1", Columns: []string{"col1", "col2"}}
|
||||
|
||||
func (t testInterface) PrimaryKeyInfo(tableName string) (*PrimaryKey, error) {
|
||||
return testPkey, nil
|
||||
}
|
||||
|
||||
var testFkeys = []ForeignKey{
|
||||
{
|
||||
Name: "fkey1",
|
||||
Column: "col1",
|
||||
ForeignTable: "table2",
|
||||
ForeignColumn: "col2",
|
||||
},
|
||||
{
|
||||
Name: "fkey2",
|
||||
Column: "col2",
|
||||
ForeignTable: "table1",
|
||||
ForeignColumn: "col1",
|
||||
},
|
||||
}
|
||||
|
||||
func (t testInterface) ForeignKeyInfo(tableName string) ([]ForeignKey, error) {
|
||||
return testFkeys, nil
|
||||
}
|
||||
|
||||
func (t testInterface) TranslateColumnType(column Column) Column {
|
||||
column.Type = "string"
|
||||
return column
|
||||
}
|
||||
|
||||
func (t testInterface) Open() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t testInterface) Close() {}
|
||||
|
||||
func TestTables(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tables, err := Tables(testInterface{})
|
||||
tables, err := Tables(mockDriver{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(tables) != 2 {
|
||||
t.Errorf("Expected len 2, got: %d\n", len(tables))
|
||||
if len(tables) != 7 {
|
||||
t.Errorf("Expected len 7, got: %d\n", len(tables))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tables[0].Columns, testCols) {
|
||||
t.Errorf("Did not get expected columns, got:\n%#v\n%#v", tables[0].Columns, testCols)
|
||||
pilots := GetTable(tables, "pilots")
|
||||
if len(pilots.Columns) != 2 {
|
||||
t.Error()
|
||||
}
|
||||
if pilots.ToManyRelationships[0].ForeignTable != "jets" {
|
||||
t.Error("want a to many to jets")
|
||||
}
|
||||
if pilots.ToManyRelationships[1].ForeignTable != "licenses" {
|
||||
t.Error("want a to many to languages")
|
||||
}
|
||||
if pilots.ToManyRelationships[2].ForeignTable != "languages" {
|
||||
t.Error("want a to many to languages")
|
||||
}
|
||||
|
||||
if !tables[0].IsJoinTable || !tables[1].IsJoinTable {
|
||||
t.Errorf("Expected IsJoinTable to be true")
|
||||
jets := GetTable(tables, "jets")
|
||||
if len(jets.ToManyRelationships) != 0 {
|
||||
t.Error("want no to many relationships")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tables[0].PKey, testPkey) {
|
||||
t.Errorf("Did not get expected PKey, got:\n#%v\n%#v", tables[0].PKey, testPkey)
|
||||
languages := GetTable(tables, "pilot_languages")
|
||||
if !languages.IsJoinTable {
|
||||
t.Error("languages is a join table")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tables[0].FKeys, testFkeys) {
|
||||
t.Errorf("Did not get expected Fkey, got:\n%#v\n%#v", tables[0].FKeys, testFkeys)
|
||||
hangars := GetTable(tables, "hangars")
|
||||
if len(hangars.ToManyRelationships) != 1 || hangars.ToManyRelationships[0].ForeignTable != "hangars" {
|
||||
t.Error("want 1 to many relationships")
|
||||
}
|
||||
|
||||
if len(tables[0].ToManyRelationships) != 1 {
|
||||
t.Error("wanted a to many relationship")
|
||||
}
|
||||
if len(tables[1].ToManyRelationships) != 1 {
|
||||
t.Error("wanted a to many relationship")
|
||||
if len(hangars.FKeys) != 1 || hangars.FKeys[0].ForeignTable != "hangars" {
|
||||
t.Error("want one hangar foreign key to itself")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,6 @@ func toManyRelationships(table Table, tables []Table) []ToManyRelationship {
|
|||
var relationships []ToManyRelationship
|
||||
|
||||
for _, t := range tables {
|
||||
if t.Name == table.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, f := range t.FKeys {
|
||||
if f.ForeignTable != table.Name {
|
||||
continue
|
||||
|
@ -58,7 +54,7 @@ func buildRelationship(localTable Table, foreignKey ForeignKey, foreignTable Tab
|
|||
if !foreignTable.IsJoinTable {
|
||||
col := localTable.GetColumn(foreignKey.ForeignColumn)
|
||||
return ToManyRelationship{
|
||||
Table: foreignKey.Table,
|
||||
Table: localTable.Name,
|
||||
Column: foreignKey.ForeignColumn,
|
||||
Nullable: col.Nullable,
|
||||
Unique: col.Unique,
|
||||
|
@ -72,7 +68,7 @@ func buildRelationship(localTable Table, foreignKey ForeignKey, foreignTable Tab
|
|||
|
||||
col := foreignTable.GetColumn(foreignKey.Column)
|
||||
relationship := ToManyRelationship{
|
||||
Table: foreignKey.Table,
|
||||
Table: localTable.Name,
|
||||
Column: foreignKey.ForeignColumn,
|
||||
Nullable: col.Nullable,
|
||||
Unique: col.Unique,
|
||||
|
|
|
@ -77,6 +77,7 @@ func TestToManyRelationships(t *testing.T) {
|
|||
|
||||
expected := []ToManyRelationship{
|
||||
{
|
||||
Table: "pilots",
|
||||
Column: "id",
|
||||
Nullable: false,
|
||||
Unique: false,
|
||||
|
@ -89,6 +90,7 @@ func TestToManyRelationships(t *testing.T) {
|
|||
ToJoinTable: false,
|
||||
},
|
||||
{
|
||||
Table: "pilots",
|
||||
Column: "id",
|
||||
Nullable: false,
|
||||
Unique: false,
|
||||
|
@ -101,6 +103,7 @@ func TestToManyRelationships(t *testing.T) {
|
|||
ToJoinTable: false,
|
||||
},
|
||||
{
|
||||
Table: "pilots",
|
||||
Column: "id",
|
||||
Nullable: false,
|
||||
Unique: false,
|
||||
|
@ -209,6 +212,7 @@ func TestToManyRelationshipsNull(t *testing.T) {
|
|||
|
||||
expected := []ToManyRelationship{
|
||||
{
|
||||
Table: "pilots",
|
||||
Column: "id",
|
||||
Nullable: true,
|
||||
Unique: true,
|
||||
|
@ -221,6 +225,7 @@ func TestToManyRelationshipsNull(t *testing.T) {
|
|||
ToJoinTable: false,
|
||||
},
|
||||
{
|
||||
Table: "pilots",
|
||||
Column: "id",
|
||||
Nullable: true,
|
||||
Unique: true,
|
||||
|
@ -233,6 +238,7 @@ func TestToManyRelationshipsNull(t *testing.T) {
|
|||
ToJoinTable: false,
|
||||
},
|
||||
{
|
||||
Table: "pilots",
|
||||
Column: "id",
|
||||
Nullable: true,
|
||||
Unique: true,
|
||||
|
|
|
@ -110,7 +110,7 @@ func (q *Query) BindFast(obj interface{}, titleCases map[string]string) error {
|
|||
// loadRelationships dynamically calls the template generated eager load
|
||||
// functions of the form:
|
||||
//
|
||||
// func (t *TableLoaded) LoadRelationshipName(exec Executor, singular bool, obj interface{})
|
||||
// func (t *TableR) LoadRelationshipName(exec Executor, singular bool, obj interface{})
|
||||
//
|
||||
// The arguments to this function are:
|
||||
// - t is not considered here, and is always passed nil. The function exists on a loaded
|
||||
|
@ -125,11 +125,11 @@ func (q *Query) loadRelationships(obj interface{}, singular bool) error {
|
|||
typ = typ.Elem().Elem()
|
||||
}
|
||||
|
||||
rel, found := typ.FieldByName("Loaded")
|
||||
rel, found := typ.FieldByName("R")
|
||||
// If the users object has no loaded struct, it must be
|
||||
// a custom object and we should not attempt to load any relationships.
|
||||
if !found {
|
||||
return errors.New("load query mod was used but bound struct contained no Loaded field")
|
||||
return errors.New("load query mod was used but bound struct contained no R field")
|
||||
}
|
||||
|
||||
for _, relationship := range q.load {
|
||||
|
|
|
@ -166,9 +166,9 @@ func TestBindSingular(t *testing.T) {
|
|||
|
||||
var loadFunctionCalled bool
|
||||
|
||||
type testLoadedStruct struct{}
|
||||
type testRStruct struct{}
|
||||
|
||||
func (r *testLoadedStruct) LoadTestOne(exec Executor, singular bool, obj interface{}) error {
|
||||
func (r *testRStruct) LoadTestOne(exec Executor, singular bool, obj interface{}) error {
|
||||
loadFunctionCalled = true
|
||||
return nil
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ func TestLoadRelationshipsSlice(t *testing.T) {
|
|||
|
||||
testSlice := []*struct {
|
||||
ID int
|
||||
Loaded *testLoadedStruct
|
||||
R *testRStruct
|
||||
}{}
|
||||
|
||||
q := Query{load: []string{"TestOne"}, executor: nil}
|
||||
|
@ -198,7 +198,7 @@ func TestLoadRelationshipsSingular(t *testing.T) {
|
|||
|
||||
testSingular := struct {
|
||||
ID int
|
||||
Loaded *testLoadedStruct
|
||||
R *testRStruct
|
||||
}{}
|
||||
|
||||
q := Query{load: []string{"TestOne"}, executor: nil}
|
||||
|
|
|
@ -11,15 +11,15 @@ type {{$modelName}} struct {
|
|||
{{end -}}
|
||||
{{- if .Table.IsJoinTable -}}
|
||||
{{- else}}
|
||||
Loaded *{{$modelName}}Loaded `boil:"-" json:"-" toml:"-" yaml:"-"`
|
||||
R *{{$modelName}}R `boil:"-" json:"-" toml:"-" yaml:"-"`
|
||||
{{end -}}
|
||||
}
|
||||
|
||||
{{- $dot := . -}}
|
||||
{{- if .Table.IsJoinTable -}}
|
||||
{{- else}}
|
||||
// {{$modelName}}Loaded are where relationships are eagerly loaded.
|
||||
type {{$modelName}}Loaded struct {
|
||||
// {{$modelName}}R is where relationships are stored.
|
||||
type {{$modelName}}R struct {
|
||||
{{range .Table.FKeys -}}
|
||||
{{- $rel := textsFromForeignKey $dot.PkgName $dot.Tables $dot.Table . -}}
|
||||
{{- template "relationship_to_one_struct_helper" $rel}}
|
||||
|
|
|
@ -28,11 +28,11 @@ func ({{$rel.Function.Receiver}} *{{$rel.LocalTable.NameGo}}) {{$rel.Function.Na
|
|||
{{if .ToJoinTable -}}
|
||||
queryMods = append(queryMods,
|
||||
qm.InnerJoin(`"{{.JoinTable}}" as "{{id 1}}" on "{{id 0}}"."{{.ForeignColumn}}" = "{{id 1}}"."{{.JoinForeignColumn}}"`),
|
||||
qm.Where(`"{{id 1}}"."{{.JoinLocalColumn}}"=$1`, {{.Column | titleCase | printf "%s.%s" $rel.Function.Receiver }}),
|
||||
qm.Where(`"{{id 1}}"."{{.JoinLocalColumn}}"=$1`, {{$rel.Function.Receiver}}.{{$rel.LocalTable.ColumnNameGo}}),
|
||||
)
|
||||
{{else -}}
|
||||
queryMods = append(queryMods,
|
||||
qm.Where(`"{{id 0}}"."{{.ForeignColumn}}"=$1`, {{.Column | titleCase | printf "%s.%s" $rel.Function.Receiver }}),
|
||||
qm.Where(`"{{id 0}}"."{{.ForeignColumn}}"=$1`, {{$rel.Function.Receiver}}.{{$rel.LocalTable.ColumnNameGo}}),
|
||||
)
|
||||
{{end}}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{{- $slice := printf "%sSlice" .LocalTable.NameGo -}}
|
||||
// Load{{.Function.Name}} allows an eager lookup of values, cached into the
|
||||
// loaded structs of the objects.
|
||||
func (r *{{.LocalTable.NameGo}}Loaded) Load{{.Function.Name}}(e boil.Executor, singular bool, {{$arg}} interface{}) error {
|
||||
func (r *{{.LocalTable.NameGo}}R) Load{{.Function.Name}}(e boil.Executor, singular bool, {{$arg}} interface{}) error {
|
||||
var slice []*{{.LocalTable.NameGo}}
|
||||
var object *{{.LocalTable.NameGo}}
|
||||
|
||||
|
@ -45,20 +45,20 @@ func (r *{{.LocalTable.NameGo}}Loaded) Load{{.Function.Name}}(e boil.Executor, s
|
|||
}
|
||||
|
||||
if singular && len(resultSlice) != 0 {
|
||||
if object.Loaded == nil {
|
||||
object.Loaded = &{{.LocalTable.NameGo}}Loaded{}
|
||||
if object.R == nil {
|
||||
object.R = &{{.LocalTable.NameGo}}R{}
|
||||
}
|
||||
object.Loaded.{{.Function.Name}} = resultSlice[0]
|
||||
object.R.{{.Function.Name}} = resultSlice[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, foreign := range resultSlice {
|
||||
for _, local := range slice {
|
||||
if local.{{.Function.LocalAssignment}} == foreign.{{.Function.ForeignAssignment}} {
|
||||
if local.Loaded == nil {
|
||||
local.Loaded = &{{.LocalTable.NameGo}}Loaded{}
|
||||
if local.R == nil {
|
||||
local.R = &{{.LocalTable.NameGo}}R{}
|
||||
}
|
||||
local.Loaded.{{.Function.Name}} = foreign
|
||||
local.R.{{.Function.Name}} = foreign
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
{{- $slice := printf "%sSlice" $rel.LocalTable.NameGo -}}
|
||||
// Load{{$rel.Function.Name}} allows an eager lookup of values, cached into the
|
||||
// loaded structs of the objects.
|
||||
func (r *{{$rel.LocalTable.NameGo}}Loaded) Load{{$rel.Function.Name}}(e boil.Executor, singular bool, {{$arg}} interface{}) error {
|
||||
func (r *{{$rel.LocalTable.NameGo}}R) Load{{$rel.Function.Name}}(e boil.Executor, singular bool, {{$arg}} interface{}) error {
|
||||
var slice []*{{$rel.LocalTable.NameGo}}
|
||||
var object *{{$rel.LocalTable.NameGo}}
|
||||
|
||||
|
@ -82,10 +82,10 @@ func (r *{{$rel.LocalTable.NameGo}}Loaded) Load{{$rel.Function.Name}}(e boil.Exe
|
|||
{{end}}
|
||||
|
||||
if singular {
|
||||
if object.Loaded == nil {
|
||||
object.Loaded = &{{$rel.LocalTable.NameGo}}Loaded{}
|
||||
if object.R == nil {
|
||||
object.R = &{{$rel.LocalTable.NameGo}}R{}
|
||||
}
|
||||
object.Loaded.{{$rel.Function.Name}} = resultSlice
|
||||
object.R.{{$rel.Function.Name}} = resultSlice
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -94,10 +94,10 @@ func (r *{{$rel.LocalTable.NameGo}}Loaded) Load{{$rel.Function.Name}}(e boil.Exe
|
|||
localJoinCol := localJoinCols[i]
|
||||
for _, local := range slice {
|
||||
if local.{{$rel.Function.LocalAssignment}} == localJoinCol {
|
||||
if local.Loaded == nil {
|
||||
local.Loaded = &{{$rel.LocalTable.NameGo}}Loaded{}
|
||||
if local.R == nil {
|
||||
local.R = &{{$rel.LocalTable.NameGo}}R{}
|
||||
}
|
||||
local.Loaded.{{$rel.Function.Name}} = append(local.Loaded.{{$rel.Function.Name}}, foreign)
|
||||
local.R.{{$rel.Function.Name}} = append(local.R.{{$rel.Function.Name}}, foreign)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -106,10 +106,10 @@ func (r *{{$rel.LocalTable.NameGo}}Loaded) Load{{$rel.Function.Name}}(e boil.Exe
|
|||
for _, foreign := range resultSlice {
|
||||
for _, local := range slice {
|
||||
if local.{{$rel.Function.LocalAssignment}} == foreign.{{$rel.Function.ForeignAssignment}} {
|
||||
if local.Loaded == nil {
|
||||
local.Loaded = &{{$rel.LocalTable.NameGo}}Loaded{}
|
||||
if local.R == nil {
|
||||
local.R = &{{$rel.LocalTable.NameGo}}R{}
|
||||
}
|
||||
local.Loaded.{{$rel.Function.Name}} = append(local.Loaded.{{$rel.Function.Name}}, foreign)
|
||||
local.R.{{$rel.Function.Name}} = append(local.R.{{$rel.Function.Name}}, foreign)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
216
templates/relationship_to_many_setops.tpl
Normal file
216
templates/relationship_to_many_setops.tpl
Normal file
|
@ -0,0 +1,216 @@
|
|||
{{- if .Table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- $dot := . -}}
|
||||
{{- $table := .Table -}}
|
||||
{{- range .Table.ToManyRelationships -}}
|
||||
{{- $varNameSingular := .ForeignTable | singular | camelCase -}}
|
||||
{{- if (and .ForeignColumnUnique (not .ToJoinTable)) -}}
|
||||
{{- template "relationship_to_one_setops_helper" (textsFromOneToOneRelationship $dot.PkgName $dot.Tables $table .) -}}
|
||||
{{- else -}}
|
||||
{{- $rel := textsFromRelationship $dot.Tables $table .}}
|
||||
|
||||
// Add{{$rel.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 {{$rel.Function.Receiver}}.R.{{$rel.Function.Name}}.
|
||||
// Sets related.R.{{$rel.Function.ForeignName}} appropriately.
|
||||
func ({{$rel.Function.Receiver}} *{{$rel.LocalTable.NameGo}}) Add{{$rel.Function.Name}}(exec boil.Executor, insert bool, related ...*{{$rel.ForeignTable.NameGo}}) error {
|
||||
var err error
|
||||
for _, rel := range related {
|
||||
{{if not .ToJoinTable -}}
|
||||
rel.{{$rel.Function.ForeignAssignment}} = {{$rel.Function.Receiver}}.{{$rel.Function.LocalAssignment}}
|
||||
{{if .ForeignColumnNullable -}}
|
||||
rel.{{$rel.ForeignTable.ColumnNameGo}}.Valid = true
|
||||
{{end -}}
|
||||
{{end -}}
|
||||
if insert {
|
||||
if err = rel.Insert(exec); err != nil {
|
||||
return errors.Wrap(err, "failed to insert into foreign table")
|
||||
}
|
||||
}{{if not .ToJoinTable}} else {
|
||||
if err = rel.Update(exec, "{{.ForeignColumn}}"); err != nil {
|
||||
return errors.Wrap(err, "failed to update foreign table")
|
||||
}
|
||||
}{{end -}}
|
||||
}
|
||||
|
||||
{{if .ToJoinTable -}}
|
||||
for _, rel := range related {
|
||||
query := `insert into "{{.JoinTable}}" ({{.JoinLocalColumn}}, {{.JoinForeignColumn}}) values ($1, $2)`
|
||||
values := []interface{}{{"{"}}{{$rel.Function.Receiver}}.{{$rel.LocalTable.ColumnNameGo}}, rel.{{$rel.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 {{$rel.Function.Receiver}}.R == nil {
|
||||
{{$rel.Function.Receiver}}.R = &{{$rel.LocalTable.NameGo}}R{
|
||||
{{$rel.Function.Name}}: related,
|
||||
}
|
||||
} else {
|
||||
{{$rel.Function.Receiver}}.R.{{$rel.Function.Name}} = append({{$rel.Function.Receiver}}.R.{{$rel.Function.Name}}, related...)
|
||||
}
|
||||
|
||||
{{if .ToJoinTable -}}
|
||||
for _, rel := range related {
|
||||
if rel.R == nil {
|
||||
rel.R = &{{$rel.ForeignTable.NameGo}}R{
|
||||
{{$rel.Function.ForeignName}}: {{$rel.LocalTable.NameGo}}Slice{{"{"}}{{$rel.Function.Receiver}}{{"}"}},
|
||||
}
|
||||
} else {
|
||||
rel.R.{{$rel.Function.ForeignName}} = append(rel.R.{{$rel.Function.ForeignName}}, {{$rel.Function.Receiver}})
|
||||
}
|
||||
}
|
||||
{{else -}}
|
||||
for _, rel := range related {
|
||||
if rel.R == nil {
|
||||
rel.R = &{{$rel.ForeignTable.NameGo}}R{
|
||||
{{$rel.Function.ForeignName}}: {{$rel.Function.Receiver}},
|
||||
}
|
||||
} else {
|
||||
rel.R.{{$rel.Function.ForeignName}} = {{$rel.Function.Receiver}}
|
||||
}
|
||||
}
|
||||
{{end -}}
|
||||
|
||||
return nil
|
||||
}
|
||||
{{- if .ForeignColumnNullable}}
|
||||
|
||||
// Set{{$rel.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 {{$rel.Function.Receiver}}.R.{{$rel.Function.ForeignName}}'s {{$rel.Function.Name}} accordingly.
|
||||
// Replaces {{$rel.Function.Receiver}}.R.{{$rel.Function.Name}} with related.
|
||||
// Sets related.R.{{$rel.Function.ForeignName}}'s {{$rel.Function.Name}} accordingly.
|
||||
func ({{$rel.Function.Receiver}} *{{$rel.LocalTable.NameGo}}) Set{{$rel.Function.Name}}(exec boil.Executor, insert bool, related ...*{{$rel.ForeignTable.NameGo}}) error {
|
||||
{{if .ToJoinTable -}}
|
||||
query := `delete from "{{.JoinTable}}" where "{{.JoinLocalColumn}}" = $1`
|
||||
values := []interface{}{{"{"}}{{$rel.Function.Receiver}}.{{$rel.LocalTable.ColumnNameGo}}}
|
||||
{{else -}}
|
||||
query := `update "{{.ForeignTable}}" set "{{.ForeignColumn}}" = null where "{{.ForeignColumn}}" = $1`
|
||||
values := []interface{}{{"{"}}{{$rel.Function.Receiver}}.{{$rel.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{{$rel.LocalTable.NameGo}}From{{$rel.ForeignTable.NameGo}}Slice({{$rel.Function.Receiver}}, related)
|
||||
{{$rel.Function.Receiver}}.R.{{$rel.Function.Name}} = nil
|
||||
{{else -}}
|
||||
if {{$rel.Function.Receiver}}.R != nil {
|
||||
for _, rel := range {{$rel.Function.Receiver}}.R.{{$rel.Function.Name}} {
|
||||
rel.{{$rel.ForeignTable.ColumnNameGo}}.Valid = false
|
||||
if rel.R == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rel.R.{{$rel.Function.ForeignName}} = nil
|
||||
}
|
||||
|
||||
{{$rel.Function.Receiver}}.R.{{$rel.Function.Name}} = nil
|
||||
}
|
||||
{{end -}}
|
||||
|
||||
return {{$rel.Function.Receiver}}.Add{{$rel.Function.Name}}(exec, insert, related...)
|
||||
}
|
||||
|
||||
// Remove{{$rel.Function.Name}} relationships from objects passed in.
|
||||
// Removes related items from R.{{$rel.Function.Name}} (uses pointer comparison, removal does not keep order)
|
||||
// Sets related.R.{{$rel.Function.ForeignName}}.
|
||||
func ({{$rel.Function.Receiver}} *{{$rel.LocalTable.NameGo}}) Remove{{$rel.Function.Name}}(exec boil.Executor, related ...*{{$rel.ForeignTable.NameGo}}) error {
|
||||
var err error
|
||||
{{if .ToJoinTable -}}
|
||||
query := fmt.Sprintf(
|
||||
`delete from "{{.JoinTable}}" where "{{.JoinLocalColumn}}" = $1 and "{{.JoinForeignColumn}}" in (%s)`,
|
||||
strmangle.Placeholders(len(related), 1, 1),
|
||||
)
|
||||
values := []interface{}{{"{"}}{{$rel.Function.Receiver}}.{{$rel.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.{{$rel.ForeignTable.ColumnNameGo}}.Valid = false
|
||||
{{if not .ToJoinTable -}}
|
||||
if rel.R != nil {
|
||||
rel.R.{{$rel.Function.ForeignName}} = nil
|
||||
}
|
||||
{{end -}}
|
||||
if err = rel.Update(exec, "{{.ForeignColumn}}"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
{{end -}}
|
||||
|
||||
{{if .ToJoinTable -}}
|
||||
remove{{$rel.LocalTable.NameGo}}From{{$rel.ForeignTable.NameGo}}Slice({{$rel.Function.Receiver}}, related)
|
||||
{{end -}}
|
||||
if {{$rel.Function.Receiver}}.R == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, rel := range related {
|
||||
for i, ri := range {{$rel.Function.Receiver}}.R.{{$rel.Function.Name}} {
|
||||
if rel != ri {
|
||||
continue
|
||||
}
|
||||
|
||||
ln := len({{$rel.Function.Receiver}}.R.{{$rel.Function.Name}})
|
||||
if ln > 1 && i < ln-1 {
|
||||
{{$rel.Function.Receiver}}.R.{{$rel.Function.Name}}[i] = {{$rel.Function.Receiver}}.R.{{$rel.Function.Name}}[ln-1]
|
||||
}
|
||||
{{$rel.Function.Receiver}}.R.{{$rel.Function.Name}} = {{$rel.Function.Receiver}}.R.{{$rel.Function.Name}}[:ln-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
{{if .ToJoinTable -}}
|
||||
func remove{{$rel.LocalTable.NameGo}}From{{$rel.ForeignTable.NameGo}}Slice({{$rel.Function.Receiver}} *{{$rel.LocalTable.NameGo}}, related []*{{$rel.ForeignTable.NameGo}}) {
|
||||
for _, rel := range related {
|
||||
if rel.R == nil {
|
||||
continue
|
||||
}
|
||||
for i, ri := range rel.R.{{$rel.Function.ForeignName}} {
|
||||
if {{$rel.Function.Receiver}}.{{$rel.Function.LocalAssignment}} != ri.{{$rel.Function.LocalAssignment}} {
|
||||
continue
|
||||
}
|
||||
|
||||
ln := len(rel.R.{{$rel.Function.ForeignName}})
|
||||
if ln > 1 && i < ln-1 {
|
||||
rel.R.{{$rel.Function.ForeignName}}[i] = rel.R.{{$rel.Function.ForeignName}}[ln-1]
|
||||
}
|
||||
rel.R.{{$rel.Function.ForeignName}} = rel.R.{{$rel.Function.ForeignName}}[:ln-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
{{end -}}{{- /* if join table */ -}}
|
||||
{{- end -}}{{- /* if nullable foreign key */ -}}
|
||||
{{- end -}}{{- /* if unique foreign key */ -}}
|
||||
{{- end -}}{{- /* range relationships */ -}}
|
||||
{{- end -}}{{- /* outer if join table */ -}}
|
103
templates/relationship_to_one_setops.tpl
Normal file
103
templates/relationship_to_one_setops.tpl
Normal file
|
@ -0,0 +1,103 @@
|
|||
{{- define "relationship_to_one_setops_helper" -}}
|
||||
{{- $varNameSingular := .ForeignKey.ForeignTable | singular | camelCase}}
|
||||
|
||||
// Set{{.Function.Name}} of the {{.ForeignKey.Table | singular}} to the related item.
|
||||
// Sets {{.Function.Receiver}}.R.{{.Function.Name}} to related.
|
||||
// Adds {{.Function.Receiver}} to related.R.{{.Function.ForeignName}}.
|
||||
func ({{.Function.Receiver}} *{{.LocalTable.NameGo}}) Set{{.Function.Name}}(exec boil.Executor, insert bool, related *{{.ForeignTable.NameGo}}) error {
|
||||
var err error
|
||||
if insert {
|
||||
if err = related.Insert(exec); err != nil {
|
||||
return errors.Wrap(err, "failed to insert into foreign table")
|
||||
}
|
||||
}
|
||||
|
||||
oldVal := {{.Function.Receiver}}.{{.LocalTable.ColumnNameGo}}
|
||||
{{.Function.Receiver}}.{{.Function.LocalAssignment}} = related.{{.Function.ForeignAssignment}}
|
||||
if err = {{.Function.Receiver}}.Update(exec, "{{.ForeignKey.Column}}"); err != nil {
|
||||
{{.Function.Receiver}}.{{.LocalTable.ColumnNameGo}} = oldVal
|
||||
return errors.Wrap(err, "failed to update local table")
|
||||
}
|
||||
|
||||
if {{.Function.Receiver}}.R == nil {
|
||||
{{.Function.Receiver}}.R = &{{.LocalTable.NameGo}}R{
|
||||
{{.Function.Name}}: related,
|
||||
}
|
||||
} else {
|
||||
{{.Function.Receiver}}.R.{{.Function.Name}} = related
|
||||
}
|
||||
|
||||
{{if (or .ForeignKey.Unique .Function.OneToOne) -}}
|
||||
if related.R == nil {
|
||||
related.R = &{{.ForeignTable.NameGo}}R{
|
||||
{{.Function.ForeignName}}: {{.Function.Receiver}},
|
||||
}
|
||||
} else {
|
||||
related.R.{{.Function.ForeignName}} = {{.Function.Receiver}}
|
||||
}
|
||||
{{else -}}
|
||||
if related.R == nil {
|
||||
related.R = &{{.ForeignTable.NameGo}}R{
|
||||
{{.Function.ForeignName}}: {{.LocalTable.NameGo}}Slice{{"{"}}{{.Function.Receiver}}{{"}"}},
|
||||
}
|
||||
} else {
|
||||
related.R.{{.Function.ForeignName}} = append(related.R.{{.Function.ForeignName}}, {{.Function.Receiver}})
|
||||
}
|
||||
{{end -}}
|
||||
|
||||
{{if .ForeignKey.Nullable}}
|
||||
{{.Function.Receiver}}.{{.LocalTable.ColumnNameGo}}.Valid = true
|
||||
{{end -}}
|
||||
return nil
|
||||
}
|
||||
{{- if .ForeignKey.Nullable}}
|
||||
|
||||
// Remove{{.Function.Name}} relationship.
|
||||
// Sets {{.Function.Receiver}}.R.{{.Function.Name}} to nil.
|
||||
// Removes {{.Function.Receiver}} from all passed in related items' relationships struct (Optional).
|
||||
func ({{.Function.Receiver}} *{{.LocalTable.NameGo}}) Remove{{.Function.Name}}(exec boil.Executor, related ...*{{.ForeignTable.NameGo}}) error {
|
||||
var err error
|
||||
|
||||
{{.Function.Receiver}}.{{.LocalTable.ColumnNameGo}}.Valid = false
|
||||
if err = {{.Function.Receiver}}.Update(exec, "{{.ForeignKey.Column}}"); err != nil {
|
||||
{{.Function.Receiver}}.{{.LocalTable.ColumnNameGo}}.Valid = true
|
||||
return errors.Wrap(err, "failed to update local table")
|
||||
}
|
||||
|
||||
for _, rel := range related {
|
||||
if rel.R == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
{{if .ForeignKey.Unique -}}
|
||||
rel.R.{{.Function.ForeignName}} = nil
|
||||
{{else -}}
|
||||
for i, ri := range rel.R.{{.Function.ForeignName}} {
|
||||
if {{.Function.Receiver}}.{{.Function.LocalAssignment}} != ri.{{.Function.LocalAssignment}} {
|
||||
continue
|
||||
}
|
||||
|
||||
ln := len(rel.R.{{.Function.ForeignName}})
|
||||
if ln > 1 && i < ln-1 {
|
||||
rel.R.{{.Function.ForeignName}}[i], rel.R.{{.Function.ForeignName}}[ln-1] =
|
||||
rel.R.{{.Function.ForeignName}}[ln-1], rel.R.{{.Function.ForeignName}}[i]
|
||||
}
|
||||
rel.R.{{.Function.ForeignName}} = rel.R.{{.Function.ForeignName}}[:ln-1]
|
||||
break
|
||||
}
|
||||
{{end -}}
|
||||
}
|
||||
|
||||
{{.Function.Receiver}}.R.{{.Function.Name}} = nil
|
||||
return nil
|
||||
}
|
||||
{{end -}}
|
||||
{{- end -}}
|
||||
{{- if .Table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- $dot := . -}}
|
||||
{{- range .Table.FKeys -}}
|
||||
{{- $rel := textsFromForeignKey $dot.PkgName $dot.Tables $dot.Table . -}}
|
||||
{{- template "relationship_to_one_setops_helper" $rel -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
|
@ -41,11 +41,11 @@ func test{{$rel.LocalTable.NameGo}}ToMany{{$rel.Function.Name}}(t *testing.T) {
|
|||
}
|
||||
|
||||
{{if .ToJoinTable -}}
|
||||
_, err = tx.Exec(`insert into {{.JoinTable}} ({{.JoinLocalColumn}}, {{.JoinForeignColumn}}) values ($1, $2)`, a.{{.Column | titleCase}}, b.{{.ForeignColumn | titleCase}})
|
||||
_, err = tx.Exec(`insert into "{{.JoinTable}}" ({{.JoinLocalColumn}}, {{.JoinForeignColumn}}) values ($1, $2)`, a.{{$rel.LocalTable.ColumnNameGo}}, b.{{$rel.ForeignTable.ColumnNameGo}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = tx.Exec(`insert into {{.JoinTable}} ({{.JoinLocalColumn}}, {{.JoinForeignColumn}}) values ($1, $2)`, a.{{.Column | titleCase}}, c.{{.ForeignColumn | titleCase}})
|
||||
_, err = tx.Exec(`insert into "{{.JoinTable}}" ({{.JoinLocalColumn}}, {{.JoinForeignColumn}}) values ($1, $2)`, a.{{$rel.LocalTable.ColumnNameGo}}, c.{{$rel.ForeignTable.ColumnNameGo}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -75,18 +75,18 @@ func test{{$rel.LocalTable.NameGo}}ToMany{{$rel.Function.Name}}(t *testing.T) {
|
|||
}
|
||||
|
||||
slice := {{$rel.LocalTable.NameGo}}Slice{&a}
|
||||
if err = a.Loaded.Load{{$rel.Function.Name}}(tx, false, &slice); err != nil {
|
||||
if err = a.R.Load{{$rel.Function.Name}}(tx, false, &slice); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := len(a.Loaded.{{$rel.Function.Name}}); got != 2 {
|
||||
if got := len(a.R.{{$rel.Function.Name}}); got != 2 {
|
||||
t.Error("number of eager loaded records wrong, got:", got)
|
||||
}
|
||||
|
||||
a.Loaded.{{$rel.Function.Name}} = nil
|
||||
if err = a.Loaded.Load{{$rel.Function.Name}}(tx, true, &a); err != nil {
|
||||
a.R.{{$rel.Function.Name}} = nil
|
||||
if err = a.R.Load{{$rel.Function.Name}}(tx, true, &a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := len(a.Loaded.{{$rel.Function.Name}}); got != 2 {
|
||||
if got := len(a.R.{{$rel.Function.Name}}); got != 2 {
|
||||
t.Error("number of eager loaded records wrong, got:", got)
|
||||
}
|
||||
|
||||
|
|
308
templates_test/relationship_to_many_setops.tpl
Normal file
308
templates_test/relationship_to_many_setops.tpl
Normal file
|
@ -0,0 +1,308 @@
|
|||
{{- if .Table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- $dot := . -}}
|
||||
{{- $table := .Table -}}
|
||||
{{- range .Table.ToManyRelationships -}}
|
||||
{{- if (and .ForeignColumnUnique (not .ToJoinTable)) -}}
|
||||
{{- template "relationship_to_one_setops_test_helper" (textsFromOneToOneRelationship $dot.PkgName $dot.Tables $table .) -}}
|
||||
{{- else -}}
|
||||
{{- $varNameSingular := .Table | singular | camelCase -}}
|
||||
{{- $foreignVarNameSingular := .ForeignTable | singular | camelCase -}}
|
||||
{{- $rel := textsFromRelationship $dot.Tables $table .}}
|
||||
|
||||
func test{{$rel.LocalTable.NameGo}}ToManyAddOp{{$rel.Function.Name}}(t *testing.T) {
|
||||
var err error
|
||||
|
||||
tx := MustTx(boil.Begin())
|
||||
defer tx.Rollback()
|
||||
|
||||
var a {{$rel.LocalTable.NameGo}}
|
||||
var b, c, d, e {{$rel.ForeignTable.NameGo}}
|
||||
|
||||
seed := randomize.NewSeed()
|
||||
if err = randomize.Struct(seed, &a, {{$varNameSingular}}DBTypes, false, {{$varNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foreigners := []*{{$rel.ForeignTable.NameGo}}{&b, &c, &d, &e}
|
||||
for _, x := range foreigners {
|
||||
if err = randomize.Struct(seed, x, {{$foreignVarNameSingular}}DBTypes, false, {{$foreignVarNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = b.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = c.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
foreignersSplitByInsertion := [][]*{{$rel.ForeignTable.NameGo}}{
|
||||
{&b, &c},
|
||||
{&d, &e},
|
||||
}
|
||||
|
||||
for i, x := range foreignersSplitByInsertion {
|
||||
err = a.Add{{$rel.Function.Name}}(tx, i != 0, x...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
first := x[0]
|
||||
second := x[1]
|
||||
{{- if .ToJoinTable}}
|
||||
|
||||
if first.R.{{$rel.Function.ForeignName}}[0] != &a {
|
||||
t.Error("relationship was not added properly to the slice")
|
||||
}
|
||||
if second.R.{{$rel.Function.ForeignName}}[0] != &a {
|
||||
t.Error("relationship was not added properly to the slice")
|
||||
}
|
||||
{{- else}}
|
||||
|
||||
if a.{{$rel.Function.LocalAssignment}} != first.{{$rel.Function.ForeignAssignment}} {
|
||||
t.Error("foreign key was wrong value", a.{{$rel.Function.LocalAssignment}}, first.{{$rel.Function.ForeignAssignment}})
|
||||
}
|
||||
if a.{{$rel.Function.LocalAssignment}} != second.{{$rel.Function.ForeignAssignment}} {
|
||||
t.Error("foreign key was wrong value", a.{{$rel.Function.LocalAssignment}}, second.{{$rel.Function.ForeignAssignment}})
|
||||
}
|
||||
|
||||
if first.R.{{$rel.Function.ForeignName}} != &a {
|
||||
t.Error("relationship was not added properly to the foreign slice")
|
||||
}
|
||||
if second.R.{{$rel.Function.ForeignName}} != &a {
|
||||
t.Error("relationship was not added properly to the foreign slice")
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
if a.R.{{$rel.Function.Name}}[i*2] != first {
|
||||
t.Error("relationship struct slice not set to correct value")
|
||||
}
|
||||
if a.R.{{$rel.Function.Name}}[i*2+1] != second {
|
||||
t.Error("relationship struct slice not set to correct value")
|
||||
}
|
||||
|
||||
count, err := a.{{$rel.Function.Name}}(tx).Count()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want := int64((i+1)*2); count != want {
|
||||
t.Error("want", want, "got", count)
|
||||
}
|
||||
}
|
||||
}
|
||||
{{if .ForeignColumnNullable}}
|
||||
|
||||
func test{{$rel.LocalTable.NameGo}}ToManySetOp{{$rel.Function.Name}}(t *testing.T) {
|
||||
var err error
|
||||
|
||||
tx := MustTx(boil.Begin())
|
||||
defer tx.Rollback()
|
||||
|
||||
var a {{$rel.LocalTable.NameGo}}
|
||||
var b, c, d, e {{$rel.ForeignTable.NameGo}}
|
||||
|
||||
seed := randomize.NewSeed()
|
||||
if err = randomize.Struct(seed, &a, {{$varNameSingular}}DBTypes, false, {{$varNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foreigners := []*{{$rel.ForeignTable.NameGo}}{&b, &c, &d, &e}
|
||||
for _, x := range foreigners {
|
||||
if err = randomize.Struct(seed, x, {{$foreignVarNameSingular}}DBTypes, false, {{$foreignVarNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = a.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = b.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = c.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = a.Set{{$rel.Function.Name}}(tx, false, &b, &c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
count, err := a.{{$rel.Function.Name}}(tx).Count()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 2 {
|
||||
t.Error("count was wrong:", count)
|
||||
}
|
||||
|
||||
err = a.Set{{$rel.Function.Name}}(tx, true, &d, &e)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
count, err = a.{{$rel.Function.Name}}(tx).Count()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 2 {
|
||||
t.Error("count was wrong:", count)
|
||||
}
|
||||
|
||||
{{- if .ToJoinTable}}
|
||||
|
||||
if len(b.R.{{$rel.Function.ForeignName}}) != 0 {
|
||||
t.Error("relationship was not removed properly from the slice")
|
||||
}
|
||||
if len(c.R.{{$rel.Function.ForeignName}}) != 0 {
|
||||
t.Error("relationship was not removed properly from the slice")
|
||||
}
|
||||
if d.R.{{$rel.Function.ForeignName}}[0] != &a {
|
||||
t.Error("relationship was not added properly to the slice")
|
||||
}
|
||||
if e.R.{{$rel.Function.ForeignName}}[0] != &a {
|
||||
t.Error("relationship was not added properly to the slice")
|
||||
}
|
||||
{{- else}}
|
||||
|
||||
if b.{{$rel.ForeignTable.ColumnNameGo}}.Valid {
|
||||
t.Error("want b's foreign key value to be nil")
|
||||
}
|
||||
if c.{{$rel.ForeignTable.ColumnNameGo}}.Valid {
|
||||
t.Error("want c's foreign key value to be nil")
|
||||
}
|
||||
if a.{{$rel.Function.LocalAssignment}} != d.{{$rel.Function.ForeignAssignment}} {
|
||||
t.Error("foreign key was wrong value", a.{{$rel.Function.LocalAssignment}}, d.{{$rel.Function.ForeignAssignment}})
|
||||
}
|
||||
if a.{{$rel.Function.LocalAssignment}} != e.{{$rel.Function.ForeignAssignment}} {
|
||||
t.Error("foreign key was wrong value", a.{{$rel.Function.LocalAssignment}}, e.{{$rel.Function.ForeignAssignment}})
|
||||
}
|
||||
|
||||
if b.R.{{$rel.Function.ForeignName}} != nil {
|
||||
t.Error("relationship was not removed properly from the foreign struct")
|
||||
}
|
||||
if c.R.{{$rel.Function.ForeignName}} != nil {
|
||||
t.Error("relationship was not removed properly from the foreign struct")
|
||||
}
|
||||
if d.R.{{$rel.Function.ForeignName}} != &a {
|
||||
t.Error("relationship was not added properly to the foreign struct")
|
||||
}
|
||||
if e.R.{{$rel.Function.ForeignName}} != &a {
|
||||
t.Error("relationship was not added properly to the foreign struct")
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
if a.R.{{$rel.Function.Name}}[0] != &d {
|
||||
t.Error("relationship struct slice not set to correct value")
|
||||
}
|
||||
if a.R.{{$rel.Function.Name}}[1] != &e {
|
||||
t.Error("relationship struct slice not set to correct value")
|
||||
}
|
||||
}
|
||||
|
||||
func test{{$rel.LocalTable.NameGo}}ToManyRemoveOp{{$rel.Function.Name}}(t *testing.T) {
|
||||
var err error
|
||||
|
||||
tx := MustTx(boil.Begin())
|
||||
defer tx.Rollback()
|
||||
|
||||
var a {{$rel.LocalTable.NameGo}}
|
||||
var b, c, d, e {{$rel.ForeignTable.NameGo}}
|
||||
|
||||
seed := randomize.NewSeed()
|
||||
if err = randomize.Struct(seed, &a, {{$varNameSingular}}DBTypes, false, {{$varNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foreigners := []*{{$rel.ForeignTable.NameGo}}{&b, &c, &d, &e}
|
||||
for _, x := range foreigners {
|
||||
if err = randomize.Struct(seed, x, {{$foreignVarNameSingular}}DBTypes, false, {{$foreignVarNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = a.Add{{$rel.Function.Name}}(tx, true, foreigners...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
count, err := a.{{$rel.Function.Name}}(tx).Count()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 4 {
|
||||
t.Error("count was wrong:", count)
|
||||
}
|
||||
|
||||
err = a.Remove{{$rel.Function.Name}}(tx, foreigners[:2]...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
count, err = a.{{$rel.Function.Name}}(tx).Count()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count != 2 {
|
||||
t.Error("count was wrong:", count)
|
||||
}
|
||||
|
||||
{{- if .ToJoinTable}}
|
||||
|
||||
if len(b.R.{{$rel.Function.ForeignName}}) != 0 {
|
||||
t.Error("relationship was not removed properly from the slice")
|
||||
}
|
||||
if len(c.R.{{$rel.Function.ForeignName}}) != 0 {
|
||||
t.Error("relationship was not removed properly from the slice")
|
||||
}
|
||||
if d.R.{{$rel.Function.ForeignName}}[0] != &a {
|
||||
t.Error("relationship was not added properly to the foreign struct")
|
||||
}
|
||||
if e.R.{{$rel.Function.ForeignName}}[0] != &a {
|
||||
t.Error("relationship was not added properly to the foreign struct")
|
||||
}
|
||||
{{- else}}
|
||||
|
||||
if b.{{$rel.ForeignTable.ColumnNameGo}}.Valid {
|
||||
t.Error("want b's foreign key value to be nil")
|
||||
}
|
||||
if c.{{$rel.ForeignTable.ColumnNameGo}}.Valid {
|
||||
t.Error("want c's foreign key value to be nil")
|
||||
}
|
||||
|
||||
if b.R.{{$rel.Function.ForeignName}} != nil {
|
||||
t.Error("relationship was not removed properly from the foreign struct")
|
||||
}
|
||||
if c.R.{{$rel.Function.ForeignName}} != nil {
|
||||
t.Error("relationship was not removed properly from the foreign struct")
|
||||
}
|
||||
if d.R.{{$rel.Function.ForeignName}} != &a {
|
||||
t.Error("relationship to a should have been preserved")
|
||||
}
|
||||
if e.R.{{$rel.Function.ForeignName}} != &a {
|
||||
t.Error("relationship to a should have been preserved")
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
if len(a.R.{{$rel.Function.Name}}) != 2 {
|
||||
t.Error("should have preserved two relationships")
|
||||
}
|
||||
|
||||
// Removal doesn't do a stable deletion for performance so we have to flip the order
|
||||
if a.R.{{$rel.Function.Name}}[1] != &d {
|
||||
t.Error("relationship to d should have been preserved")
|
||||
}
|
||||
if a.R.{{$rel.Function.Name}}[0] != &e {
|
||||
t.Error("relationship to e should have been preserved")
|
||||
}
|
||||
}
|
||||
{{end -}}
|
||||
{{- end -}}{{- /* if unique foreign key */ -}}
|
||||
{{- end -}}{{- /* range relationships */ -}}
|
||||
{{- end -}}{{- /* outer if join table */ -}}
|
|
@ -12,7 +12,7 @@ func test{{.LocalTable.NameGo}}ToOne{{.ForeignTable.NameGo}}_{{.Function.Name}}(
|
|||
foreign.{{.ForeignKey.ForeignColumn | titleCase}}.Valid = true
|
||||
{{end}}
|
||||
|
||||
{{if not .Function.ReverseInserts -}}
|
||||
{{if not .Function.OneToOne -}}
|
||||
if err := foreign.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -42,18 +42,18 @@ func test{{.LocalTable.NameGo}}ToOne{{.ForeignTable.NameGo}}_{{.Function.Name}}(
|
|||
}
|
||||
|
||||
slice := {{.LocalTable.NameGo}}Slice{&local}
|
||||
if err = local.Loaded.Load{{.Function.Name}}(tx, false, &slice); err != nil {
|
||||
if err = local.R.Load{{.Function.Name}}(tx, false, &slice); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if local.Loaded.{{.Function.Name}} == nil {
|
||||
if local.R.{{.Function.Name}} == nil {
|
||||
t.Error("struct should have been eager loaded")
|
||||
}
|
||||
|
||||
local.Loaded.{{.Function.Name}} = nil
|
||||
if err = local.Loaded.Load{{.Function.Name}}(tx, true, &local); err != nil {
|
||||
local.R.{{.Function.Name}} = nil
|
||||
if err = local.R.Load{{.Function.Name}}(tx, true, &local); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if local.Loaded.{{.Function.Name}} == nil {
|
||||
if local.R.{{.Function.Name}} == nil {
|
||||
t.Error("struct should have been eager loaded")
|
||||
}
|
||||
}
|
||||
|
|
133
templates_test/relationship_to_one_setops.tpl
Normal file
133
templates_test/relationship_to_one_setops.tpl
Normal file
|
@ -0,0 +1,133 @@
|
|||
{{- define "relationship_to_one_setops_test_helper" -}}
|
||||
{{- $varNameSingular := .ForeignKey.Table | singular | camelCase -}}
|
||||
{{- $foreignVarNameSingular := .ForeignKey.ForeignTable | singular | camelCase -}}
|
||||
func test{{.LocalTable.NameGo}}ToOneSetOp{{.ForeignTable.NameGo}}_{{.Function.Name}}(t *testing.T) {
|
||||
var err error
|
||||
|
||||
tx := MustTx(boil.Begin())
|
||||
defer tx.Rollback()
|
||||
|
||||
var a {{.LocalTable.NameGo}}
|
||||
var b, c {{.ForeignTable.NameGo}}
|
||||
|
||||
seed := randomize.NewSeed()
|
||||
if err = randomize.Struct(seed, &a, {{$varNameSingular}}DBTypes, false, {{$varNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = randomize.Struct(seed, &b, {{$foreignVarNameSingular}}DBTypes, false, {{$foreignVarNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = randomize.Struct(seed, &c, {{$foreignVarNameSingular}}DBTypes, false, {{$foreignVarNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := a.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = b.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, x := range []*{{.ForeignTable.NameGo}}{&b, &c} {
|
||||
err = a.Set{{.Function.Name}}(tx, i != 0, x)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if a.{{.Function.LocalAssignment}} != x.{{.Function.ForeignAssignment}} {
|
||||
t.Error("foreign key was wrong value", a.{{.Function.LocalAssignment}})
|
||||
}
|
||||
if a.R.{{.Function.Name}} != x {
|
||||
t.Error("relationship struct not set to correct value")
|
||||
}
|
||||
|
||||
zero := reflect.Zero(reflect.TypeOf(a.{{.Function.LocalAssignment}}))
|
||||
reflect.Indirect(reflect.ValueOf(&a.{{.Function.LocalAssignment}})).Set(zero)
|
||||
|
||||
if err = a.Reload(tx); err != nil {
|
||||
t.Fatal("failed to reload", err)
|
||||
}
|
||||
|
||||
if a.{{.Function.LocalAssignment}} != x.{{.Function.ForeignAssignment}} {
|
||||
t.Error("foreign key was wrong value", a.{{.Function.LocalAssignment}}, x.{{.Function.ForeignAssignment}})
|
||||
}
|
||||
|
||||
{{if .ForeignKey.Unique -}}
|
||||
if x.R.{{.Function.ForeignName}} != &a {
|
||||
t.Error("failed to append to foreign relationship struct")
|
||||
}
|
||||
{{else -}}
|
||||
if x.R.{{.Function.ForeignName}}[0] != &a {
|
||||
t.Error("failed to append to foreign relationship struct")
|
||||
}
|
||||
{{end -}}
|
||||
}
|
||||
}
|
||||
{{- if .ForeignKey.Nullable}}
|
||||
|
||||
func test{{.LocalTable.NameGo}}ToOneRemoveOp{{.ForeignTable.NameGo}}_{{.Function.Name}}(t *testing.T) {
|
||||
var err error
|
||||
|
||||
tx := MustTx(boil.Begin())
|
||||
defer tx.Rollback()
|
||||
|
||||
var a {{.LocalTable.NameGo}}
|
||||
var b {{.ForeignTable.NameGo}}
|
||||
|
||||
seed := randomize.NewSeed()
|
||||
if err = randomize.Struct(seed, &a, {{$varNameSingular}}DBTypes, false, {{$varNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = randomize.Struct(seed, &b, {{$foreignVarNameSingular}}DBTypes, false, {{$foreignVarNameSingular}}PrimaryKeyColumns...); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = a.Insert(tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = a.Set{{.Function.Name}}(tx, true, &b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = a.Remove{{.Function.Name}}(tx, &b); err != nil {
|
||||
t.Error("failed to remove relationship")
|
||||
}
|
||||
|
||||
count, err := a.{{.Function.Name}}(tx).Count()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Error("want no relationships remaining")
|
||||
}
|
||||
|
||||
if a.R.{{.Function.Name}} != nil {
|
||||
t.Error("R struct entry should be nil")
|
||||
}
|
||||
|
||||
if a.{{.LocalTable.ColumnNameGo}}.Valid {
|
||||
t.Error("R struct entry should be nil")
|
||||
}
|
||||
|
||||
{{if .ForeignKey.Unique -}}
|
||||
if b.R.{{.Function.ForeignName}} != nil {
|
||||
t.Error("failed to remove a from b's relationships")
|
||||
}
|
||||
{{else -}}
|
||||
if len(b.R.{{.Function.ForeignName}}) != 0 {
|
||||
t.Error("failed to remove a from b's relationships")
|
||||
}
|
||||
{{end -}}
|
||||
}
|
||||
{{end -}}
|
||||
{{- end -}}
|
||||
{{- if .Table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- $dot := . -}}
|
||||
{{- range .Table.FKeys -}}
|
||||
{{- $rel := textsFromForeignKey $dot.PkgName $dot.Tables $dot.Table .}}
|
||||
|
||||
{{template "relationship_to_one_setops_test_helper" $rel -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
|
@ -138,8 +138,23 @@ func TestInsert(t *testing.T) {
|
|||
{{- end -}}
|
||||
}
|
||||
|
||||
// TestToOne tests cannot be run in parallel
|
||||
// or deadlocks can occur.
|
||||
func TestToOne(t *testing.T) {
|
||||
{{- $dot := . -}}
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- range $table.FKeys -}}
|
||||
{{- $rel := textsFromForeignKey $dot.PkgName $dot.Tables $table . -}}
|
||||
t.Run("{{$rel.LocalTable.NameGo}}To{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}}", test{{$rel.LocalTable.NameGo}}ToOne{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}})
|
||||
{{end -}}{{- /* fkey range */ -}}
|
||||
{{- end -}}{{- /* if join table */ -}}
|
||||
{{- end -}}{{- /* tables range */ -}}
|
||||
}
|
||||
|
||||
// TestToMany tests cannot be run in parallel
|
||||
// or postgres deadlocks will occur.
|
||||
// or deadlocks can occur.
|
||||
func TestToMany(t *testing.T) {
|
||||
{{- $dot := .}}
|
||||
{{- range $index, $table := .Tables}}
|
||||
|
@ -159,21 +174,105 @@ func TestToMany(t *testing.T) {
|
|||
{{- end -}}{{- /* outer tables range */ -}}
|
||||
}
|
||||
|
||||
// TestToOne tests cannot be run in parallel
|
||||
// or postgres deadlocks will occur.
|
||||
func TestToOne(t *testing.T) {
|
||||
// TestToOneSet tests cannot be run in parallel
|
||||
// or deadlocks can occur.
|
||||
func TestToOneSet(t *testing.T) {
|
||||
{{- $dot := . -}}
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- range $table.FKeys -}}
|
||||
{{- $rel := textsFromForeignKey $dot.PkgName $dot.Tables $table . -}}
|
||||
t.Run("{{$rel.LocalTable.NameGo}}To{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}}", test{{$rel.LocalTable.NameGo}}ToOne{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}})
|
||||
t.Run("{{$rel.LocalTable.NameGo}}To{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}}", test{{$rel.LocalTable.NameGo}}ToOneSetOp{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}})
|
||||
{{end -}}{{- /* fkey range */ -}}
|
||||
{{- end -}}{{- /* if join table */ -}}
|
||||
{{- end -}}{{- /* tables range */ -}}
|
||||
}
|
||||
|
||||
// TestToOneRemove tests cannot be run in parallel
|
||||
// or deadlocks can occur.
|
||||
func TestToOneRemove(t *testing.T) {
|
||||
{{- $dot := . -}}
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- range $table.FKeys -}}
|
||||
{{- $rel := textsFromForeignKey $dot.PkgName $dot.Tables $table . -}}
|
||||
{{- if $rel.ForeignKey.Nullable -}}
|
||||
t.Run("{{$rel.LocalTable.NameGo}}To{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}}", test{{$rel.LocalTable.NameGo}}ToOneRemoveOp{{$rel.ForeignTable.NameGo}}_{{$rel.Function.Name}})
|
||||
{{end -}}{{- /* if foreign key nullable */ -}}
|
||||
{{- end -}}{{- /* fkey range */ -}}
|
||||
{{- end -}}{{- /* if join table */ -}}
|
||||
{{- end -}}{{- /* tables range */ -}}
|
||||
}
|
||||
|
||||
// TestToManyAdd tests cannot be run in parallel
|
||||
// or deadlocks can occur.
|
||||
func TestToManyAdd(t *testing.T) {
|
||||
{{- $dot := .}}
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- $tableName := $table.Name | plural | titleCase -}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- range $table.ToManyRelationships -}}
|
||||
{{- $rel := textsFromRelationship $dot.Tables $table . -}}
|
||||
{{- if (and .ForeignColumnUnique (not .ToJoinTable)) -}}
|
||||
{{- else -}}
|
||||
t.Run("{{$rel.LocalTable.NameGo}}ToMany{{$rel.Function.Name}}", test{{$rel.LocalTable.NameGo}}ToManyAddOp{{$rel.Function.Name}})
|
||||
{{end -}}{{- /* if unique */ -}}
|
||||
{{- end -}}{{- /* range */ -}}
|
||||
{{- end -}}{{- /* outer if join table */ -}}
|
||||
{{- end -}}{{- /* outer tables range */ -}}
|
||||
}
|
||||
|
||||
// TestToManySet tests cannot be run in parallel
|
||||
// or deadlocks can occur.
|
||||
func TestToManySet(t *testing.T) {
|
||||
{{- $dot := .}}
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- $tableName := $table.Name | plural | titleCase -}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- range $table.ToManyRelationships -}}
|
||||
{{- if not .ForeignColumnNullable -}}
|
||||
{{- else -}}
|
||||
{{- $rel := textsFromRelationship $dot.Tables $table . -}}
|
||||
{{- if (and .ForeignColumnUnique (not .ToJoinTable)) -}}
|
||||
{{- $oneToOne := textsFromOneToOneRelationship $dot.PkgName $dot.Tables $table . -}}
|
||||
t.Run("{{$oneToOne.LocalTable.NameGo}}OneToOne{{$oneToOne.ForeignTable.NameGo}}_{{$oneToOne.Function.Name}}", test{{$oneToOne.LocalTable.NameGo}}ToOneSetOp{{$oneToOne.ForeignTable.NameGo}}_{{$oneToOne.Function.Name}})
|
||||
{{else -}}
|
||||
t.Run("{{$rel.LocalTable.NameGo}}ToMany{{$rel.Function.Name}}", test{{$rel.LocalTable.NameGo}}ToManySetOp{{$rel.Function.Name}})
|
||||
{{end -}}{{- /* if unique */ -}}
|
||||
{{- end -}}{{- /* if foreign column nullable */ -}}
|
||||
{{- end -}}{{- /* range */ -}}
|
||||
{{- end -}}{{- /* outer if join table */ -}}
|
||||
{{- end -}}{{- /* outer tables range */ -}}
|
||||
}
|
||||
|
||||
// TestToManyRemove tests cannot be run in parallel
|
||||
// or deadlocks can occur.
|
||||
func TestToManyRemove(t *testing.T) {
|
||||
{{- $dot := .}}
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- $tableName := $table.Name | plural | titleCase -}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
{{- else -}}
|
||||
{{- range $table.ToManyRelationships -}}
|
||||
{{- if not .ForeignColumnNullable -}}
|
||||
{{- else -}}
|
||||
{{- $rel := textsFromRelationship $dot.Tables $table . -}}
|
||||
{{- if (and .ForeignColumnUnique (not .ToJoinTable)) -}}
|
||||
{{- $oneToOne := textsFromOneToOneRelationship $dot.PkgName $dot.Tables $table . -}}
|
||||
t.Run("{{$oneToOne.LocalTable.NameGo}}OneToOne{{$oneToOne.ForeignTable.NameGo}}_{{$oneToOne.Function.Name}}", test{{$oneToOne.LocalTable.NameGo}}ToOneRemoveOp{{$oneToOne.ForeignTable.NameGo}}_{{$oneToOne.Function.Name}})
|
||||
{{else -}}
|
||||
t.Run("{{$rel.LocalTable.NameGo}}ToMany{{$rel.Function.Name}}", test{{$rel.LocalTable.NameGo}}ToManyRemoveOp{{$rel.Function.Name}})
|
||||
{{end -}}{{- /* if unique */ -}}
|
||||
{{- end -}}{{- /* if foreign column nullable */ -}}
|
||||
{{- end -}}{{- /* range */ -}}
|
||||
{{- end -}}{{- /* outer if join table */ -}}
|
||||
{{- end -}}{{- /* outer tables range */ -}}
|
||||
}
|
||||
|
||||
func TestReload(t *testing.T) {
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
|
|
|
@ -28,10 +28,11 @@ type RelationshipToOneTexts struct {
|
|||
Function struct {
|
||||
PackageName string
|
||||
Name string
|
||||
ForeignName string
|
||||
|
||||
Varname string
|
||||
Receiver string
|
||||
ReverseInserts bool
|
||||
Varname string
|
||||
Receiver string
|
||||
OneToOne bool
|
||||
|
||||
LocalAssignment string
|
||||
ForeignAssignment string
|
||||
|
@ -54,6 +55,11 @@ func textsFromForeignKey(packageName string, tables []bdb.Table, table bdb.Table
|
|||
|
||||
r.Function.PackageName = packageName
|
||||
r.Function.Name = strmangle.TitleCase(strmangle.Singular(strings.TrimSuffix(fkey.Column, "_id")))
|
||||
plurality := strmangle.Plural
|
||||
if fkey.Unique {
|
||||
plurality = strmangle.Singular
|
||||
}
|
||||
r.Function.ForeignName = mkFunctionName(strmangle.Singular(fkey.ForeignTable), strmangle.TitleCase(plurality(fkey.Table)), fkey.Column, false)
|
||||
r.Function.Varname = strmangle.CamelCase(strmangle.Singular(fkey.ForeignTable))
|
||||
r.Function.Receiver = strings.ToLower(table.Name[:1])
|
||||
|
||||
|
@ -91,7 +97,8 @@ func textsFromOneToOneRelationship(packageName string, tables []bdb.Table, table
|
|||
|
||||
rel := textsFromForeignKey(packageName, tables, table, fkey)
|
||||
rel.Function.Name = strmangle.TitleCase(strmangle.Singular(toMany.ForeignTable))
|
||||
rel.Function.ReverseInserts = true
|
||||
rel.Function.ForeignName = mkFunctionName(strmangle.Singular(toMany.Table), strmangle.TitleCase(strmangle.Singular(toMany.Table)), toMany.ForeignColumn, false)
|
||||
rel.Function.OneToOne = true
|
||||
return rel
|
||||
}
|
||||
|
||||
|
@ -100,6 +107,7 @@ type RelationshipToManyTexts struct {
|
|||
LocalTable struct {
|
||||
NameGo string
|
||||
NameSingular string
|
||||
ColumnNameGo string
|
||||
}
|
||||
|
||||
ForeignTable struct {
|
||||
|
@ -107,12 +115,14 @@ type RelationshipToManyTexts struct {
|
|||
NameSingular string
|
||||
NamePluralGo string
|
||||
NameHumanReadable string
|
||||
ColumnNameGo string
|
||||
Slice string
|
||||
}
|
||||
|
||||
Function struct {
|
||||
Name string
|
||||
Receiver string
|
||||
Name string
|
||||
ForeignName string
|
||||
Receiver string
|
||||
|
||||
LocalAssignment string
|
||||
ForeignAssignment string
|
||||
|
@ -125,23 +135,24 @@ func textsFromRelationship(tables []bdb.Table, table bdb.Table, rel bdb.ToManyRe
|
|||
r := RelationshipToManyTexts{}
|
||||
r.LocalTable.NameSingular = strmangle.Singular(table.Name)
|
||||
r.LocalTable.NameGo = strmangle.TitleCase(r.LocalTable.NameSingular)
|
||||
r.LocalTable.ColumnNameGo = strmangle.TitleCase(rel.Column)
|
||||
|
||||
r.ForeignTable.NameSingular = strmangle.Singular(rel.ForeignTable)
|
||||
r.ForeignTable.NamePluralGo = strmangle.TitleCase(strmangle.Plural(rel.ForeignTable))
|
||||
r.ForeignTable.NameGo = strmangle.TitleCase(r.ForeignTable.NameSingular)
|
||||
r.ForeignTable.ColumnNameGo = strmangle.TitleCase(rel.ForeignColumn)
|
||||
r.ForeignTable.Slice = fmt.Sprintf("%sSlice", strmangle.TitleCase(r.ForeignTable.NameSingular))
|
||||
r.ForeignTable.NameHumanReadable = strings.Replace(rel.ForeignTable, "_", " ", -1)
|
||||
|
||||
r.Function.Receiver = strings.ToLower(table.Name[:1])
|
||||
|
||||
// Check to see if the foreign key name is the same as the local table name.
|
||||
// Simple case: yes - we can name the function the same as the plural table name
|
||||
// Not simple case: We have to name the function based off the foreign key and
|
||||
if colName := strings.TrimSuffix(rel.ForeignColumn, "_id"); rel.ToJoinTable || r.LocalTable.NameSingular == colName {
|
||||
r.Function.Name = r.ForeignTable.NamePluralGo
|
||||
} else {
|
||||
r.Function.Name = strmangle.TitleCase(colName) + r.ForeignTable.NamePluralGo
|
||||
r.Function.Name = mkFunctionName(r.LocalTable.NameSingular, r.ForeignTable.NamePluralGo, rel.ForeignColumn, rel.ToJoinTable)
|
||||
plurality := strmangle.Singular
|
||||
foreignNamingColumn := rel.ForeignColumn
|
||||
if rel.ToJoinTable {
|
||||
plurality = strmangle.Plural
|
||||
foreignNamingColumn = rel.JoinLocalColumn
|
||||
}
|
||||
r.Function.ForeignName = strmangle.TitleCase(plurality(strings.TrimSuffix(foreignNamingColumn, "_id")))
|
||||
|
||||
if rel.Nullable {
|
||||
col := table.GetColumn(rel.Column)
|
||||
|
@ -160,3 +171,15 @@ func textsFromRelationship(tables []bdb.Table, table bdb.Table, rel bdb.ToManyRe
|
|||
|
||||
return r
|
||||
}
|
||||
|
||||
// mkFunctionName checks to see if the foreign key name is the same as the local table name (minus _id suffix)
|
||||
// Simple case: yes - we can name the function the same as the plural table name
|
||||
// Not simple case: We have to name the function based off the foreign key and the foreign table name
|
||||
func mkFunctionName(fkeyTableSingular, foreignTablePluralGo, fkeyColumn string, toJoinTable bool) string {
|
||||
colName := strings.TrimSuffix(fkeyColumn, "_id")
|
||||
if toJoinTable || fkeyTableSingular == colName {
|
||||
return foreignTablePluralGo
|
||||
}
|
||||
|
||||
return strmangle.TitleCase(colName) + foreignTablePluralGo
|
||||
}
|
||||
|
|
|
@ -34,9 +34,10 @@ func TestTextsFromForeignKey(t *testing.T) {
|
|||
|
||||
expect.Function.PackageName = "models"
|
||||
expect.Function.Name = "Pilot"
|
||||
expect.Function.ForeignName = "Jet"
|
||||
expect.Function.Varname = "pilot"
|
||||
expect.Function.Receiver = "j"
|
||||
expect.Function.ReverseInserts = false
|
||||
expect.Function.OneToOne = false
|
||||
|
||||
expect.Function.LocalAssignment = "PilotID.Int"
|
||||
expect.Function.ForeignAssignment = "ID"
|
||||
|
@ -44,6 +45,37 @@ func TestTextsFromForeignKey(t *testing.T) {
|
|||
if !reflect.DeepEqual(expect, texts) {
|
||||
t.Errorf("Want:\n%s\nGot:\n%s\n", spew.Sdump(expect), spew.Sdump(texts))
|
||||
}
|
||||
|
||||
texts = textsFromForeignKey("models", tables, jets, jets.FKeys[1])
|
||||
expect = RelationshipToOneTexts{}
|
||||
expect.ForeignKey = jets.FKeys[1]
|
||||
|
||||
expect.LocalTable.NameGo = "Jet"
|
||||
expect.LocalTable.ColumnNameGo = "AirportID"
|
||||
|
||||
expect.ForeignTable.Name = "airports"
|
||||
expect.ForeignTable.NameGo = "Airport"
|
||||
expect.ForeignTable.NamePluralGo = "Airports"
|
||||
expect.ForeignTable.ColumnName = "id"
|
||||
expect.ForeignTable.ColumnNameGo = "ID"
|
||||
|
||||
expect.Function.PackageName = "models"
|
||||
expect.Function.Name = "Airport"
|
||||
expect.Function.ForeignName = "Jets"
|
||||
expect.Function.Varname = "airport"
|
||||
expect.Function.Receiver = "j"
|
||||
expect.Function.OneToOne = false
|
||||
|
||||
expect.Function.LocalAssignment = "AirportID"
|
||||
expect.Function.ForeignAssignment = "ID"
|
||||
|
||||
if !reflect.DeepEqual(expect, texts) {
|
||||
t.Errorf("Want:\n%s\nGot:\n%s\n", spew.Sdump(expect), spew.Sdump(texts))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expect, texts) {
|
||||
t.Errorf("Want:\n%s\nGot:\n%s\n", spew.Sdump(expect), spew.Sdump(texts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTextsFromOneToOneRelationship(t *testing.T) {
|
||||
|
@ -59,7 +91,7 @@ func TestTextsFromOneToOneRelationship(t *testing.T) {
|
|||
expect := RelationshipToOneTexts{}
|
||||
|
||||
expect.ForeignKey = bdb.ForeignKey{
|
||||
Table: "jets",
|
||||
Table: "pilots",
|
||||
Name: "none",
|
||||
Column: "id",
|
||||
Nullable: false,
|
||||
|
@ -82,9 +114,10 @@ func TestTextsFromOneToOneRelationship(t *testing.T) {
|
|||
|
||||
expect.Function.PackageName = "models"
|
||||
expect.Function.Name = "Jet"
|
||||
expect.Function.ForeignName = "Pilot"
|
||||
expect.Function.Varname = "jet"
|
||||
expect.Function.Receiver = "p"
|
||||
expect.Function.ReverseInserts = true
|
||||
expect.Function.OneToOne = true
|
||||
|
||||
expect.Function.LocalAssignment = "ID"
|
||||
expect.Function.ForeignAssignment = "PilotID.Int"
|
||||
|
@ -107,14 +140,17 @@ func TestTextsFromRelationship(t *testing.T) {
|
|||
expect := RelationshipToManyTexts{}
|
||||
expect.LocalTable.NameGo = "Pilot"
|
||||
expect.LocalTable.NameSingular = "pilot"
|
||||
expect.LocalTable.ColumnNameGo = "ID"
|
||||
|
||||
expect.ForeignTable.NameGo = "Jet"
|
||||
expect.ForeignTable.NameSingular = "jet"
|
||||
expect.ForeignTable.NamePluralGo = "Jets"
|
||||
expect.ForeignTable.NameHumanReadable = "jets"
|
||||
expect.ForeignTable.ColumnNameGo = "PilotID"
|
||||
expect.ForeignTable.Slice = "JetSlice"
|
||||
|
||||
expect.Function.Name = "Jets"
|
||||
expect.Function.ForeignName = "Pilot"
|
||||
expect.Function.Receiver = "p"
|
||||
expect.Function.LocalAssignment = "ID"
|
||||
expect.Function.ForeignAssignment = "PilotID.Int"
|
||||
|
@ -127,14 +163,17 @@ func TestTextsFromRelationship(t *testing.T) {
|
|||
expect = RelationshipToManyTexts{}
|
||||
expect.LocalTable.NameGo = "Pilot"
|
||||
expect.LocalTable.NameSingular = "pilot"
|
||||
expect.LocalTable.ColumnNameGo = "ID"
|
||||
|
||||
expect.ForeignTable.NameGo = "License"
|
||||
expect.ForeignTable.NameSingular = "license"
|
||||
expect.ForeignTable.NamePluralGo = "Licenses"
|
||||
expect.ForeignTable.NameHumanReadable = "licenses"
|
||||
expect.ForeignTable.ColumnNameGo = "PilotID"
|
||||
expect.ForeignTable.Slice = "LicenseSlice"
|
||||
|
||||
expect.Function.Name = "Licenses"
|
||||
expect.Function.ForeignName = "Pilot"
|
||||
expect.Function.Receiver = "p"
|
||||
expect.Function.LocalAssignment = "ID"
|
||||
expect.Function.ForeignAssignment = "PilotID"
|
||||
|
@ -147,14 +186,17 @@ func TestTextsFromRelationship(t *testing.T) {
|
|||
expect = RelationshipToManyTexts{}
|
||||
expect.LocalTable.NameGo = "Pilot"
|
||||
expect.LocalTable.NameSingular = "pilot"
|
||||
expect.LocalTable.ColumnNameGo = "ID"
|
||||
|
||||
expect.ForeignTable.NameGo = "Language"
|
||||
expect.ForeignTable.NameSingular = "language"
|
||||
expect.ForeignTable.NamePluralGo = "Languages"
|
||||
expect.ForeignTable.NameHumanReadable = "languages"
|
||||
expect.ForeignTable.ColumnNameGo = "ID"
|
||||
expect.ForeignTable.Slice = "LanguageSlice"
|
||||
|
||||
expect.Function.Name = "Languages"
|
||||
expect.Function.ForeignName = "Pilots"
|
||||
expect.Function.Receiver = "p"
|
||||
expect.Function.LocalAssignment = "ID"
|
||||
expect.Function.ForeignAssignment = "ID"
|
||||
|
|
Loading…
Reference in a new issue