This commit is contained in:
Tsubasa Munekata 2024-02-22 21:28:27 +09:00
parent 72a2252049
commit 27d6d27441
No known key found for this signature in database
GPG Key ID: D3E6F3430F283A7C
3 changed files with 65 additions and 15 deletions

View File

@ -153,6 +153,11 @@ func Not(exprs ...Expression) Expression {
if len(exprs) == 0 { if len(exprs) == 0 {
return nil return nil
} }
if len(exprs) == 1 {
if andCondition, ok := exprs[0].(AndConditions); ok {
exprs = andCondition.Exprs
}
}
return NotConditions{Exprs: exprs} return NotConditions{Exprs: exprs}
} }
@ -161,19 +166,58 @@ type NotConditions struct {
} }
func (not NotConditions) Build(builder Builder) { func (not NotConditions) Build(builder Builder) {
if len(not.Exprs) > 1 { anyNegationBuilder := false
builder.WriteByte('(') for _, c := range not.Exprs {
if _, ok := c.(NegationExpressionBuilder); ok {
anyNegationBuilder = true
break
}
} }
for idx, c := range not.Exprs { if anyNegationBuilder {
if idx > 0 { if len(not.Exprs) > 1 {
builder.WriteString(AndWithSpace) builder.WriteByte('(')
} }
if negationBuilder, ok := c.(NegationExpressionBuilder); ok { for idx, c := range not.Exprs {
negationBuilder.NegationBuild(builder) if idx > 0 {
} else { builder.WriteString(AndWithSpace)
builder.WriteString("NOT ") }
if negationBuilder, ok := c.(NegationExpressionBuilder); ok {
negationBuilder.NegationBuild(builder)
} else {
builder.WriteString("NOT ")
e, wrapInParentheses := c.(Expr)
if wrapInParentheses {
sql := strings.ToUpper(e.SQL)
if wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace); wrapInParentheses {
builder.WriteByte('(')
}
}
c.Build(builder)
if wrapInParentheses {
builder.WriteByte(')')
}
}
}
if len(not.Exprs) > 1 {
builder.WriteByte(')')
}
} else {
builder.WriteString("NOT ")
if len(not.Exprs) > 1 {
builder.WriteByte('(')
}
for idx, c := range not.Exprs {
if idx > 0 {
builder.WriteString(AndWithSpace)
}
e, wrapInParentheses := c.(Expr) e, wrapInParentheses := c.(Expr)
if wrapInParentheses { if wrapInParentheses {
sql := strings.ToUpper(e.SQL) sql := strings.ToUpper(e.SQL)
@ -188,9 +232,9 @@ func (not NotConditions) Build(builder Builder) {
builder.WriteByte(')') builder.WriteByte(')')
} }
} }
}
if len(not.Exprs) > 1 { if len(not.Exprs) > 1 {
builder.WriteByte(')') builder.WriteByte(')')
}
} }
} }

View File

@ -107,10 +107,11 @@ func TestWhere(t *testing.T) {
}, },
{ {
[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{ []clause.Interface{clause.Select{}, clause.From{}, clause.Where{
Exprs: []clause.Expression{clause.Not(clause.And(clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}, WithoutParentheses: false}))}, Exprs: []clause.Expression{clause.Not(clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}},
clause.Expr{SQL: "`age` <= ?", Vars: []interface{}{60}})},
}}, }},
"SELECT * FROM `users` WHERE NOT (`users`.`id` = ? AND `score` <= ?)", "SELECT * FROM `users` WHERE NOT (`score` <= ? AND `age` <= ?)",
[]interface{}{"1", 100}, []interface{}{100, 60},
}, },
} }

View File

@ -554,6 +554,11 @@ func TestNot(t *testing.T) {
if !regexp.MustCompile("SELECT \\* FROM .*users.* WHERE .*users.*..*name.* <> .+ AND .*users.*..*age.* <> .+").MatchString(result.Statement.SQL.String()) { if !regexp.MustCompile("SELECT \\* FROM .*users.* WHERE .*users.*..*name.* <> .+ AND .*users.*..*age.* <> .+").MatchString(result.Statement.SQL.String()) {
t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String()) t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String())
} }
result = dryDB.Not(DB.Where("manager IS NULL").Where("age >= ?", 20)).Find(&User{})
if !regexp.MustCompile("SELECT \\* FROM .*users.* WHERE NOT \\(manager IS NULL AND age >= .+\\) AND .users.\\..deleted_at. IS NULL").MatchString(result.Statement.SQL.String()) {
t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String())
}
} }
func TestNotWithAllFields(t *testing.T) { func TestNotWithAllFields(t *testing.T) {