fix(limit): set default limit to math.MaxInt when only offset is provided
- Ensures MySQL compatibility by always providing a LIMIT when OFFSET is used - Updates tests to reflect new behavior - Improves code organization and documentation
This commit is contained in:
parent
48f9924d13
commit
3eac3cd8cb
@ -17,17 +17,16 @@ func (limit Limit) Name() string {
|
||||
|
||||
// Build constructs the LIMIT clause
|
||||
func (limit Limit) Build(builder Builder) {
|
||||
// If only offset is defined and limit is nil, set limit to math.MaxInt
|
||||
if limit.Limit == nil && limit.Offset > 0 {
|
||||
maxInt := math.MaxInt
|
||||
limit.Limit = &maxInt
|
||||
}
|
||||
// NOT: We don't auto-set limit here. We only rely on the final struct's Limit and Offset.
|
||||
// Any "auto offset => limit" logic is handled in MergeClause.
|
||||
|
||||
if limit.Limit != nil && *limit.Limit >= 0 {
|
||||
builder.WriteString("LIMIT ")
|
||||
builder.AddVar(builder, *limit.Limit)
|
||||
}
|
||||
|
||||
if limit.Offset > 0 {
|
||||
// Add space if LIMIT was set
|
||||
if limit.Limit != nil && *limit.Limit >= 0 {
|
||||
builder.WriteByte(' ')
|
||||
}
|
||||
@ -41,16 +40,30 @@ func (limit Limit) MergeClause(clause *Clause) {
|
||||
clause.Name = ""
|
||||
|
||||
if v, ok := clause.Expression.(Limit); ok {
|
||||
// 1) Merge offset
|
||||
if limit.Offset == 0 && v.Offset > 0 {
|
||||
limit.Offset = v.Offset
|
||||
} else if limit.Offset < 0 {
|
||||
// Negative offset => 0
|
||||
limit.Offset = 0
|
||||
}
|
||||
|
||||
// 2) Merge limit
|
||||
if (limit.Limit == nil || *limit.Limit == 0) && v.Limit != nil {
|
||||
limit.Limit = v.Limit
|
||||
}
|
||||
|
||||
if limit.Offset == 0 && v.Offset > 0 {
|
||||
limit.Offset = v.Offset
|
||||
} else if limit.Offset < 0 {
|
||||
limit.Offset = 0
|
||||
// 3) If final limit is negative => treat it as nil (meaning "no limit")
|
||||
if limit.Limit != nil && *limit.Limit < 0 {
|
||||
limit.Limit = nil
|
||||
}
|
||||
}
|
||||
|
||||
// 4) If offset > 0 but limit is still nil, set limit to math.MaxInt
|
||||
if limit.Offset > 0 && limit.Limit == nil {
|
||||
maxInt := math.MaxInt
|
||||
limit.Limit = &maxInt
|
||||
}
|
||||
|
||||
clause.Expression = limit
|
||||
}
|
||||
|
@ -13,68 +13,119 @@ func TestLimit(t *testing.T) {
|
||||
limit10 := 10
|
||||
limit50 := 50
|
||||
limitNeg10 := -10
|
||||
|
||||
results := []struct {
|
||||
Clauses []clause.Interface
|
||||
Result string
|
||||
Vars []interface{}
|
||||
}{
|
||||
// case #0 - limit10 offset20
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{
|
||||
Limit: &limit10,
|
||||
Offset: 20,
|
||||
}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Limit: &limit10, Offset: 20},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{limit10, 20},
|
||||
},
|
||||
// case #1 - limit0
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Limit: &limit0},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ?",
|
||||
[]interface{}{limit0},
|
||||
},
|
||||
// case #2 - limit0 offset0 => offset0 is effectively ignored
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}, clause.Limit{Offset: 0}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Limit: &limit0}, clause.Limit{Offset: 0},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ?",
|
||||
[]interface{}{limit0},
|
||||
},
|
||||
// case #3 - only offset=20
|
||||
// MySQL demands limit if offset>0 => math.MaxInt
|
||||
{
|
||||
// Updated test case: only Offset is given, so now we expect math.MaxInt as LIMIT
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Offset: 20},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{math.MaxInt, 20},
|
||||
},
|
||||
// case #4 - multiple offsets (20 -> 30)
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}, clause.Limit{Offset: 30}},
|
||||
"SELECT * FROM `users` OFFSET ?",
|
||||
[]interface{}{30},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Offset: 20}, clause.Limit{Offset: 30},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{math.MaxInt, 30},
|
||||
},
|
||||
// case #5 - merge offset20 with limit10
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}, clause.Limit{Limit: &limit10}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Offset: 20}, clause.Limit{Limit: &limit10},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{limit10, 20},
|
||||
},
|
||||
// case #6 - merge offset20->30 with limit10
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Limit: &limit10, Offset: 20},
|
||||
clause.Limit{Offset: 30},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{limit10, 30},
|
||||
},
|
||||
// case #7 - negative offset => offset=0 => "SELECT * FROM `users` LIMIT 10"
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Offset: -10}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Limit: &limit10, Offset: 20},
|
||||
clause.Limit{Offset: 30},
|
||||
clause.Limit{Offset: -10},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ?",
|
||||
[]interface{}{limit10},
|
||||
},
|
||||
// case #8 - negative limit => treat as nil => offset=30 => => "LIMIT ? OFFSET ?"
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Limit: &limitNeg10}},
|
||||
"SELECT * FROM `users` OFFSET ?",
|
||||
[]interface{}{30},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
// Start with limit10 offset20
|
||||
clause.Limit{Limit: &limit10, Offset: 20},
|
||||
// Then change offset to 30
|
||||
clause.Limit{Offset: 30},
|
||||
// Then set limit to negative => remove limit => offset>0 => limit=math.MaxInt
|
||||
clause.Limit{Limit: &limitNeg10},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{math.MaxInt, 30},
|
||||
},
|
||||
// case #9 - changing limit from 10 -> 50, offset=30
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Limit: &limit50}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Limit: &limit10, Offset: 20},
|
||||
clause.Limit{Offset: 30},
|
||||
clause.Limit{Limit: &limit50},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{limit50, 30},
|
||||
},
|
||||
// New test example: if only Offset is defined, Limit should become math.MaxInt
|
||||
// case #10 - only offset=100 => "LIMIT ? OFFSET ?", math.MaxInt, 100
|
||||
{
|
||||
[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 100}},
|
||||
[]clause.Interface{
|
||||
clause.Select{}, clause.From{},
|
||||
clause.Limit{Offset: 100},
|
||||
},
|
||||
"SELECT * FROM `users` LIMIT ? OFFSET ?",
|
||||
[]interface{}{math.MaxInt, 100},
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user