Merge pull request #116 from huandu/feature-generate-sql-only-when-value-is-set

Fix #115: Generate SQL only when necessary values are set
This commit is contained in:
Huan Du
2023-07-22 23:12:40 +08:00
committed by GitHub
12 changed files with 190 additions and 108 deletions
+5 -5
View File
@@ -97,7 +97,7 @@ func (args *Args) Compile(format string, initialValue ...interface{}) (query str
//
// See doc for `Compile` to learn details.
func (args *Args) CompileWithFlavor(format string, flavor Flavor, initialValue ...interface{}) (query string, values []interface{}) {
buf := &strings.Builder{}
buf := newStringBuilder()
idx := strings.IndexRune(format, '$')
offset := 0
values = initialValue
@@ -160,7 +160,7 @@ func (args *Args) CompileWithFlavor(format string, flavor Flavor, initialValue .
return
}
func (args *Args) compileNamed(buf *strings.Builder, flavor Flavor, format string, values []interface{}) (string, []interface{}) {
func (args *Args) compileNamed(buf *stringBuilder, flavor Flavor, format string, values []interface{}) (string, []interface{}) {
i := 1
for ; i < len(format) && format[i] != '}'; i++ {
@@ -182,7 +182,7 @@ func (args *Args) compileNamed(buf *strings.Builder, flavor Flavor, format strin
return format, values
}
func (args *Args) compileDigits(buf *strings.Builder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
func (args *Args) compileDigits(buf *stringBuilder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
i := 1
for ; i < len(format) && '0' <= format[i] && format[i] <= '9'; i++ {
@@ -199,7 +199,7 @@ func (args *Args) compileDigits(buf *strings.Builder, flavor Flavor, format stri
return format, values, offset
}
func (args *Args) compileSuccessive(buf *strings.Builder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
func (args *Args) compileSuccessive(buf *stringBuilder, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
if offset >= len(args.args) {
return format, values, offset
}
@@ -210,7 +210,7 @@ func (args *Args) compileSuccessive(buf *strings.Builder, flavor Flavor, format
return format, values, offset + 1
}
func (args *Args) compileArg(buf *strings.Builder, flavor Flavor, values []interface{}, arg interface{}) []interface{} {
func (args *Args) compileArg(buf *stringBuilder, flavor Flavor, values []interface{}, arg interface{}) []interface{} {
switch a := arg.(type) {
case Builder:
var s string
+21 -21
View File
@@ -14,7 +14,7 @@ type Cond struct {
// Equal represents "field = value".
func (c *Cond) Equal(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" = ")
buf.WriteString(c.Args.Add(value))
@@ -28,7 +28,7 @@ func (c *Cond) E(field string, value interface{}) string {
// NotEqual represents "field <> value".
func (c *Cond) NotEqual(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" <> ")
buf.WriteString(c.Args.Add(value))
@@ -42,7 +42,7 @@ func (c *Cond) NE(field string, value interface{}) string {
// GreaterThan represents "field > value".
func (c *Cond) GreaterThan(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" > ")
buf.WriteString(c.Args.Add(value))
@@ -56,7 +56,7 @@ func (c *Cond) G(field string, value interface{}) string {
// GreaterEqualThan represents "field >= value".
func (c *Cond) GreaterEqualThan(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" >= ")
buf.WriteString(c.Args.Add(value))
@@ -70,7 +70,7 @@ func (c *Cond) GE(field string, value interface{}) string {
// LessThan represents "field < value".
func (c *Cond) LessThan(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" < ")
buf.WriteString(c.Args.Add(value))
@@ -84,7 +84,7 @@ func (c *Cond) L(field string, value interface{}) string {
// LessEqualThan represents "field <= value".
func (c *Cond) LessEqualThan(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" <= ")
buf.WriteString(c.Args.Add(value))
@@ -104,7 +104,7 @@ func (c *Cond) In(field string, value ...interface{}) string {
vs = append(vs, c.Args.Add(v))
}
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" IN (")
buf.WriteString(strings.Join(vs, ", "))
@@ -120,7 +120,7 @@ func (c *Cond) NotIn(field string, value ...interface{}) string {
vs = append(vs, c.Args.Add(v))
}
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" NOT IN (")
buf.WriteString(strings.Join(vs, ", "))
@@ -130,7 +130,7 @@ func (c *Cond) NotIn(field string, value ...interface{}) string {
// Like represents "field LIKE value".
func (c *Cond) Like(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" LIKE ")
buf.WriteString(c.Args.Add(value))
@@ -139,7 +139,7 @@ func (c *Cond) Like(field string, value interface{}) string {
// NotLike represents "field NOT LIKE value".
func (c *Cond) NotLike(field string, value interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" NOT LIKE ")
buf.WriteString(c.Args.Add(value))
@@ -148,7 +148,7 @@ func (c *Cond) NotLike(field string, value interface{}) string {
// IsNull represents "field IS NULL".
func (c *Cond) IsNull(field string) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" IS NULL")
return buf.String()
@@ -156,7 +156,7 @@ func (c *Cond) IsNull(field string) string {
// IsNotNull represents "field IS NOT NULL".
func (c *Cond) IsNotNull(field string) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" IS NOT NULL")
return buf.String()
@@ -164,7 +164,7 @@ func (c *Cond) IsNotNull(field string) string {
// Between represents "field BETWEEN lower AND upper".
func (c *Cond) Between(field string, lower, upper interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" BETWEEN ")
buf.WriteString(c.Args.Add(lower))
@@ -175,7 +175,7 @@ func (c *Cond) Between(field string, lower, upper interface{}) string {
// NotBetween represents "field NOT BETWEEN lower AND upper".
func (c *Cond) NotBetween(field string, lower, upper interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" NOT BETWEEN ")
buf.WriteString(c.Args.Add(lower))
@@ -186,7 +186,7 @@ func (c *Cond) NotBetween(field string, lower, upper interface{}) string {
// Or represents OR logic like "expr1 OR expr2 OR expr3".
func (c *Cond) Or(orExpr ...string) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString("(")
buf.WriteString(strings.Join(orExpr, " OR "))
buf.WriteString(")")
@@ -195,7 +195,7 @@ func (c *Cond) Or(orExpr ...string) string {
// And represents AND logic like "expr1 AND expr2 AND expr3".
func (c *Cond) And(andExpr ...string) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString("(")
buf.WriteString(strings.Join(andExpr, " AND "))
buf.WriteString(")")
@@ -204,7 +204,7 @@ func (c *Cond) And(andExpr ...string) string {
// Exists represents "EXISTS (subquery)".
func (c *Cond) Exists(subquery interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString("EXISTS (")
buf.WriteString(c.Args.Add(subquery))
buf.WriteString(")")
@@ -213,7 +213,7 @@ func (c *Cond) Exists(subquery interface{}) string {
// NotExists represents "NOT EXISTS (subquery)".
func (c *Cond) NotExists(subquery interface{}) string {
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString("NOT EXISTS (")
buf.WriteString(c.Args.Add(subquery))
buf.WriteString(")")
@@ -228,7 +228,7 @@ func (c *Cond) Any(field, op string, value ...interface{}) string {
vs = append(vs, c.Args.Add(v))
}
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" ")
buf.WriteString(op)
@@ -246,7 +246,7 @@ func (c *Cond) All(field, op string, value ...interface{}) string {
vs = append(vs, c.Args.Add(v))
}
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" ")
buf.WriteString(op)
@@ -264,7 +264,7 @@ func (c *Cond) Some(field, op string, value ...interface{}) string {
vs = append(vs, c.Args.Add(v))
}
buf := &strings.Builder{}
buf := newStringBuilder()
buf.WriteString(Escape(field))
buf.WriteString(" ")
buf.WriteString(op)
+12 -9
View File
@@ -100,20 +100,25 @@ func (ctb *CreateTableBuilder) Build() (sql string, args []interface{}) {
// BuildWithFlavor returns compiled CREATE TABLE string and args with flavor and initial args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (ctb *CreateTableBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := &strings.Builder{}
buf := newStringBuilder()
ctb.injection.WriteTo(buf, createTableMarkerInit)
buf.WriteString(ctb.verb)
if len(ctb.verb) > 0 {
buf.WriteLeadingString(ctb.verb)
}
if ctb.ifNotExists {
buf.WriteString(" IF NOT EXISTS")
buf.WriteLeadingString("IF NOT EXISTS")
}
if len(ctb.table) > 0 {
buf.WriteLeadingString(ctb.table)
}
buf.WriteRune(' ')
buf.WriteString(ctb.table)
ctb.injection.WriteTo(buf, createTableMarkerAfterCreate)
if len(ctb.defs) > 0 {
buf.WriteString(" (")
buf.WriteLeadingString("(")
defs := make([]string, 0, len(ctb.defs))
@@ -128,15 +133,13 @@ func (ctb *CreateTableBuilder) BuildWithFlavor(flavor Flavor, initialArg ...inte
}
if len(ctb.options) > 0 {
buf.WriteRune(' ')
opts := make([]string, 0, len(ctb.options))
for _, opt := range ctb.options {
opts = append(opts, strings.Join(opt, " "))
}
buf.WriteString(strings.Join(opts, ", "))
buf.WriteLeadingString(strings.Join(opts, ", "))
ctb.injection.WriteTo(buf, createTableMarkerAfterOption)
}
+10 -6
View File
@@ -113,21 +113,25 @@ func (db *DeleteBuilder) Build() (sql string, args []interface{}) {
// BuildWithFlavor returns compiled DELETE string and args with flavor and initial args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := &strings.Builder{}
buf := newStringBuilder()
db.injection.WriteTo(buf, deleteMarkerInit)
buf.WriteString("DELETE FROM ")
buf.WriteString(db.table)
if len(db.table) > 0 {
buf.WriteLeadingString("DELETE FROM ")
buf.WriteString(db.table)
}
db.injection.WriteTo(buf, deleteMarkerAfterDeleteFrom)
if len(db.whereExprs) > 0 {
buf.WriteString(" WHERE ")
buf.WriteLeadingString("WHERE ")
buf.WriteString(strings.Join(db.whereExprs, " AND "))
db.injection.WriteTo(buf, deleteMarkerAfterWhere)
}
if len(db.orderByCols) > 0 {
buf.WriteString(" ORDER BY ")
buf.WriteLeadingString("ORDER BY ")
buf.WriteString(strings.Join(db.orderByCols, ", "))
if db.order != "" {
@@ -139,7 +143,7 @@ func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
}
if db.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteLeadingString("LIMIT ")
buf.WriteString(strconv.Itoa(db.limit))
db.injection.WriteTo(buf, deleteMarkerAfterLimit)
+2 -11
View File
@@ -29,22 +29,13 @@ func (injection *injection) SQL(marker injectionMarker, sql string) {
// WriteTo joins all SQL strings at the same marker value with blank (" ")
// and writes the joined value to buf.
func (injection *injection) WriteTo(buf *strings.Builder, marker injectionMarker) {
func (injection *injection) WriteTo(buf *stringBuilder, marker injectionMarker) {
sqls := injection.markerSQLs[marker]
notEmpty := buf.Len() > 0
if len(sqls) == 0 {
return
}
if notEmpty {
buf.WriteRune(' ')
}
s := strings.Join(sqls, " ")
buf.WriteString(s)
if !notEmpty {
buf.WriteRune(' ')
}
buf.WriteLeadingString(s)
}
+17 -10
View File
@@ -117,29 +117,36 @@ func (ib *InsertBuilder) Build() (sql string, args []interface{}) {
// BuildWithFlavor returns compiled INSERT string and args with flavor and initial args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (ib *InsertBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := &strings.Builder{}
buf := newStringBuilder()
ib.injection.WriteTo(buf, insertMarkerInit)
buf.WriteString(ib.verb)
buf.WriteString(" INTO ")
buf.WriteString(ib.table)
if len(ib.table) > 0 {
buf.WriteLeadingString(ib.verb)
buf.WriteString(" INTO ")
buf.WriteString(ib.table)
}
ib.injection.WriteTo(buf, insertMarkerAfterInsertInto)
if len(ib.cols) > 0 {
buf.WriteString(" (")
buf.WriteLeadingString("(")
buf.WriteString(strings.Join(ib.cols, ", "))
buf.WriteString(")")
ib.injection.WriteTo(buf, insertMarkerAfterCols)
}
buf.WriteString(" VALUES ")
values := make([]string, 0, len(ib.values))
if len(ib.values) > 0 {
buf.WriteLeadingString("VALUES ")
values := make([]string, 0, len(ib.values))
for _, v := range ib.values {
values = append(values, fmt.Sprintf("(%v)", strings.Join(v, ", ")))
for _, v := range ib.values {
values = append(values, fmt.Sprintf("(%v)", strings.Join(v, ", ")))
}
buf.WriteString(strings.Join(values, ", "))
}
buf.WriteString(strings.Join(values, ", "))
ib.injection.WriteTo(buf, insertMarkerAfterValues)
return ib.args.CompileWithFlavor(buf.String(), flavor, initialArg...)
+29 -24
View File
@@ -234,35 +234,40 @@ func (sb *SelectBuilder) Build() (sql string, args []interface{}) {
// BuildWithFlavor returns compiled SELECT string and args with flavor and initial args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := &strings.Builder{}
buf := newStringBuilder()
sb.injection.WriteTo(buf, selectMarkerInit)
buf.WriteString("SELECT ")
if sb.distinct {
buf.WriteString("DISTINCT ")
if len(sb.selectCols) > 0 {
buf.WriteLeadingString("SELECT ")
if sb.distinct {
buf.WriteString("DISTINCT ")
}
buf.WriteString(strings.Join(sb.selectCols, ", "))
}
buf.WriteString(strings.Join(sb.selectCols, ", "))
sb.injection.WriteTo(buf, selectMarkerAfterSelect)
buf.WriteString(" FROM ")
buf.WriteString(strings.Join(sb.tables, ", "))
if len(sb.tables) > 0 {
buf.WriteLeadingString("FROM ")
buf.WriteString(strings.Join(sb.tables, ", "))
}
sb.injection.WriteTo(buf, selectMarkerAfterFrom)
for i := range sb.joinTables {
if option := sb.joinOptions[i]; option != "" {
buf.WriteRune(' ')
buf.WriteString(string(option))
buf.WriteLeadingString(string(option))
}
buf.WriteString(" JOIN ")
buf.WriteLeadingString("JOIN ")
buf.WriteString(sb.joinTables[i])
if exprs := sb.joinExprs[i]; len(exprs) > 0 {
buf.WriteString(" ON ")
buf.WriteString(strings.Join(sb.joinExprs[i], " AND "))
}
}
if len(sb.joinTables) > 0 {
@@ -270,14 +275,14 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
}
if len(sb.whereExprs) > 0 {
buf.WriteString(" WHERE ")
buf.WriteLeadingString("WHERE ")
buf.WriteString(strings.Join(sb.whereExprs, " AND "))
sb.injection.WriteTo(buf, selectMarkerAfterWhere)
}
if len(sb.groupByCols) > 0 {
buf.WriteString(" GROUP BY ")
buf.WriteLeadingString("GROUP BY ")
buf.WriteString(strings.Join(sb.groupByCols, ", "))
if len(sb.havingExprs) > 0 {
@@ -289,7 +294,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
}
if len(sb.orderByCols) > 0 {
buf.WriteString(" ORDER BY ")
buf.WriteLeadingString("ORDER BY ")
buf.WriteString(strings.Join(sb.orderByCols, ", "))
if sb.order != "" {
@@ -303,27 +308,27 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
switch flavor {
case MySQL, SQLite, ClickHouse:
if sb.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteLeadingString("LIMIT ")
buf.WriteString(strconv.Itoa(sb.limit))
if sb.offset >= 0 {
buf.WriteString(" OFFSET ")
buf.WriteLeadingString("OFFSET ")
buf.WriteString(strconv.Itoa(sb.offset))
}
}
case CQL:
if sb.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteLeadingString("LIMIT ")
buf.WriteString(strconv.Itoa(sb.limit))
}
case PostgreSQL, Presto:
if sb.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteLeadingString("LIMIT ")
buf.WriteString(strconv.Itoa(sb.limit))
}
if sb.offset >= 0 {
buf.WriteString(" OFFSET ")
buf.WriteLeadingString("OFFSET ")
buf.WriteString(strconv.Itoa(sb.offset))
}
@@ -331,21 +336,21 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
// 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) {
buf.WriteString(" ORDER BY 1")
buf.WriteLeadingString("ORDER BY 1")
}
if sb.offset >= 0 {
buf.WriteString(" OFFSET ")
buf.WriteLeadingString("OFFSET ")
buf.WriteString(strconv.Itoa(sb.offset))
buf.WriteString(" ROWS")
}
if sb.limit >= 0 {
if sb.offset < 0 {
buf.WriteString(" OFFSET 0 ROWS")
buf.WriteLeadingString("OFFSET 0 ROWS")
}
buf.WriteString(" FETCH NEXT ")
buf.WriteLeadingString("FETCH NEXT ")
buf.WriteString(strconv.Itoa(sb.limit))
buf.WriteString(" ROWS ONLY")
}
@@ -356,7 +361,7 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
}
if sb.forWhat != "" {
buf.WriteString(" FOR ")
buf.WriteLeadingString("FOR ")
buf.WriteString(sb.forWhat)
sb.injection.WriteTo(buf, selectMarkerAfterFor)
+18
View File
@@ -275,3 +275,21 @@ func ExampleSelectBuilder_SQL() {
// 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 */
}
// Example for issue #115.
func ExampleSelectBuilder_customSELECT() {
sb := NewSelectBuilder()
// Set a custom SELECT clause.
sb.SQL("SELECT id, name FROM user").Where(
sb.In("id", 1, 2, 3),
)
s, args := sb.Build()
fmt.Println(s)
fmt.Println(args)
// Output:
// SELECT id, name FROM user WHERE id IN (?, ?, ?)
// [1 2 3]
}
+51
View File
@@ -0,0 +1,51 @@
// Copyright 2023 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
import (
"io"
"strings"
)
type stringBuilder struct {
builder *strings.Builder
}
var _ io.Writer = new(stringBuilder)
func newStringBuilder() *stringBuilder {
return &stringBuilder{
builder: &strings.Builder{},
}
}
// WriteLeadingString writes s to internal buffer.
// If it's not the first time to write the string, a blank (" ") will be written before s.
func (sb *stringBuilder) WriteLeadingString(s string) {
if sb.builder.Len() > 0 {
sb.builder.WriteString(" ")
}
sb.builder.WriteString(s)
}
func (sb *stringBuilder) WriteString(s string) {
sb.builder.WriteString(s)
}
func (sb *stringBuilder) WriteRune(r rune) {
sb.builder.WriteRune(r)
}
func (sb *stringBuilder) Write(data []byte) (int, error) {
return sb.builder.Write(data)
}
func (sb *stringBuilder) String() string {
return sb.builder.String()
}
func (sb *stringBuilder) Reset() {
sb.builder.Reset()
}
+1 -2
View File
@@ -9,7 +9,6 @@ import (
"reflect"
"regexp"
"sort"
"strings"
)
var (
@@ -305,7 +304,7 @@ func (s *Struct) selectFromWithTags(table string, with, without []string) (sb *S
return
}
buf := &strings.Builder{}
buf := newStringBuilder()
cols := make([]string, 0, len(tagged.ForRead))
for _, sf := range tagged.ForRead {
+8 -10
View File
@@ -129,20 +129,18 @@ func (ub *UnionBuilder) Build() (sql string, args []interface{}) {
// BuildWithFlavor returns compiled SELECT string and args with flavor and initial args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (ub *UnionBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := &strings.Builder{}
buf := newStringBuilder()
ub.injection.WriteTo(buf, unionMarkerInit)
if len(ub.builders) > 0 {
needParen := flavor != SQLite
if needParen {
buf.WriteRune('(')
}
buf.WriteString(ub.Var(ub.builders[0]))
if needParen {
buf.WriteLeadingString("(")
buf.WriteString(ub.Var(ub.builders[0]))
buf.WriteRune(')')
} else {
buf.WriteLeadingString(ub.Var(ub.builders[0]))
}
for _, b := range ub.builders[1:] {
@@ -163,7 +161,7 @@ func (ub *UnionBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}
ub.injection.WriteTo(buf, unionMarkerAfterUnion)
if len(ub.orderByCols) > 0 {
buf.WriteString(" ORDER BY ")
buf.WriteLeadingString("ORDER BY ")
buf.WriteString(strings.Join(ub.orderByCols, ", "))
if ub.order != "" {
@@ -175,14 +173,14 @@ func (ub *UnionBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}
}
if ub.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteLeadingString("LIMIT ")
buf.WriteString(strconv.Itoa(ub.limit))
}
if MySQL == flavor && ub.limit >= 0 || PostgreSQL == flavor {
if ub.offset >= 0 {
buf.WriteString(" OFFSET ")
buf.WriteLeadingString("OFFSET ")
buf.WriteString(strconv.Itoa(ub.offset))
}
}
+16 -10
View File
@@ -171,36 +171,42 @@ func (ub *UpdateBuilder) Build() (sql string, args []interface{}) {
// BuildWithFlavor returns compiled UPDATE string and args with flavor and initial args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (ub *UpdateBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := &strings.Builder{}
buf := newStringBuilder()
ub.injection.WriteTo(buf, updateMarkerInit)
buf.WriteString("UPDATE ")
buf.WriteString(ub.table)
if len(ub.table) > 0 {
buf.WriteLeadingString("UPDATE ")
buf.WriteString(ub.table)
}
ub.injection.WriteTo(buf, updateMarkerAfterUpdate)
buf.WriteString(" SET ")
buf.WriteString(strings.Join(ub.assignments, ", "))
if len(ub.assignments) > 0 {
buf.WriteLeadingString("SET ")
buf.WriteString(strings.Join(ub.assignments, ", "))
}
ub.injection.WriteTo(buf, updateMarkerAfterSet)
if len(ub.whereExprs) > 0 {
buf.WriteString(" WHERE ")
buf.WriteLeadingString("WHERE ")
buf.WriteString(strings.Join(ub.whereExprs, " AND "))
ub.injection.WriteTo(buf, updateMarkerAfterWhere)
}
if len(ub.orderByCols) > 0 {
buf.WriteString(" ORDER BY ")
buf.WriteLeadingString("ORDER BY ")
buf.WriteString(strings.Join(ub.orderByCols, ", "))
if ub.order != "" {
buf.WriteRune(' ')
buf.WriteString(ub.order)
buf.WriteLeadingString(ub.order)
}
ub.injection.WriteTo(buf, updateMarkerAfterOrderBy)
}
if ub.limit >= 0 {
buf.WriteString(" LIMIT ")
buf.WriteLeadingString("LIMIT ")
buf.WriteString(strconv.Itoa(ub.limit))
ub.injection.WriteTo(buf, updateMarkerAfterLimit)