123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- // Copyright 2016 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 (
- sql2 "database/sql"
- "fmt"
- )
- type optype byte
- const (
- condType optype = iota // only conditions
- selectType // select
- insertType // insert
- updateType // update
- deleteType // delete
- unionType // union
- )
- // all databasees
- const (
- POSTGRES = "postgres"
- SQLITE = "sqlite3"
- MYSQL = "mysql"
- MSSQL = "mssql"
- ORACLE = "oracle"
- )
- type join struct {
- joinType string
- joinTable interface{}
- joinCond Cond
- }
- type union struct {
- unionType string
- builder *Builder
- }
- type limit struct {
- limitN int
- offset int
- }
- // Builder describes a SQL statement
- type Builder struct {
- optype
- dialect string
- isNested bool
- into string
- from string
- subQuery *Builder
- cond Cond
- selects []string
- joins []join
- unions []union
- limitation *limit
- insertCols []string
- insertVals []interface{}
- updates []UpdateCond
- orderBy string
- groupBy string
- having string
- }
- // Dialect sets the db dialect of Builder.
- func Dialect(dialect string) *Builder {
- builder := &Builder{cond: NewCond(), dialect: dialect}
- return builder
- }
- // MySQL is shortcut of Dialect(MySQL)
- func MySQL() *Builder {
- return Dialect(MYSQL)
- }
- // MsSQL is shortcut of Dialect(MsSQL)
- func MsSQL() *Builder {
- return Dialect(MSSQL)
- }
- // Oracle is shortcut of Dialect(Oracle)
- func Oracle() *Builder {
- return Dialect(ORACLE)
- }
- // Postgres is shortcut of Dialect(Postgres)
- func Postgres() *Builder {
- return Dialect(POSTGRES)
- }
- // SQLite is shortcut of Dialect(SQLITE)
- func SQLite() *Builder {
- return Dialect(SQLITE)
- }
- // Where sets where SQL
- func (b *Builder) Where(cond Cond) *Builder {
- if b.cond.IsValid() {
- b.cond = b.cond.And(cond)
- } else {
- b.cond = cond
- }
- return b
- }
- // From sets from subject(can be a table name in string or a builder pointer) and its alias
- func (b *Builder) From(subject interface{}, alias ...string) *Builder {
- switch subject.(type) {
- case *Builder:
- b.subQuery = subject.(*Builder)
- if len(alias) > 0 {
- b.from = alias[0]
- } else {
- b.isNested = true
- }
- case string:
- b.from = subject.(string)
- if len(alias) > 0 {
- b.from = b.from + " " + alias[0]
- }
- }
- return b
- }
- // TableName returns the table name
- func (b *Builder) TableName() string {
- if b.optype == insertType {
- return b.into
- }
- return b.from
- }
- // Into sets insert table name
- func (b *Builder) Into(tableName string) *Builder {
- b.into = tableName
- return b
- }
- // Union sets union conditions
- func (b *Builder) Union(unionTp string, unionCond *Builder) *Builder {
- var builder *Builder
- if b.optype != unionType {
- builder = &Builder{cond: NewCond()}
- builder.optype = unionType
- builder.dialect = b.dialect
- builder.selects = b.selects
- currentUnions := b.unions
- // erase sub unions (actually append to new Builder.unions)
- b.unions = nil
- for e := range currentUnions {
- currentUnions[e].builder.dialect = b.dialect
- }
- builder.unions = append(append(builder.unions, union{"", b}), currentUnions...)
- } else {
- builder = b
- }
- if unionCond != nil {
- if unionCond.dialect == "" && builder.dialect != "" {
- unionCond.dialect = builder.dialect
- }
- builder.unions = append(builder.unions, union{unionTp, unionCond})
- }
- return builder
- }
- // Limit sets limitN condition
- func (b *Builder) Limit(limitN int, offset ...int) *Builder {
- b.limitation = &limit{limitN: limitN}
- if len(offset) > 0 {
- b.limitation.offset = offset[0]
- }
- return b
- }
- // Select sets select SQL
- func (b *Builder) Select(cols ...string) *Builder {
- b.selects = cols
- if b.optype == condType {
- b.optype = selectType
- }
- return b
- }
- // And sets AND condition
- func (b *Builder) And(cond Cond) *Builder {
- b.cond = And(b.cond, cond)
- return b
- }
- // Or sets OR condition
- func (b *Builder) Or(cond Cond) *Builder {
- b.cond = Or(b.cond, cond)
- return b
- }
- // Update sets update SQL
- func (b *Builder) Update(updates ...Cond) *Builder {
- b.updates = make([]UpdateCond, 0, len(updates))
- for _, update := range updates {
- if u, ok := update.(UpdateCond); ok && u.IsValid() {
- b.updates = append(b.updates, u)
- }
- }
- b.optype = updateType
- return b
- }
- // Delete sets delete SQL
- func (b *Builder) Delete(conds ...Cond) *Builder {
- b.cond = b.cond.And(conds...)
- b.optype = deleteType
- return b
- }
- // WriteTo implements Writer interface
- func (b *Builder) WriteTo(w Writer) error {
- switch b.optype {
- /*case condType:
- return b.cond.WriteTo(w)*/
- case selectType:
- return b.selectWriteTo(w)
- case insertType:
- return b.insertWriteTo(w)
- case updateType:
- return b.updateWriteTo(w)
- case deleteType:
- return b.deleteWriteTo(w)
- case unionType:
- return b.unionWriteTo(w)
- }
- return ErrNotSupportType
- }
- // ToSQL convert a builder to SQL and args
- func (b *Builder) ToSQL() (string, []interface{}, error) {
- w := NewWriter()
- if err := b.WriteTo(w); err != nil {
- return "", nil, err
- }
- // in case of sql.NamedArg in args
- for e := range w.args {
- if namedArg, ok := w.args[e].(sql2.NamedArg); ok {
- w.args[e] = namedArg.Value
- }
- }
- var sql = w.String()
- var err error
- switch b.dialect {
- case ORACLE, MSSQL:
- // This is for compatibility with different sql drivers
- for e := range w.args {
- w.args[e] = sql2.Named(fmt.Sprintf("p%d", e+1), w.args[e])
- }
- var prefix string
- if b.dialect == ORACLE {
- prefix = ":p"
- } else {
- prefix = "@p"
- }
- if sql, err = ConvertPlaceholder(sql, prefix); err != nil {
- return "", nil, err
- }
- case POSTGRES:
- if sql, err = ConvertPlaceholder(sql, "$"); err != nil {
- return "", nil, err
- }
- }
- return sql, w.args, nil
- }
- // ToBoundSQL generated a bound SQL string
- func (b *Builder) ToBoundSQL() (string, error) {
- w := NewWriter()
- if err := b.WriteTo(w); err != nil {
- return "", err
- }
- return ConvertToBoundSQL(w.String(), w.args)
- }
|