Skip to content

Conversation

@a631807682
Copy link
Member

  • Do only one thing
  • Non breaking API changes
  • Tested

What did this pull request do?

https://gorm.io/docs/advanced_query.html#Group-Conditions

Since it is not safe to reuse a db instance, group conditions will generate duplicate where conditions when used in scope.

User Case Description

refer to #6148 (comment)
close #7136

}

for _, tx := range txs {
stmt := tx.Statement
Copy link

@cbaker cbaker Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it work if we make a function func (db *DB) mergeClause(tx *DB) and then call it in the loop on line 397 to cut down on iterations? db.mergeClause(scope(cleanDB))

Copy link
Member Author

@a631807682 a631807682 Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is to avoid the impact of multiple AddClause on the db instance, so a clean stmt needs to be cached (Clauses are not affected by AddClause)
At the same time we need to keep the order of conditions, such as .Where(...).Scope(...)

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the scope execution mechanism in GORM to fix issues with scope chaining and clause merging. The changes ensure that scopes receive a clean database instance and their clauses are properly merged back to the parent statement.

  • Refactored executeScopes() to use a clean DB instance for each scope and collect results for merging
  • Added mergeClauses() method to consolidate clauses from multiple scopes (WHERE, Table, Select, etc.)
  • Added test case to verify complex scope scenarios with multiple table assignments and grouped conditions
  • Updated return value of executeScopes() in BuildCondition() to use the returned DB instance

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
chainable_api.go Refactored executeScopes() to use clean DB instances and added mergeClauses() helper for merging scope results; fixed documentation formatting for Unscoped()
statement.go Updated BuildCondition() to use the returned value from executeScopes() instead of ignoring it
tests/scopes_test.go Added test case "group_cond" to verify proper merging of multiple table assignments, WHERE clauses, and Select statements across scopes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if stmt.TableExpr != nil {
db.Statement.TableExpr = stmt.TableExpr
}

Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The merge logic overwrites Table with the last non-empty value from the scopes. This means if multiple scopes set different table names (like 'languages1' and 'languages2' in the test), only the last one is retained. Consider if this is intentional, as it may lead to confusion where earlier Table assignments in scopes are silently ignored. If this overwriting behavior is desired, consider adding a comment explaining that later scopes take precedence.

Suggested change
// If multiple scopes set different table names, only the last non-empty value is retained.
// Later scopes take precedence and overwrite earlier Table assignments.

Copilot uses AI. Check for mistakes.
Comment on lines +435 to +439
db.Statement.Selects = stmt.Selects
}

if stmt.Omits != nil {
db.Statement.Omits = stmt.Omits
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both Selects and Omits are being overwritten rather than merged. If multiple scopes specify different selects or omits, only the last one will apply. This could lead to unexpected behavior where earlier scope configurations are lost. Consider either accumulating these values or documenting that later scopes override earlier ones.

Suggested change
db.Statement.Selects = stmt.Selects
}
if stmt.Omits != nil {
db.Statement.Omits = stmt.Omits
if db.Statement.Selects == nil {
db.Statement.Selects = []string{}
}
db.Statement.Selects = append(db.Statement.Selects, stmt.Selects...)
}
if stmt.Omits != nil {
if db.Statement.Omits == nil {
db.Statement.Omits = []string{}
}
db.Statement.Omits = append(db.Statement.Omits, stmt.Omits...)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Grouped conditions in scopes results in duplicative where conds

2 participants