fix: 从xorm.io变更为gitea.com/laixyz,并合并了builder目录

This commit is contained in:
Lucifer Lai 2021-01-04 02:31:23 +08:00
parent f10e21cb15
commit e9b1fcf3dd
115 changed files with 5658 additions and 203 deletions

27
builder/LICENSE Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2016 The Xorm Authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the {organization} nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

206
builder/README.md Normal file
View File

@ -0,0 +1,206 @@
# SQL builder
[![Build Status](https://drone.gitea.com/api/badges/xorm/builder/status.svg)](https://drone.gitea.com/xorm/builder) [![](http://gocover.io/_badge/xorm.io/builder)](http://gocover.io/xorm.io/builder)
[![](https://goreportcard.com/badge/xorm.io/builder)](https://goreportcard.com/report/xorm.io/builder)
Package builder is a lightweight and fast SQL builder for Go and XORM.
Make sure you have installed Go 1.8+ and then:
go get xorm.io/builder
# Insert
```Go
sql, args, err := builder.Insert(Eq{"c": 1, "d": 2}).Into("table1").ToSQL()
// INSERT INTO table1 SELECT * FROM table2
sql, err := builder.Insert().Into("table1").Select().From("table2").ToBoundSQL()
// INSERT INTO table1 (a, b) SELECT b, c FROM table2
sql, err = builder.Insert("a, b").Into("table1").Select("b, c").From("table2").ToBoundSQL()
```
# Select
```Go
// Simple Query
sql, args, err := Select("c, d").From("table1").Where(Eq{"a": 1}).ToSQL()
// With join
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
RightJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
// From sub query
sql, args, err := Select("sub.id").From(Select("c").From("table1").Where(Eq{"a": 1}), "sub").Where(Eq{"b": 1}).ToSQL()
// From union query
sql, args, err = Select("sub.id").From(
Select("id").From("table1").Where(Eq{"a": 1}).Union("all", Select("id").From("table1").Where(Eq{"a": 2})),"sub").
Where(Eq{"b": 1}).ToSQL()
// With order by
sql, args, err = Select("a", "b", "c").From("table1").Where(Eq{"f1": "v1", "f2": "v2"}).
OrderBy("a ASC").ToSQL()
// With limit.
// Be careful! You should set up specific dialect for builder before performing a query with LIMIT
sql, args, err = Dialect(MYSQL).Select("a", "b", "c").From("table1").OrderBy("a ASC").
Limit(5, 10).ToSQL()
```
# Update
```Go
sql, args, err := Update(Eq{"a": 2}).From("table1").Where(Eq{"a": 1}).ToSQL()
```
# Delete
```Go
sql, args, err := Delete(Eq{"a": 1}).From("table1").ToSQL()
```
# Union
```Go
sql, args, err := Select("*").From("a").Where(Eq{"status": "1"}).
Union("all", Select("*").From("a").Where(Eq{"status": "2"})).
Union("distinct", Select("*").From("a").Where(Eq{"status": "3"})).
Union("", Select("*").From("a").Where(Eq{"status": "4"})).
ToSQL()
```
# Conditions
* `Eq` is a redefine of a map, you can give one or more conditions to `Eq`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(Eq{"a":1})
// a=? [1]
sql, args, _ := ToSQL(Eq{"b":"c"}.And(Eq{"c": 0}))
// b=? AND c=? ["c", 0]
sql, args, _ := ToSQL(Eq{"b":"c", "c":0})
// b=? AND c=? ["c", 0]
sql, args, _ := ToSQL(Eq{"b":"c"}.Or(Eq{"b":"d"}))
// b=? OR b=? ["c", "d"]
sql, args, _ := ToSQL(Eq{"b": []string{"c", "d"}})
// b IN (?,?) ["c", "d"]
sql, args, _ := ToSQL(Eq{"b": 1, "c":[]int{2, 3}})
// b=? AND c IN (?,?) [1, 2, 3]
```
* `Neq` is the same to `Eq`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(Neq{"a":1})
// a<>? [1]
sql, args, _ := ToSQL(Neq{"b":"c"}.And(Neq{"c": 0}))
// b<>? AND c<>? ["c", 0]
sql, args, _ := ToSQL(Neq{"b":"c", "c":0})
// b<>? AND c<>? ["c", 0]
sql, args, _ := ToSQL(Neq{"b":"c"}.Or(Neq{"b":"d"}))
// b<>? OR b<>? ["c", "d"]
sql, args, _ := ToSQL(Neq{"b": []string{"c", "d"}})
// b NOT IN (?,?) ["c", "d"]
sql, args, _ := ToSQL(Neq{"b": 1, "c":[]int{2, 3}})
// b<>? AND c NOT IN (?,?) [1, 2, 3]
```
* `Gt`, `Gte`, `Lt`, `Lte`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(Gt{"a", 1}.And(Gte{"b", 2}))
// a>? AND b>=? [1, 2]
sql, args, _ := ToSQL(Lt{"a", 1}.Or(Lte{"b", 2}))
// a<? OR b<=? [1, 2]
```
* `Like`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(Like{"a", "c"})
// a LIKE ? [%c%]
```
* `Expr` you can customerize your sql with `Expr`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(Expr("a = ? ", 1))
// a = ? [1]
sql, args, _ := ToSQL(Eq{"a": Expr("select id from table where c = ?", 1)})
// a=(select id from table where c = ?) [1]
```
* `In` and `NotIn`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(In("a", 1, 2, 3))
// a IN (?,?,?) [1,2,3]
sql, args, _ := ToSQL(In("a", []int{1, 2, 3}))
// a IN (?,?,?) [1,2,3]
sql, args, _ := ToSQL(In("a", Expr("select id from b where c = ?", 1))))
// a IN (select id from b where c = ?) [1]
```
* `IsNull` and `NotNull`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(IsNull{"a"})
// a IS NULL []
sql, args, _ := ToSQL(NotNull{"b"})
// b IS NOT NULL []
```
* `And(conds ...Cond)`, And can connect one or more condtions via And
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(And(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
// a=? AND b LIKE ? AND d<>? [1, %c%, 2]
```
* `Or(conds ...Cond)`, Or can connect one or more conditions via Or
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(Or(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
// a=? OR b LIKE ? OR d<>? [1, %c%, 2]
sql, args, _ := ToSQL(Or(Eq{"a":1}, And(Like{"b", "c"}, Neq{"d", 2})))
// a=? OR (b LIKE ? AND d<>?) [1, %c%, 2]
```
* `Between`
```Go
import . "xorm.io/builder"
sql, args, _ := ToSQL(Between{"a", 1, 2})
// a BETWEEN 1 AND 2
```
* Define yourself conditions
Since `Cond` is an interface.
```Go
type Cond interface {
WriteTo(Writer) error
And(...Cond) Cond
Or(...Cond) Cond
IsValid() bool
}
```
You can define yourself conditions and compose with other `Cond`.

321
builder/builder.go Normal file
View File

@ -0,0 +1,321 @@
// 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
setOpType // set operation
)
// all databasees
const (
POSTGRES = "postgres"
SQLITE = "sqlite3"
MYSQL = "mysql"
MSSQL = "mssql"
ORACLE = "oracle"
UNION = "union"
INTERSECT = "intersect"
EXCEPT = "except"
)
type join struct {
joinType string
joinTable interface{}
joinCond Cond
}
type setOp struct {
opType string
distinctType 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
setOps []setOp
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(distinctType string, cond *Builder) *Builder {
return b.setOperation(UNION, distinctType, cond)
}
// Intersect sets intersect conditions
func (b *Builder) Intersect(distinctType string, cond *Builder) *Builder {
return b.setOperation(INTERSECT, distinctType, cond)
}
// Except sets except conditions
func (b *Builder) Except(distinctType string, cond *Builder) *Builder {
return b.setOperation(EXCEPT, distinctType, cond)
}
func (b *Builder) setOperation(opType, distinctType string, cond *Builder) *Builder {
var builder *Builder
if b.optype != setOpType {
builder = &Builder{cond: NewCond()}
builder.optype = setOpType
builder.dialect = b.dialect
builder.selects = b.selects
currentSetOps := b.setOps
// erase sub setOps (actually append to new Builder.unions)
b.setOps = nil
for e := range currentSetOps {
currentSetOps[e].builder.dialect = b.dialect
}
builder.setOps = append(append(builder.setOps, setOp{opType, "", b}), currentSetOps...)
} else {
builder = b
}
if cond != nil {
if cond.dialect == "" && builder.dialect != "" {
cond.dialect = builder.dialect
}
builder.setOps = append(builder.setOps, setOp{opType, distinctType, cond})
}
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 setOpType:
return b.setOpWriteTo(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)
}

298
builder/builder_b_test.go Normal file
View File

@ -0,0 +1,298 @@
// 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"
"math/rand"
"testing"
)
type randGenConf struct {
allowCond bool
allowJoin bool
allowLimit bool
allowUnion bool
allowHaving bool
allowGroupBy bool
allowOrderBy bool
allowSubQuery bool
}
var expectedValues = []interface{}{
"dangerous", "fun", "degree", "hospital", "horseshoe", "summit", "parallel", "height", "recommend", "invite",
0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var queryFields = []string{"f1", "f2", "f2", "f4", "f5", "f6", "f7", "f8", "f9"}
func BenchmarkSelect_Simple(b *testing.B) {
rgc := randGenConf{allowCond: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery("", &rgc).ToSQL()
}
}
func BenchmarkSelect_SubQuery(b *testing.B) {
rgc := randGenConf{allowSubQuery: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery("", &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectConditional4Oracle(b *testing.B) {
rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
for i := 0; i < b.N; i++ {
randQuery(ORACLE, &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectConditional4Mssql(b *testing.B) {
rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery(MSSQL, &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectConditional4MysqlLike(b *testing.B) {
rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery(MYSQL, &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectConditional4Mixed(b *testing.B) {
rgc := randGenConf{allowLimit: true, allowCond: true, allowGroupBy: true, allowHaving: true, allowOrderBy: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery(randDialect(), &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectComplex4Oracle(b *testing.B) {
rgc := randGenConf{
allowLimit: true, allowCond: true,
allowGroupBy: true, allowHaving: true,
allowOrderBy: true, allowSubQuery: true,
}
for i := 0; i < b.N; i++ {
randQuery(ORACLE, &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectComplex4Mssql(b *testing.B) {
rgc := randGenConf{
allowLimit: true, allowCond: true,
allowGroupBy: true, allowHaving: true,
allowOrderBy: true, allowSubQuery: true,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery(MSSQL, &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectComplex4MysqlLike(b *testing.B) {
rgc := randGenConf{
allowLimit: true, allowCond: true,
allowGroupBy: true, allowHaving: true,
allowOrderBy: true, allowSubQuery: true,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery(MYSQL, &rgc).ToSQL()
}
}
func BenchmarkSelect_SelectComplex4MysqlMixed(b *testing.B) {
rgc := randGenConf{
allowLimit: true, allowCond: true,
allowGroupBy: true, allowHaving: true,
allowOrderBy: true, allowSubQuery: true,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randQuery(randDialect(), &rgc).ToSQL()
}
}
func BenchmarkInsert(b *testing.B) {
rgc := randGenConf{allowCond: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randInsertByCondition(&rgc).ToSQL()
}
}
func BenchmarkUpdate(b *testing.B) {
rgc := randGenConf{allowCond: true}
b.ResetTimer()
for i := 0; i < b.N; i++ {
randUpdateByCondition(&rgc).ToSQL()
}
}
// randQuery Generate a basic query for benchmark test. But be careful it's not a executable SQL in real db.
func randQuery(dialect string, rgc *randGenConf) *Builder {
b := randSelectByCondition(dialect, rgc)
isUnionized := rgc.allowUnion && rand.Intn(1000) >= 500
if isUnionized {
r := rand.Intn(3) + 1
for i := r; i < r; i++ {
b = b.Union("all", randSelectByCondition(dialect, rgc))
}
}
if isUnionized && rgc.allowLimit && rand.Intn(1000) >= 500 {
b = randLimit(Dialect(dialect).Select().From(b, "t"))
}
return b
}
func randInsertByCondition(rgc *randGenConf) *Builder {
fields := randSelects()
times := rand.Intn(10) + 1
eqs := Eq{}
for i := 0; i < times; i++ {
eqs[fields[rand.Intn(len(fields))]] = "expected"
}
b := Insert(eqs).From("table1")
if rgc.allowCond && rand.Intn(1000) >= 500 {
b = b.Where(randCond(b.selects, 3))
}
return b
}
func randUpdateByCondition(rgc *randGenConf) *Builder {
fields := randSelects()
times := rand.Intn(10) + 1
eqs := Eq{}
for i := 0; i < times; i++ {
eqs[fields[rand.Intn(len(fields))]] = randVal()
}
b := Update(eqs).From("table1")
if rgc.allowCond && rand.Intn(1000) >= 500 {
b.Where(randCond(fields, 3))
}
return b
}
func randSelectByCondition(dialect string, rgc *randGenConf) *Builder {
var b *Builder
if rgc.allowSubQuery {
cpRgc := *rgc
cpRgc.allowSubQuery = false
b = Dialect(dialect).Select(randSelects()...).From(randQuery(dialect, &cpRgc), randTableName(0))
} else {
b = Dialect(dialect).Select(randSelects()...).From(randTableName(0))
}
if rgc.allowJoin {
b = randJoin(b, 3)
}
if rgc.allowCond && rand.Intn(1000) >= 500 {
b = b.Where(randCond(b.selects, 3))
}
if rgc.allowLimit && rand.Intn(1000) >= 500 {
b = randLimit(b)
}
if rgc.allowOrderBy && rand.Intn(1000) >= 500 {
b = randOrderBy(b)
}
if rgc.allowHaving && rand.Intn(1000) >= 500 {
b = randHaving(b)
}
if rgc.allowGroupBy && rand.Intn(1000) >= 500 {
b = randGroupBy(b)
}
return b
}
func randDialect() string {
dialects := []string{MYSQL, ORACLE, MSSQL, SQLITE, POSTGRES}
return dialects[rand.Intn(len(dialects))]
}
func randSelects() []string {
if rand.Intn(1000) > 900 {
return []string{"*"}
}
rdx := rand.Intn(len(queryFields) / 2)
return queryFields[rdx:]
}
func randTableName(offset int) string {
return fmt.Sprintf("table%v", rand.Intn(10)+offset)
}
func randJoin(b *Builder, lessThan int) *Builder {
if lessThan <= 0 {
return b
}
times := rand.Intn(lessThan)
for i := 0; i < times; i++ {
tableName := randTableName(i * 10)
b = b.Join("", tableName, fmt.Sprintf("%v.id = %v.id", b.TableName(), tableName))
}
return b
}
func randCond(selects []string, lessThan int) Cond {
if len(selects) <= 0 {
return nil
}
cond := NewCond()
times := rand.Intn(lessThan)
for i := 0; i < times; i++ {
cond = cond.And(Eq{selects[rand.Intn(len(selects))]: randVal()})
}
return cond
}
func randLimit(b *Builder) *Builder {
r := rand.Intn(1000) + 1
if r > 500 {
return b.Limit(r, 1000)
}
return b.Limit(r)
}
func randOrderBy(b *Builder) *Builder {
return b.OrderBy(fmt.Sprintf("%v ASC", b.selects[rand.Intn(len(b.selects))]))
}
func randHaving(b *Builder) *Builder {
return b.OrderBy(fmt.Sprintf("%v = %v", b.selects[rand.Intn(len(b.selects))], randVal()))
}
func randGroupBy(b *Builder) *Builder {
return b.GroupBy(fmt.Sprintf("%v = %v", b.selects[rand.Intn(len(b.selects))], randVal()))
}
func randVal() interface{} {
return expectedValues[rand.Intn(len(expectedValues))]
}

27
builder/builder_delete.go Normal file
View File

@ -0,0 +1,27 @@
// 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 (
"fmt"
)
// Delete creates a delete Builder
func Delete(conds ...Cond) *Builder {
builder := &Builder{cond: NewCond()}
return builder.Delete(conds...)
}
func (b *Builder) deleteWriteTo(w Writer) error {
if len(b.from) <= 0 {
return ErrNoTableName
}
if _, err := fmt.Fprintf(w, "DELETE FROM %s WHERE ", b.from); err != nil {
return err
}
return b.cond.WriteTo(w)
}

View File

@ -0,0 +1,24 @@
// 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 TestBuilderDelete(t *testing.T) {
sql, args, err := Delete(Eq{"a": 1}).From("table1").ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "DELETE FROM table1 WHERE a=?", sql)
assert.EqualValues(t, []interface{}{1}, args)
}
func TestDeleteNoTable(t *testing.T) {
_, _, err := Delete(Eq{"b": "0"}).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNoTableName, err)
}

149
builder/builder_insert.go Normal file
View File

@ -0,0 +1,149 @@
// 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 (
"bytes"
"fmt"
"sort"
)
// Insert creates an insert Builder
func Insert(eq ...interface{}) *Builder {
builder := &Builder{cond: NewCond()}
return builder.Insert(eq...)
}
func (b *Builder) insertSelectWriteTo(w Writer) error {
if _, err := fmt.Fprintf(w, "INSERT INTO %s ", b.into); err != nil {
return err
}
if len(b.insertCols) > 0 {
fmt.Fprintf(w, "(")
for _, col := range b.insertCols {
fmt.Fprintf(w, col)
}
fmt.Fprintf(w, ") ")
}
return b.selectWriteTo(w)
}
func (b *Builder) insertWriteTo(w Writer) error {
if len(b.into) <= 0 {
return ErrNoTableName
}
if len(b.insertCols) <= 0 && b.from == "" {
return ErrNoColumnToInsert
}
if b.into != "" && b.from != "" {
return b.insertSelectWriteTo(w)
}
if _, err := fmt.Fprintf(w, "INSERT INTO %s (", b.into); err != nil {
return err
}
var args = make([]interface{}, 0)
var bs []byte
var valBuffer = bytes.NewBuffer(bs)
for i, col := range b.insertCols {
value := b.insertVals[i]
fmt.Fprint(w, col)
if e, ok := value.(expr); ok {
fmt.Fprintf(valBuffer, "(%s)", e.sql)
args = append(args, e.args...)
} else if value == nil {
fmt.Fprintf(valBuffer, `null`)
} else {
fmt.Fprint(valBuffer, "?")
args = append(args, value)
}
if i != len(b.insertCols)-1 {
if _, err := fmt.Fprint(w, ","); err != nil {
return err
}
if _, err := fmt.Fprint(valBuffer, ","); err != nil {
return err
}
}
}
if _, err := fmt.Fprint(w, ") Values ("); err != nil {
return err
}
if _, err := w.Write(valBuffer.Bytes()); err != nil {
return err
}
if _, err := fmt.Fprint(w, ")"); err != nil {
return err
}
w.Append(args...)
return nil
}
type insertColsSorter struct {
cols []string
vals []interface{}
}
func (s insertColsSorter) Len() int {
return len(s.cols)
}
func (s insertColsSorter) Swap(i, j int) {
s.cols[i], s.cols[j] = s.cols[j], s.cols[i]
s.vals[i], s.vals[j] = s.vals[j], s.vals[i]
}
func (s insertColsSorter) Less(i, j int) bool {
return s.cols[i] < s.cols[j]
}
// Insert sets insert SQL
func (b *Builder) Insert(eq ...interface{}) *Builder {
if len(eq) > 0 {
var paramType = -1
for _, e := range eq {
switch t := e.(type) {
case Eq:
if paramType == -1 {
paramType = 0
}
if paramType != 0 {
break
}
for k, v := range t {
b.insertCols = append(b.insertCols, k)
b.insertVals = append(b.insertVals, v)
}
case string:
if paramType == -1 {
paramType = 1
}
if paramType != 1 {
break
}
b.insertCols = append(b.insertCols, t)
}
}
}
if len(b.insertCols) == len(b.insertVals) {
sort.Sort(insertColsSorter{
cols: b.insertCols,
vals: b.insertVals,
})
}
b.optype = insertType
return b
}

View File

@ -0,0 +1,54 @@
// 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 TestBuilderInsert(t *testing.T) {
sql, err := Insert(Eq{"c": 1, "d": 2}).Into("table1").ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "INSERT INTO table1 (c,d) Values (1,2)", sql)
sql, err = Insert(Eq{"e": 3}, Eq{"c": 1}, Eq{"d": 2}).Into("table1").ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "INSERT INTO table1 (c,d,e) Values (1,2,3)", sql)
sql, err = Insert(Eq{"c": 1, "d": Expr("SELECT b FROM t WHERE d=? LIMIT 1", 2)}).Into("table1").ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "INSERT INTO table1 (c,d) Values (1,(SELECT b FROM t WHERE d=2 LIMIT 1))", sql)
sql, err = Insert(Eq{"c": 1, "d": 2}).ToBoundSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNoTableName, err)
assert.EqualValues(t, "", sql)
sql, err = Insert(Eq{}).Into("table1").ToBoundSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNoColumnToInsert, err)
assert.EqualValues(t, "", sql)
sql, err = Insert(Eq{`a`: nil}).Into(`table1`).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, `INSERT INTO table1 (a) Values (null)`, sql)
sql, args, err := Insert(Eq{`a`: nil, `b`: `str`}).Into(`table1`).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, `INSERT INTO table1 (a,b) Values (null,?)`, sql)
assert.EqualValues(t, []interface{}{`str`}, args)
}
func TestBuidlerInsert_Select(t *testing.T) {
sql, err := Insert().Into("table1").Select().From("table2").ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "INSERT INTO table1 SELECT * FROM table2", sql)
sql, err = Insert("a, b").Into("table1").Select("b, c").From("table2").ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "INSERT INTO table1 (a, b) SELECT b, c FROM table2", sql)
}

42
builder/builder_join.go Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2019 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
// InnerJoin sets inner join
func (b *Builder) InnerJoin(joinTable, joinCond interface{}) *Builder {
return b.Join("INNER", joinTable, joinCond)
}
// LeftJoin sets left join SQL
func (b *Builder) LeftJoin(joinTable, joinCond interface{}) *Builder {
return b.Join("LEFT", joinTable, joinCond)
}
// RightJoin sets right join SQL
func (b *Builder) RightJoin(joinTable, joinCond interface{}) *Builder {
return b.Join("RIGHT", joinTable, joinCond)
}
// CrossJoin sets cross join SQL
func (b *Builder) CrossJoin(joinTable, joinCond interface{}) *Builder {
return b.Join("CROSS", joinTable, joinCond)
}
// FullJoin sets full join SQL
func (b *Builder) FullJoin(joinTable, joinCond interface{}) *Builder {
return b.Join("FULL", joinTable, joinCond)
}
// Join sets join table and conditions
func (b *Builder) Join(joinType string, joinTable, joinCond interface{}) *Builder {
switch joinCond.(type) {
case Cond:
b.joins = append(b.joins, join{joinType, joinTable, joinCond.(Cond)})
case string:
b.joins = append(b.joins, join{joinType, joinTable, Expr(joinCond.(string))})
}
return b
}

View File

@ -0,0 +1,50 @@
// 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 TestJoin(t *testing.T) {
sql, args, err := Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
RightJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? RIGHT JOIN table3 ON table2.id = table3.tid WHERE a=?",
sql)
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
FullJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? FULL JOIN table3 ON table2.id = table3.tid WHERE a=?",
sql)
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
CrossJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? CROSS JOIN table3 ON table2.id = table3.tid WHERE a=?",
sql)
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
InnerJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN table2 ON table1.id=? AND table2.id<? INNER JOIN table3 ON table2.id = table3.tid WHERE a=?",
sql)
assert.EqualValues(t, []interface{}{1, 3, 1}, args)
subQuery2 := Select("e").From("table2").Where(Gt{"e": 1})
subQuery3 := Select("f").From("table3").Where(Gt{"f": "2"})
sql, args, err = Select("c, d").From("table1").LeftJoin(subQuery2, Eq{"table1.id": 1}.And(Lt{"table2.id": 3})).
InnerJoin(subQuery3, "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c, d FROM table1 LEFT JOIN (SELECT e FROM table2 WHERE e>?) ON table1.id=? AND table2.id<? INNER JOIN (SELECT f FROM table3 WHERE f>?) ON table2.id = table3.tid WHERE a=?",
sql)
assert.EqualValues(t, []interface{}{1, 1, 3, "2", 1}, args)
}

103
builder/builder_limit.go Normal file
View File

@ -0,0 +1,103 @@
// 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) limitWriteTo(w Writer) error {
if strings.TrimSpace(b.dialect) == "" {
return ErrDialectNotSetUp
}
if b.limitation != nil {
limit := b.limitation
if limit.offset < 0 || limit.limitN <= 0 {
return ErrInvalidLimitation
}
// erase limit condition
b.limitation = nil
defer func() {
b.limitation = limit
}()
ow := w.(*BytesWriter)
switch strings.ToLower(strings.TrimSpace(b.dialect)) {
case ORACLE:
if len(b.selects) == 0 {
b.selects = append(b.selects, "*")
}
var final *Builder
selects := b.selects
b.selects = append(selects, "ROWNUM RN")
var wb *Builder
if b.optype == setOpType {
wb = Dialect(b.dialect).Select("at.*", "ROWNUM RN").
From(b, "at")
} else {
wb = b
}
if limit.offset == 0 {
final = Dialect(b.dialect).Select(selects...).From(wb, "at").
Where(Lte{"at.RN": limit.limitN})
} else {
sub := Dialect(b.dialect).Select("*").
From(b, "at").Where(Lte{"at.RN": limit.offset + limit.limitN})
final = Dialect(b.dialect).Select(selects...).From(sub, "att").
Where(Gt{"att.RN": limit.offset})
}
return final.WriteTo(ow)
case SQLITE, MYSQL, POSTGRES:
// if type UNION, we need to write previous content back to current writer
if b.optype == setOpType {
if err := b.WriteTo(ow); err != nil {
return err
}
}
if limit.offset == 0 {
fmt.Fprint(ow, " LIMIT ", limit.limitN)
} else {
fmt.Fprintf(ow, " LIMIT %v OFFSET %v", limit.limitN, limit.offset)
}
case MSSQL:
if len(b.selects) == 0 {
b.selects = append(b.selects, "*")
}
var final *Builder
selects := b.selects
b.selects = append(append([]string{fmt.Sprintf("TOP %d %v", limit.limitN+limit.offset, b.selects[0])},
b.selects[1:]...), "ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN")
var wb *Builder
if b.optype == setOpType {
wb = Dialect(b.dialect).Select("*", "ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN").
From(b, "at")
} else {
wb = b
}
if limit.offset == 0 {
final = Dialect(b.dialect).Select(selects...).From(wb, "at")
} else {
final = Dialect(b.dialect).Select(selects...).From(wb, "at").Where(Gt{"at.RN": limit.offset})
}
return final.WriteTo(ow)
default:
return ErrNotSupportType
}
}
return nil
}

View File

@ -0,0 +1,129 @@
// 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_Limit4Mssql(t *testing.T) {
sqlFromFile, err := readPreparationSQLFromFile("testdata/mssql_fiddle_data.sql")
assert.NoError(t, err)
f, err := newFiddler("", MSSQL, sqlFromFile)
assert.NoError(t, err)
assert.NotEmpty(t, f.sessionCode)
// simple -- MsSQL style
sql, err := Dialect(MSSQL).Select("a", "b", "c").From("table1").
OrderBy("a ASC").Limit(5).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT TOP 5 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM table1 ORDER BY a ASC) at", sql)
assert.NoError(t, f.executableCheck(sql))
// simple with where -- MsSQL style
sql, err = Dialect(MSSQL).Select("a", "b", "c").From("table1").
Where(Neq{"a": "3"}).OrderBy("a ASC").Limit(5, 10).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT TOP 15 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM table1 WHERE a<>'3' ORDER BY a ASC) at WHERE at.RN>10", sql)
assert.NoError(t, f.executableCheck(sql))
// union with limit -- MsSQL style
sql, err = Dialect(MSSQL).Select("a", "b", "c").From(
Dialect(MSSQL).Select("a", "b", "c").From("table1").Where(Neq{"a": "1"}).
OrderBy("a ASC").Limit(5, 6).Union("ALL",
Select("a", "b", "c").From("table1").Where(Neq{"b": "2"}).OrderBy("a DESC").Limit(10)), "at").
OrderBy("b DESC").Limit(7, 9).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT TOP 16 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM ((SELECT a,b,c FROM (SELECT TOP 11 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM table1 WHERE a<>'1' ORDER BY a ASC) at WHERE at.RN>6) UNION ALL (SELECT a,b,c FROM (SELECT TOP 10 a,b,c,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RN FROM table1 WHERE b<>'2' ORDER BY a DESC) at)) at ORDER BY b DESC) at WHERE at.RN>9", sql)
assert.NoError(t, f.executableCheck(sql))
}
func TestBuilder_Limit4MysqlLike(t *testing.T) {
sqlFromFile, err := readPreparationSQLFromFile("testdata/mysql_fiddle_data.sql")
assert.NoError(t, err)
f, err := newFiddler("", MYSQL, sqlFromFile)
assert.NoError(t, err)
assert.NotEmpty(t, f.sessionCode)
// simple -- MySQL/SQLite/PostgreSQL style
sql, err := Dialect(MYSQL).Select("a", "b", "c").From("table1").OrderBy("a ASC").
Limit(5, 10).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM table1 ORDER BY a ASC LIMIT 5 OFFSET 10", sql)
assert.NoError(t, f.executableCheck(sql))
// simple -- MySQL/SQLite/PostgreSQL style
sql, err = Dialect(MYSQL).Select("a", "b", "c").From("table1").
OrderBy("a ASC").Limit(5).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM table1 ORDER BY a ASC LIMIT 5", sql)
assert.NoError(t, f.executableCheck(sql))
// simple with where -- MySQL/SQLite/PostgreSQL style
sql, err = Dialect(MYSQL).Select("a", "b", "c").From("table1").
Where(Eq{"a": "1", "b": "1"}).OrderBy("a ASC").Limit(5, 10).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM table1 WHERE a='1' AND b='1' ORDER BY a ASC LIMIT 5 OFFSET 10", sql)
assert.NoError(t, f.executableCheck(sql))
// union -- MySQL/SQLite/PostgreSQL style
sql, err = Dialect(MYSQL).Select("a", "b", "c").From(
Dialect(MYSQL).Select("a", "b", "c").From("table1").Where(Eq{"a": "1"}).OrderBy("a ASC").
Limit(5, 9).Union("ALL",
Select("a", "b", "c").From("table1").Where(Eq{"a": "2"}).OrderBy("a DESC").Limit(10)), "at").
Limit(5, 10).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM ((SELECT a,b,c FROM table1 WHERE a='1' ORDER BY a ASC LIMIT 5 OFFSET 9) UNION ALL (SELECT a,b,c FROM table1 WHERE a='2' ORDER BY a DESC LIMIT 10)) at LIMIT 5 OFFSET 10", sql)
assert.NoError(t, f.executableCheck(sql))
}
func TestBuilder_Limit4Oracle(t *testing.T) {
sqlFromFile, err := readPreparationSQLFromFile("testdata/oracle_fiddle_data.sql")
assert.NoError(t, err)
f, err := newFiddler("", ORACLE, sqlFromFile)
assert.NoError(t, err)
assert.NotEmpty(t, f.sessionCode)
// simple -- OracleSQL style
sql, err := Dialect(ORACLE).Select("a", "b", "c").From("table1").OrderBy("a ASC").
Limit(5, 10).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT * FROM (SELECT a,b,c,ROWNUM RN FROM table1 ORDER BY a ASC) at WHERE at.RN<=15) att WHERE att.RN>10", sql)
assert.NoError(t, f.executableCheck(sql))
// simple with join -- OracleSQL style
sql, err = Dialect(ORACLE).Select("a", "b", "c", "d").From("table1 t1").
InnerJoin("table2 t2", "t1.id = t2.ref_id").OrderBy("a ASC").Limit(5, 10).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c,d FROM (SELECT * FROM (SELECT a,b,c,d,ROWNUM RN FROM table1 t1 INNER JOIN table2 t2 ON t1.id = t2.ref_id ORDER BY a ASC) at WHERE at.RN<=15) att WHERE att.RN>10", sql)
assert.NoError(t, f.executableCheck(sql))
// simple -- OracleSQL style
sql, err = Dialect(ORACLE).Select("a", "b", "c").From("table1").
OrderBy("a ASC").Limit(5).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT a,b,c,ROWNUM RN FROM table1 ORDER BY a ASC) at WHERE at.RN<=5", sql)
assert.NoError(t, f.executableCheck(sql))
// simple with where -- OracleSQL style
sql, err = Dialect(ORACLE).Select("a", "b", "c").From("table1").Where(Neq{"a": "10", "b": "20"}).
OrderBy("a ASC").Limit(5, 1).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT * FROM (SELECT a,b,c,ROWNUM RN FROM table1 WHERE a<>'10' AND b<>'20' ORDER BY a ASC) at WHERE at.RN<=6) att WHERE att.RN>1", sql)
assert.NoError(t, f.executableCheck(sql))
// union with limit -- OracleSQL style
sql, err = Dialect(ORACLE).Select("a", "b", "c").From(
Dialect(ORACLE).Select("a", "b", "c").From("table1").
Where(Neq{"a": "0"}).OrderBy("a ASC").Limit(5, 10).Union("ALL",
Select("a", "b", "c").From("table1").Where(Neq{"b": "48"}).
OrderBy("a DESC").Limit(10)), "at").
Limit(3).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a,b,c FROM (SELECT a,b,c,ROWNUM RN FROM ((SELECT a,b,c FROM (SELECT * FROM (SELECT a,b,c,ROWNUM RN FROM table1 WHERE a<>'0' ORDER BY a ASC) at WHERE at.RN<=15) att WHERE att.RN>10) UNION ALL (SELECT a,b,c FROM (SELECT a,b,c,ROWNUM RN FROM table1 WHERE b<>'48' ORDER BY a DESC) at WHERE at.RN<=10)) at) at WHERE at.RN<=3", sql)
assert.NoError(t, f.executableCheck(sql))
}

158
builder/builder_select.go Normal file
View File

@ -0,0 +1,158 @@
// 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 (
"fmt"
)
// Select creates a select Builder
func Select(cols ...string) *Builder {
builder := &Builder{cond: NewCond()}
return builder.Select(cols...)
}
func (b *Builder) selectWriteTo(w Writer) error {
if len(b.from) <= 0 && !b.isNested {
return ErrNoTableName
}
// perform limit before writing to writer when b.dialect between ORACLE and MSSQL
// this avoid a duplicate writing problem in simple limit query
if b.limitation != nil && (b.dialect == ORACLE || b.dialect == MSSQL) {
return b.limitWriteTo(w)
}
if _, err := fmt.Fprint(w, "SELECT "); err != nil {
return err
}
if len(b.selects) > 0 {
for i, s := range b.selects {
if _, err := fmt.Fprint(w, s); err != nil {
return err
}
if i != len(b.selects)-1 {
if _, err := fmt.Fprint(w, ","); err != nil {
return err
}
}
}
} else {
if _, err := fmt.Fprint(w, "*"); err != nil {
return err
}
}
if b.subQuery == nil {
if _, err := fmt.Fprint(w, " FROM ", b.from); err != nil {
return err
}
} else {
if b.cond.IsValid() && len(b.from) <= 0 {
return ErrUnnamedDerivedTable
}
if b.subQuery.dialect != "" && b.dialect != b.subQuery.dialect {
return ErrInconsistentDialect
}
// dialect of sub-query will inherit from the main one (if not set up)
if b.dialect != "" && b.subQuery.dialect == "" {
b.subQuery.dialect = b.dialect
}
switch b.subQuery.optype {
case selectType, setOpType:
fmt.Fprint(w, " FROM (")
if err := b.subQuery.WriteTo(w); err != nil {
return err
}
if len(b.from) == 0 {
fmt.Fprintf(w, ")")
} else {
fmt.Fprintf(w, ") %v", b.from)
}
default:
return ErrUnexpectedSubQuery
}
}
for _, v := range b.joins {
b, ok := v.joinTable.(*Builder)
if ok {
if _, err := fmt.Fprintf(w, " %s JOIN (", v.joinType); err != nil {
return err
}
if err := b.WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ") ON "); err != nil {
return err
}
} else {
if _, err := fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable); err != nil {
return err
}
}
if err := v.joinCond.WriteTo(w); err != nil {
return err
}
}
if b.cond.IsValid() {
if _, err := fmt.Fprint(w, " WHERE "); err != nil {
return err
}
if err := b.cond.WriteTo(w); err != nil {
return err
}
}
if len(b.groupBy) > 0 {
if _, err := fmt.Fprint(w, " GROUP BY ", b.groupBy); err != nil {
return err
}
}
if len(b.having) > 0 {
if _, err := fmt.Fprint(w, " HAVING ", b.having); err != nil {
return err
}
}
if len(b.orderBy) > 0 {
if _, err := fmt.Fprint(w, " ORDER BY ", b.orderBy); err != nil {
return err
}
}
if b.limitation != nil {
if err := b.limitWriteTo(w); err != nil {
return err
}
}
return nil
}
// OrderBy orderBy SQL
func (b *Builder) OrderBy(orderBy string) *Builder {
b.orderBy = orderBy
return b
}
// GroupBy groupby SQL
func (b *Builder) GroupBy(groupby string) *Builder {
b.groupBy = groupby
return b
}
// Having having SQL
func (b *Builder) Having(having string) *Builder {
b.having = having
return b
}

View File

@ -0,0 +1,115 @@
// 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"
"testing"
"github.com/stretchr/testify/assert"
)
func TestBuilder_Select(t *testing.T) {
sql, args, err := Select("c, d").From("table1").ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c, d FROM table1", sql)
assert.EqualValues(t, []interface{}(nil), args)
sql, args, err = Select("c, d").From("table1").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c, d FROM table1 WHERE a=?", sql)
assert.EqualValues(t, []interface{}{1}, args)
_, _, err = Select("c, d").ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNoTableName, err)
}
func TestBuilderSelectGroupBy(t *testing.T) {
sql, args, err := Select("c").From("table1").GroupBy("c").Having("count(c)=1").ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c FROM table1 GROUP BY c HAVING count(c)=1", sql)
assert.EqualValues(t, 0, len(args))
fmt.Println(sql, args)
}
func TestBuilderSelectOrderBy(t *testing.T) {
sql, args, err := Select("c").From("table1").OrderBy("c DESC").ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c FROM table1 ORDER BY c DESC", sql)
assert.EqualValues(t, 0, len(args))
fmt.Println(sql, args)
}
func TestBuilder_From(t *testing.T) {
// simple one
sql, args, err := Select("c").From("table1").ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT c FROM table1", sql)
assert.EqualValues(t, 0, len(args))
// from sub with alias
sql, args, err = Select("sub.id").From(Select("id").From("table1").Where(Eq{"a": 1}),
"sub").Where(Eq{"b": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT sub.id FROM (SELECT id FROM table1 WHERE a=?) sub WHERE b=?", sql)
assert.EqualValues(t, []interface{}{1, 1}, args)
// from sub without alias and with conditions
sql, args, err = Select("sub.id").From(Select("id").From("table1").Where(Eq{"a": 1})).Where(Eq{"b": 1}).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnnamedDerivedTable, err)
// from sub without alias and conditions
sql, args, err = Select("sub.id").From(Select("id").From("table1").Where(Eq{"a": 1})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT sub.id FROM (SELECT id FROM table1 WHERE a=?)", sql)
assert.EqualValues(t, []interface{}{1}, args)
// from union with alias
sql, args, err = Select("sub.id").From(
Select("id").From("table1").Where(Eq{"a": 1}).Union(
"all", Select("id").From("table1").Where(Eq{"a": 2})), "sub").Where(Eq{"b": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT sub.id FROM ((SELECT id FROM table1 WHERE a=?) UNION ALL (SELECT id FROM table1 WHERE a=?)) sub WHERE b=?", sql)
assert.EqualValues(t, []interface{}{1, 2, 1}, args)
// from union without alias
_, _, err = Select("sub.id").From(
Select("id").From("table1").Where(Eq{"a": 1}).Union(
"all", Select("id").From("table1").Where(Eq{"a": 2}))).Where(Eq{"b": 1}).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnnamedDerivedTable, err)
// will raise error
_, _, err = Select("c").From(Insert(Eq{"a": 1}).From("table1"), "table1").ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnexpectedSubQuery, err)
// will raise error
_, _, err = Select("c").From(Delete(Eq{"a": 1}).From("table1"), "table1").ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnexpectedSubQuery, err)
// from a sub-query in different dialect
_, _, err = MySQL().Select("sub.id").From(
Oracle().Select("id").From("table1").Where(Eq{"a": 1}), "sub").Where(Eq{"b": 1}).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrInconsistentDialect, err)
// from a sub-query (dialect set up)
sql, args, err = MySQL().Select("sub.id").From(
MySQL().Select("id").From("table1").Where(Eq{"a": 1}), "sub").Where(Eq{"b": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT sub.id FROM (SELECT id FROM table1 WHERE a=?) sub WHERE b=?", sql)
assert.EqualValues(t, []interface{}{1, 1}, args)
// from a sub-query (dialect not set up)
sql, args, err = MySQL().Select("sub.id").From(
Select("id").From("table1").Where(Eq{"a": 1}), "sub").Where(Eq{"b": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT sub.id FROM (SELECT id FROM table1 WHERE a=?) sub WHERE b=?", sql)
assert.EqualValues(t, []interface{}{1, 1}, args)
}

View File

@ -0,0 +1,51 @@
// 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) setOpWriteTo(w Writer) error {
if b.limitation != nil || b.cond.IsValid() ||
b.orderBy != "" || b.having != "" || b.groupBy != "" {
return ErrNotUnexpectedUnionConditions
}
for idx, o := range b.setOps {
current := o.builder
if current.optype != selectType {
return ErrUnsupportedUnionMembers
}
if len(b.setOps) == 1 {
if err := current.selectWriteTo(w); err != nil {
return err
}
} else {
if b.dialect != "" && b.dialect != current.dialect {
return ErrInconsistentDialect
}
if idx != 0 {
if o.distinctType == "" {
fmt.Fprint(w, fmt.Sprintf(" %s ", strings.ToUpper(o.opType)))
} else {
fmt.Fprint(w, fmt.Sprintf(" %s %s ", strings.ToUpper(o.opType), strings.ToUpper(o.distinctType)))
}
}
fmt.Fprint(w, "(")
if err := current.selectWriteTo(w); err != nil {
return err
}
fmt.Fprint(w, ")")
}
}
return nil
}

View File

@ -0,0 +1,278 @@
// Copyright 2019 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"
"testing"
"github.com/stretchr/testify/assert"
)
func TestBuilder_Union(t *testing.T) {
sql, args, err := Select("*").From("t1").Where(Eq{"status": "1"}).
Union("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Union("distinct", Select("*").From("t2").Where(Eq{"status": "3"})).
Union("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) UNION ALL (SELECT * FROM t2 WHERE status=?) UNION DISTINCT (SELECT * FROM t2 WHERE status=?) UNION (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3", "3"}, args)
// sub-query will inherit dialect from the main one
sql, args, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Union("all", Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
Union("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) UNION ALL (SELECT * FROM t2 WHERE status=? LIMIT 10) UNION (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3"}, args)
// will raise error
_, _, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Union("all", Oracle().Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrInconsistentDialect, err)
// will raise error
_, _, err = Select("*").From("table1").Where(Eq{"a": "1"}).
Union("all", Select("*").From("table2").Where(Eq{"a": "2"})).
Where(Eq{"a": 2}).Limit(5, 10).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNotUnexpectedUnionConditions, err)
// will raise error
_, _, err = Delete(Eq{"a": 1}).From("t1").
Union("all", Select("*").From("t2").Where(Eq{"status": "2"})).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnsupportedUnionMembers, err)
// will be overwrote by SELECT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Union("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Select("*").From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by DELETE op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Union("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Delete(Eq{"status": "1"}).From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by INSERT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Union("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Insert(Eq{"status": "1"}).Into("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
}
func TestBuilder_Intersect(t *testing.T) {
sql, args, err := Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Intersect("distinct", Select("*").From("t2").Where(Eq{"status": "3"})).
Intersect("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) INTERSECT ALL (SELECT * FROM t2 WHERE status=?) INTERSECT DISTINCT (SELECT * FROM t2 WHERE status=?) INTERSECT (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3", "3"}, args)
// sub-query will inherit dialect from the main one
sql, args, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
Intersect("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) INTERSECT ALL (SELECT * FROM t2 WHERE status=? LIMIT 10) INTERSECT (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3"}, args)
// will raise error
_, _, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Oracle().Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrInconsistentDialect, err)
// will raise error
_, _, err = Select("*").From("table1").Where(Eq{"a": "1"}).
Intersect("all", Select("*").From("table2").Where(Eq{"a": "2"})).
Where(Eq{"a": 2}).Limit(5, 10).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNotUnexpectedUnionConditions, err)
// will raise error
_, _, err = Delete(Eq{"a": 1}).From("t1").
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnsupportedUnionMembers, err)
// will be overwrote by SELECT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Select("*").From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by DELETE op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Delete(Eq{"status": "1"}).From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by INSERT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Insert(Eq{"status": "1"}).Into("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
}
func TestBuilder_Except(t *testing.T) {
sql, args, err := Select("*").From("t1").Where(Eq{"status": "1"}).
Except("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Except("distinct", Select("*").From("t2").Where(Eq{"status": "3"})).
Except("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) EXCEPT ALL (SELECT * FROM t2 WHERE status=?) EXCEPT DISTINCT (SELECT * FROM t2 WHERE status=?) EXCEPT (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3", "3"}, args)
// sub-query will inherit dialect from the main one
sql, args, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Except("all", Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
Except("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) EXCEPT ALL (SELECT * FROM t2 WHERE status=? LIMIT 10) EXCEPT (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3"}, args)
// will raise error
_, _, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Except("all", Oracle().Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrInconsistentDialect, err)
// will raise error
_, _, err = Select("*").From("table1").Where(Eq{"a": "1"}).
Except("all", Select("*").From("table2").Where(Eq{"a": "2"})).
Where(Eq{"a": 2}).Limit(5, 10).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNotUnexpectedUnionConditions, err)
// will raise error
_, _, err = Delete(Eq{"a": 1}).From("t1").
Except("all", Select("*").From("t2").Where(Eq{"status": "2"})).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnsupportedUnionMembers, err)
// will be overwrote by SELECT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Except("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Select("*").From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by DELETE op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Except("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Delete(Eq{"status": "1"}).From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by INSERT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Except("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Insert(Eq{"status": "1"}).Into("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
}
func TestBuilder_SetOperations(t *testing.T) {
sql, args, err := Select("*").From("t1").Where(Eq{"status": "1"}).
Union("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Intersect("distinct", Select("*").From("t2").Where(Eq{"status": "3"})).
Except("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) UNION ALL (SELECT * FROM t2 WHERE status=?) INTERSECT DISTINCT (SELECT * FROM t2 WHERE status=?) EXCEPT (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3", "3"}, args)
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Union("distinct", Select("*").From("t2").Where(Eq{"status": "3"})).
Except("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) INTERSECT ALL (SELECT * FROM t2 WHERE status=?) UNION DISTINCT (SELECT * FROM t2 WHERE status=?) EXCEPT (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3", "3"}, args)
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Except("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Intersect("distinct", Select("*").From("t2").Where(Eq{"status": "3"})).
Union("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) EXCEPT ALL (SELECT * FROM t2 WHERE status=?) INTERSECT DISTINCT (SELECT * FROM t2 WHERE status=?) UNION (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3", "3"}, args)
// sub-query will inherit dialect from the main one
sql, args, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
Intersect("", Select("*").From("t2").Where(Eq{"status": "3"})).
ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "(SELECT * FROM t1 WHERE status=?) INTERSECT ALL (SELECT * FROM t2 WHERE status=? LIMIT 10) INTERSECT (SELECT * FROM t2 WHERE status=?)", sql)
assert.EqualValues(t, []interface{}{"1", "2", "3"}, args)
// will raise error
_, _, err = MySQL().Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Oracle().Select("*").From("t2").Where(Eq{"status": "2"}).Limit(10)).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrInconsistentDialect, err)
// will raise error
_, _, err = Select("*").From("table1").Where(Eq{"a": "1"}).
Intersect("all", Select("*").From("table2").Where(Eq{"a": "2"})).
Where(Eq{"a": 2}).Limit(5, 10).
ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNotUnexpectedUnionConditions, err)
// will raise error
_, _, err = Delete(Eq{"a": 1}).From("t1").
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrUnsupportedUnionMembers, err)
// will be overwrote by SELECT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Select("*").From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by DELETE op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Delete(Eq{"status": "1"}).From("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
// will be overwrote by INSERT op
sql, args, err = Select("*").From("t1").Where(Eq{"status": "1"}).
Intersect("all", Select("*").From("t2").Where(Eq{"status": "2"})).
Insert(Eq{"status": "1"}).Into("t2").ToSQL()
assert.NoError(t, err)
fmt.Println(sql, args)
}

657
builder/builder_test.go Normal file
View File

@ -0,0 +1,657 @@
// 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 (
"testing"
"github.com/stretchr/testify/assert"
)
type MyInt int
func TestBuilderCond(t *testing.T) {
var cases = []struct {
cond Cond
sql string
args []interface{}
}{
{
Eq{"a": 1}.And(Like{"b", "c"}).Or(Eq{"a": 2}.And(Like{"b", "g"})),
"(a=? AND b LIKE ?) OR (a=? AND b LIKE ?)",
[]interface{}{1, "%c%", 2, "%g%"},
},
{
Eq{"a": 1}.Or(Like{"b", "c"}).And(Eq{"a": 2}.Or(Like{"b", "g"})),
"(a=? OR b LIKE ?) AND (a=? OR b LIKE ?)",
[]interface{}{1, "%c%", 2, "%g%"},
},
{
Eq{"d": []string{"e", "f"}},
"d IN (?,?)",
[]interface{}{"e", "f"},
},
{
Eq{"e": Select("id").From("f").Where(Eq{"g": 1})},
"e=(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Eq{"e": Expr("SELECT id FROM f WHERE g=?", 1)},
"e=(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Like{"a", "%1"}.And(Like{"b", "%2"}),
"a LIKE ? AND b LIKE ?",
[]interface{}{"%1", "%2"},
},
{
Like{"a", "%1"}.Or(Like{"b", "%2"}),
"a LIKE ? OR b LIKE ?",
[]interface{}{"%1", "%2"},
},
{
Neq{"d": "e"}.Or(Neq{"f": "g"}),
"d<>? OR f<>?",
[]interface{}{"e", "g"},
},
{
Neq{"d": []string{"e", "f"}},
"d NOT IN (?,?)",
[]interface{}{"e", "f"},
},
{
Neq{"e": Select("id").From("f").Where(Eq{"g": 1})},
"e<>(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Neq{"e": Expr("SELECT id FROM f WHERE g=?", 1)},
"e<>(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Lt{"d": 3},
"d<?",
[]interface{}{3},
},
{
Lt{"d": 3}.And(Lt{"e": 4}),
"d<? AND e<?",
[]interface{}{3, 4},
},
{
Lt{"d": 3}.Or(Lt{"e": 4}),
"d<? OR e<?",
[]interface{}{3, 4},
},
{
Lt{"e": Select("id").From("f").Where(Eq{"g": 1})},
"e<(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Lt{"e": Expr("SELECT id FROM f WHERE g=?", 1)},
"e<(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Lte{"d": 3},
"d<=?",
[]interface{}{3},
},
{
Lte{"d": 3}.And(Lte{"e": 4}),
"d<=? AND e<=?",
[]interface{}{3, 4},
},
{
Lte{"d": 3}.Or(Lte{"e": 4}),
"d<=? OR e<=?",
[]interface{}{3, 4},
},
{
Lte{"e": Select("id").From("f").Where(Eq{"g": 1})},
"e<=(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Lte{"e": Expr("SELECT id FROM f WHERE g=?", 1)},
"e<=(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Gt{"d": 3},
"d>?",
[]interface{}{3},
},
{
Gt{"d": 3}.And(Gt{"e": 4}),
"d>? AND e>?",
[]interface{}{3, 4},
},
{
Gt{"d": 3}.Or(Gt{"e": 4}),
"d>? OR e>?",
[]interface{}{3, 4},
},
{
Gt{"e": Select("id").From("f").Where(Eq{"g": 1})},
"e>(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Gt{"e": Expr("SELECT id FROM f WHERE g=?", 1)},
"e>(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Gte{"d": 3},
"d>=?",
[]interface{}{3},
},
{
Gte{"d": 3}.And(Gte{"e": 4}),
"d>=? AND e>=?",
[]interface{}{3, 4},
},
{
Gte{"d": 3}.Or(Gte{"e": 4}),
"d>=? OR e>=?",
[]interface{}{3, 4},
},
{
Gte{"e": Select("id").From("f").Where(Eq{"g": 1})},
"e>=(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Gte{"e": Expr("SELECT id FROM f WHERE g=?", 1)},
"e>=(SELECT id FROM f WHERE g=?)",
[]interface{}{1},
},
{
Between{"d", 0, 2},
"d BETWEEN ? AND ?",
[]interface{}{0, 2},
},
{
Between{"d", 0, Expr("CAST('2003-01-01' AS DATE)")},
"d BETWEEN ? AND CAST('2003-01-01' AS DATE)",
[]interface{}{0},
},
{
Between{"d", Expr("CAST('2003-01-01' AS DATE)"), 2},
"d BETWEEN CAST('2003-01-01' AS DATE) AND ?",
[]interface{}{2},
},
{
Between{"d", Expr("CAST('2003-01-01' AS DATE)"), Expr("CAST('2003-01-01' AS DATE)")},
"d BETWEEN CAST('2003-01-01' AS DATE) AND CAST('2003-01-01' AS DATE)",
[]interface{}{},
},
{
Between{"d", 0, 2}.And(Between{"e", 3, 4}),
"d BETWEEN ? AND ? AND e BETWEEN ? AND ?",
[]interface{}{0, 2, 3, 4},
},
{
Between{"d", 0, 2}.Or(Between{"e", 3, 4}),
"d BETWEEN ? AND ? OR e BETWEEN ? AND ?",
[]interface{}{0, 2, 3, 4},
},
{
Expr("a < ?", 1),
"a < ?",
[]interface{}{1},
},
{
Expr("a < ?", 1).And(Eq{"b": 2}),
"(a < ?) AND b=?",
[]interface{}{1, 2},
},
{
Expr("a < ?", 1).Or(Neq{"b": 2}),
"(a < ?) OR b<>?",
[]interface{}{1, 2},
},
{
IsNull{"d"},
"d IS NULL",
[]interface{}{},
},
{
IsNull{"d"}.And(IsNull{"e"}),
"d IS NULL AND e IS NULL",
[]interface{}{},
},
{
IsNull{"d"}.Or(IsNull{"e"}),
"d IS NULL OR e IS NULL",
[]interface{}{},
},
{
NotNull{"d"},
"d IS NOT NULL",
[]interface{}{},
},
{
NotNull{"d"}.And(NotNull{"e"}),
"d IS NOT NULL AND e IS NOT NULL",
[]interface{}{},
},
{
NotNull{"d"}.Or(NotNull{"e"}),
"d IS NOT NULL OR e IS NOT NULL",
[]interface{}{},
},
{
NotIn("a", 1, 2).And(NotIn("b", "c", "d")),
"a NOT IN (?,?) AND b NOT IN (?,?)",
[]interface{}{1, 2, "c", "d"},
},
{
In("a", 1, 2).Or(In("b", "c", "d")),
"a IN (?,?) OR b IN (?,?)",
[]interface{}{1, 2, "c", "d"},
},
{
In("a", []int{1, 2}).Or(In("b", []string{"c", "d"})),
"a IN (?,?) OR b IN (?,?)",
[]interface{}{1, 2, "c", "d"},
},
{
In("a", Expr("select id from x where name > ?", "b")),
"a IN (select id from x where name > ?)",
[]interface{}{"b"},
},
{
In("a", []MyInt{1, 2}).Or(In("b", []string{"c", "d"})),
"a IN (?,?) OR b IN (?,?)",
[]interface{}{MyInt(1), MyInt(2), "c", "d"},
},
{
In("a", []int{}),
"0=1",
[]interface{}{},
},
{
In("a", []int{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []int8{}),
"0=1",
[]interface{}{},
},
{
In("a", []int8{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []int16{}),
"0=1",
[]interface{}{},
},
{
In("a", []int16{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []int32{}),
"0=1",
[]interface{}{},
},
{
In("a", []int32{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []int64{}),
"0=1",
[]interface{}{},
},
{
In("a", []int64{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []uint{}),
"0=1",
[]interface{}{},
},
{
In("a", []uint{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []uint8{}),
"0=1",
[]interface{}{},
},
{
In("a", []uint8{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []uint16{}),
"0=1",
[]interface{}{},
},
{
In("a", []uint16{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []uint32{}),
"0=1",
[]interface{}{},
},
{
In("a", []uint32{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []uint64{}),
"0=1",
[]interface{}{},
},
{
In("a", []uint64{1}),
"a IN (?)",
[]interface{}{1},
},
{
In("a", []string{}),
"0=1",
[]interface{}{},
},
{
In("a", []interface{}{}),
"0=1",
[]interface{}{},
},
{
In("a", []MyInt{}),
"0=1",
[]interface{}{},
},
{
In("a", []interface{}{1, 2, 3}).And(Eq{"b": "c"}),
"a IN (?,?,?) AND b=?",
[]interface{}{1, 2, 3, "c"},
},
{
In("a", Select("id").From("b").Where(Eq{"c": 1})),
"a IN (SELECT id FROM b WHERE c=?)",
[]interface{}{1},
},
{
NotIn("a", Expr("select id from x where name > ?", "b")),
"a NOT IN (select id from x where name > ?)",
[]interface{}{"b"},
},
{
NotIn("a", []int{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []int{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []int8{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []int8{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []int16{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []int16{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []int32{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []int32{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []int64{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []int64{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []uint{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []uint{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []uint8{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []uint8{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []uint16{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []uint16{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []uint32{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []uint32{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []uint64{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []uint64{1}),
"a NOT IN (?)",
[]interface{}{1},
},
{
NotIn("a", []interface{}{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []string{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []MyInt{}),
"0=0",
[]interface{}{},
},
{
NotIn("a", []MyInt{1, 2}),
"a NOT IN (?,?)",
[]interface{}{1, 2},
},
{
NotIn("a", []interface{}{1, 2, 3}).And(Eq{"b": "c"}),
"a NOT IN (?,?,?) AND b=?",
[]interface{}{1, 2, 3, "c"},
},
{
NotIn("a", []interface{}{1, 2, 3}).Or(Eq{"b": "c"}),
"a NOT IN (?,?,?) OR b=?",
[]interface{}{1, 2, 3, "c"},
},
{
NotIn("a", Select("id").From("b").Where(Eq{"c": 1})),
"a NOT IN (SELECT id FROM b WHERE c=?)",
[]interface{}{1},
},
{
Or(Eq{"a": 1, "b": 2}, Eq{"c": 3, "d": 4}),
"(a=? AND b=?) OR (c=? AND d=?)",
[]interface{}{1, 2, 3, 4},
},
{
Not{Eq{"a": 1, "b": 2}},
"NOT (a=? AND b=?)",
[]interface{}{1, 2},
},
{
Not{Neq{"a": 1, "b": 2}},
"NOT (a<>? AND b<>?)",
[]interface{}{1, 2},
},
{
Not{Eq{"a": 1}.And(Eq{"b": 2})},
"NOT (a=? AND b=?)",
[]interface{}{1, 2},
},
{
Not{Neq{"a": 1}.And(Neq{"b": 2})},
"NOT (a<>? AND b<>?)",
[]interface{}{1, 2},
},
{
Not{Eq{"a": 1}}.And(Neq{"b": 2}),
"NOT a=? AND b<>?",
[]interface{}{1, 2},
},
{
Not{Eq{"a": 1}}.Or(Neq{"b": 2}),
"NOT a=? OR b<>?",
[]interface{}{1, 2},
},
}
for _, k := range cases {
sql, args, err := ToSQL(k.cond)
assert.NoError(t, err)
assert.EqualValues(t, k.sql, sql)
for i := 0; i < 10; i++ {
sql2, _, err := ToSQL(k.cond)
assert.NoError(t, err)
assert.EqualValues(t, sql, sql2)
}
assert.EqualValues(t, len(args), len(k.args))
if len(args) > 0 {
for i := 0; i < len(args); i++ {
assert.EqualValues(t, k.args[i], args[i])
}
}
}
}
func TestSubquery(t *testing.T) {
subb := Select("id").From("table_b").Where(Eq{"b": "a"})
b := Select("a, b").From("table_a").Where(
Eq{
"b_id": subb,
"id": 23,
},
)
sql, args, err := b.ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=?) AND id=?", sql)
assert.EqualValues(t, []interface{}{"a", 23}, args)
}
// https://github.com/go-xorm/xorm/issues/820
func TestExprCond(t *testing.T) {
b := Select("id").From("table1").Where(expr{sql: "a=? OR b=?", args: []interface{}{1, 2}}).Where(Or(Eq{"c": 3}, Eq{"d": 4}))
sql, args, err := b.ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "table1", b.TableName())
assert.EqualValues(t, "SELECT id FROM table1 WHERE (a=? OR b=?) AND (c=? OR d=?)", sql)
assert.EqualValues(t, []interface{}{1, 2, 3, 4}, args)
}
func TestBuilder_ToBoundSQL(t *testing.T) {
newSQL, err := Select("id").From("table").Where(In("a", 1, 2)).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT id FROM table WHERE a IN (1,2)", newSQL)
}
func TestBuilder_From2(t *testing.T) {
b := Select("id").From("table_b", "tb").Where(Eq{"b": "a"})
sql, args, err := b.ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT id FROM table_b tb WHERE b=?", sql)
assert.EqualValues(t, []interface{}{"a"}, args)
b = Select().From("table_b", "tb").Where(Eq{"b": "a"})
sql, args, err = b.ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table_b tb WHERE b=?", sql)
assert.EqualValues(t, []interface{}{"a"}, args)
}
func TestBuilder_And(t *testing.T) {
b := Select("id").From("table_b", "tb").Where(Eq{"b": "a"}).And(Neq{"c": "d"})
sql, args, err := b.ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT id FROM table_b tb WHERE b=? AND c<>?", sql)
assert.EqualValues(t, []interface{}{"a", "d"}, args)
}
func TestBuilder_Or(t *testing.T) {
b := Select("id").From("table_b", "tb").Where(Eq{"b": "a"}).Or(Neq{"c": "d"})
sql, args, err := b.ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT id FROM table_b tb WHERE b=? OR c<>?", sql)
assert.EqualValues(t, []interface{}{"a", "d"}, args)
}

57
builder/builder_update.go Normal file
View File

@ -0,0 +1,57 @@
// 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 (
"fmt"
)
// UpdateCond defines an interface that cond could be used with update
type UpdateCond interface {
IsValid() bool
OpWriteTo(op string, w Writer) error
}
// Update creates an update Builder
func Update(updates ...Cond) *Builder {
builder := &Builder{cond: NewCond()}
return builder.Update(updates...)
}
func (b *Builder) updateWriteTo(w Writer) error {
if len(b.from) <= 0 {
return ErrNoTableName
}
if len(b.updates) <= 0 {
return ErrNoColumnToUpdate
}
if _, err := fmt.Fprintf(w, "UPDATE %s SET ", b.from); err != nil {
return err
}
for i, s := range b.updates {
if err := s.OpWriteTo(",", w); err != nil {
return err
}
if i != len(b.updates)-1 {
if _, err := fmt.Fprint(w, ","); err != nil {
return err
}
}
}
if !b.cond.IsValid() {
return nil
}
if _, err := fmt.Fprint(w, " WHERE "); err != nil {
return err
}
return b.cond.WriteTo(w)
}

View File

@ -0,0 +1,62 @@
// 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 TestBuilderUpdate(t *testing.T) {
sql, args, err := Update(Eq{"a": 2}).From("table1").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=? WHERE a=?", sql)
assert.EqualValues(t, []interface{}{2, 1}, args)
sql, args, err = Update(Eq{"a": 2, "b": 1}).From("table1").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=?,b=? WHERE a=?", sql)
assert.EqualValues(t, []interface{}{2, 1, 1}, args)
sql, args, err = Update(Eq{"a": 2}, Eq{"b": 1}).From("table1").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=?,b=? WHERE a=?", sql)
assert.EqualValues(t, []interface{}{2, 1, 1}, args)
sql, args, err = Update(Eq{"a": 2, "b": Incr(1)}).From("table2").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table2 SET a=?,b=b+? WHERE a=?", sql)
assert.EqualValues(t, []interface{}{2, 1, 1}, args)
sql, args, err = Update(Eq{"a": 2, "b": Incr(1), "c": Decr(1), "d": Expr("select count(*) from table2")}).From("table2").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table2 SET a=?,b=b+?,c=c-?,d=(select count(*) from table2) WHERE a=?", sql)
assert.EqualValues(t, []interface{}{2, 1, 1, 1}, args)
sql, args, err = Update(Eq{"a": 2}).Where(Eq{"a": 1}).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNoTableName, err)
sql, args, err = Update(Eq{}).From("table1").Where(Eq{"a": 1}).ToSQL()
assert.Error(t, err)
assert.EqualValues(t, ErrNoColumnToUpdate, err)
var builder = Builder{cond: NewCond()}
sql, args, err = builder.Update(Eq{"a": 2, "b": 1}).From("table1").Where(Eq{"a": 1}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=?,b=? WHERE a=?", sql)
assert.EqualValues(t, []interface{}{2, 1, 1}, args)
sql, args, err = Update(Eq{"a": 1}, Expr("c = c+1")).From("table1").Where(Eq{"b": 2}).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=?,c = c+1 WHERE b=?", sql)
assert.EqualValues(t, []interface{}{1, 2}, args)
sql, args, err = Update(Eq{"a": 2}).From("table1").ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=?", sql)
assert.EqualValues(t, []interface{}{2}, args)
}

38
builder/cond.go Normal file
View File

@ -0,0 +1,38 @@
// 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
// Cond defines an interface
type Cond interface {
WriteTo(Writer) error
And(...Cond) Cond
Or(...Cond) Cond
IsValid() bool
}
type condEmpty struct{}
var _ Cond = condEmpty{}
// NewCond creates an empty condition
func NewCond() Cond {
return condEmpty{}
}
func (condEmpty) WriteTo(w Writer) error {
return nil
}
func (condEmpty) And(conds ...Cond) Cond {
return And(conds...)
}
func (condEmpty) Or(conds ...Cond) Cond {
return Or(conds...)
}
func (condEmpty) IsValid() bool {
return false
}

61
builder/cond_and.go Normal file
View File

@ -0,0 +1,61 @@
// 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 "fmt"
type condAnd []Cond
var _ Cond = condAnd{}
// And generates AND conditions
func And(conds ...Cond) Cond {
var result = make(condAnd, 0, len(conds))
for _, cond := range conds {
if cond == nil || !cond.IsValid() {
continue
}
result = append(result, cond)
}
return result
}
func (and condAnd) WriteTo(w Writer) error {
for i, cond := range and {
_, isOr := cond.(condOr)
_, isExpr := cond.(expr)
wrap := isOr || isExpr
if wrap {
fmt.Fprint(w, "(")
}
err := cond.WriteTo(w)
if err != nil {
return err
}
if wrap {
fmt.Fprint(w, ")")
}
if i != len(and)-1 {
fmt.Fprint(w, " AND ")
}
}
return nil
}
func (and condAnd) And(conds ...Cond) Cond {
return And(and, And(conds...))
}
func (and condAnd) Or(conds ...Cond) Cond {
return Or(and, Or(conds...))
}
func (and condAnd) IsValid() bool {
return len(and) > 0
}

65
builder/cond_between.go Normal file
View File

@ -0,0 +1,65 @@
// 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 "fmt"
// Between implmentes between condition
type Between struct {
Col string
LessVal interface{}
MoreVal interface{}
}
var _ Cond = Between{}
// WriteTo write data to Writer
func (between Between) WriteTo(w Writer) error {
if _, err := fmt.Fprintf(w, "%s BETWEEN ", between.Col); err != nil {
return err
}
if lv, ok := between.LessVal.(expr); ok {
if err := lv.WriteTo(w); err != nil {
return err
}
} else {
if _, err := fmt.Fprint(w, "?"); err != nil {
return err
}
w.Append(between.LessVal)
}
if _, err := fmt.Fprint(w, " AND "); err != nil {
return err
}
if mv, ok := between.MoreVal.(expr); ok {
if err := mv.WriteTo(w); err != nil {
return err
}
} else {
if _, err := fmt.Fprint(w, "?"); err != nil {
return err
}
w.Append(between.MoreVal)
}
return nil
}
// And implments And with other conditions
func (between Between) And(conds ...Cond) Cond {
return And(between, And(conds...))
}
// Or implments Or with other conditions
func (between Between) Or(conds ...Cond) Cond {
return Or(between, Or(conds...))
}
// IsValid tests if the condition is valid
func (between Between) IsValid() bool {
return len(between.Col) > 0
}

160
builder/cond_compare.go Normal file
View File

@ -0,0 +1,160 @@
// 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 "fmt"
// WriteMap writes conditions' SQL to Writer, op could be =, <>, >, <, <=, >= and etc.
func WriteMap(w Writer, data map[string]interface{}, op string) error {
var args = make([]interface{}, 0, len(data))
var i = 0
keys := make([]string, 0, len(data))
for k := range data {
keys = append(keys, k)
}
for _, k := range keys {
v := data[k]
switch v.(type) {
case expr:
if _, err := fmt.Fprintf(w, "%s%s(", k, op); err != nil {
return err
}
if err := v.(expr).WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
case *Builder:
if _, err := fmt.Fprintf(w, "%s%s(", k, op); err != nil {
return err
}
if err := v.(*Builder).WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
default:
if _, err := fmt.Fprintf(w, "%s%s?", k, op); err != nil {
return err
}
args = append(args, v)
}
if i != len(data)-1 {
if _, err := fmt.Fprint(w, " AND "); err != nil {
return err
}
}
i = i + 1
}
w.Append(args...)
return nil
}
// Lt defines < condition
type Lt map[string]interface{}
var _ Cond = Lt{}
// WriteTo write SQL to Writer
func (lt Lt) WriteTo(w Writer) error {
return WriteMap(w, lt, "<")
}
// And implements And with other conditions
func (lt Lt) And(conds ...Cond) Cond {
return condAnd{lt, And(conds...)}
}
// Or implements Or with other conditions
func (lt Lt) Or(conds ...Cond) Cond {
return condOr{lt, Or(conds...)}
}
// IsValid tests if this Eq is valid
func (lt Lt) IsValid() bool {
return len(lt) > 0
}
// Lte defines <= condition
type Lte map[string]interface{}
var _ Cond = Lte{}
// WriteTo write SQL to Writer
func (lte Lte) WriteTo(w Writer) error {
return WriteMap(w, lte, "<=")
}
// And implements And with other conditions
func (lte Lte) And(conds ...Cond) Cond {
return And(lte, And(conds...))
}
// Or implements Or with other conditions
func (lte Lte) Or(conds ...Cond) Cond {
return Or(lte, Or(conds...))
}
// IsValid tests if this Eq is valid
func (lte Lte) IsValid() bool {
return len(lte) > 0
}
// Gt defines > condition
type Gt map[string]interface{}
var _ Cond = Gt{}
// WriteTo write SQL to Writer
func (gt Gt) WriteTo(w Writer) error {
return WriteMap(w, gt, ">")
}
// And implements And with other conditions
func (gt Gt) And(conds ...Cond) Cond {
return And(gt, And(conds...))
}
// Or implements Or with other conditions
func (gt Gt) Or(conds ...Cond) Cond {
return Or(gt, Or(conds...))
}
// IsValid tests if this Eq is valid
func (gt Gt) IsValid() bool {
return len(gt) > 0
}
// Gte defines >= condition
type Gte map[string]interface{}
var _ Cond = Gte{}
// WriteTo write SQL to Writer
func (gte Gte) WriteTo(w Writer) error {
return WriteMap(w, gte, ">=")
}
// And implements And with other conditions
func (gte Gte) And(conds ...Cond) Cond {
return And(gte, And(conds...))
}
// Or implements Or with other conditions
func (gte Gte) Or(conds ...Cond) Cond {
return Or(gte, Or(conds...))
}
// IsValid tests if this Eq is valid
func (gte Gte) IsValid() bool {
return len(gte) > 0
}

117
builder/cond_eq.go Normal file
View File

@ -0,0 +1,117 @@
// 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 (
"fmt"
"sort"
)
// Incr implements a type used by Eq
type Incr int
// Decr implements a type used by Eq
type Decr int
// Eq defines equals conditions
type Eq map[string]interface{}
var _ Cond = Eq{}
// OpWriteTo writes conditions with special operator
func (eq Eq) OpWriteTo(op string, w Writer) error {
var i = 0
for _, k := range eq.sortedKeys() {
v := eq[k]
switch v.(type) {
case []int, []int64, []string, []int32, []int16, []int8, []uint, []uint64, []uint32, []uint16, []interface{}:
if err := In(k, v).WriteTo(w); err != nil {
return err
}
case expr:
if _, err := fmt.Fprintf(w, "%s=(", k); err != nil {
return err
}
if err := v.(expr).WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
case *Builder:
if _, err := fmt.Fprintf(w, "%s=(", k); err != nil {
return err
}
if err := v.(*Builder).WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
case Incr:
if _, err := fmt.Fprintf(w, "%s=%s+?", k, k); err != nil {
return err
}
w.Append(int(v.(Incr)))
case Decr:
if _, err := fmt.Fprintf(w, "%s=%s-?", k, k); err != nil {
return err
}
w.Append(int(v.(Decr)))
case nil:
if _, err := fmt.Fprintf(w, "%s=null", k); err != nil {
return err
}
default:
if _, err := fmt.Fprintf(w, "%s=?", k); err != nil {
return err
}
w.Append(v)
}
if i != len(eq)-1 {
if _, err := fmt.Fprint(w, op); err != nil {
return err
}
}
i = i + 1
}
return nil
}
// WriteTo writes SQL to Writer
func (eq Eq) WriteTo(w Writer) error {
return eq.OpWriteTo(" AND ", w)
}
// And implements And with other conditions
func (eq Eq) And(conds ...Cond) Cond {
return And(eq, And(conds...))
}
// Or implements Or with other conditions
func (eq Eq) Or(conds ...Cond) Cond {
return Or(eq, Or(conds...))
}
// IsValid tests if this Eq is valid
func (eq Eq) IsValid() bool {
return len(eq) > 0
}
// sortedKeys returns all keys of this Eq sorted with sort.Strings.
// It is used internally for consistent ordering when generating
// SQL, see https://gitea.com/xorm/builder/issues/10
func (eq Eq) sortedKeys() []string {
keys := make([]string, 0, len(eq))
for key := range eq {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}

43
builder/cond_expr.go Normal file
View File

@ -0,0 +1,43 @@
// 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 "fmt"
type expr struct {
sql string
args []interface{}
}
var _ Cond = expr{}
// Expr generate customerize SQL
func Expr(sql string, args ...interface{}) Cond {
return expr{sql, args}
}
func (expr expr) OpWriteTo(op string, w Writer) error {
return expr.WriteTo(w)
}
func (expr expr) WriteTo(w Writer) error {
if _, err := fmt.Fprint(w, expr.sql); err != nil {
return err
}
w.Append(expr.args...)
return nil
}
func (expr expr) And(conds ...Cond) Cond {
return And(expr, And(conds...))
}
func (expr expr) Or(conds ...Cond) Cond {
return Or(expr, Or(conds...))
}
func (expr expr) IsValid() bool {
return len(expr.sql) > 0
}

49
builder/cond_if.go Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2019 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
type condIf struct {
condition bool
condTrue Cond
condFalse Cond
}
var _ Cond = condIf{}
// If returns Cond via condition
func If(condition bool, condTrue Cond, condFalse ...Cond) Cond {
var c = condIf{
condition: condition,
condTrue: condTrue,
}
if len(condFalse) > 0 {
c.condFalse = condFalse[0]
}
return c
}
func (condIf condIf) WriteTo(w Writer) error {
if condIf.condition {
return condIf.condTrue.WriteTo(w)
} else if condIf.condFalse != nil {
return condIf.condFalse.WriteTo(w)
}
return nil
}
func (condIf condIf) And(conds ...Cond) Cond {
return And(condIf, And(conds...))
}
func (condIf condIf) Or(conds ...Cond) Cond {
return Or(condIf, Or(conds...))
}
func (condIf condIf) IsValid() bool {
if condIf.condition {
return condIf.condTrue != nil
}
return condIf.condFalse != nil
}

38
builder/cond_if_test.go Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2019 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 TestCond_If(t *testing.T) {
cond1 := If(1 > 0, Eq{"a": 1}, Eq{"b": 1})
sql, err := ToBoundSQL(cond1)
assert.NoError(t, err)
assert.EqualValues(t, "a=1", sql)
cond2 := If(1 < 0, Eq{"a": 1}, Eq{"b": 1})
sql, err = ToBoundSQL(cond2)
assert.NoError(t, err)
assert.EqualValues(t, "b=1", sql)
cond3 := If(1 > 0, cond2, Eq{"c": 1})
sql, err = ToBoundSQL(cond3)
assert.NoError(t, err)
assert.EqualValues(t, "b=1", sql)
cond4 := If(2 < 0, Eq{"d": "a"})
sql, err = ToBoundSQL(cond4)
assert.NoError(t, err)
assert.EqualValues(t, "", sql)
cond5 := And(cond1, cond2, cond3, cond4)
sql, err = ToBoundSQL(cond5)
assert.NoError(t, err)
assert.EqualValues(t, "a=1 AND b=1 AND b=1", sql)
}

237
builder/cond_in.go Normal file
View File

@ -0,0 +1,237 @@
// 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 (
"fmt"
"reflect"
"strings"
)
type condIn struct {
col string
vals []interface{}
}
var _ Cond = condIn{}
// In generates IN condition
func In(col string, values ...interface{}) Cond {
return condIn{col, values}
}
func (condIn condIn) handleBlank(w Writer) error {
_, err := fmt.Fprint(w, "0=1")
return err
}
func (condIn condIn) WriteTo(w Writer) error {
if len(condIn.vals) <= 0 {
return condIn.handleBlank(w)
}
switch condIn.vals[0].(type) {
case []int8:
vals := condIn.vals[0].([]int8)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int16:
vals := condIn.vals[0].([]int16)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int:
vals := condIn.vals[0].([]int)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int32:
vals := condIn.vals[0].([]int32)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int64:
vals := condIn.vals[0].([]int64)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint8:
vals := condIn.vals[0].([]uint8)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint16:
vals := condIn.vals[0].([]uint16)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint:
vals := condIn.vals[0].([]uint)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint32:
vals := condIn.vals[0].([]uint32)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint64:
vals := condIn.vals[0].([]uint64)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []string:
vals := condIn.vals[0].([]string)
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []interface{}:
vals := condIn.vals[0].([]interface{})
if len(vals) <= 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
w.Append(vals...)
case expr:
val := condIn.vals[0].(expr)
if _, err := fmt.Fprintf(w, "%s IN (", condIn.col); err != nil {
return err
}
if err := val.WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
case *Builder:
bd := condIn.vals[0].(*Builder)
if _, err := fmt.Fprintf(w, "%s IN (", condIn.col); err != nil {
return err
}
if err := bd.WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
default:
v := reflect.ValueOf(condIn.vals[0])
if v.Kind() == reflect.Slice {
l := v.Len()
if l == 0 {
return condIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", l)
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for i := 0; i < l; i++ {
w.Append(v.Index(i).Interface())
}
} else {
questionMark := strings.Repeat("?,", len(condIn.vals))
if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
w.Append(condIn.vals...)
}
}
return nil
}
func (condIn condIn) And(conds ...Cond) Cond {
return And(condIn, And(conds...))
}
func (condIn condIn) Or(conds ...Cond) Cond {
return Or(condIn, Or(conds...))
}
func (condIn condIn) IsValid() bool {
return len(condIn.col) > 0 && len(condIn.vals) > 0
}

41
builder/cond_like.go Normal file
View File

@ -0,0 +1,41 @@
// 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 "fmt"
// Like defines like condition
type Like [2]string
var _ Cond = Like{"", ""}
// WriteTo write SQL to Writer
func (like Like) WriteTo(w Writer) error {
if _, err := fmt.Fprintf(w, "%s LIKE ?", like[0]); err != nil {
return err
}
// FIXME: if use other regular express, this will be failed. but for compatible, keep this
if like[1][0] == '%' || like[1][len(like[1])-1] == '%' {
w.Append(like[1])
} else {
w.Append("%" + like[1] + "%")
}
return nil
}
// And implements And with other conditions
func (like Like) And(conds ...Cond) Cond {
return And(like, And(conds...))
}
// Or implements Or with other conditions
func (like Like) Or(conds ...Cond) Cond {
return Or(like, Or(conds...))
}
// IsValid tests if this condition is valid
func (like Like) IsValid() bool {
return len(like[0]) > 0 && len(like[1]) > 0
}

94
builder/cond_neq.go Normal file
View File

@ -0,0 +1,94 @@
// 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 (
"fmt"
"sort"
)
// Neq defines not equal conditions
type Neq map[string]interface{}
var _ Cond = Neq{}
// WriteTo writes SQL to Writer
func (neq Neq) WriteTo(w Writer) error {
var args = make([]interface{}, 0, len(neq))
var i = 0
for _, k := range neq.sortedKeys() {
v := neq[k]
switch v.(type) {
case []int, []int64, []string, []int32, []int16, []int8:
if err := NotIn(k, v).WriteTo(w); err != nil {
return err
}
case expr:
if _, err := fmt.Fprintf(w, "%s<>(", k); err != nil {
return err
}
if err := v.(expr).WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
case *Builder:
if _, err := fmt.Fprintf(w, "%s<>(", k); err != nil {
return err
}
if err := v.(*Builder).WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
default:
if _, err := fmt.Fprintf(w, "%s<>?", k); err != nil {
return err
}
args = append(args, v)
}
if i != len(neq)-1 {
if _, err := fmt.Fprint(w, " AND "); err != nil {
return err
}
}
i = i + 1
}
w.Append(args...)
return nil
}
// And implements And with other conditions
func (neq Neq) And(conds ...Cond) Cond {
return And(neq, And(conds...))
}
// Or implements Or with other conditions
func (neq Neq) Or(conds ...Cond) Cond {
return Or(neq, Or(conds...))
}
// IsValid tests if this condition is valid
func (neq Neq) IsValid() bool {
return len(neq) > 0
}
// sortedKeys returns all keys of this Neq sorted with sort.Strings.
// It is used internally for consistent ordering when generating
// SQL, see https://gitea.com/xorm/builder/issues/10
func (neq Neq) sortedKeys() []string {
keys := make([]string, 0, len(neq))
for key := range neq {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}

77
builder/cond_not.go Normal file
View File

@ -0,0 +1,77 @@
// 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 "fmt"
// Not defines NOT condition
type Not [1]Cond
var _ Cond = Not{}
// WriteTo writes SQL to Writer
func (not Not) WriteTo(w Writer) error {
if _, err := fmt.Fprint(w, "NOT "); err != nil {
return err
}
switch not[0].(type) {
case condAnd, condOr:
if _, err := fmt.Fprint(w, "("); err != nil {
return err
}
case Eq:
if len(not[0].(Eq)) > 1 {
if _, err := fmt.Fprint(w, "("); err != nil {
return err
}
}
case Neq:
if len(not[0].(Neq)) > 1 {
if _, err := fmt.Fprint(w, "("); err != nil {
return err
}
}
}
if err := not[0].WriteTo(w); err != nil {
return err
}
switch not[0].(type) {
case condAnd, condOr:
if _, err := fmt.Fprint(w, ")"); err != nil {
return err
}
case Eq:
if len(not[0].(Eq)) > 1 {
if _, err := fmt.Fprint(w, ")"); err != nil {
return err
}
}
case Neq:
if len(not[0].(Neq)) > 1 {
if _, err := fmt.Fprint(w, ")"); err != nil {
return err
}
}
}
return nil
}
// And implements And with other conditions
func (not Not) And(conds ...Cond) Cond {
return And(not, And(conds...))
}
// Or implements Or with other conditions
func (not Not) Or(conds ...Cond) Cond {
return Or(not, Or(conds...))
}
// IsValid tests if this condition is valid
func (not Not) IsValid() bool {
return not[0] != nil && not[0].IsValid()
}

234
builder/cond_notin.go Normal file
View File

@ -0,0 +1,234 @@
// 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 (
"fmt"
"reflect"
"strings"
)
type condNotIn condIn
var _ Cond = condNotIn{}
// NotIn generate NOT IN condition
func NotIn(col string, values ...interface{}) Cond {
return condNotIn{col, values}
}
func (condNotIn condNotIn) handleBlank(w Writer) error {
_, err := fmt.Fprint(w, "0=0")
return err
}
func (condNotIn condNotIn) WriteTo(w Writer) error {
if len(condNotIn.vals) <= 0 {
return condNotIn.handleBlank(w)
}
switch condNotIn.vals[0].(type) {
case []int8:
vals := condNotIn.vals[0].([]int8)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int16:
vals := condNotIn.vals[0].([]int16)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int:
vals := condNotIn.vals[0].([]int)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int32:
vals := condNotIn.vals[0].([]int32)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []int64:
vals := condNotIn.vals[0].([]int64)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint8:
vals := condNotIn.vals[0].([]uint8)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint16:
vals := condNotIn.vals[0].([]uint16)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint:
vals := condNotIn.vals[0].([]uint)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint32:
vals := condNotIn.vals[0].([]uint32)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []uint64:
vals := condNotIn.vals[0].([]uint64)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []string:
vals := condNotIn.vals[0].([]string)
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for _, val := range vals {
w.Append(val)
}
case []interface{}:
vals := condNotIn.vals[0].([]interface{})
if len(vals) <= 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", len(vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
w.Append(vals...)
case expr:
val := condNotIn.vals[0].(expr)
if _, err := fmt.Fprintf(w, "%s NOT IN (", condNotIn.col); err != nil {
return err
}
if err := val.WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
case *Builder:
val := condNotIn.vals[0].(*Builder)
if _, err := fmt.Fprintf(w, "%s NOT IN (", condNotIn.col); err != nil {
return err
}
if err := val.WriteTo(w); err != nil {
return err
}
if _, err := fmt.Fprintf(w, ")"); err != nil {
return err
}
default:
v := reflect.ValueOf(condNotIn.vals[0])
if v.Kind() == reflect.Slice {
l := v.Len()
if l == 0 {
return condNotIn.handleBlank(w)
}
questionMark := strings.Repeat("?,", l)
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
for i := 0; i < l; i++ {
w.Append(v.Index(i).Interface())
}
} else {
questionMark := strings.Repeat("?,", len(condNotIn.vals))
if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil {
return err
}
w.Append(condNotIn.vals...)
}
}
return nil
}
func (condNotIn condNotIn) And(conds ...Cond) Cond {
return And(condNotIn, And(conds...))
}
func (condNotIn condNotIn) Or(conds ...Cond) Cond {
return Or(condNotIn, Or(conds...))
}
func (condNotIn condNotIn) IsValid() bool {
return len(condNotIn.col) > 0 && len(condNotIn.vals) > 0
}

59
builder/cond_null.go Normal file
View File

@ -0,0 +1,59 @@
// 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 "fmt"
// IsNull defines IS NULL condition
type IsNull [1]string
var _ Cond = IsNull{""}
// WriteTo write SQL to Writer
func (isNull IsNull) WriteTo(w Writer) error {
_, err := fmt.Fprintf(w, "%s IS NULL", isNull[0])
return err
}
// And implements And with other conditions
func (isNull IsNull) And(conds ...Cond) Cond {
return And(isNull, And(conds...))
}
// Or implements Or with other conditions
func (isNull IsNull) Or(conds ...Cond) Cond {
return Or(isNull, Or(conds...))
}
// IsValid tests if this condition is valid
func (isNull IsNull) IsValid() bool {
return len(isNull[0]) > 0
}
// NotNull defines NOT NULL condition
type NotNull [1]string
var _ Cond = NotNull{""}
// WriteTo write SQL to Writer
func (notNull NotNull) WriteTo(w Writer) error {
_, err := fmt.Fprintf(w, "%s IS NOT NULL", notNull[0])
return err
}
// And implements And with other conditions
func (notNull NotNull) And(conds ...Cond) Cond {
return And(notNull, And(conds...))
}
// Or implements Or with other conditions
func (notNull NotNull) Or(conds ...Cond) Cond {
return Or(notNull, Or(conds...))
}
// IsValid tests if this condition is valid
func (notNull NotNull) IsValid() bool {
return len(notNull[0]) > 0
}

69
builder/cond_or.go Normal file
View File

@ -0,0 +1,69 @@
// 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 "fmt"
type condOr []Cond
var _ Cond = condOr{}
// Or sets OR conditions
func Or(conds ...Cond) Cond {
var result = make(condOr, 0, len(conds))
for _, cond := range conds {
if cond == nil || !cond.IsValid() {
continue
}
result = append(result, cond)
}
return result
}
// WriteTo implments Cond
func (o condOr) WriteTo(w Writer) error {
for i, cond := range o {
var needQuote bool
switch cond.(type) {
case condAnd, expr:
needQuote = true
case Eq:
needQuote = (len(cond.(Eq)) > 1)
case Neq:
needQuote = (len(cond.(Neq)) > 1)
}
if needQuote {
fmt.Fprint(w, "(")
}
err := cond.WriteTo(w)
if err != nil {
return err
}
if needQuote {
fmt.Fprint(w, ")")
}
if i != len(o)-1 {
fmt.Fprint(w, " OR ")
}
}
return nil
}
func (o condOr) And(conds ...Cond) Cond {
return And(o, And(conds...))
}
func (o condOr) Or(conds ...Cond) Cond {
return Or(o, Or(conds...))
}
func (o condOr) IsValid() bool {
return len(o) > 0
}

11
builder/cond_test.go Normal file
View File

@ -0,0 +1,11 @@
// 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"
func TestCond_NotIn(t *testing.T) {
}

120
builder/doc.go Normal file
View File

@ -0,0 +1,120 @@
// Copyright 2016 The XORM Authors. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.
/*
Package builder is a simple and powerful sql builder for Go.
Make sure you have installed Go 1.1+ and then:
go get xorm.io/builder
WARNNING: Currently, only query conditions are supported. Below is the supported conditions.
1. Eq is a redefine of a map, you can give one or more conditions to Eq
import . "xorm.io/builder"
sql, args, _ := ToSQL(Eq{"a":1})
// a=? [1]
sql, args, _ := ToSQL(Eq{"b":"c"}.And(Eq{"c": 0}))
// b=? AND c=? ["c", 0]
sql, args, _ := ToSQL(Eq{"b":"c", "c":0})
// b=? AND c=? ["c", 0]
sql, args, _ := ToSQL(Eq{"b":"c"}.Or(Eq{"b":"d"}))
// b=? OR b=? ["c", "d"]
sql, args, _ := ToSQL(Eq{"b": []string{"c", "d"}})
// b IN (?,?) ["c", "d"]
sql, args, _ := ToSQL(Eq{"b": 1, "c":[]int{2, 3}})
// b=? AND c IN (?,?) [1, 2, 3]
2. Neq is the same to Eq
import . "xorm.io/builder"
sql, args, _ := ToSQL(Neq{"a":1})
// a<>? [1]
sql, args, _ := ToSQL(Neq{"b":"c"}.And(Neq{"c": 0}))
// b<>? AND c<>? ["c", 0]
sql, args, _ := ToSQL(Neq{"b":"c", "c":0})
// b<>? AND c<>? ["c", 0]
sql, args, _ := ToSQL(Neq{"b":"c"}.Or(Neq{"b":"d"}))
// b<>? OR b<>? ["c", "d"]
sql, args, _ := ToSQL(Neq{"b": []string{"c", "d"}})
// b NOT IN (?,?) ["c", "d"]
sql, args, _ := ToSQL(Neq{"b": 1, "c":[]int{2, 3}})
// b<>? AND c NOT IN (?,?) [1, 2, 3]
3. Gt, Gte, Lt, Lte
import . "xorm.io/builder"
sql, args, _ := ToSQL(Gt{"a", 1}.And(Gte{"b", 2}))
// a>? AND b>=? [1, 2]
sql, args, _ := ToSQL(Lt{"a", 1}.Or(Lte{"b", 2}))
// a<? OR b<=? [1, 2]
4. Like
import . "xorm.io/builder"
sql, args, _ := ToSQL(Like{"a", "c"})
// a LIKE ? [%c%]
5. Expr you can customerize your sql with Expr
import . "xorm.io/builder"
sql, args, _ := ToSQL(Expr("a = ? ", 1))
// a = ? [1]
sql, args, _ := ToSQL(Eq{"a": Expr("select id from table where c = ?", 1)})
// a=(select id from table where c = ?) [1]
6. In and NotIn
import . "xorm.io/builder"
sql, args, _ := ToSQL(In("a", 1, 2, 3))
// a IN (?,?,?) [1,2,3]
sql, args, _ := ToSQL(In("a", []int{1, 2, 3}))
// a IN (?,?,?) [1,2,3]
sql, args, _ := ToSQL(In("a", Expr("select id from b where c = ?", 1))))
// a IN (select id from b where c = ?) [1]
7. IsNull and NotNull
import . "xorm.io/builder"
sql, args, _ := ToSQL(IsNull{"a"})
// a IS NULL []
sql, args, _ := ToSQL(NotNull{"b"})
// b IS NOT NULL []
8. And(conds ...Cond), And can connect one or more condtions via AND
import . "xorm.io/builder"
sql, args, _ := ToSQL(And(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
// a=? AND b LIKE ? AND d<>? [1, %c%, 2]
9. Or(conds ...Cond), Or can connect one or more conditions via Or
import . "xorm.io/builder"
sql, args, _ := ToSQL(Or(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2}))
// a=? OR b LIKE ? OR d<>? [1, %c%, 2]
sql, args, _ := ToSQL(Or(Eq{"a":1}, And(Like{"b", "c"}, Neq{"d", 2})))
// a=? OR (b LIKE ? AND d<>?) [1, %c%, 2]
10. Between
import . "xorm.io/builder"
sql, args, _ := ToSQL(Between("a", 1, 2))
// a BETWEEN 1 AND 2
11. define yourself conditions
Since Cond is a interface, you can define yourself conditions and compare with them
*/
package builder

40
builder/error.go Normal file
View File

@ -0,0 +1,40 @@
// 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 "errors"
var (
// ErrNotSupportType not supported SQL type error
ErrNotSupportType = errors.New("Not supported SQL type")
// ErrNoNotInConditions no NOT IN params error
ErrNoNotInConditions = errors.New("No NOT IN conditions")
// ErrNoInConditions no IN params error
ErrNoInConditions = errors.New("No IN conditions")
// ErrNeedMoreArguments need more arguments
ErrNeedMoreArguments = errors.New("Need more sql arguments")
// ErrNoTableName no table name
ErrNoTableName = errors.New("No table indicated")
// ErrNoColumnToUpdate no column to update
ErrNoColumnToUpdate = errors.New("No column(s) to update")
// ErrNoColumnToInsert no column to insert
ErrNoColumnToInsert = errors.New("No column(s) to insert")
// ErrNotSupportDialectType not supported dialect type error
ErrNotSupportDialectType = errors.New("Not supported dialect type")
// ErrNotUnexpectedUnionConditions using union in a wrong way
ErrNotUnexpectedUnionConditions = errors.New("Unexpected conditional fields in UNION query")
// ErrUnsupportedUnionMembers unexpected members in UNION query
ErrUnsupportedUnionMembers = errors.New("Unexpected members in UNION query")
// ErrUnexpectedSubQuery Unexpected sub-query in SELECT query
ErrUnexpectedSubQuery = errors.New("Unexpected sub-query in SELECT query")
// ErrDialectNotSetUp dialect is not setup yet
ErrDialectNotSetUp = errors.New("Dialect is not setup yet, try to use `Dialect(dbType)` at first")
// ErrInvalidLimitation offset or limit is not correct
ErrInvalidLimitation = errors.New("Offset or limit is not correct")
// ErrUnnamedDerivedTable Every derived table must have its own alias
ErrUnnamedDerivedTable = errors.New("Every derived table must have its own alias")
// ErrInconsistentDialect Inconsistent dialect in same builder
ErrInconsistentDialect = errors.New("Inconsistent dialect in same builder")
)

168
builder/sql.go Normal file
View File

@ -0,0 +1,168 @@
// 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 (
sql2 "database/sql"
"fmt"
"reflect"
"strings"
"time"
)
func condToSQL(cond Cond) (string, []interface{}, error) {
if cond == nil || !cond.IsValid() {
return "", nil, nil
}
w := NewWriter()
if err := cond.WriteTo(w); err != nil {
return "", nil, err
}
return w.String(), w.args, nil
}
func condToBoundSQL(cond Cond) (string, error) {
if cond == nil || !cond.IsValid() {
return "", nil
}
w := NewWriter()
if err := cond.WriteTo(w); err != nil {
return "", err
}
return ConvertToBoundSQL(w.String(), w.args)
}
// ToSQL convert a builder or conditions to SQL and args
func ToSQL(cond interface{}) (string, []interface{}, error) {
switch cond.(type) {
case Cond:
return condToSQL(cond.(Cond))
case *Builder:
return cond.(*Builder).ToSQL()
}
return "", nil, ErrNotSupportType
}
// ToBoundSQL convert a builder or conditions to parameters bound SQL
func ToBoundSQL(cond interface{}) (string, error) {
switch cond.(type) {
case Cond:
return condToBoundSQL(cond.(Cond))
case *Builder:
return cond.(*Builder).ToBoundSQL()
}
return "", ErrNotSupportType
}
func noSQLQuoteNeeded(a interface{}) bool {
if a == nil {
return false
}
switch a.(type) {
case int, int8, int16, int32, int64:
return true
case uint, uint8, uint16, uint32, uint64:
return true
case float32, float64:
return true
case bool:
return true
case string:
return false
case time.Time, *time.Time:
return false
}
t := reflect.TypeOf(a)
switch t.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.Bool:
return true
case reflect.String:
return false
}
return false
}
// ConvertToBoundSQL will convert SQL and args to a bound SQL
func ConvertToBoundSQL(sql string, args []interface{}) (string, error) {
buf := strings.Builder{}
var i, j, start int
for ; i < len(sql); i++ {
if sql[i] == '?' {
_, err := buf.WriteString(sql[start:i])
if err != nil {
return "", err
}
start = i + 1
if len(args) == j {
return "", ErrNeedMoreArguments
}
arg := args[j]
if namedArg, ok := arg.(sql2.NamedArg); ok {
arg = namedArg.Value
}
if noSQLQuoteNeeded(arg) {
_, err = fmt.Fprint(&buf, arg)
} else {
// replace ' -> '' (standard replacement) to avoid critical SQL injection,
// NOTICE: may allow some injection like % (or _) in LIKE query
_, err = fmt.Fprintf(&buf, "'%v'", strings.Replace(fmt.Sprintf("%v", arg), "'",
"''", -1))
}
if err != nil {
return "", err
}
j = j + 1
}
}
_, err := buf.WriteString(sql[start:])
if err != nil {
return "", err
}
return buf.String(), nil
}
// ConvertPlaceholder replaces the place holder ? to $1, $2 ... or :1, :2 ... according prefix
func ConvertPlaceholder(sql, prefix string) (string, error) {
buf := strings.Builder{}
var i, j, start int
var ready = true
for ; i < len(sql); i++ {
if sql[i] == '\'' && i > 0 && sql[i-1] != '\\' {
ready = !ready
}
if ready && sql[i] == '?' {
if _, err := buf.WriteString(sql[start:i]); err != nil {
return "", err
}
start = i + 1
j = j + 1
if _, err := buf.WriteString(fmt.Sprintf("%v%d", prefix, j)); err != nil {
return "", err
}
}
}
if _, err := buf.WriteString(sql[start:]); err != nil {
return "", err
}
return buf.String(), nil
}

257
builder/sql_test.go Normal file
View File

@ -0,0 +1,257 @@
// 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 (
sql2 "database/sql"
"fmt"
"io/ioutil"
"os"
"testing"
"gitea.com/xorm/sqlfiddle"
"github.com/stretchr/testify/assert"
)
const placeholderConverterSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=?) AND id=? AND c=? AND d=? AND e=? AND f=?"
const placeholderConvertedSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=$1) AND id=$2 AND c=$3 AND d=$4 AND e=$5 AND f=$6"
const placeholderBoundSQL = "SELECT a, b FROM table_a WHERE b_id=(SELECT id FROM table_b WHERE b=1) AND id=2.1 AND c='3' AND d=4 AND e='5' AND f=true"
func TestNoSQLQuoteNeeded(t *testing.T) {
assert.False(t, noSQLQuoteNeeded(nil))
}
func TestPlaceholderConverter(t *testing.T) {
var convertCases = []struct {
before, after string
mark string
}{
{
before: placeholderConverterSQL,
after: placeholderConvertedSQL,
mark: "$",
},
{
before: "SELECT a, b, 'a?b' FROM table_a WHERE id=?",
after: "SELECT a, b, 'a?b' FROM table_a WHERE id=:1",
mark: ":",
},
{
before: "SELECT a, b, 'a\\'?b' FROM table_a WHERE id=?",
after: "SELECT a, b, 'a\\'?b' FROM table_a WHERE id=$1",
mark: "$",
},
{
before: "SELECT a, b, 'a\\'b' FROM table_a WHERE id=?",
after: "SELECT a, b, 'a\\'b' FROM table_a WHERE id=$1",
mark: "$",
},
}
for _, kase := range convertCases {
t.Run(kase.before, func(t *testing.T) {
newSQL, err := ConvertPlaceholder(kase.before, kase.mark)
assert.NoError(t, err)
assert.EqualValues(t, kase.after, newSQL)
})
}
}
func BenchmarkPlaceholderConverter(b *testing.B) {
for i := 0; i < b.N; i++ {
ConvertPlaceholder(placeholderConverterSQL, "$")
}
}
func TestBoundSQLConverter(t *testing.T) {
newSQL, err := ConvertToBoundSQL(placeholderConverterSQL, []interface{}{1, 2.1, "3", uint(4), "5", true})
assert.NoError(t, err)
assert.EqualValues(t, placeholderBoundSQL, newSQL)
newSQL, err = ConvertToBoundSQL(placeholderConverterSQL, []interface{}{1, 2.1, sql2.Named("any", "3"), uint(4), "5", true})
assert.NoError(t, err)
assert.EqualValues(t, placeholderBoundSQL, newSQL)
newSQL, err = ConvertToBoundSQL(placeholderConverterSQL, []interface{}{1, 2.1, "3", 4, "5"})
assert.Error(t, err)
assert.EqualValues(t, ErrNeedMoreArguments, err)
newSQL, err = ToBoundSQL(Select("id").From("table").Where(In("a", 1, 2)))
assert.NoError(t, err)
assert.EqualValues(t, "SELECT id FROM table WHERE a IN (1,2)", newSQL)
newSQL, err = ToBoundSQL(Eq{"a": 1})
assert.NoError(t, err)
assert.EqualValues(t, "a=1", newSQL)
newSQL, err = ToBoundSQL(1)
assert.Error(t, err)
assert.EqualValues(t, ErrNotSupportType, err)
}
func TestSQL(t *testing.T) {
newSQL, args, err := ToSQL(In("a", 1, 2))
assert.NoError(t, err)
assert.EqualValues(t, "a IN (?,?)", newSQL)
assert.EqualValues(t, []interface{}{1, 2}, args)
newSQL, args, err = ToSQL(Select("id").From("table").Where(In("a", 1, 2)))
assert.NoError(t, err)
assert.EqualValues(t, "SELECT id FROM table WHERE a IN (?,?)", newSQL)
assert.EqualValues(t, []interface{}{1, 2}, args)
newSQL, args, err = ToSQL(1)
assert.Error(t, err)
assert.EqualValues(t, ErrNotSupportType, err)
}
type fiddler struct {
sessionCode string
dbType int
f *sqlfiddle.Fiddle
}
func readPreparationSQLFromFile(path string) (string, error) {
file, err := os.Open(path)
defer file.Close()
if err != nil {
return "", err
}
data, err := ioutil.ReadAll(file)
if err != nil {
return "", err
}
return string(data), nil
}
func newFiddler(fiddleServerAddr, dbDialect, preparationSQL string) (*fiddler, error) {
var dbType int
switch dbDialect {
case MYSQL:
dbType = sqlfiddle.Mysql5_6
case MSSQL:
dbType = sqlfiddle.MSSQL2017
case POSTGRES:
dbType = sqlfiddle.PostgreSQL96
case ORACLE:
dbType = sqlfiddle.Oracle11gR2
case SQLITE:
dbType = sqlfiddle.SQLite_WebSQL
default:
return nil, ErrNotSupportDialectType
}
f := sqlfiddle.NewFiddle(fiddleServerAddr)
response, err := f.CreateSchema(dbType, preparationSQL)
if err != nil {
return nil, err
}
return &fiddler{sessionCode: response.Code, f: f, dbType: dbType}, nil
}
func (f *fiddler) executableCheck(obj interface{}) error {
var sql string
var err error
switch obj.(type) {
case *Builder:
sql, err = obj.(*Builder).ToBoundSQL()
if err != nil {
return err
}
case string:
sql = obj.(string)
}
_, err = f.f.RunSQL(f.dbType, f.sessionCode, sql)
if err != nil {
return err
}
return nil
}
func TestReadPreparationSQLFromFile(t *testing.T) {
sqlFromFile, err := readPreparationSQLFromFile("testdata/mysql_fiddle_data.sql")
assert.NoError(t, err)
fmt.Println(sqlFromFile)
}
func TestNewFiddler(t *testing.T) {
sqlFromFile, err := readPreparationSQLFromFile("testdata/mysql_fiddle_data.sql")
assert.NoError(t, err)
f, err := newFiddler("", MYSQL, sqlFromFile)
assert.NoError(t, err)
assert.NotEmpty(t, f.sessionCode)
}
func TestExecutableCheck(t *testing.T) {
sqlFromFile, err := readPreparationSQLFromFile("testdata/mysql_fiddle_data.sql")
assert.NoError(t, err)
f, err := newFiddler("", MYSQL, sqlFromFile)
assert.NoError(t, err)
assert.NotEmpty(t, f.sessionCode)
assert.NoError(t, f.executableCheck("SELECT * FROM table1"))
err = f.executableCheck("SELECT * FROM table3")
assert.Error(t, err)
}
func TestToSQLInDifferentDialects(t *testing.T) {
sql, args, err := Postgres().Select().From("table1").Where(Eq{"a": "1"}.And(Neq{"b": "100"})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE a=$1 AND b<>$2", sql)
assert.EqualValues(t, []interface{}{"1", "100"}, args)
sql, args, err = MySQL().Select().From("table1").Where(Eq{"a": "1"}.And(Neq{"b": "100"})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE a=? AND b<>?", sql)
assert.EqualValues(t, []interface{}{"1", "100"}, args)
sql, args, err = MsSQL().Select().From("table1").Where(Eq{"a": "1"}.And(Neq{"b": "100"})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE a=@p1 AND b<>@p2", sql)
assert.EqualValues(t, []interface{}{sql2.Named("p1", "1"), sql2.Named("p2", "100")}, args)
// test sql.NamedArg in cond
sql, args, err = MsSQL().Select().From("table1").Where(Eq{"a": sql2.NamedArg{Name: "param", Value: "1"}}.And(Neq{"b": "100"})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE a=@p1 AND b<>@p2", sql)
assert.EqualValues(t, []interface{}{sql2.Named("p1", "1"), sql2.Named("p2", "100")}, args)
sql, args, err = Oracle().Select().From("table1").Where(Eq{"a": "1"}.And(Neq{"b": "100"})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE a=:p1 AND b<>:p2", sql)
assert.EqualValues(t, []interface{}{sql2.Named("p1", "1"), sql2.Named("p2", "100")}, args)
// test sql.NamedArg in cond
sql, args, err = Oracle().Select().From("table1").Where(Eq{"a": sql2.Named("a", "1")}.And(Neq{"b": "100"})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE a=:p1 AND b<>:p2", sql)
assert.EqualValues(t, []interface{}{sql2.Named("p1", "1"), sql2.Named("p2", "100")}, args)
sql, args, err = SQLite().Select().From("table1").Where(Eq{"a": "1"}.And(Neq{"b": "100"})).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE a=? AND b<>?", sql)
assert.EqualValues(t, []interface{}{"1", "100"}, args)
}
func TestToSQLInjectionHarmlessDisposal(t *testing.T) {
sql, err := MySQL().Select("*").From("table1").Where(Cond(Eq{"name": "cat';truncate table table1;"})).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "SELECT * FROM table1 WHERE name='cat'';truncate table table1;'", sql)
sql, err = MySQL().Update(Eq{`a`: 1, `b`: nil}).From(`table1`).ToBoundSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=1,b=null", sql)
sql, args, err := MySQL().Update(Eq{`a`: 1, `b`: nil}).From(`table1`).ToSQL()
assert.NoError(t, err)
assert.EqualValues(t, "UPDATE table1 SET a=?,b=null", sql)
assert.EqualValues(t, []interface{}{1}, args)
}

213
builder/testdata/mssql_fiddle_data.sql vendored Normal file
View File

@ -0,0 +1,213 @@
create table table1 (
id int primary key,
a varchar(40),
b varchar(40),
c varchar(40)
);
create table table2 (
id int primary key,
ref_id int,
d varchar(40)
);
INSERT INTO table1 (id, a, b, c)
VALUES (0, '0', '0', '0');
INSERT INTO table1 (id, a, b, c)
VALUES (1, '1', '1', '1');
INSERT INTO table1 (id, a, b, c)
VALUES (2, '2', '2', '2');
INSERT INTO table1 (id, a, b, c)
VALUES (3, '3', '3', '3');
INSERT INTO table1 (id, a, b, c)
VALUES (4, '4', '4', '4');
INSERT INTO table1 (id, a, b, c)
VALUES (5, '5', '5', '5');
INSERT INTO table1 (id, a, b, c)
VALUES (6, '6', '6', '6');
INSERT INTO table1 (id, a, b, c)
VALUES (7, '7', '7', '7');
INSERT INTO table1 (id, a, b, c)
VALUES (8, '8', '8', '8');
INSERT INTO table1 (id, a, b, c)
VALUES (9, '9', '9', '9');
INSERT INTO table1 (id, a, b, c)
VALUES (10, '10', '10', '10');
INSERT INTO table1 (id, a, b, c)
VALUES (11, '11', '11', '11');
INSERT INTO table1 (id, a, b, c)
VALUES (12, '12', '12', '12');
INSERT INTO table1 (id, a, b, c)
VALUES (13, '13', '13', '13');
INSERT INTO table1 (id, a, b, c)
VALUES (14, '14', '14', '14');
INSERT INTO table1 (id, a, b, c)
VALUES (15, '15', '15', '15');
INSERT INTO table1 (id, a, b, c)
VALUES (16, '16', '16', '16');
INSERT INTO table1 (id, a, b, c)
VALUES (17, '17', '17', '17');
INSERT INTO table1 (id, a, b, c)
VALUES (18, '18', '18', '18');
INSERT INTO table1 (id, a, b, c)
VALUES (19, '19', '19', '19');
INSERT INTO table1 (id, a, b, c)
VALUES (20, '20', '20', '20');
INSERT INTO table1 (id, a, b, c)
VALUES (21, '21', '21', '21');
INSERT INTO table1 (id, a, b, c)
VALUES (22, '22', '22', '22');
INSERT INTO table1 (id, a, b, c)
VALUES (23, '23', '23', '23');
INSERT INTO table1 (id, a, b, c)
VALUES (24, '24', '24', '24');
INSERT INTO table1 (id, a, b, c)
VALUES (25, '25', '25', '25');
INSERT INTO table1 (id, a, b, c)
VALUES (26, '26', '26', '26');
INSERT INTO table1 (id, a, b, c)
VALUES (27, '27', '27', '27');
INSERT INTO table1 (id, a, b, c)
VALUES (28, '28', '28', '28');
INSERT INTO table1 (id, a, b, c)
VALUES (29, '29', '29', '29');
INSERT INTO table1 (id, a, b, c)
VALUES (30, '30', '30', '30');
INSERT INTO table1 (id, a, b, c)
VALUES (31, '31', '31', '31');
INSERT INTO table1 (id, a, b, c)
VALUES (32, '32', '32', '32');
INSERT INTO table1 (id, a, b, c)
VALUES (33, '33', '33', '33');
INSERT INTO table1 (id, a, b, c)
VALUES (34, '34', '34', '34');
INSERT INTO table1 (id, a, b, c)
VALUES (35, '35', '35', '35');
INSERT INTO table1 (id, a, b, c)
VALUES (36, '36', '36', '36');
INSERT INTO table1 (id, a, b, c)
VALUES (37, '37', '37', '37');
INSERT INTO table1 (id, a, b, c)
VALUES (38, '38', '38', '38');
INSERT INTO table1 (id, a, b, c)
VALUES (39, '39', '39', '39');
INSERT INTO table1 (id, a, b, c)
VALUES (40, '40', '40', '40');
INSERT INTO table1 (id, a, b, c)
VALUES (41, '41', '41', '41');
INSERT INTO table1 (id, a, b, c)
VALUES (42, '42', '42', '42');
INSERT INTO table1 (id, a, b, c)
VALUES (43, '43', '43', '43');
INSERT INTO table1 (id, a, b, c)
VALUES (44, '44', '44', '44');
INSERT INTO table1 (id, a, b, c)
VALUES (45, '45', '45', '45');
INSERT INTO table1 (id, a, b, c)
VALUES (46, '46', '46', '46');
INSERT INTO table1 (id, a, b, c)
VALUES (47, '47', '47', '47');
INSERT INTO table1 (id, a, b, c)
VALUES (48, '48', '48', '48');
INSERT INTO table1 (id, a, b, c)
VALUES (49, '49', '49', '49');
INSERT INTO table2 (id, ref_id, d)
VALUES (0, '0', '0');
INSERT INTO table2 (id, ref_id, d)
VALUES (1, '1', '1');
INSERT INTO table2 (id, ref_id, d)
VALUES (2, '2', '2');
INSERT INTO table2 (id, ref_id, d)
VALUES (3, '3', '3');
INSERT INTO table2 (id, ref_id, d)
VALUES (4, '4', '4');
INSERT INTO table2 (id, ref_id, d)
VALUES (5, '5', '5');
INSERT INTO table2 (id, ref_id, d)
VALUES (6, '6', '6');
INSERT INTO table2 (id, ref_id, d)
VALUES (7, '7', '7');
INSERT INTO table2 (id, ref_id, d)
VALUES (8, '8', '8');
INSERT INTO table2 (id, ref_id, d)
VALUES (9, '9', '9');
INSERT INTO table2 (id, ref_id, d)
VALUES (10, '10', '10');
INSERT INTO table2 (id, ref_id, d)
VALUES (11, '11', '11');
INSERT INTO table2 (id, ref_id, d)
VALUES (12, '12', '12');
INSERT INTO table2 (id, ref_id, d)
VALUES (13, '13', '13');
INSERT INTO table2 (id, ref_id, d)
VALUES (14, '14', '14');
INSERT INTO table2 (id, ref_id, d)
VALUES (15, '15', '15');
INSERT INTO table2 (id, ref_id, d)
VALUES (16, '16', '16');
INSERT INTO table2 (id, ref_id, d)
VALUES (17, '17', '17');
INSERT INTO table2 (id, ref_id, d)
VALUES (18, '18', '18');
INSERT INTO table2 (id, ref_id, d)
VALUES (19, '19', '19');
INSERT INTO table2 (id, ref_id, d)
VALUES (20, '20', '20');
INSERT INTO table2 (id, ref_id, d)
VALUES (21, '21', '21');
INSERT INTO table2 (id, ref_id, d)
VALUES (22, '22', '22');
INSERT INTO table2 (id, ref_id, d)
VALUES (23, '23', '23');
INSERT INTO table2 (id, ref_id, d)
VALUES (24, '24', '24');
INSERT INTO table2 (id, ref_id, d)
VALUES (25, '25', '25');
INSERT INTO table2 (id, ref_id, d)
VALUES (26, '26', '26');
INSERT INTO table2 (id, ref_id, d)
VALUES (27, '27', '27');
INSERT INTO table2 (id, ref_id, d)
VALUES (28, '28', '28');
INSERT INTO table2 (id, ref_id, d)
VALUES (29, '29', '29');
INSERT INTO table2 (id, ref_id, d)
VALUES (30, '30', '30');
INSERT INTO table2 (id, ref_id, d)
VALUES (31, '31', '31');
INSERT INTO table2 (id, ref_id, d)
VALUES (32, '32', '32');
INSERT INTO table2 (id, ref_id, d)
VALUES (33, '33', '33');
INSERT INTO table2 (id, ref_id, d)
VALUES (34, '34', '34');
INSERT INTO table2 (id, ref_id, d)
VALUES (35, '35', '35');
INSERT INTO table2 (id, ref_id, d)
VALUES (36, '36', '36');
INSERT INTO table2 (id, ref_id, d)
VALUES (37, '37', '37');
INSERT INTO table2 (id, ref_id, d)
VALUES (38, '38', '38');
INSERT INTO table2 (id, ref_id, d)
VALUES (39, '39', '39');
INSERT INTO table2 (id, ref_id, d)
VALUES (40, '40', '40');
INSERT INTO table2 (id, ref_id, d)
VALUES (41, '41', '41');
INSERT INTO table2 (id, ref_id, d)
VALUES (42, '42', '42');
INSERT INTO table2 (id, ref_id, d)
VALUES (43, '43', '43');
INSERT INTO table2 (id, ref_id, d)
VALUES (44, '44', '44');
INSERT INTO table2 (id, ref_id, d)
VALUES (45, '45', '45');
INSERT INTO table2 (id, ref_id, d)
VALUES (46, '46', '46');
INSERT INTO table2 (id, ref_id, d)
VALUES (47, '47', '47');
INSERT INTO table2 (id, ref_id, d)
VALUES (48, '48', '48');
INSERT INTO table2 (id, ref_id, d)
VALUES (49, '49', '49');

213
builder/testdata/mysql_fiddle_data.sql vendored Normal file
View File

@ -0,0 +1,213 @@
create table table1 (
id int primary key,
a varchar(40),
b varchar(40),
c varchar(40)
);
create table table2 (
id int primary key,
ref_id int,
d varchar(40)
);
INSERT INTO table1 (id, a, b, c)
VALUES (0, '0', '0', '0');
INSERT INTO table1 (id, a, b, c)
VALUES (1, '1', '1', '1');
INSERT INTO table1 (id, a, b, c)
VALUES (2, '2', '2', '2');
INSERT INTO table1 (id, a, b, c)
VALUES (3, '3', '3', '3');
INSERT INTO table1 (id, a, b, c)
VALUES (4, '4', '4', '4');
INSERT INTO table1 (id, a, b, c)
VALUES (5, '5', '5', '5');
INSERT INTO table1 (id, a, b, c)
VALUES (6, '6', '6', '6');
INSERT INTO table1 (id, a, b, c)
VALUES (7, '7', '7', '7');
INSERT INTO table1 (id, a, b, c)
VALUES (8, '8', '8', '8');
INSERT INTO table1 (id, a, b, c)
VALUES (9, '9', '9', '9');
INSERT INTO table1 (id, a, b, c)
VALUES (10, '10', '10', '10');
INSERT INTO table1 (id, a, b, c)
VALUES (11, '11', '11', '11');
INSERT INTO table1 (id, a, b, c)
VALUES (12, '12', '12', '12');
INSERT INTO table1 (id, a, b, c)
VALUES (13, '13', '13', '13');
INSERT INTO table1 (id, a, b, c)
VALUES (14, '14', '14', '14');
INSERT INTO table1 (id, a, b, c)
VALUES (15, '15', '15', '15');
INSERT INTO table1 (id, a, b, c)
VALUES (16, '16', '16', '16');
INSERT INTO table1 (id, a, b, c)
VALUES (17, '17', '17', '17');
INSERT INTO table1 (id, a, b, c)
VALUES (18, '18', '18', '18');
INSERT INTO table1 (id, a, b, c)
VALUES (19, '19', '19', '19');
INSERT INTO table1 (id, a, b, c)
VALUES (20, '20', '20', '20');
INSERT INTO table1 (id, a, b, c)
VALUES (21, '21', '21', '21');
INSERT INTO table1 (id, a, b, c)
VALUES (22, '22', '22', '22');
INSERT INTO table1 (id, a, b, c)
VALUES (23, '23', '23', '23');
INSERT INTO table1 (id, a, b, c)
VALUES (24, '24', '24', '24');
INSERT INTO table1 (id, a, b, c)
VALUES (25, '25', '25', '25');
INSERT INTO table1 (id, a, b, c)
VALUES (26, '26', '26', '26');
INSERT INTO table1 (id, a, b, c)
VALUES (27, '27', '27', '27');
INSERT INTO table1 (id, a, b, c)
VALUES (28, '28', '28', '28');
INSERT INTO table1 (id, a, b, c)
VALUES (29, '29', '29', '29');
INSERT INTO table1 (id, a, b, c)
VALUES (30, '30', '30', '30');
INSERT INTO table1 (id, a, b, c)
VALUES (31, '31', '31', '31');
INSERT INTO table1 (id, a, b, c)
VALUES (32, '32', '32', '32');
INSERT INTO table1 (id, a, b, c)
VALUES (33, '33', '33', '33');
INSERT INTO table1 (id, a, b, c)
VALUES (34, '34', '34', '34');
INSERT INTO table1 (id, a, b, c)
VALUES (35, '35', '35', '35');
INSERT INTO table1 (id, a, b, c)
VALUES (36, '36', '36', '36');
INSERT INTO table1 (id, a, b, c)
VALUES (37, '37', '37', '37');
INSERT INTO table1 (id, a, b, c)
VALUES (38, '38', '38', '38');
INSERT INTO table1 (id, a, b, c)
VALUES (39, '39', '39', '39');
INSERT INTO table1 (id, a, b, c)
VALUES (40, '40', '40', '40');
INSERT INTO table1 (id, a, b, c)
VALUES (41, '41', '41', '41');
INSERT INTO table1 (id, a, b, c)
VALUES (42, '42', '42', '42');
INSERT INTO table1 (id, a, b, c)
VALUES (43, '43', '43', '43');
INSERT INTO table1 (id, a, b, c)
VALUES (44, '44', '44', '44');
INSERT INTO table1 (id, a, b, c)
VALUES (45, '45', '45', '45');
INSERT INTO table1 (id, a, b, c)
VALUES (46, '46', '46', '46');
INSERT INTO table1 (id, a, b, c)
VALUES (47, '47', '47', '47');
INSERT INTO table1 (id, a, b, c)
VALUES (48, '48', '48', '48');
INSERT INTO table1 (id, a, b, c)
VALUES (49, '49', '49', '49');
INSERT INTO table2 (id, ref_id, d)
VALUES (0, '0', '0');
INSERT INTO table2 (id, ref_id, d)
VALUES (1, '1', '1');
INSERT INTO table2 (id, ref_id, d)
VALUES (2, '2', '2');
INSERT INTO table2 (id, ref_id, d)
VALUES (3, '3', '3');
INSERT INTO table2 (id, ref_id, d)
VALUES (4, '4', '4');
INSERT INTO table2 (id, ref_id, d)
VALUES (5, '5', '5');
INSERT INTO table2 (id, ref_id, d)
VALUES (6, '6', '6');
INSERT INTO table2 (id, ref_id, d)
VALUES (7, '7', '7');
INSERT INTO table2 (id, ref_id, d)
VALUES (8, '8', '8');
INSERT INTO table2 (id, ref_id, d)
VALUES (9, '9', '9');
INSERT INTO table2 (id, ref_id, d)
VALUES (10, '10', '10');
INSERT INTO table2 (id, ref_id, d)
VALUES (11, '11', '11');
INSERT INTO table2 (id, ref_id, d)
VALUES (12, '12', '12');
INSERT INTO table2 (id, ref_id, d)
VALUES (13, '13', '13');
INSERT INTO table2 (id, ref_id, d)
VALUES (14, '14', '14');
INSERT INTO table2 (id, ref_id, d)
VALUES (15, '15', '15');
INSERT INTO table2 (id, ref_id, d)
VALUES (16, '16', '16');
INSERT INTO table2 (id, ref_id, d)
VALUES (17, '17', '17');
INSERT INTO table2 (id, ref_id, d)
VALUES (18, '18', '18');
INSERT INTO table2 (id, ref_id, d)
VALUES (19, '19', '19');
INSERT INTO table2 (id, ref_id, d)
VALUES (20, '20', '20');
INSERT INTO table2 (id, ref_id, d)
VALUES (21, '21', '21');
INSERT INTO table2 (id, ref_id, d)
VALUES (22, '22', '22');
INSERT INTO table2 (id, ref_id, d)
VALUES (23, '23', '23');
INSERT INTO table2 (id, ref_id, d)
VALUES (24, '24', '24');
INSERT INTO table2 (id, ref_id, d)
VALUES (25, '25', '25');
INSERT INTO table2 (id, ref_id, d)
VALUES (26, '26', '26');
INSERT INTO table2 (id, ref_id, d)
VALUES (27, '27', '27');
INSERT INTO table2 (id, ref_id, d)
VALUES (28, '28', '28');
INSERT INTO table2 (id, ref_id, d)
VALUES (29, '29', '29');
INSERT INTO table2 (id, ref_id, d)
VALUES (30, '30', '30');
INSERT INTO table2 (id, ref_id, d)
VALUES (31, '31', '31');
INSERT INTO table2 (id, ref_id, d)
VALUES (32, '32', '32');
INSERT INTO table2 (id, ref_id, d)
VALUES (33, '33', '33');
INSERT INTO table2 (id, ref_id, d)
VALUES (34, '34', '34');
INSERT INTO table2 (id, ref_id, d)
VALUES (35, '35', '35');
INSERT INTO table2 (id, ref_id, d)
VALUES (36, '36', '36');
INSERT INTO table2 (id, ref_id, d)
VALUES (37, '37', '37');
INSERT INTO table2 (id, ref_id, d)
VALUES (38, '38', '38');
INSERT INTO table2 (id, ref_id, d)
VALUES (39, '39', '39');
INSERT INTO table2 (id, ref_id, d)
VALUES (40, '40', '40');
INSERT INTO table2 (id, ref_id, d)
VALUES (41, '41', '41');
INSERT INTO table2 (id, ref_id, d)
VALUES (42, '42', '42');
INSERT INTO table2 (id, ref_id, d)
VALUES (43, '43', '43');
INSERT INTO table2 (id, ref_id, d)
VALUES (44, '44', '44');
INSERT INTO table2 (id, ref_id, d)
VALUES (45, '45', '45');
INSERT INTO table2 (id, ref_id, d)
VALUES (46, '46', '46');
INSERT INTO table2 (id, ref_id, d)
VALUES (47, '47', '47');
INSERT INTO table2 (id, ref_id, d)
VALUES (48, '48', '48');
INSERT INTO table2 (id, ref_id, d)
VALUES (49, '49', '49');

214
builder/testdata/oracle_fiddle_data.sql vendored Normal file
View File

@ -0,0 +1,214 @@
create table table1 (
id number(9) not null primary key,
a varchar2(40),
b varchar2(40),
c varchar2(40)
);
create table table2 (
id number(9) not null primary key,
ref_id number(9),
d varchar2(40)
);
INSERT INTO table1 (id, a, b, c)
VALUES (0, '0', '0', '0');
INSERT INTO table1 (id, a, b, c)
VALUES (1, '1', '1', '1');
INSERT INTO table1 (id, a, b, c)
VALUES (2, '2', '2', '2');
INSERT INTO table1 (id, a, b, c)
VALUES (3, '3', '3', '3');
INSERT INTO table1 (id, a, b, c)
VALUES (4, '4', '4', '4');
INSERT INTO table1 (id, a, b, c)
VALUES (5, '5', '5', '5');
INSERT INTO table1 (id, a, b, c)
VALUES (6, '6', '6', '6');
INSERT INTO table1 (id, a, b, c)
VALUES (7, '7', '7', '7');
INSERT INTO table1 (id, a, b, c)
VALUES (8, '8', '8', '8');
INSERT INTO table1 (id, a, b, c)
VALUES (9, '9', '9', '9');
INSERT INTO table1 (id, a, b, c)
VALUES (10, '10', '10', '10');
INSERT INTO table1 (id, a, b, c)
VALUES (11, '11', '11', '11');
INSERT INTO table1 (id, a, b, c)
VALUES (12, '12', '12', '12');
INSERT INTO table1 (id, a, b, c)
VALUES (13, '13', '13', '13');
INSERT INTO table1 (id, a, b, c)
VALUES (14, '14', '14', '14');
INSERT INTO table1 (id, a, b, c)
VALUES (15, '15', '15', '15');
INSERT INTO table1 (id, a, b, c)
VALUES (16, '16', '16', '16');
INSERT INTO table1 (id, a, b, c)
VALUES (17, '17', '17', '17');
INSERT INTO table1 (id, a, b, c)
VALUES (18, '18', '18', '18');
INSERT INTO table1 (id, a, b, c)
VALUES (19, '19', '19', '19');
INSERT INTO table1 (id, a, b, c)
VALUES (20, '20', '20', '20');
INSERT INTO table1 (id, a, b, c)
VALUES (21, '21', '21', '21');
INSERT INTO table1 (id, a, b, c)
VALUES (22, '22', '22', '22');
INSERT INTO table1 (id, a, b, c)
VALUES (23, '23', '23', '23');
INSERT INTO table1 (id, a, b, c)
VALUES (24, '24', '24', '24');
INSERT INTO table1 (id, a, b, c)
VALUES (25, '25', '25', '25');
INSERT INTO table1 (id, a, b, c)
VALUES (26, '26', '26', '26');
INSERT INTO table1 (id, a, b, c)
VALUES (27, '27', '27', '27');
INSERT INTO table1 (id, a, b, c)
VALUES (28, '28', '28', '28');
INSERT INTO table1 (id, a, b, c)
VALUES (29, '29', '29', '29');
INSERT INTO table1 (id, a, b, c)
VALUES (30, '30', '30', '30');
INSERT INTO table1 (id, a, b, c)
VALUES (31, '31', '31', '31');
INSERT INTO table1 (id, a, b, c)
VALUES (32, '32', '32', '32');
INSERT INTO table1 (id, a, b, c)
VALUES (33, '33', '33', '33');
INSERT INTO table1 (id, a, b, c)
VALUES (34, '34', '34', '34');
INSERT INTO table1 (id, a, b, c)
VALUES (35, '35', '35', '35');
INSERT INTO table1 (id, a, b, c)
VALUES (36, '36', '36', '36');
INSERT INTO table1 (id, a, b, c)
VALUES (37, '37', '37', '37');
INSERT INTO table1 (id, a, b, c)
VALUES (38, '38', '38', '38');
INSERT INTO table1 (id, a, b, c)
VALUES (39, '39', '39', '39');
INSERT INTO table1 (id, a, b, c)
VALUES (40, '40', '40', '40');
INSERT INTO table1 (id, a, b, c)
VALUES (41, '41', '41', '41');
INSERT INTO table1 (id, a, b, c)
VALUES (42, '42', '42', '42');
INSERT INTO table1 (id, a, b, c)
VALUES (43, '43', '43', '43');
INSERT INTO table1 (id, a, b, c)
VALUES (44, '44', '44', '44');
INSERT INTO table1 (id, a, b, c)
VALUES (45, '45', '45', '45');
INSERT INTO table1 (id, a, b, c)
VALUES (46, '46', '46', '46');
INSERT INTO table1 (id, a, b, c)
VALUES (47, '47', '47', '47');
INSERT INTO table1 (id, a, b, c)
VALUES (48, '48', '48', '48');
INSERT INTO table1 (id, a, b, c)
VALUES (49, '49', '49', '49');
INSERT INTO table2 (id, ref_id, d)
VALUES (0, '0', '0');
INSERT INTO table2 (id, ref_id, d)
VALUES (1, '1', '1');
INSERT INTO table2 (id, ref_id, d)
VALUES (2, '2', '2');
INSERT INTO table2 (id, ref_id, d)
VALUES (3, '3', '3');
INSERT INTO table2 (id, ref_id, d)
VALUES (4, '4', '4');
INSERT INTO table2 (id, ref_id, d)
VALUES (5, '5', '5');
INSERT INTO table2 (id, ref_id, d)
VALUES (6, '6', '6');
INSERT INTO table2 (id, ref_id, d)
VALUES (7, '7', '7');
INSERT INTO table2 (id, ref_id, d)
VALUES (8, '8', '8');
INSERT INTO table2 (id, ref_id, d)
VALUES (9, '9', '9');
INSERT INTO table2 (id, ref_id, d)
VALUES (10, '10', '10');
INSERT INTO table2 (id, ref_id, d)
VALUES (11, '11', '11');
INSERT INTO table2 (id, ref_id, d)
VALUES (12, '12', '12');
INSERT INTO table2 (id, ref_id, d)
VALUES (13, '13', '13');
INSERT INTO table2 (id, ref_id, d)
VALUES (14, '14', '14');
INSERT INTO table2 (id, ref_id, d)
VALUES (15, '15', '15');
INSERT INTO table2 (id, ref_id, d)
VALUES (16, '16', '16');
INSERT INTO table2 (id, ref_id, d)
VALUES (17, '17', '17');
INSERT INTO table2 (id, ref_id, d)
VALUES (18, '18', '18');
INSERT INTO table2 (id, ref_id, d)
VALUES (19, '19', '19');
INSERT INTO table2 (id, ref_id, d)
VALUES (20, '20', '20');
INSERT INTO table2 (id, ref_id, d)
VALUES (21, '21', '21');
INSERT INTO table2 (id, ref_id, d)
VALUES (22, '22', '22');
INSERT INTO table2 (id, ref_id, d)
VALUES (23, '23', '23');
INSERT INTO table2 (id, ref_id, d)
VALUES (24, '24', '24');
INSERT INTO table2 (id, ref_id, d)
VALUES (25, '25', '25');
INSERT INTO table2 (id, ref_id, d)
VALUES (26, '26', '26');
INSERT INTO table2 (id, ref_id, d)
VALUES (27, '27', '27');
INSERT INTO table2 (id, ref_id, d)
VALUES (28, '28', '28');
INSERT INTO table2 (id, ref_id, d)
VALUES (29, '29', '29');
INSERT INTO table2 (id, ref_id, d)
VALUES (30, '30', '30');
INSERT INTO table2 (id, ref_id, d)
VALUES (31, '31', '31');
INSERT INTO table2 (id, ref_id, d)
VALUES (32, '32', '32');
INSERT INTO table2 (id, ref_id, d)
VALUES (33, '33', '33');
INSERT INTO table2 (id, ref_id, d)
VALUES (34, '34', '34');
INSERT INTO table2 (id, ref_id, d)
VALUES (35, '35', '35');
INSERT INTO table2 (id, ref_id, d)
VALUES (36, '36', '36');
INSERT INTO table2 (id, ref_id, d)
VALUES (37, '37', '37');
INSERT INTO table2 (id, ref_id, d)
VALUES (38, '38', '38');
INSERT INTO table2 (id, ref_id, d)
VALUES (39, '39', '39');
INSERT INTO table2 (id, ref_id, d)
VALUES (40, '40', '40');
INSERT INTO table2 (id, ref_id, d)
VALUES (41, '41', '41');
INSERT INTO table2 (id, ref_id, d)
VALUES (42, '42', '42');
INSERT INTO table2 (id, ref_id, d)
VALUES (43, '43', '43');
INSERT INTO table2 (id, ref_id, d)
VALUES (44, '44', '44');
INSERT INTO table2 (id, ref_id, d)
VALUES (45, '45', '45');
INSERT INTO table2 (id, ref_id, d)
VALUES (46, '46', '46');
INSERT INTO table2 (id, ref_id, d)
VALUES (47, '47', '47');
INSERT INTO table2 (id, ref_id, d)
VALUES (48, '48', '48');
INSERT INTO table2 (id, ref_id, d)
VALUES (49, '49', '49');

42
builder/writer.go Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2019 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 (
"io"
"strings"
)
// Writer defines the interface
type Writer interface {
io.Writer
Append(...interface{})
}
var _ Writer = NewWriter()
// BytesWriter implments Writer and save SQL in bytes.Buffer
type BytesWriter struct {
*strings.Builder
args []interface{}
}
// NewWriter creates a new string writer
func NewWriter() *BytesWriter {
w := &BytesWriter{
Builder: &strings.Builder{},
}
return w
}
// Append appends args to Writer
func (w *BytesWriter) Append(args ...interface{}) {
w.args = append(w.args, args...)
}
// Args returns args
func (w *BytesWriter) Args() []interface{} {
return w.args
}

View File

@ -12,7 +12,7 @@ import (
"strings"
"time"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/schemas"
)
const (

View File

@ -7,8 +7,8 @@ package caches
import (
"testing"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
"xorm.io/xorm/schemas"
)
func TestLRUCache(t *testing.T) {

View File

@ -13,9 +13,9 @@ import (
"regexp"
"sync"
"xorm.io/xorm/contexts"
"xorm.io/xorm/log"
"xorm.io/xorm/names"
"gitea.com/laixyz/xorm/contexts"
"gitea.com/laixyz/xorm/log"
"gitea.com/laixyz/xorm/names"
)
var (

View File

@ -11,7 +11,7 @@ import (
"testing"
"time"
"xorm.io/xorm/names"
"gitea.com/laixyz/xorm/names"
_ "github.com/go-sql-driver/mysql"
_ "github.com/mattn/go-sqlite3"

View File

@ -10,7 +10,7 @@ import (
"errors"
"reflect"
"xorm.io/xorm/contexts"
"gitea.com/laixyz/xorm/contexts"
)
// Stmt reprents a stmt objects

View File

@ -8,7 +8,7 @@ import (
"context"
"database/sql"
"xorm.io/xorm/contexts"
"gitea.com/laixyz/xorm/contexts"
)
var (

View File

@ -10,8 +10,8 @@ import (
"strings"
"time"
"xorm.io/xorm/core"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/schemas"
)
// URI represents an uri to visit database

View File

@ -12,8 +12,8 @@ import (
"strconv"
"strings"
"xorm.io/xorm/core"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/schemas"
)
var (

View File

@ -14,8 +14,8 @@ import (
"strings"
"time"
"xorm.io/xorm/core"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/schemas"
)
var (

View File

@ -12,8 +12,8 @@ import (
"strconv"
"strings"
"xorm.io/xorm/core"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/schemas"
)
var (

View File

@ -12,8 +12,8 @@ import (
"strconv"
"strings"
"xorm.io/xorm/core"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/schemas"
)
// from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html

View File

@ -12,8 +12,8 @@ import (
"regexp"
"strings"
"xorm.io/xorm/core"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/schemas"
)
var (

View File

@ -9,8 +9,8 @@ import (
"reflect"
"strings"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/names"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/names"
)
// TableNameWithSchema will add schema prefix on table name if possible

View File

@ -7,7 +7,7 @@ package dialects
import (
"testing"
"xorm.io/xorm/names"
"gitea.com/laixyz/xorm/names"
"github.com/stretchr/testify/assert"
)

View File

@ -7,7 +7,7 @@ package dialects
import (
"time"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/schemas"
)
// FormatTime format time as column type

View File

@ -17,15 +17,15 @@ import (
"strings"
"time"
"xorm.io/xorm/caches"
"xorm.io/xorm/contexts"
"xorm.io/xorm/core"
"xorm.io/xorm/dialects"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/log"
"xorm.io/xorm/names"
"xorm.io/xorm/schemas"
"xorm.io/xorm/tags"
"gitea.com/laixyz/xorm/caches"
"gitea.com/laixyz/xorm/contexts"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/log"
"gitea.com/laixyz/xorm/names"
"gitea.com/laixyz/xorm/schemas"
"gitea.com/laixyz/xorm/tags"
)
// Engine is the major struct of xorm, it means a database manager.

View File

@ -8,11 +8,11 @@ import (
"context"
"time"
"xorm.io/xorm/caches"
"xorm.io/xorm/contexts"
"xorm.io/xorm/dialects"
"xorm.io/xorm/log"
"xorm.io/xorm/names"
"gitea.com/laixyz/xorm/caches"
"gitea.com/laixyz/xorm/contexts"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/log"
"gitea.com/laixyz/xorm/names"
)
// EngineGroup defines an engine group

14
go.mod
View File

@ -1,14 +1,14 @@
module xorm.io/xorm
module gitea.com/laixyz/xorm
go 1.11
go 1.15
require (
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a
github.com/denisenkom/go-mssqldb v0.9.0
github.com/go-sql-driver/mysql v1.5.0
github.com/lib/pq v1.7.0
github.com/mattn/go-sqlite3 v1.14.0
github.com/stretchr/testify v1.4.0
github.com/lib/pq v1.9.0
github.com/mattn/go-sqlite3 v1.14.6
github.com/stretchr/testify v1.6.1
github.com/syndtr/goleveldb v1.0.0
github.com/ziutek/mymysql v1.5.4
xorm.io/builder v0.3.7
)

35
go.sum
View File

@ -1,11 +1,9 @@
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
@ -18,10 +16,10 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pO
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -30,31 +28,21 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
@ -63,8 +51,7 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -8,7 +8,7 @@ import (
"testing"
"time"
"xorm.io/xorm/caches"
"gitea.com/laixyz/xorm/caches"
"github.com/stretchr/testify/assert"
)

View File

@ -7,9 +7,9 @@ package integrations
import (
"testing"
"xorm.io/xorm"
"xorm.io/xorm/log"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm"
"gitea.com/laixyz/xorm/log"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
)

View File

@ -11,8 +11,8 @@ import (
"testing"
"time"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm"
"gitea.com/laixyz/xorm/schemas"
_ "github.com/denisenkom/go-mssqldb"
_ "github.com/go-sql-driver/mysql"

View File

@ -9,7 +9,7 @@ import (
"fmt"
"testing"
"xorm.io/xorm"
"gitea.com/laixyz/xorm"
"github.com/stretchr/testify/assert"
)

View File

@ -7,9 +7,9 @@ package integrations
import (
"testing"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
"xorm.io/builder"
"xorm.io/xorm/schemas"
)
func TestSetExpr(t *testing.T) {

View File

@ -9,8 +9,8 @@ import (
"fmt"
"testing"
"gitea.com/laixyz/xorm/builder"
"github.com/stretchr/testify/assert"
"xorm.io/builder"
)
func TestBuilder(t *testing.T) {

View File

@ -8,8 +8,8 @@ import (
"testing"
"time"
"xorm.io/xorm/caches"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/caches"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
)

View File

@ -8,8 +8,8 @@ import (
"testing"
"time"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/names"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/names"
"github.com/stretchr/testify/assert"
)

View File

@ -11,8 +11,8 @@ import (
"testing"
"time"
"xorm.io/xorm/contexts"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/contexts"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
)

View File

@ -10,7 +10,7 @@ import (
"testing"
"time"
"xorm.io/xorm"
"gitea.com/laixyz/xorm"
"github.com/stretchr/testify/assert"
)

View File

@ -9,8 +9,8 @@ import (
"testing"
"time"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
"xorm.io/xorm/schemas"
)
type IntId struct {

View File

@ -10,8 +10,8 @@ import (
"testing"
"time"
"xorm.io/builder"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
)

View File

@ -9,8 +9,8 @@ import (
"strconv"
"testing"
"gitea.com/laixyz/xorm/builder"
"github.com/stretchr/testify/assert"
"xorm.io/builder"
)
func isFloatEq(i, j float64, precision int) bool {

View File

@ -9,9 +9,9 @@ import (
"testing"
"time"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/names"
"github.com/stretchr/testify/assert"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/names"
)
func TestTransaction(t *testing.T) {

View File

@ -10,11 +10,11 @@ import (
"testing"
"time"
"gitea.com/laixyz/xorm"
"gitea.com/laixyz/xorm/internal/statements"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/names"
"github.com/stretchr/testify/assert"
"xorm.io/xorm"
"xorm.io/xorm/internal/statements"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/names"
)
func TestUpdateMap(t *testing.T) {

View File

@ -11,10 +11,10 @@ import (
"testing"
"time"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/names"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/names"
"xorm.io/xorm/schemas"
)
type tempUser struct {

View File

@ -12,12 +12,12 @@ import (
"strings"
"testing"
"xorm.io/xorm"
"xorm.io/xorm/caches"
"xorm.io/xorm/dialects"
"xorm.io/xorm/log"
"xorm.io/xorm/names"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm"
"gitea.com/laixyz/xorm/caches"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/log"
"gitea.com/laixyz/xorm/names"
"gitea.com/laixyz/xorm/schemas"
)
var (

View File

@ -10,7 +10,7 @@ import (
"testing"
"time"
"xorm.io/xorm/internal/utils"
"gitea.com/laixyz/xorm/internal/utils"
"github.com/stretchr/testify/assert"
)

View File

@ -9,10 +9,10 @@ import (
"fmt"
"testing"
"xorm.io/xorm"
"xorm.io/xorm/convert"
"xorm.io/xorm/internal/json"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm"
"gitea.com/laixyz/xorm/convert"
"gitea.com/laixyz/xorm/internal/json"
"gitea.com/laixyz/xorm/schemas"
"github.com/stretchr/testify/assert"
)

View File

@ -10,12 +10,12 @@ import (
"reflect"
"time"
"xorm.io/xorm/caches"
"xorm.io/xorm/contexts"
"xorm.io/xorm/dialects"
"xorm.io/xorm/log"
"xorm.io/xorm/names"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/caches"
"gitea.com/laixyz/xorm/contexts"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/log"
"gitea.com/laixyz/xorm/names"
"gitea.com/laixyz/xorm/schemas"
)
// Interface defines the interface which Engine, EngineGroup and Session will implementate.

View File

@ -8,8 +8,8 @@ import (
"fmt"
"strings"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/schemas"
)
func (statement *Statement) ConvertIDSQL(sqlStr string) string {

View File

@ -7,7 +7,7 @@ package statements
import (
"strings"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/schemas"
)
type columnMap []string

View File

@ -8,8 +8,8 @@ import (
"fmt"
"strings"
"xorm.io/builder"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/schemas"
)
type ErrUnsupportedExprType struct {

View File

@ -8,8 +8,8 @@ import (
"fmt"
"strings"
"xorm.io/builder"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/schemas"
)
func (statement *Statement) writeInsertOutput(buf *strings.Builder, table *schemas.Table) error {

View File

@ -8,8 +8,8 @@ import (
"fmt"
"reflect"
"xorm.io/builder"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/schemas"
)
var (

View File

@ -10,8 +10,8 @@ import (
"reflect"
"strings"
"xorm.io/builder"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/schemas"
)
func (statement *Statement) GenQuerySQL(sqlOrArgs ...interface{}) (string, []interface{}, error) {

View File

@ -12,14 +12,14 @@ import (
"strings"
"time"
"xorm.io/builder"
"xorm.io/xorm/contexts"
"xorm.io/xorm/convert"
"xorm.io/xorm/dialects"
"xorm.io/xorm/internal/json"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/schemas"
"xorm.io/xorm/tags"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/contexts"
"gitea.com/laixyz/xorm/convert"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/internal/json"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/schemas"
"gitea.com/laixyz/xorm/tags"
)
var (

View File

@ -10,8 +10,8 @@ import (
"strings"
"time"
"xorm.io/builder"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/schemas"
)
func quoteNeeded(a interface{}) bool {

View File

@ -10,12 +10,12 @@ import (
"testing"
"time"
"gitea.com/laixyz/xorm/caches"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/names"
"gitea.com/laixyz/xorm/schemas"
"gitea.com/laixyz/xorm/tags"
"github.com/stretchr/testify/assert"
"xorm.io/xorm/caches"
"xorm.io/xorm/dialects"
"xorm.io/xorm/names"
"xorm.io/xorm/schemas"
"xorm.io/xorm/tags"
_ "github.com/mattn/go-sqlite3"
)

View File

@ -11,11 +11,11 @@ import (
"reflect"
"time"
"xorm.io/xorm/convert"
"xorm.io/xorm/dialects"
"xorm.io/xorm/internal/json"
"xorm.io/xorm/internal/utils"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/convert"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/internal/json"
"gitea.com/laixyz/xorm/internal/utils"
"gitea.com/laixyz/xorm/schemas"
)
func (statement *Statement) ifAddColUpdate(col *schemas.Column, includeVersion, includeUpdated, includeNil,

View File

@ -11,10 +11,10 @@ import (
"reflect"
"time"
"xorm.io/xorm/convert"
"xorm.io/xorm/dialects"
"xorm.io/xorm/internal/json"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/convert"
"gitea.com/laixyz/xorm/dialects"
"gitea.com/laixyz/xorm/internal/json"
"gitea.com/laixyz/xorm/schemas"
)
var (

View File

@ -7,7 +7,7 @@ package log
import (
"fmt"
"xorm.io/xorm/contexts"
"gitea.com/laixyz/xorm/contexts"
)
// LogContext represents a log context

View File

@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"xorm.io/xorm"
"gitea.com/laixyz/xorm"
)
// MigrateFunc is the func signature for migrating.

View File

@ -6,9 +6,9 @@ import (
"os"
"testing"
"gitea.com/laixyz/xorm"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
"xorm.io/xorm"
)
type Person struct {

View File

@ -10,9 +10,9 @@ import (
"fmt"
"reflect"
"xorm.io/builder"
"xorm.io/xorm/core"
"xorm.io/xorm/internal/utils"
"gitea.com/laixyz/xorm/builder"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/internal/utils"
)
// Rows rows wrapper a rows to

View File

@ -8,7 +8,7 @@ import (
"bytes"
"encoding/gob"
"xorm.io/xorm/internal/utils"
"gitea.com/laixyz/xorm/internal/utils"
)
type PK []interface{}

View File

@ -18,13 +18,13 @@ import (
"strings"
"time"
"xorm.io/xorm/contexts"
"xorm.io/xorm/convert"
"xorm.io/xorm/core"
"xorm.io/xorm/internal/json"
"xorm.io/xorm/internal/statements"
"xorm.io/xorm/log"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/contexts"
"gitea.com/laixyz/xorm/convert"
"gitea.com/laixyz/xorm/core"
"gitea.com/laixyz/xorm/internal/json"
"gitea.com/laixyz/xorm/internal/statements"
"gitea.com/laixyz/xorm/log"
"gitea.com/laixyz/xorm/schemas"
)
// ErrFieldIsNotExist columns does not exist

View File

@ -9,7 +9,7 @@ import (
"strings"
"time"
"xorm.io/xorm/schemas"
"gitea.com/laixyz/xorm/schemas"
)
func setColumnInt(bean interface{}, col *schemas.Column, t int64) {

Some files were not shown because too many files have changed in this diff Show More