From 1cf491c2f3d1e782bb58d0ad0e1bfb981722be3a Mon Sep 17 00:00:00 2001 From: molon <3739161+molon@users.noreply.github.com> Date: Sun, 16 Feb 2025 23:38:01 +0800 Subject: [PATCH 1/2] make NotConditions obey De Morgan's laws --- clause/where.go | 2 +- clause/where_test.go | 33 +++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/clause/where.go b/clause/where.go index 2c3c90f18..39a9f8e34 100644 --- a/clause/where.go +++ b/clause/where.go @@ -181,7 +181,7 @@ func (not NotConditions) Build(builder Builder) { for idx, c := range not.Exprs { if idx > 0 { - builder.WriteString(AndWithSpace) + builder.WriteString(OrWithSpace) } if negationBuilder, ok := c.(NegationExpressionBuilder); ok { diff --git a/clause/where_test.go b/clause/where_test.go index ad23a4ed5..fe87c0ba4 100644 --- a/clause/where_test.go +++ b/clause/where_test.go @@ -56,7 +56,7 @@ func TestWhere(t *testing.T) { }, clause.Where{ Exprs: []clause.Expression{clause.Or(clause.Not(clause.Gt{Column: "score", Value: 100}), clause.Like{Column: "name", Value: "%linus%"})}, }}, - "SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) OR `name` <> ? AND (`score` <= ? OR `name` LIKE ?)", + "SELECT * FROM `users` WHERE (`users`.`id` <> ? OR `age` <= ?) OR `name` <> ? AND (`score` <= ? OR `name` LIKE ?)", []interface{}{"1", 18, "jinzhu", 100, "%linus%"}, }, { @@ -70,21 +70,21 @@ func TestWhere(t *testing.T) { []clause.Interface{clause.Select{}, clause.From{}, clause.Where{ Exprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, clause.Gt{Column: "age", Value: 18}), clause.And(clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}, WithoutParentheses: false})}, }}, - "SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) AND `score` <= ?", + "SELECT * FROM `users` WHERE (`users`.`id` <> ? OR `age` <= ?) AND `score` <= ?", []interface{}{"1", 18, 100}, }, { []clause.Interface{clause.Select{}, clause.From{}, clause.Where{ Exprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, clause.Gt{Column: "age", Value: 18}), clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}, WithoutParentheses: false}}, }}, - "SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) AND `score` <= ?", + "SELECT * FROM `users` WHERE (`users`.`id` <> ? OR `age` <= ?) AND `score` <= ?", []interface{}{"1", 18, 100}, }, { []clause.Interface{clause.Select{}, clause.From{}, clause.Where{ Exprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, clause.Gt{Column: "age", Value: 18}), clause.Or(clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}, WithoutParentheses: false})}, }}, - "SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `age` <= ?) OR `score` <= ?", + "SELECT * FROM `users` WHERE (`users`.`id` <> ? OR `age` <= ?) OR `score` <= ?", []interface{}{"1", 18, 100}, }, { @@ -102,7 +102,7 @@ func TestWhere(t *testing.T) { Exprs: []clause.Expression{clause.Not(clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, clause.And(clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}, WithoutParentheses: false}))}, }}, - "SELECT * FROM `users` WHERE (`users`.`id` <> ? AND NOT `score` <= ?)", + "SELECT * FROM `users` WHERE (`users`.`id` <> ? OR NOT `score` <= ?)", []interface{}{"1", 100}, }, { @@ -116,16 +116,21 @@ func TestWhere(t *testing.T) { { []clause.Interface{clause.Select{}, clause.From{}, clause.Where{ Exprs: []clause.Expression{ - clause.Not(clause.AndConditions{ - Exprs: []clause.Expression{ - clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, - clause.Gt{Column: "age", Value: 18}, - }}, clause.OrConditions{ - Exprs: []clause.Expression{ - clause.Lt{Column: "score", Value: 100}, + clause.Not( + clause.AndConditions{ + Exprs: []clause.Expression{ + clause.Eq{Column: clause.PrimaryColumn, Value: "1"}, + clause.Gt{Column: "age", Value: 18}, + }, }, - }), - }}}, + clause.OrConditions{ + Exprs: []clause.Expression{ + clause.Lt{Column: "score", Value: 100}, + }, + }, + ), + }, + }}, "SELECT * FROM `users` WHERE NOT ((`users`.`id` = ? AND `age` > ?) OR `score` < ?)", []interface{}{"1", 18, 100}, }, From 1bfe3141f64564e9b72009174647bbcc7a22eb32 Mon Sep 17 00:00:00 2001 From: molon <3739161+molon@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:02:34 +0800 Subject: [PATCH 2/2] fix NotConditions related tests --- tests/query_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/query_test.go b/tests/query_test.go index 566763c51..24510b78b 100644 --- a/tests/query_test.go +++ b/tests/query_test.go @@ -551,7 +551,7 @@ func TestNot(t *testing.T) { } result = dryDB.Not(User{Name: "jinzhu", Age: 18}).First(&User{}) - 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.* <> .+ OR .*users.*..*age.* <> .+").MatchString(result.Statement.SQL.String()) { t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String()) } @@ -608,7 +608,7 @@ func TestNotWithAllFields(t *testing.T) { } result = dryDB.Not(User{Name: "jinzhu", Age: 18}).First(&User{}) - if !regexp.MustCompile(userQuery + "WHERE .*users.*..*name.* <> .+ AND .*users.*..*age.* <> .+").MatchString(result.Statement.SQL.String()) { + if !regexp.MustCompile(userQuery + "WHERE .*users.*..*name.* <> .+ OR .*users.*..*age.* <> .+").MatchString(result.Statement.SQL.String()) { t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String()) } }