Skip to content

Commit e83d572

Browse files
authored
Merge pull request #189 from huandu/feature/limit-offset-using-args
Fix #186: use args in LIMIT and/or OFFSET expression
2 parents 2253d89 + c5c2c82 commit e83d572

10 files changed

+149
-133
lines changed

Diff for: README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,14 @@ sql := sqlbuilder.CreateTable("users").
9696
SQL(
9797
sqlbuilder.Select("columns[0] id", "columns[1] name", "columns[2] year").
9898
From("`all-users.csv`").
99-
Limit(100).
10099
String(),
101100
).
102101
String()
103102

104103
fmt.Println(sql)
105104

106105
// Output:
107-
// CREATE TABLE users PARTITION BY (year) AS SELECT columns[0] id, columns[1] name, columns[2] year FROM `all-users.csv` LIMIT 100
106+
// CREATE TABLE users PARTITION BY (year) AS SELECT columns[0] id, columns[1] name, columns[2] year FROM `all-users.csv`
108107
```
109108

110109
Below are several utility methods designed to address special cases.

Diff for: delete.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33

44
package sqlbuilder
55

6-
import (
7-
"strconv"
8-
)
9-
106
const (
117
deleteMarkerInit injectionMarker = iota
128
deleteMarkerAfterWith
@@ -31,7 +27,6 @@ func newDeleteBuilder() *DeleteBuilder {
3127
Cond: Cond{
3228
Args: args,
3329
},
34-
limit: -1,
3530
args: args,
3631
injection: newInjection(),
3732
}
@@ -51,7 +46,7 @@ type DeleteBuilder struct {
5146
tables []string
5247
orderByCols []string
5348
order string
54-
limit int
49+
limitVar string
5550

5651
args *Args
5752

@@ -152,7 +147,12 @@ func (db *DeleteBuilder) Desc() *DeleteBuilder {
152147

153148
// Limit sets the LIMIT in DELETE.
154149
func (db *DeleteBuilder) Limit(limit int) *DeleteBuilder {
155-
db.limit = limit
150+
if limit < 0 {
151+
db.limitVar = ""
152+
return db
153+
}
154+
155+
db.limitVar = db.Var(limit)
156156
db.marker = deleteMarkerAfterLimit
157157
return db
158158
}
@@ -211,9 +211,9 @@ func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
211211
db.injection.WriteTo(buf, deleteMarkerAfterOrderBy)
212212
}
213213

214-
if db.limit >= 0 {
214+
if len(db.limitVar) > 0 {
215215
buf.WriteLeadingString("LIMIT ")
216-
buf.WriteString(strconv.Itoa(db.limit))
216+
buf.WriteString(db.limitVar)
217217

218218
db.injection.WriteTo(buf, deleteMarkerAfterLimit)
219219
}

Diff for: delete_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func ExampleDeleteFrom() {
2121
fmt.Println(sql)
2222

2323
// Output:
24-
// DELETE FROM demo.user WHERE status = 1 LIMIT 10
24+
// DELETE FROM demo.user WHERE status = 1 LIMIT ?
2525
}
2626

2727
func ExampleDeleteBuilder() {
@@ -65,8 +65,8 @@ func ExampleDeleteBuilder_SQL() {
6565
fmt.Println(args)
6666

6767
// Output:
68-
// /* before */ DELETE FROM demo.user PARTITION (p0) WHERE id > ? /* after where */ ORDER BY id /* after order by */ LIMIT 10 /* after limit */
69-
// [1234]
68+
// /* before */ DELETE FROM demo.user PARTITION (p0) WHERE id > ? /* after where */ ORDER BY id /* after order by */ LIMIT ? /* after limit */
69+
// [1234 10]
7070
}
7171

7272
func ExampleDeleteBuilder_With() {

Diff for: select.go

+57-42
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package sqlbuilder
55

66
import (
77
"fmt"
8-
"strconv"
98
"strings"
109
)
1110

@@ -51,8 +50,6 @@ func newSelectBuilder() *SelectBuilder {
5150
Cond: Cond{
5251
Args: args,
5352
},
54-
limit: -1,
55-
offset: -1,
5653
args: args,
5754
injection: newInjection(),
5855
}
@@ -79,8 +76,8 @@ type SelectBuilder struct {
7976
groupByCols []string
8077
orderByCols []string
8178
order string
82-
limit int
83-
offset int
79+
limitVar string
80+
offsetVar string
8481
forWhat string
8582

8683
args *Args
@@ -249,14 +246,24 @@ func (sb *SelectBuilder) Desc() *SelectBuilder {
249246

250247
// Limit sets the LIMIT in SELECT.
251248
func (sb *SelectBuilder) Limit(limit int) *SelectBuilder {
252-
sb.limit = limit
249+
if limit < 0 {
250+
sb.limitVar = ""
251+
return sb
252+
}
253+
254+
sb.limitVar = sb.Var(limit)
253255
sb.marker = selectMarkerAfterLimit
254256
return sb
255257
}
256258

257259
// Offset sets the LIMIT offset in SELECT.
258260
func (sb *SelectBuilder) Offset(offset int) *SelectBuilder {
259-
sb.offset = offset
261+
if offset < 0 {
262+
sb.offsetVar = ""
263+
return sb
264+
}
265+
266+
sb.offsetVar = sb.Var(offset)
260267
sb.marker = selectMarkerAfterLimit
261268
return sb
262269
}
@@ -314,7 +321,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
314321
buf := newStringBuilder()
315322
sb.injection.WriteTo(buf, selectMarkerInit)
316323

317-
oraclePage := flavor == Oracle && (sb.limit >= 0 || sb.offset >= 0)
324+
oraclePage := flavor == Oracle && (len(sb.limitVar) > 0 || len(sb.offsetVar) > 0)
318325

319326
if sb.cteBuilderVar != "" {
320327
buf.WriteLeadingString(sb.cteBuilderVar)
@@ -349,7 +356,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
349356

350357
if oraclePage {
351358
if len(sb.selectCols) > 0 {
352-
buf.WriteLeadingString("FROM ( SELECT ")
359+
buf.WriteLeadingString("FROM (SELECT ")
353360

354361
if sb.distinct {
355362
buf.WriteString("DISTINCT ")
@@ -368,7 +375,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
368375
}
369376

370377
buf.WriteStrings(selectCols, ", ")
371-
buf.WriteLeadingString("FROM ( SELECT ")
378+
buf.WriteLeadingString("FROM (SELECT ")
372379
buf.WriteStrings(sb.selectCols, ", ")
373380
}
374381
}
@@ -436,92 +443,100 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
436443

437444
switch flavor {
438445
case MySQL, SQLite, ClickHouse:
439-
if sb.limit >= 0 {
446+
if len(sb.limitVar) > 0 {
440447
buf.WriteLeadingString("LIMIT ")
441-
buf.WriteString(strconv.Itoa(sb.limit))
448+
buf.WriteString(sb.limitVar)
442449

443-
if sb.offset >= 0 {
450+
if len(sb.offsetVar) > 0 {
444451
buf.WriteLeadingString("OFFSET ")
445-
buf.WriteString(strconv.Itoa(sb.offset))
452+
buf.WriteString(sb.offsetVar)
446453
}
447454
}
448455
case CQL:
449-
if sb.limit >= 0 {
456+
if len(sb.limitVar) > 0 {
450457
buf.WriteLeadingString("LIMIT ")
451-
buf.WriteString(strconv.Itoa(sb.limit))
458+
buf.WriteString(sb.limitVar)
452459
}
453460
case PostgreSQL, Presto:
454-
if sb.limit >= 0 {
461+
if len(sb.limitVar) > 0 {
455462
buf.WriteLeadingString("LIMIT ")
456-
buf.WriteString(strconv.Itoa(sb.limit))
463+
buf.WriteString(sb.limitVar)
457464
}
458465

459-
if sb.offset >= 0 {
466+
if len(sb.offsetVar) > 0 {
460467
buf.WriteLeadingString("OFFSET ")
461-
buf.WriteString(strconv.Itoa(sb.offset))
468+
buf.WriteString(sb.offsetVar)
462469
}
463470

464471
case SQLServer:
465472
// If ORDER BY is not set, sort column #1 by default.
466473
// It's required to make OFFSET...FETCH work.
467-
if len(sb.orderByCols) == 0 && (sb.limit >= 0 || sb.offset >= 0) {
474+
if len(sb.orderByCols) == 0 && (len(sb.limitVar) > 0 || len(sb.offsetVar) > 0) {
468475
buf.WriteLeadingString("ORDER BY 1")
469476
}
470477

471-
if sb.offset >= 0 {
478+
if len(sb.offsetVar) > 0 {
472479
buf.WriteLeadingString("OFFSET ")
473-
buf.WriteString(strconv.Itoa(sb.offset))
480+
buf.WriteString(sb.offsetVar)
474481
buf.WriteString(" ROWS")
475482
}
476483

477-
if sb.limit >= 0 {
478-
if sb.offset < 0 {
484+
if len(sb.limitVar) > 0 {
485+
if len(sb.offsetVar) == 0 {
479486
buf.WriteLeadingString("OFFSET 0 ROWS")
480487
}
481488

482489
buf.WriteLeadingString("FETCH NEXT ")
483-
buf.WriteString(strconv.Itoa(sb.limit))
490+
buf.WriteString(sb.limitVar)
484491
buf.WriteString(" ROWS ONLY")
485492
}
486493

487494
case Oracle:
488495
if oraclePage {
489-
buf.WriteString(" ) ")
496+
buf.WriteString(") ")
497+
490498
if len(sb.tables) > 0 {
491499
buf.WriteStrings(sb.tables, ", ")
492500
}
493501

494-
min := sb.offset
495-
if min < 0 {
496-
min = 0
497-
}
502+
buf.WriteString(") WHERE ")
498503

499-
buf.WriteString(" ) WHERE ")
500-
if sb.limit >= 0 {
504+
if len(sb.limitVar) > 0 {
501505
buf.WriteString("r BETWEEN ")
502-
buf.WriteString(strconv.Itoa(min + 1))
503-
buf.WriteString(" AND ")
504-
buf.WriteString(strconv.Itoa(sb.limit + min))
506+
507+
if len(sb.offsetVar) > 0 {
508+
buf.WriteString(sb.offsetVar)
509+
buf.WriteString(" + 1 AND ")
510+
buf.WriteString(sb.limitVar)
511+
buf.WriteString(" + ")
512+
buf.WriteString(sb.offsetVar)
513+
} else {
514+
buf.WriteString("1 AND ")
515+
buf.WriteString(sb.limitVar)
516+
buf.WriteString(" + 1")
517+
}
505518
} else {
519+
// As oraclePage is true, sb.offsetVar must not be empty.
506520
buf.WriteString("r >= ")
507-
buf.WriteString(strconv.Itoa(min + 1))
521+
buf.WriteString(sb.offsetVar)
522+
buf.WriteString(" + 1")
508523
}
509524
}
510525
case Informix:
511526
// [SKIP N] FIRST M
512527
// M must be greater than 0
513-
if sb.limit > 0 {
514-
if sb.offset >= 0 {
528+
if len(sb.limitVar) > 0 {
529+
if len(sb.offsetVar) > 0 {
515530
buf.WriteLeadingString("SKIP ")
516-
buf.WriteString(strconv.Itoa(sb.offset))
531+
buf.WriteString(sb.offsetVar)
517532
}
518533

519534
buf.WriteLeadingString("FIRST ")
520-
buf.WriteString(strconv.Itoa(sb.limit))
535+
buf.WriteString(sb.limitVar)
521536
}
522537
}
523538

524-
if sb.limit >= 0 {
539+
if len(sb.limitVar) > 0 {
525540
sb.injection.WriteTo(buf, selectMarkerAfterLimit)
526541
}
527542

0 commit comments

Comments
 (0)