github.com/yongjacky/phoenix-go-orm-builder@v0.3.5/builder.go (about)

     1  // Copyright 2016 The Xorm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package builder
     6  
     7  import (
     8  	sql2 "database/sql"
     9  	"fmt"
    10  )
    11  
    12  type optype byte
    13  
    14  const (
    15  	condType   optype = iota // only conditions
    16  	selectType               // select
    17  	insertType               // insert
    18  	updateType               // update
    19  	deleteType               // delete
    20  	setOpType                // set operation
    21  )
    22  
    23  // all databasees
    24  const (
    25  	POSTGRES = "postgres"
    26  	SQLITE   = "sqlite3"
    27  	MYSQL    = "mysql"
    28  	MSSQL    = "mssql"
    29  	ORACLE   = "oracle"
    30  
    31  	UNION     = "union"
    32  	INTERSECT = "intersect"
    33  	EXCEPT    = "except"
    34  )
    35  
    36  type join struct {
    37  	joinType  string
    38  	joinTable interface{}
    39  	joinCond  Cond
    40  }
    41  
    42  type setOp struct {
    43  	opType       string
    44  	distinctType string
    45  	builder      *Builder
    46  }
    47  
    48  type limit struct {
    49  	limitN int
    50  	offset int
    51  }
    52  
    53  type union struct {
    54  	unionType string
    55  	builder   *Builder
    56  }
    57  
    58  // Builder describes a SQL statement
    59  type Builder struct {
    60  	optype
    61  	dialect    string
    62  	isNested   bool
    63  	into       string
    64  	from       string
    65  	subQuery   *Builder
    66  	cond       Cond
    67  	selects    []string
    68  	joins      []join
    69  	setOps     []setOp
    70  	limitation *limit
    71  	insertCols []string
    72  	insertVals []interface{}
    73  	updates    []UpdateCond
    74  	orderBy    string
    75  	groupBy    string
    76  	having     string
    77  	unions     []union
    78  }
    79  
    80  // Dialect sets the db dialect of Builder.
    81  func Dialect(dialect string) *Builder {
    82  	builder := &Builder{cond: NewCond(), dialect: dialect}
    83  	return builder
    84  }
    85  
    86  // MySQL is shortcut of Dialect(MySQL)
    87  func MySQL() *Builder {
    88  	return Dialect(MYSQL)
    89  }
    90  
    91  // MsSQL is shortcut of Dialect(MsSQL)
    92  func MsSQL() *Builder {
    93  	return Dialect(MSSQL)
    94  }
    95  
    96  // Oracle is shortcut of Dialect(Oracle)
    97  func Oracle() *Builder {
    98  	return Dialect(ORACLE)
    99  }
   100  
   101  // Postgres is shortcut of Dialect(Postgres)
   102  func Postgres() *Builder {
   103  	return Dialect(POSTGRES)
   104  }
   105  
   106  // SQLite is shortcut of Dialect(SQLITE)
   107  func SQLite() *Builder {
   108  	return Dialect(SQLITE)
   109  }
   110  
   111  // Where sets where SQL
   112  func (b *Builder) Where(cond Cond) *Builder {
   113  	if b.cond.IsValid() {
   114  		b.cond = b.cond.And(cond)
   115  	} else {
   116  		b.cond = cond
   117  	}
   118  	return b
   119  }
   120  
   121  // From sets from subject(can be a table name in string or a builder pointer) and its alias
   122  func (b *Builder) From(subject interface{}, alias ...string) *Builder {
   123  	switch subject.(type) {
   124  	case *Builder:
   125  		b.subQuery = subject.(*Builder)
   126  
   127  		if len(alias) > 0 {
   128  			b.from = alias[0]
   129  		} else {
   130  			b.isNested = true
   131  		}
   132  	case string:
   133  		b.from = subject.(string)
   134  
   135  		if len(alias) > 0 {
   136  			b.from = b.from + " " + alias[0]
   137  		}
   138  	}
   139  
   140  	return b
   141  }
   142  
   143  // TableName returns the table name
   144  func (b *Builder) TableName() string {
   145  	if b.optype == insertType {
   146  		return b.into
   147  	}
   148  	return b.from
   149  }
   150  
   151  // Into sets insert table name
   152  func (b *Builder) Into(tableName string) *Builder {
   153  	b.into = tableName
   154  	return b
   155  }
   156  
   157  // Union sets union conditions
   158  func (b *Builder) Union(distinctType string, cond *Builder) *Builder {
   159  	return b.setOperation(UNION, distinctType, cond)
   160  }
   161  
   162  // Intersect sets intersect conditions
   163  func (b *Builder) Intersect(distinctType string, cond *Builder) *Builder {
   164  	return b.setOperation(INTERSECT, distinctType, cond)
   165  }
   166  
   167  // Except sets except conditions
   168  func (b *Builder) Except(distinctType string, cond *Builder) *Builder {
   169  	return b.setOperation(EXCEPT, distinctType, cond)
   170  }
   171  
   172  func (b *Builder) setOperation(opType, distinctType string, cond *Builder) *Builder {
   173  
   174  	var builder *Builder
   175  	if b.optype != setOpType {
   176  		builder = &Builder{cond: NewCond()}
   177  		builder.optype = setOpType
   178  		builder.dialect = b.dialect
   179  		builder.selects = b.selects
   180  
   181  		currentSetOps := b.setOps
   182  		// erase sub setOps (actually append to new Builder.unions)
   183  		b.setOps = nil
   184  
   185  		for e := range currentSetOps {
   186  			currentSetOps[e].builder.dialect = b.dialect
   187  		}
   188  
   189  		builder.setOps = append(append(builder.setOps, setOp{opType, "", b}), currentSetOps...)
   190  	} else {
   191  		builder = b
   192  	}
   193  
   194  	if cond != nil {
   195  		if cond.dialect == "" && builder.dialect != "" {
   196  			cond.dialect = builder.dialect
   197  		}
   198  
   199  		builder.setOps = append(builder.setOps, setOp{opType, distinctType, cond})
   200  	}
   201  
   202  	return builder
   203  }
   204  
   205  // Limit sets limitN condition
   206  func (b *Builder) Limit(limitN int, offset ...int) *Builder {
   207  	b.limitation = &limit{limitN: limitN}
   208  
   209  	if len(offset) > 0 {
   210  		b.limitation.offset = offset[0]
   211  	}
   212  
   213  	return b
   214  }
   215  
   216  // Select sets select SQL
   217  func (b *Builder) Select(cols ...string) *Builder {
   218  	b.selects = cols
   219  	if b.optype == condType {
   220  		b.optype = selectType
   221  	}
   222  	return b
   223  }
   224  
   225  // And sets AND condition
   226  func (b *Builder) And(cond Cond) *Builder {
   227  	b.cond = And(b.cond, cond)
   228  	return b
   229  }
   230  
   231  // Or sets OR condition
   232  func (b *Builder) Or(cond Cond) *Builder {
   233  	b.cond = Or(b.cond, cond)
   234  	return b
   235  }
   236  
   237  // Update sets update SQL
   238  func (b *Builder) Update(updates ...Cond) *Builder {
   239  	b.updates = make([]UpdateCond, 0, len(updates))
   240  	for _, update := range updates {
   241  		if u, ok := update.(UpdateCond); ok && u.IsValid() {
   242  			b.updates = append(b.updates, u)
   243  		}
   244  	}
   245  	b.optype = updateType
   246  	return b
   247  }
   248  
   249  // Delete sets delete SQL
   250  func (b *Builder) Delete(conds ...Cond) *Builder {
   251  	b.cond = b.cond.And(conds...)
   252  	b.optype = deleteType
   253  	return b
   254  }
   255  
   256  // WriteTo implements Writer interface
   257  func (b *Builder) WriteTo(w Writer) error {
   258  	switch b.optype {
   259  	/*case condType:
   260  	return b.cond.WriteTo(w)*/
   261  	case selectType:
   262  		return b.selectWriteTo(w)
   263  	case insertType:
   264  		return b.insertWriteTo(w)
   265  	case updateType:
   266  		return b.updateWriteTo(w)
   267  	case deleteType:
   268  		return b.deleteWriteTo(w)
   269  	case setOpType:
   270  		return b.setOpWriteTo(w)
   271  	}
   272  
   273  	return ErrNotSupportType
   274  }
   275  
   276  // ToSQL convert a builder to SQL and args
   277  func (b *Builder) ToSQL() (string, []interface{}, error) {
   278  	w := NewWriter()
   279  	if err := b.WriteTo(w); err != nil {
   280  		return "", nil, err
   281  	}
   282  
   283  	// in case of sql.NamedArg in args
   284  	for e := range w.args {
   285  		if namedArg, ok := w.args[e].(sql2.NamedArg); ok {
   286  			w.args[e] = namedArg.Value
   287  		}
   288  	}
   289  
   290  	var sql = w.String()
   291  	var err error
   292  
   293  	switch b.dialect {
   294  	case ORACLE, MSSQL:
   295  		// This is for compatibility with different sql drivers
   296  		for e := range w.args {
   297  			w.args[e] = sql2.Named(fmt.Sprintf("p%d", e+1), w.args[e])
   298  		}
   299  
   300  		var prefix string
   301  		if b.dialect == ORACLE {
   302  			prefix = ":p"
   303  		} else {
   304  			prefix = "@p"
   305  		}
   306  
   307  		if sql, err = ConvertPlaceholder(sql, prefix); err != nil {
   308  			return "", nil, err
   309  		}
   310  	case POSTGRES:
   311  		if sql, err = ConvertPlaceholder(sql, "$"); err != nil {
   312  			return "", nil, err
   313  		}
   314  	}
   315  
   316  	return sql, w.args, nil
   317  }
   318  
   319  // ToBoundSQL generated a bound SQL string
   320  func (b *Builder) ToBoundSQL() (string, error) {
   321  	w := NewWriter()
   322  	if err := b.WriteTo(w); err != nil {
   323  		return "", err
   324  	}
   325  
   326  	return ConvertToBoundSQL(w.String(), w.args)
   327  }