fix(database/gdb): Raw SQL Count ignores Where condition (#4611)

## Summary
- Fixed a bug where `Raw()` with `Where()` and
`Count()`/`ScanAndCount()` was ignoring the Where conditions in Count
queries
- The issue was in `getFormattedSqlAndArgs()` which returned `nil` for
`conditionArgs` without calling `formatCondition()` for Raw SQL in
`SelectTypeCount` case

## Changes
- Modified `database/gdb/gdb_model_select.go` to call
`formatCondition()` for Raw SQL Count queries
- Added comprehensive test cases for MySQL and PostgreSQL drivers
- Fixed incorrect test expectation in `Test_Model_Raw`

## Test plan
- [x] Added `Test_Issue4500` with 6 edge cases covering:
  - Raw SQL with WHERE + external Where condition
  - Raw SQL without WHERE + external Where condition  
  - Raw + Where + ScanAndCount
  - Raw + multiple Where conditions
  - Raw SQL with no external Where (baseline)
  - Verify All() still works correctly
- [x] All tests pass on PostgreSQL

Closes #4500
This commit is contained in:
Jack Ling
2026-01-16 13:05:33 +08:00
committed by GitHub
parent a4f98c2490
commit 5d1712b4ab
8 changed files with 201 additions and 7 deletions

View File

@ -2359,7 +2359,12 @@ func Test_Model_Raw(t *testing.T) {
OrderDesc("id").
Count()
t.AssertNil(err)
t.Assert(count, int64(6))
// After fix for issue #4500, Where conditions are correctly applied to Raw SQL Count.
// Raw SQL matches: id in (1, 5, 7, 8, 9, 10)
// WhereLT("id", 8): id < 8 -> (1, 5, 7)
// WhereIn("id", {1-7}): id in (1, 2, 3, 4, 5, 6, 7) -> (1, 5, 7)
// Result: 3 records match all conditions
t.Assert(count, int64(3))
})
}

View File

@ -1865,3 +1865,87 @@ func Test_Issue4086(t *testing.T) {
})
})
}
// https://github.com/gogf/gf/issues/4500
// Raw() Count ignores Where condition
func Test_Issue4500(t *testing.T) {
table := createInitTable()
defer dropTable(table)
// Test 1: Raw SQL with WHERE + external Where condition + Count
// This tests that formatCondition correctly uses AND when Raw SQL already has WHERE
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
WhereLT("id", 8).
Count()
t.AssertNil(err)
// Raw SQL: id IN (1,5,7,8,9,10) = 6 records
// Where: id < 8 filters to {1,5,7} = 3 records
t.Assert(count, 3)
})
// Test 2: Raw SQL without WHERE + external Where condition + Count
// This tests that formatCondition correctly adds WHERE
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s", table)).
WhereLT("id", 5).
Count()
t.AssertNil(err)
// Raw SQL: all 10 records
// Where: id < 5 = {1,2,3,4} = 4 records
t.Assert(count, 4)
})
// Test 3: Raw + Where + ScanAndCount
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
}
var users []User
var total int
err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
WhereLT("id", 8).
ScanAndCount(&users, &total, false)
t.AssertNil(err)
// Both scan result and count should respect Where condition
t.Assert(len(users), 3)
t.Assert(total, 3)
})
// Test 4: Raw + multiple Where conditions + Count
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id > ?", table), 0).
WhereLT("id", 5).
WhereGTE("id", 2).
Count()
t.AssertNil(err)
// Raw: id > 0 (all 10 records)
// Where: id < 5 AND id >= 2 = {2, 3, 4} = 3 records
t.Assert(count, 3)
})
// Test 5: Raw SQL with no external Where + Count (baseline test)
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 2, 3}).
Count()
t.AssertNil(err)
// Should count 3 records
t.Assert(count, 3)
})
// Test 6: Verify All() still works correctly with Raw + Where
gtest.C(t, func(t *gtest.T) {
all, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
WhereLT("id", 8).
All()
t.AssertNil(err)
t.Assert(len(all), 3)
})
}

View File

@ -2944,7 +2944,9 @@ func Test_Model_Raw(t *testing.T) {
Limit(2).
Count()
t.AssertNil(err)
t.Assert(count, int64(6))
// Raw SQL selects {1,5,7,8,9,10}, Where filters to id < 8 AND id IN {1,2,3,4,5,6,7}
// Result: {1,5,7} = 3 records
t.Assert(count, int64(3))
})
}

View File

@ -1347,7 +1347,12 @@ func Test_Model_Raw(t *testing.T) {
OrderDesc("ID").
Count()
t.AssertNil(err)
t.Assert(count, 6)
// After fix for issue #4500, Where conditions are correctly applied to Raw SQL Count.
// Raw SQL matches: id in (1, 5, 7, 8, 9, 10)
// WhereLT("ID", 8): id < 8 -> (1, 5, 7)
// WhereIn("ID", {1-7}): id in (1, 2, 3, 4, 5, 6, 7) -> (1, 5, 7)
// Result: 3 records match all conditions
t.Assert(count, int64(3))
})
}

View File

@ -206,6 +206,90 @@ func Test_Issue4033(t *testing.T) {
})
}
// https://github.com/gogf/gf/issues/4500
// Raw() Count ignores Where condition
func Test_Issue4500(t *testing.T) {
table := createInitTable()
defer dropTable(table)
// Test 1: Raw SQL with WHERE + external Where condition + Count
// This tests that formatCondition correctly uses AND when Raw SQL already has WHERE
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
WhereLT("id", 8).
Count()
t.AssertNil(err)
// Raw SQL: id IN (1,5,7,8,9,10) = 6 records
// Where: id < 8 filters to {1,5,7} = 3 records
t.Assert(count, 3)
})
// Test 2: Raw SQL without WHERE + external Where condition + Count
// This tests that formatCondition correctly adds WHERE
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s", table)).
WhereLT("id", 5).
Count()
t.AssertNil(err)
// Raw SQL: all 10 records
// Where: id < 5 = {1,2,3,4} = 4 records
t.Assert(count, 4)
})
// Test 3: Raw + Where + ScanAndCount
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
}
var users []User
var total int
err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
WhereLT("id", 8).
ScanAndCount(&users, &total, false)
t.AssertNil(err)
// Both scan result and count should respect Where condition
t.Assert(len(users), 3)
t.Assert(total, 3)
})
// Test 4: Raw + multiple Where conditions + Count
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id > ?", table), 0).
WhereLT("id", 5).
WhereGTE("id", 2).
Count()
t.AssertNil(err)
// Raw: id > 0 (all 10 records)
// Where: id < 5 AND id >= 2 = {2, 3, 4} = 3 records
t.Assert(count, 3)
})
// Test 5: Raw SQL with no external Where + Count (baseline test)
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 2, 3}).
Count()
t.AssertNil(err)
// Should count 3 records
t.Assert(count, 3)
})
// Test 6: Verify All() still works correctly with Raw + Where
gtest.C(t, func(t *gtest.T) {
all, err := db.
Raw(fmt.Sprintf("SELECT * FROM %s WHERE id IN (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
WhereLT("id", 8).
All()
t.AssertNil(err)
t.Assert(len(all), 3)
})
}
// https://github.com/gogf/gf/issues/4595
// FieldsPrefix silently drops fields when using table alias before LeftJoin.
func Test_Issue4595(t *testing.T) {

View File

@ -3482,7 +3482,12 @@ func Test_Model_Raw(t *testing.T) {
Limit(2).
Count()
t.AssertNil(err)
t.Assert(count, int64(6))
// After fix for issue #4500, Where conditions are correctly applied to Raw SQL Count.
// Raw SQL matches: id in (1, 5, 7, 8, 9, 10)
// WhereLT("id", 8): id < 8 -> (1, 5, 7)
// WhereIn("id", {1-7}): id in (1, 2, 3, 4, 5, 6, 7) -> (1, 5, 7)
// Result: 3 records match all conditions
t.Assert(count, int64(3))
})
}

View File

@ -3480,7 +3480,12 @@ func Test_Model_Raw(t *testing.T) {
Limit(2).
Count()
t.AssertNil(err)
t.Assert(count, int64(6))
// After fix for issue #4500, Where conditions are correctly applied to Raw SQL Count.
// Raw SQL matches: id in (1, 5, 7, 8, 9, 10)
// WhereLT("id", 8): id < 8 -> (1, 5, 7)
// WhereIn("id", {1-7}): id in (1, 2, 3, 4, 5, 6, 7) -> (1, 5, 7)
// Result: 3 records match all conditions
t.Assert(count, int64(3))
})
}

View File

@ -724,8 +724,12 @@ func (m *Model) getFormattedSqlAndArgs(
}
// Raw SQL Model.
if m.rawSql != "" {
sqlWithHolder = fmt.Sprintf("SELECT %s FROM (%s) AS T", queryFields, m.rawSql)
return sqlWithHolder, nil
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(ctx, false, true)
sqlWithHolder = fmt.Sprintf(
"SELECT %s FROM (%s%s) AS T",
queryFields, m.rawSql, conditionWhere+conditionExtra,
)
return sqlWithHolder, conditionArgs
}
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(ctx, false, true)
sqlWithHolder = fmt.Sprintf("SELECT %s FROM %s%s", queryFields, m.tables, conditionWhere+conditionExtra)