mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
fix #186: use args in LIMIT and/or OFFSET expression
This commit is contained in:
@@ -96,7 +96,6 @@ sql := sqlbuilder.CreateTable("users").
|
||||
SQL(
|
||||
sqlbuilder.Select("columns[0] id", "columns[1] name", "columns[2] year").
|
||||
From("`all-users.csv`").
|
||||
Limit(100).
|
||||
String(),
|
||||
).
|
||||
String()
|
||||
@@ -104,7 +103,7 @@ sql := sqlbuilder.CreateTable("users").
|
||||
fmt.Println(sql)
|
||||
|
||||
// Output:
|
||||
// CREATE TABLE users PARTITION BY (year) AS SELECT columns[0] id, columns[1] name, columns[2] year FROM `all-users.csv` LIMIT 100
|
||||
// CREATE TABLE users PARTITION BY (year) AS SELECT columns[0] id, columns[1] name, columns[2] year FROM `all-users.csv`
|
||||
```
|
||||
|
||||
Below are several utility methods designed to address special cases.
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
|
||||
package sqlbuilder
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
deleteMarkerInit injectionMarker = iota
|
||||
deleteMarkerAfterWith
|
||||
@@ -31,7 +27,6 @@ func newDeleteBuilder() *DeleteBuilder {
|
||||
Cond: Cond{
|
||||
Args: args,
|
||||
},
|
||||
limit: -1,
|
||||
args: args,
|
||||
injection: newInjection(),
|
||||
}
|
||||
@@ -51,7 +46,7 @@ type DeleteBuilder struct {
|
||||
tables []string
|
||||
orderByCols []string
|
||||
order string
|
||||
limit int
|
||||
limitVar string
|
||||
|
||||
args *Args
|
||||
|
||||
@@ -152,7 +147,12 @@ func (db *DeleteBuilder) Desc() *DeleteBuilder {
|
||||
|
||||
// Limit sets the LIMIT in DELETE.
|
||||
func (db *DeleteBuilder) Limit(limit int) *DeleteBuilder {
|
||||
db.limit = limit
|
||||
if limit < 0 {
|
||||
db.limitVar = ""
|
||||
return db
|
||||
}
|
||||
|
||||
db.limitVar = db.Var(limit)
|
||||
db.marker = deleteMarkerAfterLimit
|
||||
return db
|
||||
}
|
||||
@@ -212,9 +212,9 @@ func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
db.injection.WriteTo(buf, deleteMarkerAfterOrderBy)
|
||||
}
|
||||
|
||||
if db.limit >= 0 {
|
||||
if len(db.limitVar) > 0 {
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(db.limit))
|
||||
buf.WriteString(db.limitVar)
|
||||
|
||||
db.injection.WriteTo(buf, deleteMarkerAfterLimit)
|
||||
}
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ func ExampleDeleteFrom() {
|
||||
fmt.Println(sql)
|
||||
|
||||
// Output:
|
||||
// DELETE FROM demo.user WHERE status = 1 LIMIT 10
|
||||
// DELETE FROM demo.user WHERE status = 1 LIMIT ?
|
||||
}
|
||||
|
||||
func ExampleDeleteBuilder() {
|
||||
@@ -65,8 +65,8 @@ func ExampleDeleteBuilder_SQL() {
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// /* before */ DELETE FROM demo.user PARTITION (p0) WHERE id > ? /* after where */ ORDER BY id /* after order by */ LIMIT 10 /* after limit */
|
||||
// [1234]
|
||||
// /* before */ DELETE FROM demo.user PARTITION (p0) WHERE id > ? /* after where */ ORDER BY id /* after order by */ LIMIT ? /* after limit */
|
||||
// [1234 10]
|
||||
}
|
||||
|
||||
func ExampleDeleteBuilder_With() {
|
||||
|
||||
@@ -5,7 +5,6 @@ package sqlbuilder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -51,8 +50,6 @@ func newSelectBuilder() *SelectBuilder {
|
||||
Cond: Cond{
|
||||
Args: args,
|
||||
},
|
||||
limit: -1,
|
||||
offset: -1,
|
||||
args: args,
|
||||
injection: newInjection(),
|
||||
}
|
||||
@@ -79,8 +76,8 @@ type SelectBuilder struct {
|
||||
groupByCols []string
|
||||
orderByCols []string
|
||||
order string
|
||||
limit int
|
||||
offset int
|
||||
limitVar string
|
||||
offsetVar string
|
||||
forWhat string
|
||||
|
||||
args *Args
|
||||
@@ -249,14 +246,24 @@ func (sb *SelectBuilder) Desc() *SelectBuilder {
|
||||
|
||||
// Limit sets the LIMIT in SELECT.
|
||||
func (sb *SelectBuilder) Limit(limit int) *SelectBuilder {
|
||||
sb.limit = limit
|
||||
if limit < 0 {
|
||||
sb.limitVar = ""
|
||||
return sb
|
||||
}
|
||||
|
||||
sb.limitVar = sb.Var(limit)
|
||||
sb.marker = selectMarkerAfterLimit
|
||||
return sb
|
||||
}
|
||||
|
||||
// Offset sets the LIMIT offset in SELECT.
|
||||
func (sb *SelectBuilder) Offset(offset int) *SelectBuilder {
|
||||
sb.offset = offset
|
||||
if offset < 0 {
|
||||
sb.offsetVar = ""
|
||||
return sb
|
||||
}
|
||||
|
||||
sb.offsetVar = sb.Var(offset)
|
||||
sb.marker = selectMarkerAfterLimit
|
||||
return sb
|
||||
}
|
||||
@@ -314,7 +321,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
buf := newStringBuilder()
|
||||
sb.injection.WriteTo(buf, selectMarkerInit)
|
||||
|
||||
oraclePage := flavor == Oracle && (sb.limit >= 0 || sb.offset >= 0)
|
||||
oraclePage := flavor == Oracle && (len(sb.limitVar) > 0 || len(sb.offsetVar) > 0)
|
||||
|
||||
if sb.cteBuilderVar != "" {
|
||||
buf.WriteLeadingString(sb.cteBuilderVar)
|
||||
@@ -349,7 +356,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
|
||||
if oraclePage {
|
||||
if len(sb.selectCols) > 0 {
|
||||
buf.WriteLeadingString("FROM ( SELECT ")
|
||||
buf.WriteLeadingString("FROM (SELECT ")
|
||||
|
||||
if sb.distinct {
|
||||
buf.WriteString("DISTINCT ")
|
||||
@@ -368,7 +375,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
}
|
||||
|
||||
buf.WriteStrings(selectCols, ", ")
|
||||
buf.WriteLeadingString("FROM ( SELECT ")
|
||||
buf.WriteLeadingString("FROM (SELECT ")
|
||||
buf.WriteStrings(sb.selectCols, ", ")
|
||||
}
|
||||
}
|
||||
@@ -436,92 +443,100 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
|
||||
switch flavor {
|
||||
case MySQL, SQLite, ClickHouse:
|
||||
if sb.limit >= 0 {
|
||||
if len(sb.limitVar) > 0 {
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
buf.WriteString(sb.limitVar)
|
||||
|
||||
if sb.offset >= 0 {
|
||||
if len(sb.offsetVar) > 0 {
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(sb.offset))
|
||||
buf.WriteString(sb.offsetVar)
|
||||
}
|
||||
}
|
||||
case CQL:
|
||||
if sb.limit >= 0 {
|
||||
if len(sb.limitVar) > 0 {
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
buf.WriteString(sb.limitVar)
|
||||
}
|
||||
case PostgreSQL, Presto:
|
||||
if sb.limit >= 0 {
|
||||
if len(sb.limitVar) > 0 {
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
buf.WriteString(sb.limitVar)
|
||||
}
|
||||
|
||||
if sb.offset >= 0 {
|
||||
if len(sb.offsetVar) > 0 {
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(sb.offset))
|
||||
buf.WriteString(sb.offsetVar)
|
||||
}
|
||||
|
||||
case SQLServer:
|
||||
// If ORDER BY is not set, sort column #1 by default.
|
||||
// It's required to make OFFSET...FETCH work.
|
||||
if len(sb.orderByCols) == 0 && (sb.limit >= 0 || sb.offset >= 0) {
|
||||
if len(sb.orderByCols) == 0 && (len(sb.limitVar) > 0 || len(sb.offsetVar) > 0) {
|
||||
buf.WriteLeadingString("ORDER BY 1")
|
||||
}
|
||||
|
||||
if sb.offset >= 0 {
|
||||
if len(sb.offsetVar) > 0 {
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(sb.offset))
|
||||
buf.WriteString(sb.offsetVar)
|
||||
buf.WriteString(" ROWS")
|
||||
}
|
||||
|
||||
if sb.limit >= 0 {
|
||||
if sb.offset < 0 {
|
||||
if len(sb.limitVar) > 0 {
|
||||
if len(sb.offsetVar) == 0 {
|
||||
buf.WriteLeadingString("OFFSET 0 ROWS")
|
||||
}
|
||||
|
||||
buf.WriteLeadingString("FETCH NEXT ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
buf.WriteString(sb.limitVar)
|
||||
buf.WriteString(" ROWS ONLY")
|
||||
}
|
||||
|
||||
case Oracle:
|
||||
if oraclePage {
|
||||
buf.WriteString(" ) ")
|
||||
buf.WriteString(") ")
|
||||
|
||||
if len(sb.tables) > 0 {
|
||||
buf.WriteStrings(sb.tables, ", ")
|
||||
}
|
||||
|
||||
min := sb.offset
|
||||
if min < 0 {
|
||||
min = 0
|
||||
}
|
||||
buf.WriteString(") WHERE ")
|
||||
|
||||
buf.WriteString(" ) WHERE ")
|
||||
if sb.limit >= 0 {
|
||||
if len(sb.limitVar) > 0 {
|
||||
buf.WriteString("r BETWEEN ")
|
||||
buf.WriteString(strconv.Itoa(min + 1))
|
||||
buf.WriteString(" AND ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit + min))
|
||||
|
||||
if len(sb.offsetVar) > 0 {
|
||||
buf.WriteString(sb.offsetVar)
|
||||
buf.WriteString(" + 1 AND ")
|
||||
buf.WriteString(sb.limitVar)
|
||||
buf.WriteString(" + ")
|
||||
buf.WriteString(sb.offsetVar)
|
||||
} else {
|
||||
buf.WriteString("1 AND ")
|
||||
buf.WriteString(sb.limitVar)
|
||||
buf.WriteString(" + 1")
|
||||
}
|
||||
} else {
|
||||
// As oraclePage is true, sb.offsetVar must not be empty.
|
||||
buf.WriteString("r >= ")
|
||||
buf.WriteString(strconv.Itoa(min + 1))
|
||||
buf.WriteString(sb.offsetVar)
|
||||
buf.WriteString(" + 1")
|
||||
}
|
||||
}
|
||||
case Informix:
|
||||
// [SKIP N] FIRST M
|
||||
// M must be greater than 0
|
||||
if sb.limit > 0 {
|
||||
if sb.offset >= 0 {
|
||||
if len(sb.limitVar) > 0 {
|
||||
if len(sb.offsetVar) > 0 {
|
||||
buf.WriteLeadingString("SKIP ")
|
||||
buf.WriteString(strconv.Itoa(sb.offset))
|
||||
buf.WriteString(sb.offsetVar)
|
||||
}
|
||||
|
||||
buf.WriteLeadingString("FIRST ")
|
||||
buf.WriteString(strconv.Itoa(sb.limit))
|
||||
buf.WriteString(sb.limitVar)
|
||||
}
|
||||
}
|
||||
|
||||
if sb.limit >= 0 {
|
||||
if len(sb.limitVar) > 0 {
|
||||
sb.injection.WriteTo(buf, selectMarkerAfterLimit)
|
||||
}
|
||||
|
||||
|
||||
+45
-49
@@ -12,22 +12,18 @@ import (
|
||||
)
|
||||
|
||||
func ExampleSelect() {
|
||||
// Build a SQL to create a HIVE table.
|
||||
s := CreateTable("users").
|
||||
SQL("PARTITION BY (year)").
|
||||
SQL("AS").
|
||||
SQL(
|
||||
Select("columns[0] id", "columns[1] name", "columns[2] year").
|
||||
From("`all-users.csv`").
|
||||
Limit(100).
|
||||
String(),
|
||||
).
|
||||
String()
|
||||
// Build a SQL to create a HIVE table using MySQL-like SQL syntax.
|
||||
sql, args := Select("columns[0] id", "columns[1] name", "columns[2] year").
|
||||
From(MySQL.Quote("all-users.csv")).
|
||||
Limit(100).
|
||||
Build()
|
||||
|
||||
fmt.Println(s)
|
||||
fmt.Println(sql)
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// CREATE TABLE users PARTITION BY (year) AS SELECT columns[0] id, columns[1] name, columns[2] year FROM `all-users.csv` LIMIT 100
|
||||
// SELECT columns[0] id, columns[1] name, columns[2] year FROM `all-users.csv` LIMIT ?
|
||||
// [100]
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder() {
|
||||
@@ -56,8 +52,8 @@ func ExampleSelectBuilder() {
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// SELECT DISTINCT id, name, COUNT(*) AS t FROM demo.user WHERE id > ? AND name LIKE ? AND (id_card IS NULL OR status IN (?, ?, ?)) AND id NOT IN (SELECT id FROM banned) AND modified_at > created_at + ? GROUP BY status HAVING status NOT IN (?, ?) ORDER BY modified_at ASC LIMIT 10 OFFSET 5
|
||||
// [1234 %Du 1 2 5 86400 4 5]
|
||||
// SELECT DISTINCT id, name, COUNT(*) AS t FROM demo.user WHERE id > ? AND name LIKE ? AND (id_card IS NULL OR status IN (?, ?, ?)) AND id NOT IN (SELECT id FROM banned) AND modified_at > created_at + ? GROUP BY status HAVING status NOT IN (?, ?) ORDER BY modified_at ASC LIMIT ? OFFSET ?
|
||||
// [1234 %Du 1 2 5 86400 4 5 10 5]
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder_advancedUsage() {
|
||||
@@ -191,65 +187,65 @@ func ExampleSelectBuilder_limit_offset() {
|
||||
// MySQL
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user
|
||||
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT 1 OFFSET 1
|
||||
// #3: SELECT * FROM user LIMIT ? OFFSET ?
|
||||
// #4: SELECT * FROM user LIMIT ?
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT ? OFFSET ?
|
||||
//
|
||||
// PostgreSQL
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user OFFSET 0
|
||||
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT 1 OFFSET 1
|
||||
// #2: SELECT * FROM user OFFSET $1
|
||||
// #3: SELECT * FROM user LIMIT $1 OFFSET $2
|
||||
// #4: SELECT * FROM user LIMIT $1
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT $1 OFFSET $2
|
||||
//
|
||||
// SQLite
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user
|
||||
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT 1 OFFSET 1
|
||||
// #3: SELECT * FROM user LIMIT ? OFFSET ?
|
||||
// #4: SELECT * FROM user LIMIT ?
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT ? OFFSET ?
|
||||
//
|
||||
// SQLServer
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS
|
||||
// #3: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
||||
// #4: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
|
||||
// #5: SELECT * FROM user ORDER BY id OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
|
||||
// #2: SELECT * FROM user ORDER BY 1 OFFSET @p1 ROWS
|
||||
// #3: SELECT * FROM user ORDER BY 1 OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY
|
||||
// #4: SELECT * FROM user ORDER BY 1 OFFSET 0 ROWS FETCH NEXT @p1 ROWS ONLY
|
||||
// #5: SELECT * FROM user ORDER BY id OFFSET @p1 ROWS FETCH NEXT @p2 ROWS ONLY
|
||||
//
|
||||
// CQL
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user
|
||||
// #3: SELECT * FROM user LIMIT 1
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT 1
|
||||
// #3: SELECT * FROM user LIMIT ?
|
||||
// #4: SELECT * FROM user LIMIT ?
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT ?
|
||||
//
|
||||
// ClickHouse
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user
|
||||
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT 1 OFFSET 1
|
||||
// #3: SELECT * FROM user LIMIT ? OFFSET ?
|
||||
// #4: SELECT * FROM user LIMIT ?
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT ? OFFSET ?
|
||||
//
|
||||
// Presto
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user OFFSET 0
|
||||
// #3: SELECT * FROM user LIMIT 1 OFFSET 0
|
||||
// #4: SELECT * FROM user LIMIT 1
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT 1 OFFSET 1
|
||||
// #2: SELECT * FROM user OFFSET ?
|
||||
// #3: SELECT * FROM user LIMIT ? OFFSET ?
|
||||
// #4: SELECT * FROM user LIMIT ?
|
||||
// #5: SELECT * FROM user ORDER BY id LIMIT ? OFFSET ?
|
||||
//
|
||||
// Oracle
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM ( SELECT ROWNUM r, * FROM ( SELECT * FROM user ) user ) WHERE r >= 1
|
||||
// #3: SELECT * FROM ( SELECT ROWNUM r, * FROM ( SELECT * FROM user ) user ) WHERE r BETWEEN 1 AND 1
|
||||
// #4: SELECT * FROM ( SELECT ROWNUM r, * FROM ( SELECT * FROM user ) user ) WHERE r BETWEEN 1 AND 1
|
||||
// #5: SELECT * FROM ( SELECT ROWNUM r, * FROM ( SELECT * FROM user ORDER BY id ) user ) WHERE r BETWEEN 2 AND 2
|
||||
// #2: SELECT * FROM (SELECT ROWNUM r, * FROM (SELECT * FROM user) user) WHERE r >= :1 + 1
|
||||
// #3: SELECT * FROM (SELECT ROWNUM r, * FROM (SELECT * FROM user) user) WHERE r BETWEEN :1 + 1 AND :2 + :3
|
||||
// #4: SELECT * FROM (SELECT ROWNUM r, * FROM (SELECT * FROM user) user) WHERE r BETWEEN 1 AND :1 + 1
|
||||
// #5: SELECT * FROM (SELECT ROWNUM r, * FROM (SELECT * FROM user ORDER BY id) user) WHERE r BETWEEN :1 + 1 AND :2 + :3
|
||||
//
|
||||
// Informix
|
||||
// #1: SELECT * FROM user
|
||||
// #2: SELECT * FROM user
|
||||
// #3: SELECT * FROM user SKIP 0 FIRST 1
|
||||
// #4: SELECT * FROM user FIRST 1
|
||||
// #5: SELECT * FROM user ORDER BY id SKIP 1 FIRST 1
|
||||
// #3: SELECT * FROM user SKIP ? FIRST ?
|
||||
// #4: SELECT * FROM user FIRST ?
|
||||
// #5: SELECT * FROM user ORDER BY id SKIP ? FIRST ?
|
||||
}
|
||||
|
||||
func ExampleSelectBuilder_ForUpdate() {
|
||||
@@ -314,7 +310,7 @@ func ExampleSelectBuilder_SQL() {
|
||||
fmt.Println(s)
|
||||
|
||||
// Output:
|
||||
// /* before */ SELECT u.id, u.name, c.type, p.nickname /* after select */ FROM user u /* after from */ JOIN contract c ON u.id = c.user_id RIGHT OUTER JOIN person p ON u.id = p.user_id /* after join */ WHERE u.modified_at > u.created_at /* after where */ ORDER BY id /* after order by */ LIMIT 10 /* after limit */ FOR SHARE /* after for */
|
||||
// /* before */ SELECT u.id, u.name, c.type, p.nickname /* after select */ FROM user u /* after from */ JOIN contract c ON u.id = c.user_id RIGHT OUTER JOIN person p ON u.id = p.user_id /* after join */ WHERE u.modified_at > u.created_at /* after where */ ORDER BY id /* after order by */ LIMIT ? /* after limit */ FOR SHARE /* after for */
|
||||
}
|
||||
|
||||
// Example for issue #115.
|
||||
@@ -365,7 +361,7 @@ func ExampleSelectBuilder_With() {
|
||||
fmt.Println(sql)
|
||||
|
||||
// Output:
|
||||
// WITH users AS (SELECT id, name FROM users WHERE prime IS NOT NULL), orders AS (SELECT id, user_id FROM orders) SELECT orders.id FROM orders JOIN users ON orders.user_id = users.id LIMIT 10
|
||||
// WITH users AS (SELECT id, name FROM users WHERE prime IS NOT NULL), orders AS (SELECT id, user_id FROM orders) SELECT orders.id FROM orders JOIN users ON orders.user_id = users.id LIMIT ?
|
||||
}
|
||||
|
||||
func TestSelectBuilderSelectMore(t *testing.T) {
|
||||
@@ -412,5 +408,5 @@ func ExampleSelectBuilder_LateralAs() {
|
||||
fmt.Println(sb)
|
||||
|
||||
// Output:
|
||||
// SELECT salesperson.name, max_sale.amount, max_sale.customer_name FROM salesperson, LATERAL (SELECT amount, customer_name FROM all_sales WHERE all_sales.salesperson_id = salesperson.id ORDER BY amount DESC LIMIT 1) AS max_sale
|
||||
// SELECT salesperson.name, max_sale.amount, max_sale.customer_name FROM salesperson, LATERAL (SELECT amount, customer_name FROM all_sales WHERE all_sales.salesperson_id = salesperson.id ORDER BY amount DESC LIMIT ?) AS max_sale
|
||||
}
|
||||
|
||||
+1
-1
@@ -1036,6 +1036,6 @@ func ExampleFieldMapperFunc() {
|
||||
fmt.Println(sql1 == sql2)
|
||||
|
||||
// Output:
|
||||
// SELECT orders.id, orders.user_id, orders.product_name, orders.status, orders.user_addr_line1, orders.user_addr_line2, orders.created_at FROM orders LIMIT 10
|
||||
// SELECT orders.id, orders.user_id, orders.product_name, orders.status, orders.user_addr_line1, orders.user_addr_line2, orders.created_at FROM orders LIMIT ?
|
||||
// true
|
||||
}
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
|
||||
package sqlbuilder
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
unionDistinct = " UNION " // Default union type is DISTINCT.
|
||||
unionAll = " UNION ALL "
|
||||
@@ -26,9 +22,6 @@ func NewUnionBuilder() *UnionBuilder {
|
||||
|
||||
func newUnionBuilder() *UnionBuilder {
|
||||
return &UnionBuilder{
|
||||
limit: -1,
|
||||
offset: -1,
|
||||
|
||||
args: &Args{},
|
||||
injection: newInjection(),
|
||||
}
|
||||
@@ -40,8 +33,8 @@ type UnionBuilder struct {
|
||||
builderVars []string
|
||||
orderByCols []string
|
||||
order string
|
||||
limit int
|
||||
offset int
|
||||
limitVar string
|
||||
offsetVar string
|
||||
|
||||
args *Args
|
||||
|
||||
@@ -107,14 +100,24 @@ func (ub *UnionBuilder) Desc() *UnionBuilder {
|
||||
|
||||
// Limit sets the LIMIT in SELECT.
|
||||
func (ub *UnionBuilder) Limit(limit int) *UnionBuilder {
|
||||
ub.limit = limit
|
||||
if limit < 0 {
|
||||
ub.limitVar = ""
|
||||
return ub
|
||||
}
|
||||
|
||||
ub.limitVar = ub.Var(limit)
|
||||
ub.marker = unionMarkerAfterLimit
|
||||
return ub
|
||||
}
|
||||
|
||||
// Offset sets the LIMIT offset in SELECT.
|
||||
func (ub *UnionBuilder) Offset(offset int) *UnionBuilder {
|
||||
ub.offset = offset
|
||||
if offset < 0 {
|
||||
ub.offsetVar = ""
|
||||
return ub
|
||||
}
|
||||
|
||||
ub.offsetVar = ub.Var(offset)
|
||||
ub.marker = unionMarkerAfterLimit
|
||||
return ub
|
||||
}
|
||||
@@ -177,20 +180,20 @@ func (ub *UnionBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}
|
||||
ub.injection.WriteTo(buf, unionMarkerAfterOrderBy)
|
||||
}
|
||||
|
||||
if ub.limit >= 0 {
|
||||
if len(ub.limitVar) > 0 {
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(ub.limit))
|
||||
buf.WriteString(ub.limitVar)
|
||||
|
||||
}
|
||||
|
||||
if ((MySQL == flavor || Informix == flavor) && ub.limit >= 0) || PostgreSQL == flavor {
|
||||
if ub.offset >= 0 {
|
||||
if ((MySQL == flavor || Informix == flavor) && len(ub.limitVar) > 0) || PostgreSQL == flavor {
|
||||
if len(ub.offsetVar) > 0 {
|
||||
buf.WriteLeadingString("OFFSET ")
|
||||
buf.WriteString(strconv.Itoa(ub.offset))
|
||||
buf.WriteString(ub.offsetVar)
|
||||
}
|
||||
}
|
||||
|
||||
if ub.limit >= 0 {
|
||||
if len(ub.limitVar) > 0 {
|
||||
ub.injection.WriteTo(buf, unionMarkerAfterLimit)
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -54,8 +54,8 @@ func ExampleUnionAll() {
|
||||
fmt.Println(args)
|
||||
|
||||
// Output:
|
||||
// (SELECT id, name, created_at FROM demo.user WHERE id > ?) UNION ALL (TABLE demo.user_profile) ORDER BY created_at ASC LIMIT 100 OFFSET 5
|
||||
// [1234]
|
||||
// (SELECT id, name, created_at FROM demo.user WHERE id > ?) UNION ALL (TABLE demo.user_profile) ORDER BY created_at ASC LIMIT ? OFFSET ?
|
||||
// [1234 100 5]
|
||||
}
|
||||
|
||||
func ExampleUnionBuilder_SQL() {
|
||||
@@ -80,7 +80,7 @@ func ExampleUnionBuilder_SQL() {
|
||||
fmt.Println(sql)
|
||||
|
||||
// Output:
|
||||
// /* before */ (SELECT id, name, created_at FROM demo.user) UNION (SELECT id, avatar FROM demo.user_profile) /* after union */ ORDER BY created_at DESC /* after order by */ LIMIT 100 OFFSET 5 /* after limit */
|
||||
// /* before */ (SELECT id, name, created_at FROM demo.user) UNION (SELECT id, avatar FROM demo.user_profile) /* after union */ ORDER BY created_at DESC /* after order by */ LIMIT ? OFFSET ? /* after limit */
|
||||
}
|
||||
|
||||
func TestUnionForSQLite(t *testing.T) {
|
||||
|
||||
@@ -5,7 +5,6 @@ package sqlbuilder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,7 +32,6 @@ func newUpdateBuilder() *UpdateBuilder {
|
||||
Cond: Cond{
|
||||
Args: args,
|
||||
},
|
||||
limit: -1,
|
||||
args: args,
|
||||
injection: newInjection(),
|
||||
}
|
||||
@@ -54,7 +52,7 @@ type UpdateBuilder struct {
|
||||
assignments []string
|
||||
orderByCols []string
|
||||
order string
|
||||
limit int
|
||||
limitVar string
|
||||
|
||||
args *Args
|
||||
|
||||
@@ -208,7 +206,12 @@ func (ub *UpdateBuilder) Desc() *UpdateBuilder {
|
||||
|
||||
// Limit sets the LIMIT in UPDATE.
|
||||
func (ub *UpdateBuilder) Limit(limit int) *UpdateBuilder {
|
||||
ub.limit = limit
|
||||
if limit < 0 {
|
||||
ub.limitVar = ""
|
||||
return ub
|
||||
}
|
||||
|
||||
ub.limitVar = ub.Var(limit)
|
||||
ub.marker = updateMarkerAfterLimit
|
||||
return ub
|
||||
}
|
||||
@@ -300,9 +303,9 @@ func (ub *UpdateBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
|
||||
ub.injection.WriteTo(buf, updateMarkerAfterOrderBy)
|
||||
}
|
||||
|
||||
if ub.limit >= 0 {
|
||||
if len(ub.limitVar) > 0 {
|
||||
buf.WriteLeadingString("LIMIT ")
|
||||
buf.WriteString(strconv.Itoa(ub.limit))
|
||||
buf.WriteString(ub.limitVar)
|
||||
|
||||
ub.injection.WriteTo(buf, updateMarkerAfterLimit)
|
||||
}
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ func ExampleUpdateBuilder_SQL() {
|
||||
fmt.Println(sql)
|
||||
|
||||
// Output:
|
||||
// /* before */ UPDATE demo.user /* after update */ SET type = ? /* after set */ ORDER BY id DESC /* after order by */ LIMIT 10 /* after limit */
|
||||
// /* before */ UPDATE demo.user /* after update */ SET type = ? /* after set */ ORDER BY id DESC /* after order by */ LIMIT ? /* after limit */
|
||||
}
|
||||
|
||||
func ExampleUpdateBuilder_NumAssignment() {
|
||||
|
||||
Reference in New Issue
Block a user