diff --git a/api.go b/api.go index 30e0b762..6be438bf 100644 --- a/api.go +++ b/api.go @@ -40,7 +40,7 @@ func (s *DB) Joins(query string, args ...interface{}) *DB { // Group specify the group method on the find func (s *DB) Group(column string) *DB { tx := s.init() - tx.Statement.GroupBy.GroupByColumns = append(tx.Statement.GroupBy.GroupByColumns, column) + tx.Statement.GroupBy.Columns = append(tx.Statement.GroupBy.Columns, column) return tx } diff --git a/dialects/common/sqlbuilder/conditions.go b/dialects/common/sqlbuilder/conditions.go new file mode 100644 index 00000000..d213b637 --- /dev/null +++ b/dialects/common/sqlbuilder/conditions.go @@ -0,0 +1,110 @@ +package sqlbuilder + +import ( + "fmt" + + "github.com/jinzhu/gorm" +) + +// ConditionInterface condition interface +type ConditionInterface interface { + ToSQL(*gorm.DB) (string, []interface{}) +} + +// BuildConditions build conditions +func BuildConditions(tx *gorm.DB) chan *Builder { + queryChan := make(chan *Builder) + + go func() { + builder := &Builder{} + + for i, c := range tx.Statement.Conditions { + if i > 0 { + builder.SQL.WriteString(" AND ") + } + buildCondition(tx, c, builder) + } + + queryChan <- builder + }() + return queryChan +} + +func buildCondition(tx *gorm.DB, c gorm.ConditionInterface, builder *Builder) { + switch cond := c.(type) { + case gorm.And: + builder.SQL.WriteString("(") + for i, v := range cond { + if i > 0 { + builder.SQL.WriteString(" AND ") + } + buildCondition(tx, v, builder) + } + builder.SQL.WriteString(")") + case gorm.Or: + builder.SQL.WriteString("(") + for i, v := range cond { + if i > 0 { + builder.SQL.WriteString(" OR ") + } + buildCondition(tx, v, builder) + } + builder.SQL.WriteString(")") + case gorm.Not: + builder.SQL.WriteString("NOT (") + for i, v := range cond { + if i > 0 { + builder.SQL.WriteString(" AND ") + } + buildCondition(tx, v, builder) + } + builder.SQL.WriteString(")") + case gorm.Raw: + builder.SQL.WriteString(cond.SQL) + builder.Args = append(builder.Args, cond.Args...) + case gorm.Eq: + if cond.Value == nil { + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" IS NULL") + } else { + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" = ?") + builder.Args = append(builder.Args, cond.Value) + } + case gorm.Neq: + if cond.Value == nil { + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" IS NOT NULL") + } else { + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" <> ?") + builder.Args = append(builder.Args, cond.Value) + } + case gorm.Gt: + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" > ?") + builder.Args = append(builder.Args, cond.Value) + case gorm.Gte: + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" >= ?") + builder.Args = append(builder.Args, cond.Value) + case gorm.Lt: + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" < ?") + builder.Args = append(builder.Args, cond.Value) + case gorm.Lte: + builder.SQL.WriteString(tx.Dialect().Quote(cond.Column)) + builder.SQL.WriteString(" <= ?") + builder.Args = append(builder.Args, cond.Value) + default: + if sqlCond, ok := cond.(ConditionInterface); ok { + sql, as := sqlCond.ToSQL(tx) + builder.SQL.WriteString(sql) + builder.Args = append(builder.Args, as...) + } else { + tx.AddError(fmt.Errorf("unsupported condition: %#v", cond)) + } + } + + return +} diff --git a/dialects/common/sqlbuilder/group.go b/dialects/common/sqlbuilder/group.go new file mode 100644 index 00000000..c1ff4d40 --- /dev/null +++ b/dialects/common/sqlbuilder/group.go @@ -0,0 +1,32 @@ +package sqlbuilder + +import ( + "strings" + + "github.com/jinzhu/gorm" +) + +// BuildGroupCondition build group condition +func BuildGroupCondition(tx *gorm.DB) chan *Builder { + groupChan := make(chan *Builder) + + go func() { + builder := &Builder{} + if groupBy := tx.Statement.GroupBy; len(groupBy.Columns) > 0 { + builder.SQL.WriteString(strings.Join(tx.Statement.GroupBy.Columns, ", ")) + + if len(groupBy.Having) > 0 { + builder.SQL.WriteString(" HAVING ") + for i, having := range groupBy.Having { + if i > 0 { + builder.SQL.WriteString(" AND ") + } + buildCondition(tx, having, builder) + } + } + } + groupChan <- builder + }() + + return groupChan +} diff --git a/dialects/common/sqlbuilder/sqlbuilder.go b/dialects/common/sqlbuilder/sqlbuilder.go index 0f3fe7ef..89adc00b 100644 --- a/dialects/common/sqlbuilder/sqlbuilder.go +++ b/dialects/common/sqlbuilder/sqlbuilder.go @@ -1,112 +1,9 @@ package sqlbuilder -import ( - "bytes" - "fmt" +import "bytes" - "github.com/jinzhu/gorm" -) - -func buildCondition(tx *gorm.DB, c gorm.ConditionInterface, s *bytes.Buffer) []interface{} { - args := []interface{}{} - - switch cond := c.(type) { - case gorm.And: - s.WriteString("(") - for i, v := range cond { - if i > 0 { - s.WriteString(" AND ") - } - args = append(args, buildCondition(tx, v, s)...) - } - s.WriteString(")") - case gorm.Or: - s.WriteString("(") - for i, v := range cond { - if i > 0 { - s.WriteString(" OR ") - } - args = append(args, buildCondition(tx, v, s)...) - } - s.WriteString(")") - case gorm.Not: - s.WriteString("NOT (") - for i, v := range cond { - if i > 0 { - s.WriteString(" AND ") - } - args = append(args, buildCondition(tx, v, s)...) - } - s.WriteString(")") - case gorm.Raw: - s.WriteString(cond.SQL) - args = append(args, cond.Args...) - case gorm.Eq: - if cond.Value == nil { - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" IS NULL") - } else { - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" = ?") - args = append(args, cond.Value) - } - case gorm.Neq: - if cond.Value == nil { - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" IS NOT NULL") - } else { - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" <> ?") - args = append(args, cond.Value) - } - case gorm.Gt: - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" > ?") - args = append(args, cond.Value) - case gorm.Gte: - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" >= ?") - args = append(args, cond.Value) - case gorm.Lt: - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" < ?") - args = append(args, cond.Value) - case gorm.Lte: - s.WriteString(tx.Dialect().Quote(cond.Column)) - s.WriteString(" <= ?") - args = append(args, cond.Value) - default: - if sqlCond, ok := cond.(ConditionInterface); ok { - sql, as := sqlCond.ToSQL(tx) - s.WriteString(sql) - args = append(args, as) - } else { - tx.AddError(fmt.Errorf("unsupported condition: %#v", cond)) - } - } - - return args -} - -// ConditionInterface condition interface -type ConditionInterface interface { - ToSQL(*gorm.DB) (string, []interface{}) -} - -// BuildConditions build conditions -func BuildConditions(tx *gorm.DB) chan string { - queryChan := make(chan string) - - go func() { - s := bytes.NewBufferString("") - args := []interface{}{} - - for i, c := range tx.Statement.Conditions { - if i > 0 { - s.WriteString(" AND ") - } - args = append(args, buildCondition(tx, c, s)...) - } - }() - return queryChan +// Builder sql builder +type Builder struct { + SQL bytes.Buffer + Args []interface{} } diff --git a/statement.go b/statement.go index 99b48e9e..e09a28e1 100644 --- a/statement.go +++ b/statement.go @@ -47,8 +47,8 @@ type Join struct { // GroupBy group by statement type GroupBy struct { - GroupByColumns []string - Having []ConditionInterface + Columns []string + Having []ConditionInterface } // OrderCondition order condition, could be string or sql expr @@ -84,7 +84,7 @@ func (stmt *Statement) Clone() *Statement { // BuildCondition build condition func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) ConditionInterface { if sql, ok := query.(string); ok { - return Raw{Value: sql, Args: args} + return Raw{SQL: sql, Args: args} } andConds := And([]ConditionInterface{ConditionInterface(query)})