package orm
import (
)
type join struct {
Parent *join
BaseModel TableModel
JoinModel TableModel
Rel *Relation
ApplyQuery func(*Query) (*Query, error)
Columns []string
on []*condAppender
}
func ( *join) ( *condAppender) {
.on = append(.on, )
}
func ( *join) ( QueryFormatter, *Query) error {
switch .Rel.Type {
case HasManyRelation:
return .selectMany(, )
case Many2ManyRelation:
return .selectM2M(, )
}
panic("not reached")
}
func ( *join) ( QueryFormatter, *Query) error {
, := .manyQuery()
if != nil {
return
}
if == nil {
return nil
}
return .Select()
}
func ( *join) ( *Query) (*Query, error) {
:= newManyModel()
if == nil {
return nil, nil
}
= .Model()
if .ApplyQuery != nil {
var error
, = .ApplyQuery()
if != nil {
return nil,
}
}
if len(.columns) == 0 {
.columns = append(.columns, &hasManyColumnsAppender{})
}
:= .BaseModel.Table()
var []byte
if len(.Rel.JoinFKs) > 1 {
= append(, '(')
}
= appendColumns(, .JoinModel.Table().Alias, .Rel.JoinFKs)
if len(.Rel.JoinFKs) > 1 {
= append(, ')')
}
= append(, " IN ("...)
= appendChildValues(
, .JoinModel.Root(), .JoinModel.ParentIndex(), .Rel.BaseFKs)
= append(, ")"...)
= .Where(internal.BytesToString())
if .Rel.Polymorphic != nil {
= .Where(`? IN (?, ?)`,
.Rel.Polymorphic.Column,
.ModelName, .TypeName)
}
return , nil
}
func ( *join) ( QueryFormatter, *Query) error {
, := .m2mQuery(, )
if != nil {
return
}
if == nil {
return nil
}
return .Select()
}
func ( *join) ( QueryFormatter, *Query) (*Query, error) {
:= newM2MModel()
if == nil {
return nil, nil
}
= .Model()
if .ApplyQuery != nil {
var error
, = .ApplyQuery()
if != nil {
return nil,
}
}
if len(.columns) == 0 {
.columns = append(.columns, &hasManyColumnsAppender{})
}
:= .JoinModel.ParentIndex()
:= .BaseModel.Table()
var []byte
= append(, "JOIN "...)
= .FormatQuery(, string(.Rel.M2MTableName))
= append(, " AS "...)
= append(, .Rel.M2MTableAlias...)
= append(, " ON ("...)
for , := range .Rel.M2MBaseFKs {
if > 0 {
= append(, ", "...)
}
= append(, .Rel.M2MTableAlias...)
= append(, '.')
= types.AppendIdent(, , 1)
}
= append(, ") IN ("...)
= appendChildValues(, .BaseModel.Root(), , .PKs)
= append(, ")"...)
= .Join(internal.BytesToString())
:= .JoinModel.Table()
for , := range .Rel.M2MJoinFKs {
:= .PKs[]
= .Where("?.? = ?.?",
.Alias, .Column,
.Rel.M2MTableAlias, types.Ident())
}
return , nil
}
func ( *join) () bool {
if .Parent != nil {
switch .Parent.Rel.Type {
case HasOneRelation, BelongsToRelation:
return true
}
}
return false
}
func ( *join) ( []byte) []byte {
= append(, '"')
= appendAlias(, )
= append(, '"')
return
}
func ( *join) ( []byte, string) []byte {
= append(, '"')
= appendAlias(, )
= append(, "__"...)
= append(, ...)
= append(, '"')
return
}
func ( *join) ( []byte) []byte {
if .hasParent() {
= append(, '"')
= appendAlias(, .Parent)
= append(, '"')
return
}
return append(, .BaseModel.Table().Alias...)
}
func ( *join) ( []byte, queryFlag) []byte {
= append(, '.')
= append(, .JoinModel.Table().SoftDeleteField.Column...)
if hasFlag(, deletedFlag) {
= append(, " IS NOT NULL"...)
} else {
= append(, " IS NULL"...)
}
return
}
func ( []byte, *join) []byte {
if .hasParent() {
= (, .Parent)
= append(, "__"...)
}
= append(, .Rel.Field.SQLName...)
return
}
func ( *join) ( []byte) []byte {
if .Columns == nil {
for , := range .JoinModel.Table().Fields {
if > 0 {
= append(, ", "...)
}
= .appendAlias()
= append(, '.')
= append(, .Column...)
= append(, " AS "...)
= .appendAliasColumn(, .SQLName)
}
return
}
for , := range .Columns {
if > 0 {
= append(, ", "...)
}
= .appendAlias()
= append(, '.')
= types.AppendIdent(, , 1)
= append(, " AS "...)
= .appendAliasColumn(, )
}
return
}
func ( *join) ( QueryFormatter, []byte, *Query) ( []byte, error) {
:= .JoinModel.Table().SoftDeleteField != nil && !.hasFlag(allWithDeletedFlag)
= append(, "LEFT JOIN "...)
= .FormatQuery(, string(.JoinModel.Table().SQLNameForSelects))
= append(, " AS "...)
= .appendAlias()
= append(, " ON "...)
if {
= append(, '(')
}
if len(.Rel.BaseFKs) > 1 {
= append(, '(')
}
for , := range .Rel.BaseFKs {
if > 0 {
= append(, " AND "...)
}
= .appendAlias()
= append(, '.')
= append(, .Rel.JoinFKs[].Column...)
= append(, " = "...)
= .appendBaseAlias()
= append(, '.')
= append(, .Column...)
}
if len(.Rel.BaseFKs) > 1 {
= append(, ')')
}
for , := range .on {
= .AppendSep()
, = .AppendQuery(, )
if != nil {
return nil,
}
}
if {
= append(, ')')
}
if {
= append(, " AND "...)
= .appendAlias()
= .appendSoftDelete(, .flags)
}
return , nil
}
type hasManyColumnsAppender struct {
*join
}
var _ QueryAppender = (*hasManyColumnsAppender)(nil)
func ( *hasManyColumnsAppender) ( QueryFormatter, []byte) ([]byte, error) {
if .Rel.M2MTableAlias != "" {
= append(, .Rel.M2MTableAlias...)
= append(, ".*, "...)
}
:= .JoinModel.Table()
if .Columns != nil {
for , := range .Columns {
if > 0 {
= append(, ", "...)
}
= append(, .Alias...)
= append(, '.')
= types.AppendIdent(, , 1)
}
return , nil
}
= appendColumns(, .Alias, .Fields)
return , nil
}
func ( []byte, reflect.Value, []int, []*Field) []byte {
:= make(map[string]struct{})
walk(, , func( reflect.Value) {
:= len()
if len() > 1 {
= append(, '(')
}
for , := range {
if > 0 {
= append(, ", "...)
}
= .AppendValue(, , 1)
}
if len() > 1 {
= append(, ')')
}
= append(, ", "...)
if , := [string([:])]; {
= [:]
} else {
[string([:])] = struct{}{}
}
})
if len() > 0 {
= [:len()-2]
}
return
}