Skip to content

Commit 23f6840

Browse files
committedNov 27, 2019
Add limit and offset parse error
1 parent 5940839 commit 23f6840

File tree

6 files changed

+113
-12
lines changed

6 files changed

+113
-12
lines changed
 

‎dialect.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type Dialect interface {
3737
ModifyColumn(tableName string, columnName string, typ string) error
3838

3939
// LimitAndOffsetSQL return generated SQL with Limit and Offset, as mssql has special case
40-
LimitAndOffsetSQL(limit, offset interface{}) string
40+
LimitAndOffsetSQL(limit, offset interface{}) (string, error)
4141
// SelectFromDummyTable return select values, for most dbs, `SELECT values` just works, mysql needs `SELECT value FROM DUAL`
4242
SelectFromDummyTable() string
4343
// LastInsertIDOutputInterstitial most dbs support LastInsertId, but mssql needs to use `OUTPUT`

‎dialect_common.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,23 @@ func (s commonDialect) CurrentDatabase() (name string) {
139139
return
140140
}
141141

142-
func (commonDialect) LimitAndOffsetSQL(limit, offset interface{}) (sql string) {
142+
// LimitAndOffsetSQL return generated SQL with Limit and Offset
143+
func (s commonDialect) LimitAndOffsetSQL(limit, offset interface{}) (sql string, err error) {
143144
if limit != nil {
144-
if parsedLimit, err := strconv.ParseInt(fmt.Sprint(limit), 0, 0); err == nil && parsedLimit >= 0 {
145+
parsedLimit, err := s.parseInt(limit)
146+
if err != nil {
147+
return "", err
148+
}
149+
if parsedLimit >= 0 {
145150
sql += fmt.Sprintf(" LIMIT %d", parsedLimit)
146151
}
147152
}
148153
if offset != nil {
149-
if parsedOffset, err := strconv.ParseInt(fmt.Sprint(offset), 0, 0); err == nil && parsedOffset >= 0 {
154+
parsedOffset, err := s.parseInt(offset)
155+
if err != nil {
156+
return "", err
157+
}
158+
if parsedOffset >= 0 {
150159
sql += fmt.Sprintf(" OFFSET %d", parsedOffset)
151160
}
152161
}
@@ -181,6 +190,10 @@ func (commonDialect) NormalizeIndexAndColumn(indexName, columnName string) (stri
181190
return indexName, columnName
182191
}
183192

193+
func (commonDialect) parseInt(value interface{}) (int64, error) {
194+
return strconv.ParseInt(fmt.Sprint(value), 0, 0)
195+
}
196+
184197
// IsByteArrayOrSlice returns true of the reflected value is an array or slice
185198
func IsByteArrayOrSlice(value reflect.Value) bool {
186199
return (value.Kind() == reflect.Array || value.Kind() == reflect.Slice) && value.Type().Elem() == reflect.TypeOf(uint8(0))

‎dialect_mysql.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"reflect"
88
"regexp"
9-
"strconv"
109
"strings"
1110
"time"
1211
"unicode/utf8"
@@ -140,13 +139,21 @@ func (s mysql) ModifyColumn(tableName string, columnName string, typ string) err
140139
return err
141140
}
142141

143-
func (s mysql) LimitAndOffsetSQL(limit, offset interface{}) (sql string) {
142+
func (s mysql) LimitAndOffsetSQL(limit, offset interface{}) (sql string, err error) {
144143
if limit != nil {
145-
if parsedLimit, err := strconv.ParseInt(fmt.Sprint(limit), 0, 0); err == nil && parsedLimit >= 0 {
144+
parsedLimit, err := s.parseInt(limit)
145+
if err != nil {
146+
return "", err
147+
}
148+
if parsedLimit >= 0 {
146149
sql += fmt.Sprintf(" LIMIT %d", parsedLimit)
147150

148151
if offset != nil {
149-
if parsedOffset, err := strconv.ParseInt(fmt.Sprint(offset), 0, 0); err == nil && parsedOffset >= 0 {
152+
parsedOffset, err := s.parseInt(offset)
153+
if err != nil {
154+
return "", err
155+
}
156+
if parsedOffset >= 0 {
150157
sql += fmt.Sprintf(" OFFSET %d", parsedOffset)
151158
}
152159
}

‎dialects/mssql/mssql.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,25 @@ func (s mssql) CurrentDatabase() (name string) {
168168
return
169169
}
170170

171-
func (mssql) LimitAndOffsetSQL(limit, offset interface{}) (sql string) {
171+
func (mssql) LimitAndOffsetSQL(limit, offset interface{}) (sql string, err error) {
172+
parseInt := func(value interface{}) (int64, error) {
173+
return strconv.ParseInt(fmt.Sprint(value), 0, 0)
174+
}
172175
if offset != nil {
173-
if parsedOffset, err := strconv.ParseInt(fmt.Sprint(offset), 0, 0); err == nil && parsedOffset >= 0 {
176+
parsedOffset, err := parseInt(offset)
177+
if err != nil {
178+
return "", err
179+
}
180+
if parsedOffset >= 0 {
174181
sql += fmt.Sprintf(" OFFSET %d ROWS", parsedOffset)
175182
}
176183
}
177184
if limit != nil {
178-
if parsedLimit, err := strconv.ParseInt(fmt.Sprint(limit), 0, 0); err == nil && parsedLimit >= 0 {
185+
parsedLimit, err := parseInt(limit)
186+
if err != nil {
187+
return "", err
188+
}
189+
if parsedLimit >= 0 {
179190
if sql == "" {
180191
// add default zero offset
181192
sql += " OFFSET 0 ROWS"

‎query_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,74 @@ func TestOffset(t *testing.T) {
457457
}
458458
}
459459

460+
func TestLimitAndOffsetSQL(t *testing.T) {
461+
user1 := User{Name: "TestLimitAndOffsetSQL1", Age: 10}
462+
user2 := User{Name: "TestLimitAndOffsetSQL2", Age: 20}
463+
user3 := User{Name: "TestLimitAndOffsetSQL3", Age: 30}
464+
user4 := User{Name: "TestLimitAndOffsetSQL4", Age: 40}
465+
user5 := User{Name: "TestLimitAndOffsetSQL5", Age: 50}
466+
if err := DB.Save(&user1).Save(&user2).Save(&user3).Save(&user4).Save(&user5).Error; err != nil {
467+
t.Fatal(err)
468+
}
469+
470+
tests := []struct {
471+
name string
472+
limit, offset interface{}
473+
users []*User
474+
ok bool
475+
}{
476+
{
477+
name: "OK",
478+
limit: float64(2),
479+
offset: float64(2),
480+
users: []*User{
481+
&User{Name: "TestLimitAndOffsetSQL3", Age: 30},
482+
&User{Name: "TestLimitAndOffsetSQL2", Age: 20},
483+
},
484+
ok: true,
485+
},
486+
{
487+
name: "Limit parse error",
488+
limit: float64(1000000), // 1e+06
489+
offset: float64(2),
490+
ok: false,
491+
},
492+
{
493+
name: "Offset parse error",
494+
limit: float64(2),
495+
offset: float64(1000000), // 1e+06
496+
ok: false,
497+
},
498+
}
499+
500+
for _, tt := range tests {
501+
t.Run(tt.name, func(t *testing.T) {
502+
var users []*User
503+
err := DB.Where("name LIKE ?", "TestLimitAndOffsetSQL%").Order("age desc").Limit(tt.limit).Offset(tt.offset).Find(&users).Error
504+
if tt.ok {
505+
if err != nil {
506+
t.Errorf("error expected nil, but got %v", err)
507+
}
508+
if len(users) != len(tt.users) {
509+
t.Errorf("users length expected %d, but got %d", len(tt.users), len(users))
510+
}
511+
for i := range tt.users {
512+
if users[i].Name != tt.users[i].Name {
513+
t.Errorf("users[%d] name expected %s, but got %s", i, tt.users[i].Name, users[i].Name)
514+
}
515+
if users[i].Age != tt.users[i].Age {
516+
t.Errorf("users[%d] age expected %d, but got %d", i, tt.users[i].Age, users[i].Age)
517+
}
518+
}
519+
} else {
520+
if err == nil {
521+
t.Error("error expected not nil, but got nil")
522+
}
523+
}
524+
})
525+
}
526+
}
527+
460528
func TestOr(t *testing.T) {
461529
user1 := User{Name: "OrUser1", Age: 1}
462530
user2 := User{Name: "OrUser2", Age: 10}

‎scope.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,9 @@ func (scope *Scope) orderSQL() string {
797797
}
798798

799799
func (scope *Scope) limitAndOffsetSQL() string {
800-
return scope.Dialect().LimitAndOffsetSQL(scope.Search.limit, scope.Search.offset)
800+
sql, err := scope.Dialect().LimitAndOffsetSQL(scope.Search.limit, scope.Search.offset)
801+
scope.Err(err)
802+
return sql
801803
}
802804

803805
func (scope *Scope) groupSQL() string {

0 commit comments

Comments
 (0)
Please sign in to comment.