From 441478994b8438cf24a96aa519e84fdc11fe6ca1 Mon Sep 17 00:00:00 2001
From: Huan Du
Date: Sun, 4 Feb 2018 21:49:36 +0800
Subject: [PATCH] fix #6. support Postgre-flavor markers.
---
README.md | 14 ++++++++++++
args.go | 52 +++++++++++++++++++++++++++++-------------
args_test.go | 38 +++++++++++++++++++++++++++++++
builder.go | 59 ++++++++++++++++++++++++++++++++++--------------
builder_test.go | 11 +++++++++
delete.go | 19 +++++++++++++++-
flavor.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++
flavor_test.go | 40 +++++++++++++++++++++++++++++++++
insert.go | 19 +++++++++++++++-
select.go | 19 +++++++++++++++-
struct.go | 16 +++++++++----
struct_test.go | 15 +++++++++++++
update.go | 19 +++++++++++++++-
13 files changed, 341 insertions(+), 40 deletions(-)
create mode 100644 flavor.go
create mode 100644 flavor_test.go
diff --git a/README.md b/README.md
index 4a97d98..9382256 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,20 @@ Following builders are implemented right now. API document and examples are prov
* [Build](https://godoc.org/github.com/huandu/go-sqlbuilder#Build): Advanced freestyle builder using special syntax defined in [Args#Compile](https://godoc.org/github.com/huandu/go-sqlbuilder#Args.Compile).
* [BuildNamed](https://godoc.org/github.com/huandu/go-sqlbuilder#BuildNamed): Advanced freestyle builder using `${key}` to refer the value of a map by key.
+### Build SQL for MySQL or PostgreSQL ###
+
+Parameter markers are different in MySQL and PostgreSQL. This package provides some methods to set the type of markers (we call it "flavor") in all builders.
+
+By default, all builders uses `DefaultFlavor` to build SQL. The default value is `MySQL`.
+
+There is a `BuildWithFlavor` method in `Builder` interface. We can use it to build a SQL with provided flavor.
+
+We can wrap any `Builder` with a default flavor through `WithFlavor`.
+
+To be more verbose, we can use `PostgreSQL.NewSelectBuilder()` to create a `SelectBuilder` with the `PostgreSQL` flavor. All builders can be created in this way.
+
+Right now, there are only two flavors, `MySQL` and `PostgreSQL`. Open new issue to me to ask for a new flavor if you find it necessary.
+
### Using `Struct` as a light weight ORM ###
`Struct` stores type information and struct fields of a struct. It's a factory of builders. We can use `Struct` methods to create initialized SELECT/INSERT/UPDATE/DELETE builders to work with the struct. It can help us to save time and avoid human-error on writing column names.
diff --git a/args.go b/args.go
index 7c54167..97d6179 100644
--- a/args.go
+++ b/args.go
@@ -14,6 +14,9 @@ import (
// Args stores arguments associated with a SQL.
type Args struct {
+ // The default flavor used by `Args#Compile`
+ Flavor Flavor
+
args []interface{}
namedArgs map[string]int
sqlNamedArgs map[string]int
@@ -60,7 +63,7 @@ func (args *Args) add(arg interface{}) int {
return idx
}
-// Compile analyzes builder's format to standard sql and returns associated args.
+// Compile compiles builder's format to standard sql and returns associated args.
//
// The format string uses a special syntax to represent arguments.
//
@@ -69,10 +72,21 @@ func (args *Args) add(arg interface{}) int {
// ${name} refers a named argument created by `Named` with `name`.
// $$ is a "$" string.
func (args *Args) Compile(format string) (query string, values []interface{}) {
+ return args.CompileWithFlavor(format, args.Flavor)
+}
+
+// CompileWithFlavor compiles builder's format to standard sql with flavor and returns associated args.
+//
+// See doc for `Compile` to learn details.
+func (args *Args) CompileWithFlavor(format string, flavor Flavor) (query string, values []interface{}) {
buf := &bytes.Buffer{}
idx := strings.IndexRune(format, '$')
offset := 0
+ if flavor == invalidFlavor {
+ flavor = DefaultFlavor
+ }
+
for idx >= 0 && len(format) > 0 {
if idx > 0 {
buf.WriteString(format[:idx])
@@ -89,11 +103,11 @@ func (args *Args) Compile(format string) (query string, values []interface{}) {
buf.WriteRune('$')
format = format[1:]
} else if format[0] == '{' {
- format, values = args.compileNamed(buf, format, values)
+ format, values = args.compileNamed(buf, flavor, format, values)
} else if !args.onlyNamed && '0' <= format[0] && format[0] <= '9' {
- format, values, offset = args.compileDigits(buf, format, values, offset)
+ format, values, offset = args.compileDigits(buf, flavor, format, values, offset)
} else if !args.onlyNamed && format[0] == '?' {
- format, values, offset = args.compileSuccessive(buf, format[1:], values, offset)
+ format, values, offset = args.compileSuccessive(buf, flavor, format[1:], values, offset)
}
idx = strings.IndexRune(format, '$')
@@ -123,7 +137,7 @@ func (args *Args) Compile(format string) (query string, values []interface{}) {
return
}
-func (args *Args) compileNamed(buf *bytes.Buffer, format string, values []interface{}) (string, []interface{}) {
+func (args *Args) compileNamed(buf *bytes.Buffer, flavor Flavor, format string, values []interface{}) (string, []interface{}) {
i := 1
for ; i < len(format) && format[i] != '}'; i++ {
@@ -139,13 +153,13 @@ func (args *Args) compileNamed(buf *bytes.Buffer, format string, values []interf
format = format[i+1:]
if p, ok := args.namedArgs[name]; ok {
- format, values, _ = args.compileSuccessive(buf, format, values, p)
+ format, values, _ = args.compileSuccessive(buf, flavor, format, values, p)
}
return format, values
}
-func (args *Args) compileDigits(buf *bytes.Buffer, format string, values []interface{}, offset int) (string, []interface{}, int) {
+func (args *Args) compileDigits(buf *bytes.Buffer, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
i := 1
for ; i < len(format) && '0' <= format[i] && format[i] <= '9'; i++ {
@@ -156,27 +170,27 @@ func (args *Args) compileDigits(buf *bytes.Buffer, format string, values []inter
format = format[i:]
if pointer, err := strconv.Atoi(digits); err == nil {
- return args.compileSuccessive(buf, format, values, pointer)
+ return args.compileSuccessive(buf, flavor, format, values, pointer)
}
return format, values, offset
}
-func (args *Args) compileSuccessive(buf *bytes.Buffer, format string, values []interface{}, offset int) (string, []interface{}, int) {
+func (args *Args) compileSuccessive(buf *bytes.Buffer, flavor Flavor, format string, values []interface{}, offset int) (string, []interface{}, int) {
if offset >= len(args.args) {
return format, values, offset
}
arg := args.args[offset]
- values = args.compileArg(buf, values, arg)
+ values = args.compileArg(buf, flavor, values, arg)
return format, values, offset + 1
}
-func (args *Args) compileArg(buf *bytes.Buffer, values []interface{}, arg interface{}) []interface{} {
+func (args *Args) compileArg(buf *bytes.Buffer, flavor Flavor, values []interface{}, arg interface{}) []interface{} {
switch a := arg.(type) {
case Builder:
- s, nestedArgs := a.Build()
+ s, nestedArgs := a.BuildWithFlavor(flavor)
buf.WriteString(s)
values = append(values, nestedArgs...)
case sql.NamedArg:
@@ -186,15 +200,23 @@ func (args *Args) compileArg(buf *bytes.Buffer, values []interface{}, arg interf
buf.WriteString(a.expr)
case listArgs:
if len(a.args) > 0 {
- values = args.compileArg(buf, values, a.args[0])
+ values = args.compileArg(buf, flavor, values, a.args[0])
}
for i := 1; i < len(a.args); i++ {
buf.WriteString(", ")
- values = args.compileArg(buf, values, a.args[i])
+ values = args.compileArg(buf, flavor, values, a.args[i])
}
default:
- buf.WriteRune('?')
+ switch flavor {
+ case MySQL:
+ buf.WriteRune('?')
+ case PostgreSQL:
+ fmt.Fprintf(buf, "$%v", len(values)+1)
+ default:
+ panic(fmt.Errorf("Args.CompileWithFlavor: invalid flavor %v (%v)", flavor, int(flavor)))
+ }
+
values = append(values, arg)
}
diff --git a/args_test.go b/args_test.go
index 5163ecb..37cbd60 100644
--- a/args_test.go
+++ b/args_test.go
@@ -4,7 +4,9 @@
package sqlbuilder
import (
+ "bytes"
"fmt"
+ "strings"
"testing"
)
@@ -35,4 +37,40 @@ func TestArgs(t *testing.T) {
t.Fatalf("invalid compile result. [expected:%v] [actual:%v]", expected, actual)
}
}
+
+ old := DefaultFlavor
+ DefaultFlavor = PostgreSQL
+ defer func() {
+ DefaultFlavor = old
+ }()
+
+ // PostgreSQL flavor compiled sql.
+ for expected, c := range cases {
+ args := new(Args)
+
+ for i := 1; i < len(c); i++ {
+ args.Add(c[i])
+ }
+
+ sql, values := args.Compile(c[0].(string))
+ actual := fmt.Sprintf("%v\n%v", sql, values)
+ expected = toPostgreSQL(expected)
+
+ if actual != expected {
+ t.Fatalf("invalid compile result. [expected:%v] [actual:%v]", expected, actual)
+ }
+ }
+}
+
+func toPostgreSQL(sql string) string {
+ parts := strings.Split(sql, "?")
+ buf := &bytes.Buffer{}
+ buf.WriteString(parts[0])
+
+ for i, p := range parts[1:] {
+ fmt.Fprintf(buf, "$%v", i+1)
+ buf.WriteString(p)
+ }
+
+ return buf.String()
}
diff --git a/builder.go b/builder.go
index e84d078..714529f 100644
--- a/builder.go
+++ b/builder.go
@@ -12,35 +12,59 @@ import (
// `SELECT * FROM t1 WHERE id IN (SELECT id FROM t2)`.
type Builder interface {
Build() (sql string, args []interface{})
+ BuildWithFlavor(flavor Flavor) (sql string, args []interface{})
}
type compiledBuilder struct {
- sql string
- args []interface{}
+ args *Args
+ format string
}
func (cb *compiledBuilder) Build() (sql string, args []interface{}) {
- return cb.sql, cb.args
+ return cb.args.Compile(cb.format)
+}
+
+func (cb *compiledBuilder) BuildWithFlavor(flavor Flavor) (sql string, args []interface{}) {
+ return cb.args.CompileWithFlavor(cb.format, flavor)
+}
+
+type flavoredBuilder struct {
+ builder Builder
+ flavor Flavor
+}
+
+func (fb *flavoredBuilder) Build() (sql string, args []interface{}) {
+ return fb.builder.BuildWithFlavor(fb.flavor)
+}
+
+func (fb *flavoredBuilder) BuildWithFlavor(flavor Flavor) (sql string, args []interface{}) {
+ return fb.builder.BuildWithFlavor(flavor)
+}
+
+// WithFlavor creates a new Builder based on builder with a default flavor.
+func WithFlavor(builder Builder, flavor Flavor) Builder {
+ return &flavoredBuilder{
+ builder: builder,
+ flavor: flavor,
+ }
}
// Buildf creates a Builder from a format string using `fmt.Sprintf`-like syntax.
// As all arguments will be converted to a string internally, e.g. "$0",
// only `%v` and `%s` are valid.
func Buildf(format string, arg ...interface{}) Builder {
- args := &Args{}
+ args := &Args{
+ Flavor: DefaultFlavor,
+ }
vars := make([]interface{}, 0, len(arg))
for _, a := range arg {
vars = append(vars, args.Add(a))
}
- format = Escape(format)
- str := fmt.Sprintf(format, vars...)
- sql, values := args.Compile(str)
-
return &compiledBuilder{
- sql: sql,
- args: values,
+ args: args,
+ format: fmt.Sprintf(Escape(format), vars...),
}
}
@@ -48,16 +72,17 @@ func Buildf(format string, arg ...interface{}) Builder {
// The format string uses special syntax to represent arguments.
// See doc in `Args#Compile` for syntax details.
func Build(format string, arg ...interface{}) Builder {
- args := &Args{}
+ args := &Args{
+ Flavor: DefaultFlavor,
+ }
for _, a := range arg {
args.Add(a)
}
- sql, values := args.Compile(format)
return &compiledBuilder{
- sql: sql,
- args: values,
+ args: args,
+ format: format,
}
}
@@ -65,6 +90,7 @@ func Build(format string, arg ...interface{}) Builder {
// The format string uses `${key}` to refer the value of named by key.
func BuildNamed(format string, named map[string]interface{}) Builder {
args := &Args{
+ Flavor: DefaultFlavor,
onlyNamed: true,
}
@@ -72,9 +98,8 @@ func BuildNamed(format string, named map[string]interface{}) Builder {
args.Add(Named(n, v))
}
- sql, values := args.Compile(format)
return &compiledBuilder{
- sql: sql,
- args: values,
+ args: args,
+ format: format,
}
}
diff --git a/builder_test.go b/builder_test.go
index 4368b3b..a138bc3 100644
--- a/builder_test.go
+++ b/builder_test.go
@@ -55,3 +55,14 @@ func ExampleBuildNamed() {
// SELECT * FROM user WHERE status IN (?, ?, ?) AND name LIKE ? AND created_at > @start AND modified_at < @start + 86400
// [1 2 5 Huan% {{} start 1234567890}]
}
+
+func ExampleWithFlavor() {
+ sql, args := WithFlavor(Buildf("SELECT * FROM foo WHERE id = %v", 1234), PostgreSQL).Build()
+
+ fmt.Println(sql)
+ fmt.Println(args)
+
+ // Output:
+ // SELECT * FROM foo WHERE id = $1
+ // [1234]
+}
diff --git a/delete.go b/delete.go
index 3b8750c..ae49690 100644
--- a/delete.go
+++ b/delete.go
@@ -10,6 +10,10 @@ import (
// NewDeleteBuilder creates a new DELETE builder.
func NewDeleteBuilder() *DeleteBuilder {
+ return DefaultFlavor.NewDeleteBuilder()
+}
+
+func newDeleteBuilder() *DeleteBuilder {
args := &Args{}
return &DeleteBuilder{
Cond: Cond{
@@ -50,6 +54,12 @@ func (db *DeleteBuilder) String() string {
// Build returns compiled DELETE string and args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (db *DeleteBuilder) Build() (sql string, args []interface{}) {
+ return db.BuildWithFlavor(db.args.Flavor)
+}
+
+// BuildWithFlavor returns compiled DELETE string and args with flavor.
+// They can be used in `DB#Query` of package `database/sql` directly.
+func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor) (sql string, args []interface{}) {
buf := &bytes.Buffer{}
buf.WriteString("DELETE FROM ")
buf.WriteString(db.table)
@@ -59,5 +69,12 @@ func (db *DeleteBuilder) Build() (sql string, args []interface{}) {
buf.WriteString(strings.Join(db.whereExprs, " AND "))
}
- return db.args.Compile(buf.String())
+ return db.args.CompileWithFlavor(buf.String(), flavor)
+}
+
+// SetFlavor sets the flavor of compiled sql.
+func (db *DeleteBuilder) SetFlavor(flavor Flavor) (old Flavor) {
+ old = db.args.Flavor
+ db.args.Flavor = flavor
+ return
}
diff --git a/flavor.go b/flavor.go
new file mode 100644
index 0000000..9f3e5a2
--- /dev/null
+++ b/flavor.go
@@ -0,0 +1,60 @@
+// Copyright 2018 Huan Du. All rights reserved.
+// Licensed under the MIT license that can be found in the LICENSE file.
+
+package sqlbuilder
+
+// Supported flavors.
+const (
+ invalidFlavor Flavor = iota
+
+ MySQL
+ PostgreSQL
+)
+
+var (
+ // DefaultFlavor is the default flavor for all builders.
+ DefaultFlavor = MySQL
+)
+
+// Flavor is the flag to control the format of compiled sql.
+type Flavor int
+
+// String returns the name of f.
+func (f Flavor) String() string {
+ switch f {
+ case MySQL:
+ return "MySQL"
+ case PostgreSQL:
+ return "PostgreSQL"
+ }
+
+ return ""
+}
+
+// NewDeleteBuilder creates a new DELETE builder with flavor.
+func (f Flavor) NewDeleteBuilder() *DeleteBuilder {
+ b := newDeleteBuilder()
+ b.SetFlavor(f)
+ return b
+}
+
+// NewInsertBuilder creates a new INSERT builder with flavor.
+func (f Flavor) NewInsertBuilder() *InsertBuilder {
+ b := newInsertBuilder()
+ b.SetFlavor(f)
+ return b
+}
+
+// NewSelectBuilder creates a new SELECT builder with flavor.
+func (f Flavor) NewSelectBuilder() *SelectBuilder {
+ b := newSelectBuilder()
+ b.SetFlavor(f)
+ return b
+}
+
+// NewUpdateBuilder creates a new UPDATE builder with flavor.
+func (f Flavor) NewUpdateBuilder() *UpdateBuilder {
+ b := newUpdateBuilder()
+ b.SetFlavor(f)
+ return b
+}
diff --git a/flavor_test.go b/flavor_test.go
new file mode 100644
index 0000000..59ebe36
--- /dev/null
+++ b/flavor_test.go
@@ -0,0 +1,40 @@
+// Copyright 2018 Huan Du. All rights reserved.
+// Licensed under the MIT license that can be found in the LICENSE file.
+
+package sqlbuilder
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestFlavor(t *testing.T) {
+ cases := map[Flavor]string{
+ 0: "",
+ MySQL: "MySQL",
+ PostgreSQL: "PostgreSQL",
+ }
+
+ for f, expected := range cases {
+ if actual := f.String(); actual != expected {
+ t.Fatalf("invalid flavor name. [expected:%v] [actual:%v]", expected, actual)
+ }
+ }
+}
+
+func ExampleFlavor() {
+ // Create a flavored builder.
+ sb := PostgreSQL.NewSelectBuilder()
+ sb.Select("name").From("user").Where(
+ sb.E("id", 1234),
+ sb.G("rank", 3),
+ )
+ sql, args := sb.Build()
+
+ fmt.Println(sql)
+ fmt.Println(args)
+
+ // Output:
+ // SELECT name FROM user WHERE id = $1 AND rank > $2
+ // [1234 3]
+}
diff --git a/insert.go b/insert.go
index e31fd7a..ef81072 100644
--- a/insert.go
+++ b/insert.go
@@ -11,6 +11,10 @@ import (
// NewInsertBuilder creates a new INSERT builder.
func NewInsertBuilder() *InsertBuilder {
+ return DefaultFlavor.NewInsertBuilder()
+}
+
+func newInsertBuilder() *InsertBuilder {
args := &Args{}
return &InsertBuilder{
args: args,
@@ -59,6 +63,12 @@ func (ib *InsertBuilder) String() string {
// Build returns compiled INSERT string and args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (ib *InsertBuilder) Build() (sql string, args []interface{}) {
+ return ib.BuildWithFlavor(ib.args.Flavor)
+}
+
+// BuildWithFlavor returns compiled INSERT string and args.
+// They can be used in `DB#Query` of package `database/sql` directly.
+func (ib *InsertBuilder) BuildWithFlavor(flavor Flavor) (sql string, args []interface{}) {
buf := &bytes.Buffer{}
buf.WriteString("INSERT INTO ")
buf.WriteString(ib.table)
@@ -77,5 +87,12 @@ func (ib *InsertBuilder) Build() (sql string, args []interface{}) {
}
buf.WriteString(strings.Join(values, ", "))
- return ib.args.Compile(buf.String())
+ return ib.args.CompileWithFlavor(buf.String(), flavor)
+}
+
+// SetFlavor sets the flavor of compiled sql.
+func (ib *InsertBuilder) SetFlavor(flavor Flavor) (old Flavor) {
+ old = ib.args.Flavor
+ ib.args.Flavor = flavor
+ return
}
diff --git a/select.go b/select.go
index 3a0c522..10a350f 100644
--- a/select.go
+++ b/select.go
@@ -12,6 +12,10 @@ import (
// NewSelectBuilder creates a new SELECT builder.
func NewSelectBuilder() *SelectBuilder {
+ return DefaultFlavor.NewSelectBuilder()
+}
+
+func newSelectBuilder() *SelectBuilder {
args := &Args{}
return &SelectBuilder{
Cond: Cond{
@@ -127,6 +131,12 @@ func (sb *SelectBuilder) String() string {
// Build returns compiled SELECT string and args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (sb *SelectBuilder) Build() (sql string, args []interface{}) {
+ return sb.BuildWithFlavor(sb.args.Flavor)
+}
+
+// BuildWithFlavor returns compiled SELECT string and args.
+// They can be used in `DB#Query` of package `database/sql` directly.
+func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor) (sql string, args []interface{}) {
buf := &bytes.Buffer{}
buf.WriteString("SELECT ")
@@ -173,5 +183,12 @@ func (sb *SelectBuilder) Build() (sql string, args []interface{}) {
}
}
- return sb.Args.Compile(buf.String())
+ return sb.Args.CompileWithFlavor(buf.String(), flavor)
+}
+
+// SetFlavor sets the flavor of compiled sql.
+func (sb *SelectBuilder) SetFlavor(flavor Flavor) (old Flavor) {
+ old = sb.args.Flavor
+ sb.args.Flavor = flavor
+ return
}
diff --git a/struct.go b/struct.go
index 053cc5a..c341027 100644
--- a/struct.go
+++ b/struct.go
@@ -22,6 +22,8 @@ var (
// All methods in Struct are thread-safe.
// We can define a global variable to hold a Struct and use it in any goroutine.
type Struct struct {
+ Flavor Flavor
+
structType reflect.Type
fieldAlias map[string]string
taggedFields map[string][]string
@@ -46,6 +48,12 @@ func NewStruct(structValue interface{}) *Struct {
return s
}
+// For sets the default flavor of s.
+func (s *Struct) For(flavor Flavor) *Struct {
+ s.Flavor = flavor
+ return s
+}
+
func (s *Struct) parse(t reflect.Type) {
l := t.NumField()
@@ -109,7 +117,7 @@ func (s *Struct) SelectFrom(table string) *SelectBuilder {
//
// Caller is responsible to set WHERE condition to find right record.
func (s *Struct) SelectFromForTag(table string, tag string) *SelectBuilder {
- sb := NewSelectBuilder()
+ sb := s.Flavor.NewSelectBuilder()
sb.From(table)
sb.Limit(1)
@@ -143,7 +151,7 @@ func (s *Struct) Update(table string, value interface{}) *UpdateBuilder {
//
// Caller is responsible to set WHERE condition to match right record.
func (s *Struct) UpdateForTag(table string, tag string, value interface{}) *UpdateBuilder {
- ub := NewUpdateBuilder()
+ ub := s.Flavor.NewUpdateBuilder()
ub.Update(table)
if s.taggedFields == nil {
@@ -187,7 +195,7 @@ func (s *Struct) InsertInto(table string, value ...interface{}) *InsertBuilder {
// Bulk insert is supported. Item in value that is not the same as that of s will be skipped.
// If no item in value is valid, InsertIntoForTag returns a dummy `InsertBuilder` with table name.
func (s *Struct) InsertIntoForTag(table string, tag string, value ...interface{}) *InsertBuilder {
- ib := NewInsertBuilder()
+ ib := s.Flavor.NewInsertBuilder()
ib.InsertInto(table)
if s.taggedFields == nil {
@@ -235,7 +243,7 @@ func (s *Struct) InsertIntoForTag(table string, tag string, value ...interface{}
//
// Caller is responsible to set WHERE condition to match right record.
func (s *Struct) DeleteFrom(table string) *DeleteBuilder {
- db := NewDeleteBuilder()
+ db := s.Flavor.NewDeleteBuilder()
db.DeleteFrom(table)
return db
}
diff --git a/struct_test.go b/struct_test.go
index 530c099..1600c69 100644
--- a/struct_test.go
+++ b/struct_test.go
@@ -502,3 +502,18 @@ func ExampleStruct_buildDELETE() {
// DELETE FROM user WHERE id = ?
// [1234]
}
+
+func ExampleStruct_forPostgreSQL() {
+ userStruct := NewStruct(new(User)).For(PostgreSQL)
+
+ sb := userStruct.SelectFrom("user")
+ sb.Where(sb.E("id", 1234))
+ sql, args := sb.Build()
+
+ fmt.Println(sql)
+ fmt.Println(args)
+
+ // Output:
+ // SELECT id, name, status FROM user WHERE id = $1 LIMIT 1
+ // [1234]
+}
diff --git a/update.go b/update.go
index 05fabd2..4b8a807 100644
--- a/update.go
+++ b/update.go
@@ -11,6 +11,10 @@ import (
// NewUpdateBuilder creates a new UPDATE builder.
func NewUpdateBuilder() *UpdateBuilder {
+ return DefaultFlavor.NewUpdateBuilder()
+}
+
+func newUpdateBuilder() *UpdateBuilder {
args := &Args{}
return &UpdateBuilder{
Cond: Cond{
@@ -99,6 +103,12 @@ func (ub *UpdateBuilder) String() string {
// Build returns compiled UPDATE string and args.
// They can be used in `DB#Query` of package `database/sql` directly.
func (ub *UpdateBuilder) Build() (sql string, args []interface{}) {
+ return ub.BuildWithFlavor(ub.args.Flavor)
+}
+
+// BuildWithFlavor returns compiled UPDATE string and args.
+// They can be used in `DB#Query` of package `database/sql` directly.
+func (ub *UpdateBuilder) BuildWithFlavor(flavor Flavor) (sql string, args []interface{}) {
buf := &bytes.Buffer{}
buf.WriteString("UPDATE ")
buf.WriteString(ub.table)
@@ -110,5 +120,12 @@ func (ub *UpdateBuilder) Build() (sql string, args []interface{}) {
buf.WriteString(strings.Join(ub.whereExprs, " AND "))
}
- return ub.args.Compile(buf.String())
+ return ub.args.CompileWithFlavor(buf.String(), flavor)
+}
+
+// SetFlavor sets the flavor of compiled sql.
+func (ub *UpdateBuilder) SetFlavor(flavor Flavor) (old Flavor) {
+ old = ub.args.Flavor
+ ub.args.Flavor = flavor
+ return
}