Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using clause.Not and clause.And result in erroneous simplification #7294

Open
ezk84 opened this issue Nov 26, 2024 · 2 comments
Open

Using clause.Not and clause.And result in erroneous simplification #7294

ezk84 opened this issue Nov 26, 2024 · 2 comments
Assignees
Labels
type:with reproduction steps with reproduction steps

Comments

@ezk84
Copy link
Contributor

ezk84 commented Nov 26, 2024

GORM Playground Link

go-gorm/playground#767

Description

When using clause.Not and clause.And to create query Where conditions and faulty simplification is applied:

NOT(col1 = val1 AND col2 = val2) gets turned into (col1 <> val1 AND col2 <> val2), which is wrong.

The right simplification is to turn the AND into an OR, by De Morgan's Laws, so (col1 <> OR val1 AND col2 <> val2)

This is a regression that was introduced into gorm in 1.25.6. Testing against 1.25.5 returns the correct condition and expected rows in the included tests (see gorm playground PR).

Test for completeness

func TestGORM(t *testing.T) {
	DB.Create(&User{Name: "jinzhu", Active: true})
	DB.Create(&User{Name: "ezequiel", Active: true})
	DB.Create(&User{Name: "jinzhu", Active: false})
	DB.Create(&User{Name: "ezequiel", Active: false})

	var results []User
	conds := clause.Not(clause.And(
		clause.Eq{
			Column: clause.Column{Name: "name"},
			Value:  "jinzhu",
		},
		clause.Eq{
			Column: clause.Column{Name: "active"},
			Value:  true,
		},
	))
	// Query with `NOT(name = 'jinzhu' AND active = true)` should return last three records
	// By De Morgan's Law this is equivalent to `name != 'jinzhu' OR active != true`
	// but current version (since 1.25.6) transforms this to
	// `name != 'jinzhu' AND active != true` which returns only the last record

	err := DB.
		Model(&User{}).
		Clauses(clause.Where{Exprs: []clause.Expression{conds}}).
		Find(&results).
		Error
	if err != nil {
		t.Errorf("Failed, got error: %v", err)
	}
	if len(results) != 3 {
		t.Errorf("Failed, expected 3 records, got %v", len(results))
	}
	for _, r := range results {
		if r.Name == "jinzhu" && r.Active {
			t.Errorf("Failed, unexpected record: %v", r)
		}
	}

	conds = clause.Not(clause.Or(
		clause.Eq{
			Column: clause.Column{Name: "name"},
			Value:  "jinzhu",
		},
		clause.Eq{
			Column: clause.Column{Name: "active"},
			Value:  true,
		},
	))
	// Query with `NOT(name = 'jinzhu' OR active = true)` should return last record
	err = DB.
		Model(&User{}).
		Clauses(clause.Where{Exprs: []clause.Expression{conds}}).
		Find(&results).
		Error
	if err != nil {
		t.Errorf("Failed, got error: %v", err)
	}
	if len(results) != 1 {
		t.Errorf("Failed, expected 1 records, got %v", len(results))
	}
	for _, r := range results {
		if r.Name == "jinzhu" || r.Active {
			t.Errorf("Failed, unexpected record: %v", r)
		}
	}
@github-actions github-actions bot added the type:with reproduction steps with reproduction steps label Nov 26, 2024
@ezk84
Copy link
Contributor Author

ezk84 commented Nov 26, 2024

I thought I had submitted this issue before, but must have missed submitting here in addition to the PR in gorm-playground.

@ezk84
Copy link
Contributor Author

ezk84 commented Nov 26, 2024

Have also seen a merged PR #6984 that is related, but the above test still fails for current master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:with reproduction steps with reproduction steps
Projects
None yet
Development

No branches or pull requests

2 participants