
use golangci/golangci-lint-action instead of reviewdog/action-golangci-lint as the second was not reporting any failures even if there was some. Report code coverage with codecov/codecov-action I have set some flags per dialect and go version Several linters has been fixed, some disabled so the build can pass
635 lines
19 KiB
Go
635 lines
19 KiB
Go
package schema
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/jinzhu/inflection"
|
|
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
// RelationshipType relationship type
|
|
type RelationshipType string
|
|
|
|
const (
|
|
HasOne RelationshipType = "has_one" // HasOneRel has one relationship
|
|
HasMany RelationshipType = "has_many" // HasManyRel has many relationship
|
|
BelongsTo RelationshipType = "belongs_to" // BelongsToRel belongs to relationship
|
|
Many2Many RelationshipType = "many_to_many" // Many2ManyRel many to many relationship
|
|
has RelationshipType = "has"
|
|
)
|
|
|
|
type Relationships struct {
|
|
HasOne []*Relationship
|
|
BelongsTo []*Relationship
|
|
HasMany []*Relationship
|
|
Many2Many []*Relationship
|
|
Relations map[string]*Relationship
|
|
}
|
|
|
|
type Relationship struct {
|
|
Name string
|
|
Type RelationshipType
|
|
Field *Field
|
|
Polymorphic *Polymorphic
|
|
References []*Reference
|
|
Schema *Schema
|
|
FieldSchema *Schema
|
|
JoinTable *Schema
|
|
foreignKeys, primaryKeys []string
|
|
}
|
|
|
|
type Polymorphic struct {
|
|
PolymorphicID *Field
|
|
PolymorphicType *Field
|
|
Value string
|
|
}
|
|
|
|
type Reference struct {
|
|
PrimaryKey *Field
|
|
PrimaryValue string
|
|
ForeignKey *Field
|
|
OwnPrimaryKey bool
|
|
}
|
|
|
|
func (schema *Schema) parseRelation(field *Field) *Relationship {
|
|
var (
|
|
err error
|
|
fieldValue = reflect.New(field.IndirectFieldType).Interface()
|
|
relation = &Relationship{
|
|
Name: field.Name,
|
|
Field: field,
|
|
Schema: schema,
|
|
foreignKeys: toColumns(field.TagSettings["FOREIGNKEY"]),
|
|
primaryKeys: toColumns(field.TagSettings["REFERENCES"]),
|
|
}
|
|
)
|
|
|
|
cacheStore := schema.cacheStore
|
|
|
|
if relation.FieldSchema, err = getOrParse(fieldValue, cacheStore, schema.namer); err != nil {
|
|
schema.err = err
|
|
return nil
|
|
}
|
|
|
|
if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
|
|
schema.buildPolymorphicRelation(relation, field, polymorphic)
|
|
} else if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
|
|
schema.buildMany2ManyRelation(relation, field, many2many)
|
|
} else if belongsTo := field.TagSettings["BELONGSTO"]; belongsTo != "" {
|
|
schema.guessRelation(relation, field, guessBelongs)
|
|
} else {
|
|
switch field.IndirectFieldType.Kind() {
|
|
case reflect.Struct:
|
|
schema.guessRelation(relation, field, guessGuess)
|
|
case reflect.Slice:
|
|
schema.guessRelation(relation, field, guessHas)
|
|
default:
|
|
schema.err = fmt.Errorf("unsupported data type %v for %v on field %s", relation.FieldSchema, schema, field.Name)
|
|
}
|
|
}
|
|
|
|
if relation.Type == has {
|
|
// don't add relations to embedded schema, which might be shared
|
|
if relation.FieldSchema != relation.Schema && relation.Polymorphic == nil && field.OwnerSchema == nil {
|
|
relation.FieldSchema.Relationships.Relations["_"+relation.Schema.Name+"_"+relation.Name] = relation
|
|
}
|
|
|
|
switch field.IndirectFieldType.Kind() {
|
|
case reflect.Struct:
|
|
relation.Type = HasOne
|
|
case reflect.Slice:
|
|
relation.Type = HasMany
|
|
}
|
|
}
|
|
|
|
if schema.err == nil {
|
|
schema.Relationships.Relations[relation.Name] = relation
|
|
switch relation.Type {
|
|
case HasOne:
|
|
schema.Relationships.HasOne = append(schema.Relationships.HasOne, relation)
|
|
case HasMany:
|
|
schema.Relationships.HasMany = append(schema.Relationships.HasMany, relation)
|
|
case BelongsTo:
|
|
schema.Relationships.BelongsTo = append(schema.Relationships.BelongsTo, relation)
|
|
case Many2Many:
|
|
schema.Relationships.Many2Many = append(schema.Relationships.Many2Many, relation)
|
|
}
|
|
}
|
|
|
|
return relation
|
|
}
|
|
|
|
// User has many Toys, its `Polymorphic` is `Owner`, Pet has one Toy, its `Polymorphic` is `Owner`
|
|
// type User struct {
|
|
// Toys []Toy `gorm:"polymorphic:Owner;"`
|
|
// }
|
|
// type Pet struct {
|
|
// Toy Toy `gorm:"polymorphic:Owner;"`
|
|
// }
|
|
// type Toy struct {
|
|
// OwnerID int
|
|
// OwnerType string
|
|
// }
|
|
func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field, polymorphic string) {
|
|
relation.Polymorphic = &Polymorphic{
|
|
Value: schema.Table,
|
|
PolymorphicType: relation.FieldSchema.FieldsByName[polymorphic+"Type"],
|
|
PolymorphicID: relation.FieldSchema.FieldsByName[polymorphic+"ID"],
|
|
}
|
|
|
|
if value, ok := field.TagSettings["POLYMORPHICVALUE"]; ok {
|
|
relation.Polymorphic.Value = strings.TrimSpace(value)
|
|
}
|
|
|
|
if relation.Polymorphic.PolymorphicType == nil {
|
|
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", relation.FieldSchema, schema, field.Name, polymorphic+"Type")
|
|
}
|
|
|
|
if relation.Polymorphic.PolymorphicID == nil {
|
|
schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", relation.FieldSchema, schema, field.Name, polymorphic+"ID")
|
|
}
|
|
|
|
if schema.err == nil {
|
|
relation.References = append(relation.References, &Reference{
|
|
PrimaryValue: relation.Polymorphic.Value,
|
|
ForeignKey: relation.Polymorphic.PolymorphicType,
|
|
})
|
|
|
|
primaryKeyField := schema.PrioritizedPrimaryField
|
|
if len(relation.foreignKeys) > 0 {
|
|
if primaryKeyField = schema.LookUpField(relation.foreignKeys[0]); primaryKeyField == nil || len(relation.foreignKeys) > 1 {
|
|
schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %s", relation.foreignKeys, schema, field.Name)
|
|
}
|
|
}
|
|
|
|
// use same data type for foreign keys
|
|
if copyableDataType(primaryKeyField.DataType) {
|
|
relation.Polymorphic.PolymorphicID.DataType = primaryKeyField.DataType
|
|
}
|
|
relation.Polymorphic.PolymorphicID.GORMDataType = primaryKeyField.GORMDataType
|
|
if relation.Polymorphic.PolymorphicID.Size == 0 {
|
|
relation.Polymorphic.PolymorphicID.Size = primaryKeyField.Size
|
|
}
|
|
|
|
relation.References = append(relation.References, &Reference{
|
|
PrimaryKey: primaryKeyField,
|
|
ForeignKey: relation.Polymorphic.PolymorphicID,
|
|
OwnPrimaryKey: true,
|
|
})
|
|
}
|
|
|
|
relation.Type = has
|
|
}
|
|
|
|
func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Field, many2many string) {
|
|
relation.Type = Many2Many
|
|
|
|
joinTableFields := make([]reflect.StructField, 0)
|
|
var (
|
|
err error
|
|
fieldsMap = map[string]*Field{}
|
|
ownFieldsMap = map[string]bool{} // fix self join many2many
|
|
joinForeignKeys = toColumns(field.TagSettings["JOINFOREIGNKEY"])
|
|
joinReferences = toColumns(field.TagSettings["JOINREFERENCES"])
|
|
)
|
|
|
|
ownForeignFields := schema.PrimaryFields
|
|
refForeignFields := relation.FieldSchema.PrimaryFields
|
|
|
|
if len(relation.foreignKeys) > 0 {
|
|
ownForeignFields = []*Field{}
|
|
for _, foreignKey := range relation.foreignKeys {
|
|
if field := schema.LookUpField(foreignKey); field != nil {
|
|
ownForeignFields = append(ownForeignFields, field)
|
|
} else {
|
|
schema.err = fmt.Errorf("invalid foreign key: %s", foreignKey)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(relation.primaryKeys) > 0 {
|
|
refForeignFields = []*Field{}
|
|
for _, foreignKey := range relation.primaryKeys {
|
|
if field := relation.FieldSchema.LookUpField(foreignKey); field != nil {
|
|
refForeignFields = append(refForeignFields, field)
|
|
} else {
|
|
schema.err = fmt.Errorf("invalid foreign key: %s", foreignKey)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
for idx, ownField := range ownForeignFields {
|
|
joinFieldName := strings.Title(schema.Name) + ownField.Name
|
|
if len(joinForeignKeys) > idx {
|
|
joinFieldName = strings.Title(joinForeignKeys[idx])
|
|
}
|
|
|
|
ownFieldsMap[joinFieldName] = true
|
|
fieldsMap[joinFieldName] = ownField
|
|
joinTableFields = append(joinTableFields, reflect.StructField{
|
|
Name: joinFieldName,
|
|
PkgPath: ownField.StructField.PkgPath,
|
|
Type: ownField.StructField.Type,
|
|
Tag: removeSettingFromTag(ownField.StructField.Tag, "column", "autoincrement", "index", "unique", "uniqueindex"),
|
|
})
|
|
}
|
|
|
|
for idx, relField := range refForeignFields {
|
|
joinFieldName := strings.Title(relation.FieldSchema.Name) + relField.Name
|
|
if len(joinReferences) > idx {
|
|
joinFieldName = strings.Title(joinReferences[idx])
|
|
}
|
|
|
|
if _, ok := ownFieldsMap[joinFieldName]; ok {
|
|
if field.Name != relation.FieldSchema.Name {
|
|
joinFieldName = inflection.Singular(field.Name) + relField.Name
|
|
} else {
|
|
joinFieldName += "Reference"
|
|
}
|
|
}
|
|
|
|
fieldsMap[joinFieldName] = relField
|
|
joinTableFields = append(joinTableFields, reflect.StructField{
|
|
Name: joinFieldName,
|
|
PkgPath: relField.StructField.PkgPath,
|
|
Type: relField.StructField.Type,
|
|
Tag: removeSettingFromTag(relField.StructField.Tag, "column", "autoincrement", "index", "unique", "uniqueindex"),
|
|
})
|
|
}
|
|
|
|
joinTableFields = append(joinTableFields, reflect.StructField{
|
|
Name: strings.Title(schema.Name) + field.Name,
|
|
Type: schema.ModelType,
|
|
Tag: `gorm:"-"`,
|
|
})
|
|
|
|
if relation.JoinTable, err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore, schema.namer); err != nil {
|
|
schema.err = err
|
|
}
|
|
relation.JoinTable.Name = many2many
|
|
relation.JoinTable.Table = schema.namer.JoinTableName(many2many)
|
|
relation.JoinTable.PrimaryFields = make([]*Field, 0, len(relation.JoinTable.Fields))
|
|
|
|
relName := relation.Schema.Name
|
|
relRefName := relation.FieldSchema.Name
|
|
if relName == relRefName {
|
|
relRefName = relation.Field.Name
|
|
}
|
|
|
|
if _, ok := relation.JoinTable.Relationships.Relations[relName]; !ok {
|
|
relation.JoinTable.Relationships.Relations[relName] = &Relationship{
|
|
Name: relName,
|
|
Type: BelongsTo,
|
|
Schema: relation.JoinTable,
|
|
FieldSchema: relation.Schema,
|
|
}
|
|
} else {
|
|
relation.JoinTable.Relationships.Relations[relName].References = []*Reference{}
|
|
}
|
|
|
|
if _, ok := relation.JoinTable.Relationships.Relations[relRefName]; !ok {
|
|
relation.JoinTable.Relationships.Relations[relRefName] = &Relationship{
|
|
Name: relRefName,
|
|
Type: BelongsTo,
|
|
Schema: relation.JoinTable,
|
|
FieldSchema: relation.FieldSchema,
|
|
}
|
|
} else {
|
|
relation.JoinTable.Relationships.Relations[relRefName].References = []*Reference{}
|
|
}
|
|
|
|
// build references
|
|
for _, f := range relation.JoinTable.Fields {
|
|
if f.Creatable || f.Readable || f.Updatable {
|
|
// use same data type for foreign keys
|
|
if copyableDataType(fieldsMap[f.Name].DataType) {
|
|
f.DataType = fieldsMap[f.Name].DataType
|
|
}
|
|
f.GORMDataType = fieldsMap[f.Name].GORMDataType
|
|
if f.Size == 0 {
|
|
f.Size = fieldsMap[f.Name].Size
|
|
}
|
|
relation.JoinTable.PrimaryFields = append(relation.JoinTable.PrimaryFields, f)
|
|
ownPrimaryField := schema == fieldsMap[f.Name].Schema && ownFieldsMap[f.Name]
|
|
|
|
if ownPrimaryField {
|
|
joinRel := relation.JoinTable.Relationships.Relations[relName]
|
|
joinRel.Field = relation.Field
|
|
joinRel.References = append(joinRel.References, &Reference{
|
|
PrimaryKey: fieldsMap[f.Name],
|
|
ForeignKey: f,
|
|
})
|
|
} else {
|
|
joinRefRel := relation.JoinTable.Relationships.Relations[relRefName]
|
|
if joinRefRel.Field == nil {
|
|
joinRefRel.Field = relation.Field
|
|
}
|
|
joinRefRel.References = append(joinRefRel.References, &Reference{
|
|
PrimaryKey: fieldsMap[f.Name],
|
|
ForeignKey: f,
|
|
})
|
|
}
|
|
|
|
relation.References = append(relation.References, &Reference{
|
|
PrimaryKey: fieldsMap[f.Name],
|
|
ForeignKey: f,
|
|
OwnPrimaryKey: ownPrimaryField,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
type guessLevel int
|
|
|
|
const (
|
|
guessGuess guessLevel = iota
|
|
guessBelongs
|
|
guessEmbeddedBelongs
|
|
guessHas
|
|
guessEmbeddedHas
|
|
)
|
|
|
|
func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl guessLevel) {
|
|
var (
|
|
primaryFields, foreignFields []*Field
|
|
primarySchema, foreignSchema = schema, relation.FieldSchema
|
|
gl = cgl
|
|
)
|
|
|
|
if gl == guessGuess {
|
|
if field.Schema == relation.FieldSchema {
|
|
gl = guessBelongs
|
|
} else {
|
|
gl = guessHas
|
|
}
|
|
}
|
|
|
|
reguessOrErr := func() {
|
|
switch cgl {
|
|
case guessGuess:
|
|
schema.guessRelation(relation, field, guessBelongs)
|
|
case guessBelongs:
|
|
schema.guessRelation(relation, field, guessEmbeddedBelongs)
|
|
case guessEmbeddedBelongs:
|
|
schema.guessRelation(relation, field, guessHas)
|
|
case guessHas:
|
|
schema.guessRelation(relation, field, guessEmbeddedHas)
|
|
// case guessEmbeddedHas:
|
|
default:
|
|
schema.err = fmt.Errorf("invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface", schema, field.Name)
|
|
}
|
|
}
|
|
|
|
switch gl {
|
|
case guessBelongs:
|
|
primarySchema, foreignSchema = relation.FieldSchema, schema
|
|
case guessEmbeddedBelongs:
|
|
if field.OwnerSchema != nil {
|
|
primarySchema, foreignSchema = relation.FieldSchema, field.OwnerSchema
|
|
} else {
|
|
reguessOrErr()
|
|
return
|
|
}
|
|
case guessHas:
|
|
case guessEmbeddedHas:
|
|
if field.OwnerSchema != nil {
|
|
primarySchema, foreignSchema = field.OwnerSchema, relation.FieldSchema
|
|
} else {
|
|
reguessOrErr()
|
|
return
|
|
}
|
|
}
|
|
|
|
if len(relation.foreignKeys) > 0 {
|
|
for _, foreignKey := range relation.foreignKeys {
|
|
if f := foreignSchema.LookUpField(foreignKey); f != nil {
|
|
foreignFields = append(foreignFields, f)
|
|
} else {
|
|
reguessOrErr()
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
var primaryFields []*Field
|
|
|
|
if len(relation.primaryKeys) > 0 {
|
|
for _, primaryKey := range relation.primaryKeys {
|
|
if f := primarySchema.LookUpField(primaryKey); f != nil {
|
|
primaryFields = append(primaryFields, f)
|
|
}
|
|
}
|
|
} else {
|
|
primaryFields = primarySchema.PrimaryFields
|
|
}
|
|
|
|
for _, primaryField := range primaryFields {
|
|
lookUpName := primarySchema.Name + primaryField.Name
|
|
if gl == guessBelongs {
|
|
lookUpName = field.Name + primaryField.Name
|
|
}
|
|
|
|
lookUpNames := []string{lookUpName}
|
|
if len(primaryFields) == 1 {
|
|
lookUpNames = append(lookUpNames, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID", strings.TrimSuffix(lookUpName, primaryField.Name)+"Id", schema.namer.ColumnName(foreignSchema.Table, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID"))
|
|
}
|
|
|
|
for _, name := range lookUpNames {
|
|
if f := foreignSchema.LookUpField(name); f != nil {
|
|
foreignFields = append(foreignFields, f)
|
|
primaryFields = append(primaryFields, primaryField)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(foreignFields) == 0 {
|
|
reguessOrErr()
|
|
return
|
|
} else if len(relation.primaryKeys) > 0 {
|
|
for idx, primaryKey := range relation.primaryKeys {
|
|
if f := primarySchema.LookUpField(primaryKey); f != nil {
|
|
if len(primaryFields) < idx+1 {
|
|
primaryFields = append(primaryFields, f)
|
|
} else if f != primaryFields[idx] {
|
|
reguessOrErr()
|
|
return
|
|
}
|
|
} else {
|
|
reguessOrErr()
|
|
return
|
|
}
|
|
}
|
|
} else if len(primaryFields) == 0 {
|
|
if len(foreignFields) == 1 && primarySchema.PrioritizedPrimaryField != nil {
|
|
primaryFields = append(primaryFields, primarySchema.PrioritizedPrimaryField)
|
|
} else if len(primarySchema.PrimaryFields) == len(foreignFields) {
|
|
primaryFields = append(primaryFields, primarySchema.PrimaryFields...)
|
|
} else {
|
|
reguessOrErr()
|
|
return
|
|
}
|
|
}
|
|
|
|
// build references
|
|
for idx, foreignField := range foreignFields {
|
|
// use same data type for foreign keys
|
|
if copyableDataType(primaryFields[idx].DataType) {
|
|
foreignField.DataType = primaryFields[idx].DataType
|
|
}
|
|
foreignField.GORMDataType = primaryFields[idx].GORMDataType
|
|
if foreignField.Size == 0 {
|
|
foreignField.Size = primaryFields[idx].Size
|
|
}
|
|
|
|
relation.References = append(relation.References, &Reference{
|
|
PrimaryKey: primaryFields[idx],
|
|
ForeignKey: foreignField,
|
|
OwnPrimaryKey: (schema == primarySchema && gl == guessHas) || (field.OwnerSchema == primarySchema && gl == guessEmbeddedHas),
|
|
})
|
|
}
|
|
|
|
if gl == guessHas || gl == guessEmbeddedHas {
|
|
relation.Type = has
|
|
} else {
|
|
relation.Type = BelongsTo
|
|
}
|
|
}
|
|
|
|
type Constraint struct {
|
|
Name string
|
|
Field *Field
|
|
Schema *Schema
|
|
ForeignKeys []*Field
|
|
ReferenceSchema *Schema
|
|
References []*Field
|
|
OnDelete string
|
|
OnUpdate string
|
|
}
|
|
|
|
func (rel *Relationship) ParseConstraint() *Constraint {
|
|
str := rel.Field.TagSettings["CONSTRAINT"]
|
|
if str == "-" {
|
|
return nil
|
|
}
|
|
|
|
if rel.Type == BelongsTo {
|
|
for _, r := range rel.FieldSchema.Relationships.Relations {
|
|
if r != rel && r.FieldSchema == rel.Schema && len(rel.References) == len(r.References) {
|
|
matched := true
|
|
for idx, ref := range r.References {
|
|
if !(rel.References[idx].PrimaryKey == ref.PrimaryKey && rel.References[idx].ForeignKey == ref.ForeignKey &&
|
|
rel.References[idx].PrimaryValue == ref.PrimaryValue) {
|
|
matched = false
|
|
}
|
|
}
|
|
|
|
if matched {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var (
|
|
name string
|
|
idx = strings.Index(str, ",")
|
|
settings = ParseTagSetting(str, ",")
|
|
)
|
|
|
|
// optimize match english letters and midline
|
|
// The following code is basically called in for.
|
|
// In order to avoid the performance problems caused by repeated compilation of regular expressions,
|
|
// it only needs to be done once outside, so optimization is done here.
|
|
if idx != -1 && regEnLetterAndMidline.MatchString(str[0:idx]) {
|
|
name = str[0:idx]
|
|
} else {
|
|
name = rel.Schema.namer.RelationshipFKName(*rel)
|
|
}
|
|
|
|
constraint := Constraint{
|
|
Name: name,
|
|
Field: rel.Field,
|
|
OnUpdate: settings["ONUPDATE"],
|
|
OnDelete: settings["ONDELETE"],
|
|
}
|
|
|
|
for _, ref := range rel.References {
|
|
if ref.PrimaryKey != nil && (rel.JoinTable == nil || ref.OwnPrimaryKey) {
|
|
constraint.ForeignKeys = append(constraint.ForeignKeys, ref.ForeignKey)
|
|
constraint.References = append(constraint.References, ref.PrimaryKey)
|
|
|
|
if ref.OwnPrimaryKey {
|
|
constraint.Schema = ref.ForeignKey.Schema
|
|
constraint.ReferenceSchema = rel.Schema
|
|
} else {
|
|
constraint.Schema = rel.Schema
|
|
constraint.ReferenceSchema = ref.PrimaryKey.Schema
|
|
}
|
|
}
|
|
}
|
|
|
|
return &constraint
|
|
}
|
|
|
|
func (rel *Relationship) ToQueryConditions(reflectValue reflect.Value) (conds []clause.Expression) {
|
|
table := rel.FieldSchema.Table
|
|
foreignFields := []*Field{}
|
|
relForeignKeys := []string{}
|
|
|
|
if rel.JoinTable != nil {
|
|
table = rel.JoinTable.Table
|
|
for _, ref := range rel.References {
|
|
if ref.OwnPrimaryKey {
|
|
foreignFields = append(foreignFields, ref.PrimaryKey)
|
|
relForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)
|
|
} else if ref.PrimaryValue != "" {
|
|
conds = append(conds, clause.Eq{
|
|
Column: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},
|
|
Value: ref.PrimaryValue,
|
|
})
|
|
} else {
|
|
conds = append(conds, clause.Eq{
|
|
Column: clause.Column{Table: rel.JoinTable.Table, Name: ref.ForeignKey.DBName},
|
|
Value: clause.Column{Table: rel.FieldSchema.Table, Name: ref.PrimaryKey.DBName},
|
|
})
|
|
}
|
|
}
|
|
} else {
|
|
for _, ref := range rel.References {
|
|
if ref.OwnPrimaryKey {
|
|
relForeignKeys = append(relForeignKeys, ref.ForeignKey.DBName)
|
|
foreignFields = append(foreignFields, ref.PrimaryKey)
|
|
} else if ref.PrimaryValue != "" {
|
|
conds = append(conds, clause.Eq{
|
|
Column: clause.Column{Table: rel.FieldSchema.Table, Name: ref.ForeignKey.DBName},
|
|
Value: ref.PrimaryValue,
|
|
})
|
|
} else {
|
|
relForeignKeys = append(relForeignKeys, ref.PrimaryKey.DBName)
|
|
foreignFields = append(foreignFields, ref.ForeignKey)
|
|
}
|
|
}
|
|
}
|
|
|
|
_, foreignValues := GetIdentityFieldValuesMap(reflectValue, foreignFields)
|
|
column, values := ToQueryValues(table, relForeignKeys, foreignValues)
|
|
|
|
conds = append(conds, clause.IN{Column: column, Values: values})
|
|
return
|
|
}
|
|
|
|
func copyableDataType(str DataType) bool {
|
|
for _, s := range []string{"auto_increment", "primary key"} {
|
|
if strings.Contains(strings.ToLower(string(str)), s) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|