diff --git a/builder.go b/builder.go index 101b24b..f772bdb 100644 --- a/builder.go +++ b/builder.go @@ -12,12 +12,13 @@ import ( type optype byte const ( - condType optype = iota // only conditions - selectType // select - insertType // insert - updateType // update - deleteType // delete - unionType // union + condType optype = iota // only conditions + selectType // select + insertType // insert + updateType // update + deleteType // delete + unionType // union + replaceType // replace ) const ( @@ -47,20 +48,21 @@ type limit struct { // Builder describes a SQL statement type Builder struct { optype - dialect string - isNested bool - tableName string - subQuery *Builder - cond Cond - selects []string - joins []join - unions []union - limitation *limit - inserts Eq - updates []Eq - orderBy string - groupBy string - having string + dialect string + isNested bool + tableName string + subQuery *Builder + cond Cond + selects []string + joins []join + unions []union + limitation *limit + inserts Eq + updates []Eq + replacements []interface{} + orderBy string + groupBy string + having string } // Dialect sets the db dialect of Builder. @@ -131,6 +133,14 @@ func (b *Builder) TableName() string { return b.tableName } +// Replace replace SQL +func (b *Builder) Replace(replacements ...interface{}) *Builder { + b.replacements = replacements + b.optype = replaceType + + return b +} + // Into sets insert table name func (b *Builder) Into(tableName string) *Builder { b.tableName = tableName @@ -278,6 +288,8 @@ func (b *Builder) WriteTo(w Writer) error { return b.deleteWriteTo(w) case unionType: return b.unionWriteTo(w) + case replaceType: + return b.replaceWriteTo(w) } return ErrNotSupportType diff --git a/builder_replace.go b/builder_replace.go new file mode 100644 index 0000000..8525f07 --- /dev/null +++ b/builder_replace.go @@ -0,0 +1,80 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "fmt" + "strings" +) + +func (b *Builder) replaceWriteTo(w Writer) error { + if len(b.tableName) == 0 { + return ErrNoTableName + } + + if len(b.replacements) == 0 && b.subQuery == nil { + return fmt.Errorf("empty replacements") + } + + var cols []string + var conditions []Eq + if len(b.replacements) > 0 { + switch b.replacements[0].(type) { + case string: + cols = make([]string, 0, len(b.replacements)) + for e := range b.replacements { + if val, ok := b.replacements[e].(string); ok { + cols = append(cols, val) + } else { + return fmt.Errorf("non-uniform types") + } + } + + if b.subQuery == nil { + return fmt.Errorf("derived table should not be empty") + } + case Eq: + conditions = make([]Eq, 0, len(b.replacements)) + for e := range b.replacements { + if val, ok := b.replacements[e].(Eq); ok { + conditions = append(conditions, val) + } else { + return fmt.Errorf("non-uniform types in replacements") + } + } + default: + return fmt.Errorf("unsupported replacement type, supposed to be string or Eq cond") + } + } + + switch b.dialect { + case MYSQL: + if len(conditions) > 0 { + fmt.Fprintf(w, "REPLACE INTO %v SET ", b.tableName) + + for e := range conditions { + if err := conditions[e].WriteTo(w); err != nil { + return err + } + + if e != len(conditions)-1 { + fmt.Fprintf(w, ",") + } + } + } else { + if len(cols) == 0 { + fmt.Fprintf(w, "REPLACE INTO %v ", b.tableName) + } else { + fmt.Fprintf(w, "REPLACE INTO %v(%v) ", b.tableName, strings.Join(cols, ",")) + } + + return b.subQuery.WriteTo(w) + } + default: + return ErrNotSupportType + } + + return nil +} diff --git a/builder_replace_test.go b/builder_replace_test.go new file mode 100644 index 0000000..4e188df --- /dev/null +++ b/builder_replace_test.go @@ -0,0 +1,32 @@ +// Copyright 2018 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBuilder_Replace(t *testing.T) { + sql, err := MySQL().Replace(Eq{"a": 1}, Eq{"b": 2}, Eq{"c": "3"}).Into("table1").ToBoundSQL() + assert.NoError(t, err) + assert.EqualValues(t, "REPLACE INTO table1 SET a=1,b=2,c='3'", sql) + + sql, err = MySQL().Replace().Into("table1").From( + Select("a", "b", "c").From("table2")).ToBoundSQL() + assert.NoError(t, err) + assert.EqualValues(t, "REPLACE INTO table1 SELECT a,b,c FROM table2", sql) + + sql, err = MySQL().Replace("a", "b", "c").Into("table1").From( + Select("a", "b", "c").From("table2")).ToBoundSQL() + assert.NoError(t, err) + assert.EqualValues(t, "REPLACE INTO table1(a,b,c) SELECT a,b,c FROM table2", sql) + + sql, err = MySQL().Replace("a", "b", "c").Into("table1").From( + Select("a", "b", "c").From("table2").Where(Neq{"a": 1})).ToBoundSQL() + assert.NoError(t, err) + assert.EqualValues(t, "REPLACE INTO table1(a,b,c) SELECT a,b,c FROM table2 WHERE a<>1", sql) +}