fix MSSQL bug on org (#3405)

This commit is contained in:
Lunny Xiao 2018-01-27 09:20:59 -06:00 committed by Lauris BH
parent a0c397df08
commit 97fe773491
28 changed files with 1011 additions and 164 deletions

View file

@ -45,6 +45,10 @@ func (b *Builder) selectWriteTo(w Writer) error {
} }
} }
if !b.cond.IsValid() {
return nil
}
if _, err := fmt.Fprint(w, " WHERE "); err != nil { if _, err := fmt.Fprint(w, " WHERE "); err != nil {
return err return err
} }

View file

@ -25,7 +25,9 @@ func And(conds ...Cond) Cond {
func (and condAnd) WriteTo(w Writer) error { func (and condAnd) WriteTo(w Writer) error {
for i, cond := range and { for i, cond := range and {
_, isOr := cond.(condOr) _, isOr := cond.(condOr)
if isOr { _, isExpr := cond.(expr)
wrap := isOr || isExpr
if wrap {
fmt.Fprint(w, "(") fmt.Fprint(w, "(")
} }
@ -34,7 +36,7 @@ func (and condAnd) WriteTo(w Writer) error {
return err return err
} }
if isOr { if wrap {
fmt.Fprint(w, ")") fmt.Fprint(w, ")")
} }

View file

@ -1,11 +1,12 @@
package core package core
import ( import (
"errors"
"fmt"
"time"
"bytes" "bytes"
"encoding/gob" "encoding/gob"
"errors"
"fmt"
"strings"
"time"
) )
const ( const (
@ -55,11 +56,10 @@ func encodeIds(ids []PK) (string, error) {
return buf.String(), err return buf.String(), err
} }
func decodeIds(s string) ([]PK, error) { func decodeIds(s string) ([]PK, error) {
pks := make([]PK, 0) pks := make([]PK, 0)
dec := gob.NewDecoder(bytes.NewBufferString(s)) dec := gob.NewDecoder(strings.NewReader(s))
err := dec.Decode(&pks) err := dec.Decode(&pks)
return pks, err return pks, err

View file

@ -12,3 +12,4 @@ test:
override: override:
# './...' is a relative pattern which means all subdirectories # './...' is a relative pattern which means all subdirectories
- go test -v -race - go test -v -race
- go test -v -race --dbtype=sqlite3

View file

@ -79,6 +79,10 @@ func (col *Column) String(d Dialect) string {
} }
} }
if col.Default != "" {
sql += "DEFAULT " + col.Default + " "
}
if d.ShowCreateNull() { if d.ShowCreateNull() {
if col.Nullable { if col.Nullable {
sql += "NULL " sql += "NULL "
@ -87,10 +91,6 @@ func (col *Column) String(d Dialect) string {
} }
} }
if col.Default != "" {
sql += "DEFAULT " + col.Default + " "
}
return sql return sql
} }
@ -99,6 +99,10 @@ func (col *Column) StringNoPk(d Dialect) string {
sql += d.SqlType(col) + " " sql += d.SqlType(col) + " "
if col.Default != "" {
sql += "DEFAULT " + col.Default + " "
}
if d.ShowCreateNull() { if d.ShowCreateNull() {
if col.Nullable { if col.Nullable {
sql += "NULL " sql += "NULL "
@ -107,10 +111,6 @@ func (col *Column) StringNoPk(d Dialect) string {
} }
} }
if col.Default != "" {
sql += "DEFAULT " + col.Default + " "
}
return sql return sql
} }

View file

@ -44,6 +44,9 @@ func convertTime(dest *NullTime, src interface{}) error {
} }
*dest = NullTime(t) *dest = NullTime(t)
return nil return nil
case time.Time:
*dest = NullTime(s)
return nil
case nil: case nil:
default: default:
return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest) return fmt.Errorf("unsupported driver -> Scan pair: %T -> %T", src, dest)

View file

@ -32,13 +32,10 @@ proposed functionality.
We appreciate any bug reports, but especially ones with self-contained We appreciate any bug reports, but especially ones with self-contained
(doesn't depend on code outside of xorm), minimal (can't be simplified (doesn't depend on code outside of xorm), minimal (can't be simplified
further) test cases. It's especially helpful if you can submit a pull further) test cases. It's especially helpful if you can submit a pull
request with just the failing test case (you'll probably want to request with just the failing test case(you can find some example test file like [session_get_test.go](https://github.com/go-xorm/xorm/blob/master/session_get_test.go)).
pattern it after the tests in
[base.go](https://github.com/go-xorm/tests/blob/master/base.go) AND
[benchmark.go](https://github.com/go-xorm/tests/blob/master/benchmark.go).
If you implements a new database interface, you maybe need to add a <databasename>_test.go file. If you implements a new database interface, you maybe need to add a test_<databasename>.sh file.
For example, [mysql_test.go](https://github.com/go-xorm/tests/blob/master/mysql/mysql_test.go) For example, [mysql_test.go](https://github.com/go-xorm/xorm/blob/master/test_mysql.sh)
### New functionality ### New functionality

View file

@ -28,6 +28,8 @@ Xorm is a simple and powerful ORM for Go.
* SQL Builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder) * SQL Builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder)
* Automatical Read/Write seperatelly
# Drivers Support # Drivers Support
Drivers for Go's sql package which currently support database/sql includes: Drivers for Go's sql package which currently support database/sql includes:
@ -48,6 +50,13 @@ Drivers for Go's sql package which currently support database/sql includes:
# Changelog # Changelog
* **v0.6.4**
* Automatical Read/Write seperatelly
* Query/QueryString/QueryInterface and action with Where/And
* Get support non-struct variables
* BufferSize on Iterate
* fix some other bugs.
* **v0.6.3** * **v0.6.3**
* merge tests to main project * merge tests to main project
* add `Exist` function * add `Exist` function
@ -61,13 +70,6 @@ Drivers for Go's sql package which currently support database/sql includes:
* add Scan features to Get * add Scan features to Get
* add QueryString method * add QueryString method
* **v0.6.0**
* remove support for ql
* add query condition builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder), so `Where`, `And`, `Or`
methods can use `builder.Cond` as parameter
* add Sum, SumInt, SumInt64 and NotIn methods
* some bugs fixed
[More changes ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16) [More changes ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16)
# Installation # Installation
@ -106,15 +108,36 @@ type User struct {
err := engine.Sync2(new(User)) err := engine.Sync2(new(User))
``` ```
* `Query` runs a SQL string, the returned results is `[]map[string][]byte`, `QueryString` returns `[]map[string]string`. * Create Engine Group
```Go
dataSourceNameSlice := []string{masterDataSourceName, slave1DataSourceName, slave2DataSourceName}
engineGroup, err := xorm.NewEngineGroup(driverName, dataSourceNameSlice)
```
```Go
masterEngine, err := xorm.NewEngine(driverName, masterDataSourceName)
slave1Engine, err := xorm.NewEngine(driverName, slave1DataSourceName)
slave2Engine, err := xorm.NewEngine(driverName, slave2DataSourceName)
engineGroup, err := xorm.NewEngineGroup(masterEngine, []*Engine{slave1Engine, slave2Engine})
```
Then all place where `engine` you can just use `engineGroup`.
* `Query` runs a SQL string, the returned results is `[]map[string][]byte`, `QueryString` returns `[]map[string]string`, `QueryInterface` returns `[]map[string]interface{}`.
```Go ```Go
results, err := engine.Query("select * from user") results, err := engine.Query("select * from user")
results, err := engine.Where("a = 1").Query()
results, err := engine.QueryString("select * from user") results, err := engine.QueryString("select * from user")
results, err := engine.Where("a = 1").QueryString()
results, err := engine.QueryInterface("select * from user")
results, err := engine.Where("a = 1").QueryInterface()
``` ```
* `Execute` runs a SQL string, it returns `affected` and `error` * `Exec` runs a SQL string, it returns `affected` and `error`
```Go ```Go
affected, err := engine.Exec("update user set age = ? where name = ?", age, name) affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
@ -125,62 +148,76 @@ affected, err := engine.Exec("update user set age = ? where name = ?", age, name
```Go ```Go
affected, err := engine.Insert(&user) affected, err := engine.Insert(&user)
// INSERT INTO struct () values () // INSERT INTO struct () values ()
affected, err := engine.Insert(&user1, &user2) affected, err := engine.Insert(&user1, &user2)
// INSERT INTO struct1 () values () // INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values () // INSERT INTO struct2 () values ()
affected, err := engine.Insert(&users) affected, err := engine.Insert(&users)
// INSERT INTO struct () values (),(),() // INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&user1, &users) affected, err := engine.Insert(&user1, &users)
// INSERT INTO struct1 () values () // INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),() // INSERT INTO struct2 () values (),(),()
``` ```
* Query one record from database * `Get` query one record from database
```Go ```Go
has, err := engine.Get(&user) has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1 // SELECT * FROM user LIMIT 1
has, err := engine.Where("name = ?", name).Desc("id").Get(&user) has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
var name string var name string
has, err := engine.Where("id = ?", id).Cols("name").Get(&name) has, err := engine.Where("id = ?", id).Cols("name").Get(&name)
// SELECT name FROM user WHERE id = ? // SELECT name FROM user WHERE id = ?
var id int64 var id int64
has, err := engine.Where("name = ?", name).Cols("id").Get(&id) has, err := engine.Where("name = ?", name).Cols("id").Get(&id)
has, err := engine.SQL("select id from user").Get(&id)
// SELECT id FROM user WHERE name = ? // SELECT id FROM user WHERE name = ?
var valuesMap = make(map[string]string) var valuesMap = make(map[string]string)
has, err := engine.Where("id = ?", id).Get(&valuesMap) has, err := engine.Where("id = ?", id).Get(&valuesMap)
// SELECT * FROM user WHERE id = ? // SELECT * FROM user WHERE id = ?
var valuesSlice = make([]interface{}, len(cols)) var valuesSlice = make([]interface{}, len(cols))
has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
// SELECT col1, col2, col3 FROM user WHERE id = ? // SELECT col1, col2, col3 FROM user WHERE id = ?
``` ```
* Check if one record exist on table * `Exist` check if one record exist on table
```Go ```Go
has, err := testEngine.Exist(new(RecordExist)) has, err := testEngine.Exist(new(RecordExist))
// SELECT * FROM record_exist LIMIT 1 // SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Exist(&RecordExist{ has, err = testEngine.Exist(&RecordExist{
Name: "test1", Name: "test1",
}) })
// SELECT * FROM record_exist WHERE name = ? LIMIT 1 // SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{})
// SELECT * FROM record_exist WHERE name = ? LIMIT 1 // SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist() has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist()
// select * from record_exist where name = ? // select * from record_exist where name = ?
has, err = testEngine.Table("record_exist").Exist() has, err = testEngine.Table("record_exist").Exist()
// SELECT * FROM record_exist LIMIT 1 // SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist()
// SELECT * FROM record_exist WHERE name = ? LIMIT 1 // SELECT * FROM record_exist WHERE name = ? LIMIT 1
``` ```
* Query multiple records from database, also you can use join and extends * `Find` query multiple records from database, also you can use join and extends
```Go ```Go
var users []User var users []User
err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 // SELECT * FROM user WHERE name = ? AND age > 10 limit 10 offset 0
type Detail struct { type Detail struct {
Id int64 Id int64
@ -193,14 +230,14 @@ type UserDetail struct {
} }
var users []UserDetail var users []UserDetail
err := engine.Table("user").Select("user.*, detail.*") err := engine.Table("user").Select("user.*, detail.*").
Join("INNER", "detail", "detail.user_id = user.id"). Join("INNER", "detail", "detail.user_id = user.id").
Where("user.name = ?", name).Limit(10, 0). Where("user.name = ?", name).Limit(10, 0).
Find(&users) Find(&users)
// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 // SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 10 offset 0
``` ```
* Query multiple records and record by record handle, there are two methods Iterate and Rows * `Iterate` and `Rows` query multiple records and record by record handle, there are two methods Iterate and Rows
```Go ```Go
err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error { err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
@ -209,6 +246,13 @@ err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
}) })
// SELECT * FROM user // SELECT * FROM user
err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user Limit 0, 100
// SELECT * FROM user Limit 101, 100
rows, err := engine.Rows(&User{Name:name}) rows, err := engine.Rows(&User{Name:name})
// SELECT * FROM user // SELECT * FROM user
defer rows.Close() defer rows.Close()
@ -218,7 +262,7 @@ for rows.Next() {
} }
``` ```
* Update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on. * `Update` update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on.
```Go ```Go
affected, err := engine.Id(1).Update(&user) affected, err := engine.Id(1).Update(&user)
@ -243,21 +287,39 @@ affected, err := engine.Id(1).AllCols().Update(&user)
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ? // UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
``` ```
* Delete one or more records, Delete MUST have condition * `Delete` delete one or more records, Delete MUST have condition
```Go ```Go
affected, err := engine.Where(...).Delete(&user) affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ... // DELETE FROM user Where ...
affected, err := engine.Id(2).Delete(&user)
affected, err := engine.ID(2).Delete(&user)
// DELETE FROM user Where id = ?
``` ```
* Count records * `Count` count records
```Go ```Go
counts, err := engine.Count(&user) counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user // SELECT count(*) AS total FROM user
``` ```
* `Sum` sum functions
```Go
agesFloat64, err := engine.Sum(&user, "age")
// SELECT sum(age) AS total FROM user
agesInt64, err := engine.SumInt(&user, "age")
// SELECT sum(age) AS total FROM user
sumFloat64Slice, err := engine.Sums(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
sumInt64Slice, err := engine.SumsInt(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
```
* Query conditions builder * Query conditions builder
```Go ```Go
@ -265,6 +327,59 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))
// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?) // SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
``` ```
* Multiple operations in one go routine, no transation here but resue session memory
```Go
session := engine.NewSession()
defer session.Close()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
return nil
```
* Transation should on one go routine. There is transaction and resue session memory
```Go
session := engine.NewSession()
defer session.Close()
// add Begin() before any action
if err := session.Begin(); err != nil {
// if returned then will rollback automatically
return err
}
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
// add Commit() after all actions
return session.Commit()
```
# Cases # Cases
* [studygolang](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang) * [studygolang](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang)

View file

@ -115,12 +115,33 @@ type User struct {
err := engine.Sync2(new(User)) err := engine.Sync2(new(User))
``` ```
* `Query` 最原始的也支持SQL语句查询返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string * 创建Engine组
```Go
dataSourceNameSlice := []string{masterDataSourceName, slave1DataSourceName, slave2DataSourceName}
engineGroup, err := xorm.NewEngineGroup(driverName, dataSourceNameSlice)
```
```Go
masterEngine, err := xorm.NewEngine(driverName, masterDataSourceName)
slave1Engine, err := xorm.NewEngine(driverName, slave1DataSourceName)
slave2Engine, err := xorm.NewEngine(driverName, slave2DataSourceName)
engineGroup, err := xorm.NewEngineGroup(masterEngine, []*Engine{slave1Engine, slave2Engine})
```
所有使用 `engine` 都可以简单的用 `engineGroup` 来替换。
* `Query` 最原始的也支持SQL语句查询返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string, `QueryInterface` 返回 `[]map[string]interface{}`.
```Go ```Go
results, err := engine.Query("select * from user") results, err := engine.Query("select * from user")
results, err := engine.Where("a = 1").Query()
results, err := engine.QueryString("select * from user") results, err := engine.QueryString("select * from user")
results, err := engine.Where("a = 1").QueryString()
results, err := engine.QueryInterface("select * from user")
results, err := engine.Where("a = 1").QueryInterface()
``` ```
* `Exec` 执行一个SQL语句 * `Exec` 执行一个SQL语句
@ -129,67 +150,81 @@ results, err := engine.QueryString("select * from user")
affected, err := engine.Exec("update user set age = ? where name = ?", age, name) affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
``` ```
* 插入一条或者多条记录 * `Insert` 插入一条或者多条记录
```Go ```Go
affected, err := engine.Insert(&user) affected, err := engine.Insert(&user)
// INSERT INTO struct () values () // INSERT INTO struct () values ()
affected, err := engine.Insert(&user1, &user2) affected, err := engine.Insert(&user1, &user2)
// INSERT INTO struct1 () values () // INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values () // INSERT INTO struct2 () values ()
affected, err := engine.Insert(&users) affected, err := engine.Insert(&users)
// INSERT INTO struct () values (),(),() // INSERT INTO struct () values (),(),()
affected, err := engine.Insert(&user1, &users) affected, err := engine.Insert(&user1, &users)
// INSERT INTO struct1 () values () // INSERT INTO struct1 () values ()
// INSERT INTO struct2 () values (),(),() // INSERT INTO struct2 () values (),(),()
``` ```
* 查询单条记录 * `Get` 查询单条记录
```Go ```Go
has, err := engine.Get(&user) has, err := engine.Get(&user)
// SELECT * FROM user LIMIT 1 // SELECT * FROM user LIMIT 1
has, err := engine.Where("name = ?", name).Desc("id").Get(&user) has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
// SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
var name string var name string
has, err := engine.Where("id = ?", id).Cols("name").Get(&name) has, err := engine.Where("id = ?", id).Cols("name").Get(&name)
// SELECT name FROM user WHERE id = ? // SELECT name FROM user WHERE id = ?
var id int64 var id int64
has, err := engine.Where("name = ?", name).Cols("id").Get(&id) has, err := engine.Where("name = ?", name).Cols("id").Get(&id)
has, err := engine.SQL("select id from user").Get(&id)
// SELECT id FROM user WHERE name = ? // SELECT id FROM user WHERE name = ?
var valuesMap = make(map[string]string) var valuesMap = make(map[string]string)
has, err := engine.Where("id = ?", id).Get(&valuesMap) has, err := engine.Where("id = ?", id).Get(&valuesMap)
// SELECT * FROM user WHERE id = ? // SELECT * FROM user WHERE id = ?
var valuesSlice = make([]interface{}, len(cols)) var valuesSlice = make([]interface{}, len(cols))
has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
// SELECT col1, col2, col3 FROM user WHERE id = ? // SELECT col1, col2, col3 FROM user WHERE id = ?
``` ```
* 检测记录是否存在 * `Exist` 检测记录是否存在
```Go ```Go
has, err := testEngine.Exist(new(RecordExist)) has, err := testEngine.Exist(new(RecordExist))
// SELECT * FROM record_exist LIMIT 1 // SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Exist(&RecordExist{ has, err = testEngine.Exist(&RecordExist{
Name: "test1", Name: "test1",
}) })
// SELECT * FROM record_exist WHERE name = ? LIMIT 1 // SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{})
// SELECT * FROM record_exist WHERE name = ? LIMIT 1 // SELECT * FROM record_exist WHERE name = ? LIMIT 1
has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist() has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist()
// select * from record_exist where name = ? // select * from record_exist where name = ?
has, err = testEngine.Table("record_exist").Exist() has, err = testEngine.Table("record_exist").Exist()
// SELECT * FROM record_exist LIMIT 1 // SELECT * FROM record_exist LIMIT 1
has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist()
// SELECT * FROM record_exist WHERE name = ? LIMIT 1 // SELECT * FROM record_exist WHERE name = ? LIMIT 1
``` ```
* 查询多条记录当然可以使用Join和extends来组合使用 * `Find` 查询多条记录当然可以使用Join和extends来组合使用
```Go ```Go
var users []User var users []User
err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users) err := engine.Where("name = ?", name).And("age > 10").Limit(10, 0).Find(&users)
// SELECT * FROM user WHERE name = ? AND age > 10 limit 0 offset 10 // SELECT * FROM user WHERE name = ? AND age > 10 limit 10 offset 0
type Detail struct { type Detail struct {
Id int64 Id int64
@ -206,10 +241,10 @@ err := engine.Table("user").Select("user.*, detail.*")
Join("INNER", "detail", "detail.user_id = user.id"). Join("INNER", "detail", "detail.user_id = user.id").
Where("user.name = ?", name).Limit(10, 0). Where("user.name = ?", name).Limit(10, 0).
Find(&users) Find(&users)
// SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 // SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 10 offset 0
``` ```
* 根据条件遍历数据库,可以有两种方式: Iterate and Rows * `Iterate``Rows` 根据条件遍历数据库,可以有两种方式: Iterate and Rows
```Go ```Go
err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error { err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
@ -218,6 +253,13 @@ err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
}) })
// SELECT * FROM user // SELECT * FROM user
err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean interface{}) error {
user := bean.(*User)
return nil
})
// SELECT * FROM user Limit 0, 100
// SELECT * FROM user Limit 101, 100
rows, err := engine.Rows(&User{Name:name}) rows, err := engine.Rows(&User{Name:name})
// SELECT * FROM user // SELECT * FROM user
defer rows.Close() defer rows.Close()
@ -227,7 +269,7 @@ for rows.Next() {
} }
``` ```
* 更新数据除非使用Cols,AllCols函数指明默认只更新非空和非0的字段 * `Update` 更新数据除非使用Cols,AllCols函数指明默认只更新非空和非0的字段
```Go ```Go
affected, err := engine.Id(1).Update(&user) affected, err := engine.Id(1).Update(&user)
@ -252,20 +294,39 @@ affected, err := engine.Id(1).AllCols().Update(&user)
// UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ? // UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ?
``` ```
* 删除记录需要注意删除必须至少有一个条件否则会报错。要清空数据库可以用EmptyTable * `Delete` 删除记录需要注意删除必须至少有一个条件否则会报错。要清空数据库可以用EmptyTable
```Go ```Go
affected, err := engine.Where(...).Delete(&user) affected, err := engine.Where(...).Delete(&user)
// DELETE FROM user Where ... // DELETE FROM user Where ...
affected, err := engine.ID(2).Delete(&user)
// DELETE FROM user Where id = ?
``` ```
* 获取记录条数 * `Count` 获取记录条数
```Go ```Go
counts, err := engine.Count(&user) counts, err := engine.Count(&user)
// SELECT count(*) AS total FROM user // SELECT count(*) AS total FROM user
``` ```
* `Sum` 求和函数
```Go
agesFloat64, err := engine.Sum(&user, "age")
// SELECT sum(age) AS total FROM user
agesInt64, err := engine.SumInt(&user, "age")
// SELECT sum(age) AS total FROM user
sumFloat64Slice, err := engine.Sums(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
sumInt64Slice, err := engine.SumsInt(&user, "age", "score")
// SELECT sum(age), sum(score) FROM user
```
* 条件编辑器 * 条件编辑器
```Go ```Go
@ -273,6 +334,59 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))
// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?) // SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?)
``` ```
* 在一个Go程中多次操作数据库但没有事务
```Go
session := engine.NewSession()
defer session.Close()
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
return nil
```
* 在一个Go程中有事务
```Go
session := engine.NewSession()
defer session.Close()
// add Begin() before any action
if err := session.Begin(); err != nil {
// if returned then will rollback automatically
return err
}
user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()}
if _, err := session.Insert(&user1); err != nil {
return err
}
user2 := Userinfo{Username: "yyy"}
if _, err := session.Where("id = ?", 2).Update(&user2); err != nil {
return err
}
if _, err := session.Exec("delete from userinfo where username = ?", user2.Username); err != nil {
return err
}
// add Commit() after all actions
return session.Commit()
```
# 案例 # 案例
* [Go语言中文网](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang) * [Go语言中文网](http://studygolang.com/) - [github.com/studygolang/studygolang](https://github.com/studygolang/studygolang)

26
vendor/github.com/go-xorm/xorm/context.go generated vendored Normal file
View file

@ -0,0 +1,26 @@
// Copyright 2017 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.
// +build go1.8
package xorm
import "context"
// PingContext tests if database is alive
func (engine *Engine) PingContext(ctx context.Context) error {
session := engine.NewSession()
defer session.Close()
return session.PingContext(ctx)
}
// PingContext test if database is ok
func (session *Session) PingContext(ctx context.Context) error {
if session.isAutoClose {
defer session.Close()
}
session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName())
return session.DB().PingContext(ctx)
}

View file

@ -209,10 +209,10 @@ func convertAssign(dest, src interface{}) error {
if src == nil { if src == nil {
dv.Set(reflect.Zero(dv.Type())) dv.Set(reflect.Zero(dv.Type()))
return nil return nil
} else { }
dv.Set(reflect.New(dv.Type().Elem())) dv.Set(reflect.New(dv.Type().Elem()))
return convertAssign(dv.Interface(), src) return convertAssign(dv.Interface(), src)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s := asString(src) s := asString(src)
i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())

View file

@ -8,7 +8,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
"sort"
"strconv" "strconv"
"strings" "strings"
@ -765,13 +764,18 @@ var (
"YES": true, "YES": true,
"ZONE": true, "ZONE": true,
} }
// DefaultPostgresSchema default postgres schema
DefaultPostgresSchema = "public"
) )
type postgres struct { type postgres struct {
core.Base core.Base
schema string
} }
func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error {
db.schema = DefaultPostgresSchema
return db.Base.Init(d, db, uri, drivername, dataSourceName) return db.Base.Init(d, db, uri, drivername, dataSourceName)
} }
@ -923,7 +927,7 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) {
func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
// FIXME: the schema should be replaced by user custom's // FIXME: the schema should be replaced by user custom's
args := []interface{}{tableName, "public"} args := []interface{}{tableName, db.schema}
s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix , s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix ,
CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey, CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey,
CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey
@ -1024,8 +1028,7 @@ WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.att
} }
func (db *postgres) GetTables() ([]*core.Table, error) { func (db *postgres) GetTables() ([]*core.Table, error) {
// FIXME: replace public to user customrize schema args := []interface{}{db.schema}
args := []interface{}{"public"}
s := fmt.Sprintf("SELECT tablename FROM pg_tables WHERE schemaname = $1") s := fmt.Sprintf("SELECT tablename FROM pg_tables WHERE schemaname = $1")
db.LogSQL(s, args) db.LogSQL(s, args)
@ -1050,8 +1053,7 @@ func (db *postgres) GetTables() ([]*core.Table, error) {
} }
func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) { func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) {
// FIXME: replace the public schema to user specify schema args := []interface{}{db.schema, tableName}
args := []interface{}{"public", tableName}
s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE schemaname=$1 AND tablename=$2") s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE schemaname=$1 AND tablename=$2")
db.LogSQL(s, args) db.LogSQL(s, args)
@ -1117,10 +1119,6 @@ func (vs values) Get(k string) (v string) {
return vs[k] return vs[k]
} }
func errorf(s string, args ...interface{}) {
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
}
func parseURL(connstr string) (string, error) { func parseURL(connstr string) (string, error) {
u, err := url.Parse(connstr) u, err := url.Parse(connstr)
if err != nil { if err != nil {
@ -1131,46 +1129,18 @@ func parseURL(connstr string) (string, error) {
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
} }
var kvs []string
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
accrue := func(k, v string) {
if v != "" {
kvs = append(kvs, k+"="+escaper.Replace(v))
}
}
if u.User != nil {
v := u.User.Username()
accrue("user", v)
v, _ = u.User.Password()
accrue("password", v)
}
i := strings.Index(u.Host, ":")
if i < 0 {
accrue("host", u.Host)
} else {
accrue("host", u.Host[:i])
accrue("port", u.Host[i+1:])
}
if u.Path != "" { if u.Path != "" {
accrue("dbname", u.Path[1:]) return escaper.Replace(u.Path[1:]), nil
} }
q := u.Query() return "", nil
for k := range q {
accrue(k, q.Get(k))
}
sort.Strings(kvs) // Makes testing easier (not a performance concern)
return strings.Join(kvs, " "), nil
} }
func parseOpts(name string, o values) { func parseOpts(name string, o values) error {
if len(name) == 0 { if len(name) == 0 {
return return fmt.Errorf("invalid options: %s", name)
} }
name = strings.TrimSpace(name) name = strings.TrimSpace(name)
@ -1179,31 +1149,36 @@ func parseOpts(name string, o values) {
for _, p := range ps { for _, p := range ps {
kv := strings.Split(p, "=") kv := strings.Split(p, "=")
if len(kv) < 2 { if len(kv) < 2 {
errorf("invalid option: %q", p) return fmt.Errorf("invalid option: %q", p)
} }
o.Set(kv[0], kv[1]) o.Set(kv[0], kv[1])
} }
return nil
} }
func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
db := &core.Uri{DbType: core.POSTGRES} db := &core.Uri{DbType: core.POSTGRES}
o := make(values)
var err error var err error
if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") { if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
dataSourceName, err = parseURL(dataSourceName) db.DbName, err = parseURL(dataSourceName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else {
o := make(values)
err = parseOpts(dataSourceName, o)
if err != nil {
return nil, err
} }
parseOpts(dataSourceName, o)
db.DbName = o.Get("dbname") db.DbName = o.Get("dbname")
}
if db.DbName == "" { if db.DbName == "" {
return nil, errors.New("dbname is empty") return nil, errors.New("dbname is empty")
} }
/*db.Schema = o.Get("schema")
if len(db.Schema) == 0 {
db.Schema = "public"
}*/
return db, nil return db, nil
} }

View file

@ -47,6 +47,23 @@ type Engine struct {
disableGlobalCache bool disableGlobalCache bool
tagHandlers map[string]tagHandler tagHandlers map[string]tagHandler
engineGroup *EngineGroup
}
// BufferSize sets buffer size for iterate
func (engine *Engine) BufferSize(size int) *Session {
session := engine.NewSession()
session.isAutoClose = true
return session.BufferSize(size)
}
// CondDeleted returns the conditions whether a record is soft deleted.
func (engine *Engine) CondDeleted(colName string) builder.Cond {
if engine.dialect.DBType() == core.MSSQL {
return builder.IsNull{colName}
}
return builder.IsNull{colName}.Or(builder.Eq{colName: zeroTime1})
} }
// ShowSQL show SQL statement or not on logger if log level is great than INFO // ShowSQL show SQL statement or not on logger if log level is great than INFO
@ -79,6 +96,11 @@ func (engine *Engine) SetLogger(logger core.ILogger) {
engine.dialect.SetLogger(logger) engine.dialect.SetLogger(logger)
} }
// SetLogLevel sets the logger level
func (engine *Engine) SetLogLevel(level core.LogLevel) {
engine.logger.SetLevel(level)
}
// SetDisableGlobalCache disable global cache or not // SetDisableGlobalCache disable global cache or not
func (engine *Engine) SetDisableGlobalCache(disable bool) { func (engine *Engine) SetDisableGlobalCache(disable bool) {
if engine.disableGlobalCache != disable { if engine.disableGlobalCache != disable {
@ -201,6 +223,11 @@ func (engine *Engine) SetDefaultCacher(cacher core.Cacher) {
engine.Cacher = cacher engine.Cacher = cacher
} }
// GetDefaultCacher returns the default cacher
func (engine *Engine) GetDefaultCacher() core.Cacher {
return engine.Cacher
}
// NoCache If you has set default cacher, and you want temporilly stop use cache, // NoCache If you has set default cacher, and you want temporilly stop use cache,
// you can use NoCache() // you can use NoCache()
func (engine *Engine) NoCache() *Session { func (engine *Engine) NoCache() *Session {
@ -736,6 +763,13 @@ func (engine *Engine) OrderBy(order string) *Session {
return session.OrderBy(order) return session.OrderBy(order)
} }
// Prepare enables prepare statement
func (engine *Engine) Prepare() *Session {
session := engine.NewSession()
session.isAutoClose = true
return session.Prepare()
}
// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN // Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session { func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session {
session := engine.NewSession() session := engine.NewSession()
@ -757,7 +791,8 @@ func (engine *Engine) Having(conditions string) *Session {
return session.Having(conditions) return session.Having(conditions)
} }
func (engine *Engine) unMapType(t reflect.Type) { // UnMapType removes the datbase mapper of a type
func (engine *Engine) UnMapType(t reflect.Type) {
engine.mutex.Lock() engine.mutex.Lock()
defer engine.mutex.Unlock() defer engine.mutex.Unlock()
delete(engine.Tables, t) delete(engine.Tables, t)
@ -914,7 +949,7 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
} }
if pStart > -1 { if pStart > -1 {
if !strings.HasSuffix(k, ")") { if !strings.HasSuffix(k, ")") {
return nil, errors.New("cannot match ) charactor") return nil, fmt.Errorf("field %s tag %s cannot match ) charactor", col.FieldName, key)
} }
ctx.tagName = k[:pStart] ctx.tagName = k[:pStart]
@ -1341,24 +1376,24 @@ func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error)
} }
// Query a raw sql and return records as []map[string][]byte // Query a raw sql and return records as []map[string][]byte
func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { func (engine *Engine) Query(sqlorArgs ...interface{}) (resultsSlice []map[string][]byte, err error) {
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
return session.Query(sql, paramStr...) return session.Query(sqlorArgs...)
} }
// QueryString runs a raw sql and return records as []map[string]string // QueryString runs a raw sql and return records as []map[string]string
func (engine *Engine) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { func (engine *Engine) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) {
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
return session.QueryString(sqlStr, args...) return session.QueryString(sqlorArgs...)
} }
// QueryInterface runs a raw sql and return records as []map[string]interface{} // QueryInterface runs a raw sql and return records as []map[string]interface{}
func (engine *Engine) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { func (engine *Engine) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) {
session := engine.NewSession() session := engine.NewSession()
defer session.Close() defer session.Close()
return session.QueryInterface(sqlStr, args...) return session.QueryInterface(sqlorArgs...)
} }
// Insert one or more records // Insert one or more records
@ -1564,24 +1599,39 @@ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{}
return return
} }
// GetColumnMapper returns the column name mapper
func (engine *Engine) GetColumnMapper() core.IMapper {
return engine.ColumnMapper
}
// GetTableMapper returns the table name mapper
func (engine *Engine) GetTableMapper() core.IMapper {
return engine.TableMapper
}
// GetTZLocation returns time zone of the application
func (engine *Engine) GetTZLocation() *time.Location {
return engine.TZLocation
}
// SetTZLocation sets time zone of the application
func (engine *Engine) SetTZLocation(tz *time.Location) {
engine.TZLocation = tz
}
// GetTZDatabase returns time zone of the database
func (engine *Engine) GetTZDatabase() *time.Location {
return engine.DatabaseTZ
}
// SetTZDatabase sets time zone of the database
func (engine *Engine) SetTZDatabase(tz *time.Location) {
engine.DatabaseTZ = tz
}
// Unscoped always disable struct tag "deleted" // Unscoped always disable struct tag "deleted"
func (engine *Engine) Unscoped() *Session { func (engine *Engine) Unscoped() *Session {
session := engine.NewSession() session := engine.NewSession()
session.isAutoClose = true session.isAutoClose = true
return session.Unscoped() return session.Unscoped()
} }
// CondDeleted returns the conditions whether a record is soft deleted.
func (engine *Engine) CondDeleted(colName string) builder.Cond {
if engine.dialect.DBType() == core.MSSQL {
return builder.IsNull{colName}
}
return builder.IsNull{colName}.Or(builder.Eq{colName: zeroTime1})
}
// BufferSize sets buffer size for iterate
func (engine *Engine) BufferSize(size int) *Session {
session := engine.NewSession()
session.isAutoClose = true
return session.BufferSize(size)
}

194
vendor/github.com/go-xorm/xorm/engine_group.go generated vendored Normal file
View file

@ -0,0 +1,194 @@
// Copyright 2017 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 xorm
import (
"github.com/go-xorm/core"
)
// EngineGroup defines an engine group
type EngineGroup struct {
*Engine
slaves []*Engine
policy GroupPolicy
}
// NewEngineGroup creates a new engine group
func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) {
var eg EngineGroup
if len(policies) > 0 {
eg.policy = policies[0]
} else {
eg.policy = RoundRobinPolicy()
}
driverName, ok1 := args1.(string)
conns, ok2 := args2.([]string)
if ok1 && ok2 {
engines := make([]*Engine, len(conns))
for i, conn := range conns {
engine, err := NewEngine(driverName, conn)
if err != nil {
return nil, err
}
engine.engineGroup = &eg
engines[i] = engine
}
eg.Engine = engines[0]
eg.slaves = engines[1:]
return &eg, nil
}
master, ok3 := args1.(*Engine)
slaves, ok4 := args2.([]*Engine)
if ok3 && ok4 {
master.engineGroup = &eg
for i := 0; i < len(slaves); i++ {
slaves[i].engineGroup = &eg
}
eg.Engine = master
eg.slaves = slaves
return &eg, nil
}
return nil, ErrParamsType
}
// Close the engine
func (eg *EngineGroup) Close() error {
err := eg.Engine.Close()
if err != nil {
return err
}
for i := 0; i < len(eg.slaves); i++ {
err := eg.slaves[i].Close()
if err != nil {
return err
}
}
return nil
}
// Master returns the master engine
func (eg *EngineGroup) Master() *Engine {
return eg.Engine
}
// Ping tests if database is alive
func (eg *EngineGroup) Ping() error {
if err := eg.Engine.Ping(); err != nil {
return err
}
for _, slave := range eg.slaves {
if err := slave.Ping(); err != nil {
return err
}
}
return nil
}
// SetColumnMapper set the column name mapping rule
func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) {
eg.Engine.ColumnMapper = mapper
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].ColumnMapper = mapper
}
}
// SetDefaultCacher set the default cacher
func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) {
eg.Engine.SetDefaultCacher(cacher)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetDefaultCacher(cacher)
}
}
// SetLogger set the new logger
func (eg *EngineGroup) SetLogger(logger core.ILogger) {
eg.Engine.SetLogger(logger)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetLogger(logger)
}
}
// SetLogLevel sets the logger level
func (eg *EngineGroup) SetLogLevel(level core.LogLevel) {
eg.Engine.SetLogLevel(level)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetLogLevel(level)
}
}
// SetMapper set the name mapping rules
func (eg *EngineGroup) SetMapper(mapper core.IMapper) {
eg.Engine.SetMapper(mapper)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetMapper(mapper)
}
}
// SetMaxIdleConns set the max idle connections on pool, default is 2
func (eg *EngineGroup) SetMaxIdleConns(conns int) {
eg.Engine.db.SetMaxIdleConns(conns)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].db.SetMaxIdleConns(conns)
}
}
// SetMaxOpenConns is only available for go 1.2+
func (eg *EngineGroup) SetMaxOpenConns(conns int) {
eg.Engine.db.SetMaxOpenConns(conns)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].db.SetMaxOpenConns(conns)
}
}
// SetPolicy set the group policy
func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup {
eg.policy = policy
return eg
}
// SetTableMapper set the table name mapping rule
func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) {
eg.Engine.TableMapper = mapper
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].TableMapper = mapper
}
}
// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO
func (eg *EngineGroup) ShowExecTime(show ...bool) {
eg.Engine.ShowExecTime(show...)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].ShowExecTime(show...)
}
}
// ShowSQL show SQL statement or not on logger if log level is great than INFO
func (eg *EngineGroup) ShowSQL(show ...bool) {
eg.Engine.ShowSQL(show...)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].ShowSQL(show...)
}
}
// Slave returns one of the physical databases which is a slave according the policy
func (eg *EngineGroup) Slave() *Engine {
switch len(eg.slaves) {
case 0:
return eg.Engine
case 1:
return eg.slaves[0]
}
return eg.policy.Slave(eg)
}
// Slaves returns all the slaves
func (eg *EngineGroup) Slaves() []*Engine {
return eg.slaves
}

116
vendor/github.com/go-xorm/xorm/engine_group_policy.go generated vendored Normal file
View file

@ -0,0 +1,116 @@
// Copyright 2017 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 xorm
import (
"math/rand"
"sync"
"time"
)
// GroupPolicy is be used by chosing the current slave from slaves
type GroupPolicy interface {
Slave(*EngineGroup) *Engine
}
// GroupPolicyHandler should be used when a function is a GroupPolicy
type GroupPolicyHandler func(*EngineGroup) *Engine
// Slave implements the chosen of slaves
func (h GroupPolicyHandler) Slave(eg *EngineGroup) *Engine {
return h(eg)
}
// RandomPolicy implmentes randomly chose the slave of slaves
func RandomPolicy() GroupPolicyHandler {
var r = rand.New(rand.NewSource(time.Now().UnixNano()))
return func(g *EngineGroup) *Engine {
return g.Slaves()[r.Intn(len(g.Slaves()))]
}
}
// WeightRandomPolicy implmentes randomly chose the slave of slaves
func WeightRandomPolicy(weights []int) GroupPolicyHandler {
var rands = make([]int, 0, len(weights))
for i := 0; i < len(weights); i++ {
for n := 0; n < weights[i]; n++ {
rands = append(rands, i)
}
}
var r = rand.New(rand.NewSource(time.Now().UnixNano()))
return func(g *EngineGroup) *Engine {
var slaves = g.Slaves()
idx := rands[r.Intn(len(rands))]
if idx >= len(slaves) {
idx = len(slaves) - 1
}
return slaves[idx]
}
}
func RoundRobinPolicy() GroupPolicyHandler {
var pos = -1
var lock sync.Mutex
return func(g *EngineGroup) *Engine {
var slaves = g.Slaves()
lock.Lock()
defer lock.Unlock()
pos++
if pos >= len(slaves) {
pos = 0
}
return slaves[pos]
}
}
func WeightRoundRobinPolicy(weights []int) GroupPolicyHandler {
var rands = make([]int, 0, len(weights))
for i := 0; i < len(weights); i++ {
for n := 0; n < weights[i]; n++ {
rands = append(rands, i)
}
}
var pos = -1
var lock sync.Mutex
return func(g *EngineGroup) *Engine {
var slaves = g.Slaves()
lock.Lock()
defer lock.Unlock()
pos++
if pos >= len(rands) {
pos = 0
}
idx := rands[pos]
if idx >= len(slaves) {
idx = len(slaves) - 1
}
return slaves[idx]
}
}
// LeastConnPolicy implements GroupPolicy, every time will get the least connections slave
func LeastConnPolicy() GroupPolicyHandler {
return func(g *EngineGroup) *Engine {
var slaves = g.Slaves()
connections := 0
idx := 0
for i := 0; i < len(slaves); i++ {
openConnections := slaves[i].DB().Stats().OpenConnections
if i == 0 {
connections = openConnections
idx = i
} else if openConnections <= connections {
connections = openConnections
idx = i
}
}
return slaves[idx]
}
}

View file

@ -12,3 +12,11 @@ import "time"
func (engine *Engine) SetConnMaxLifetime(d time.Duration) { func (engine *Engine) SetConnMaxLifetime(d time.Duration) {
engine.db.SetConnMaxLifetime(d) engine.db.SetConnMaxLifetime(d)
} }
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) {
eg.Engine.SetConnMaxLifetime(d)
for i := 0; i < len(eg.slaves); i++ {
eg.slaves[i].SetConnMaxLifetime(d)
}
}

View file

@ -23,4 +23,6 @@ var (
ErrNeedDeletedCond = errors.New("Delete need at least one condition") ErrNeedDeletedCond = errors.New("Delete need at least one condition")
// ErrNotImplemented not implemented // ErrNotImplemented not implemented
ErrNotImplemented = errors.New("Not implemented") ErrNotImplemented = errors.New("Not implemented")
// ErrConditionType condition type unsupported
ErrConditionType = errors.New("Unsupported conditon type")
) )

103
vendor/github.com/go-xorm/xorm/interface.go generated vendored Normal file
View file

@ -0,0 +1,103 @@
// Copyright 2017 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 xorm
import (
"database/sql"
"reflect"
"time"
"github.com/go-xorm/core"
)
// Interface defines the interface which Engine, EngineGroup and Session will implementate.
type Interface interface {
AllCols() *Session
Alias(alias string) *Session
Asc(colNames ...string) *Session
BufferSize(size int) *Session
Cols(columns ...string) *Session
Count(...interface{}) (int64, error)
CreateIndexes(bean interface{}) error
CreateUniques(bean interface{}) error
Decr(column string, arg ...interface{}) *Session
Desc(...string) *Session
Delete(interface{}) (int64, error)
Distinct(columns ...string) *Session
DropIndexes(bean interface{}) error
Exec(string, ...interface{}) (sql.Result, error)
Exist(bean ...interface{}) (bool, error)
Find(interface{}, ...interface{}) error
Get(interface{}) (bool, error)
GroupBy(keys string) *Session
ID(interface{}) *Session
In(string, ...interface{}) *Session
Incr(column string, arg ...interface{}) *Session
Insert(...interface{}) (int64, error)
InsertOne(interface{}) (int64, error)
IsTableEmpty(bean interface{}) (bool, error)
IsTableExist(beanOrTableName interface{}) (bool, error)
Iterate(interface{}, IterFunc) error
Limit(int, ...int) *Session
NoAutoCondition(...bool) *Session
NotIn(string, ...interface{}) *Session
Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session
Omit(columns ...string) *Session
OrderBy(order string) *Session
Ping() error
Query(sqlOrAgrs ...interface{}) (resultsSlice []map[string][]byte, err error)
QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error)
QueryString(sqlorArgs ...interface{}) ([]map[string]string, error)
Rows(bean interface{}) (*Rows, error)
SetExpr(string, string) *Session
SQL(interface{}, ...interface{}) *Session
Sum(bean interface{}, colName string) (float64, error)
SumInt(bean interface{}, colName string) (int64, error)
Sums(bean interface{}, colNames ...string) ([]float64, error)
SumsInt(bean interface{}, colNames ...string) ([]int64, error)
Table(tableNameOrBean interface{}) *Session
Unscoped() *Session
Update(bean interface{}, condiBeans ...interface{}) (int64, error)
UseBool(...string) *Session
Where(interface{}, ...interface{}) *Session
}
// EngineInterface defines the interface which Engine, EngineGroup will implementate.
type EngineInterface interface {
Interface
Before(func(interface{})) *Session
Charset(charset string) *Session
CreateTables(...interface{}) error
DBMetas() ([]*core.Table, error)
Dialect() core.Dialect
DropTables(...interface{}) error
DumpAllToFile(fp string, tp ...core.DbType) error
GetColumnMapper() core.IMapper
GetDefaultCacher() core.Cacher
GetTableMapper() core.IMapper
GetTZDatabase() *time.Location
GetTZLocation() *time.Location
NewSession() *Session
NoAutoTime() *Session
Quote(string) string
SetDefaultCacher(core.Cacher)
SetLogLevel(core.LogLevel)
SetMapper(core.IMapper)
SetTZDatabase(tz *time.Location)
SetTZLocation(tz *time.Location)
ShowSQL(show ...bool)
Sync(...interface{}) error
Sync2(...interface{}) error
StoreEngine(storeEngine string) *Session
TableInfo(bean interface{}) *Table
UnMapType(reflect.Type)
}
var (
_ Interface = &Session{}
_ EngineInterface = &Engine{}
_ EngineInterface = &EngineGroup{}
)

View file

@ -76,6 +76,7 @@ func (session *Session) Init() {
session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0) session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0)
session.beforeClosures = make([]func(interface{}), 0) session.beforeClosures = make([]func(interface{}), 0)
session.afterClosures = make([]func(interface{}), 0) session.afterClosures = make([]func(interface{}), 0)
session.stmtCache = make(map[uint32]*core.Stmt)
session.afterProcessors = make([]executedProcessor, 0) session.afterProcessors = make([]executedProcessor, 0)
@ -262,13 +263,13 @@ func (session *Session) canCache() bool {
return true return true
} }
func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) { func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, err error) {
crc := crc32.ChecksumIEEE([]byte(sqlStr)) crc := crc32.ChecksumIEEE([]byte(sqlStr))
// TODO try hash(sqlStr+len(sqlStr)) // TODO try hash(sqlStr+len(sqlStr))
var has bool var has bool
stmt, has = session.stmtCache[crc] stmt, has = session.stmtCache[crc]
if !has { if !has {
stmt, err = session.DB().Prepare(sqlStr) stmt, err = db.Prepare(sqlStr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -461,6 +462,10 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b
hasAssigned = true hasAssigned = true
if len(bs) > 0 { if len(bs) > 0 {
if fieldType.Kind() == reflect.String {
fieldValue.SetString(string(bs))
continue
}
if fieldValue.CanAddr() { if fieldValue.CanAddr() {
err := json.Unmarshal(bs, fieldValue.Addr().Interface()) err := json.Unmarshal(bs, fieldValue.Addr().Interface())
if err != nil { if err != nil {

View file

@ -34,27 +34,27 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
sd, err := strconv.ParseInt(sdata, 10, 64) sd, err := strconv.ParseInt(sdata, 10, 64)
if err == nil { if err == nil {
x = time.Unix(sd, 0) x = time.Unix(sd, 0)
session.engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) //session.engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
} else { } else {
session.engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) //session.engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
} }
} else if len(sdata) > 19 && strings.Contains(sdata, "-") { } else if len(sdata) > 19 && strings.Contains(sdata, "-") {
x, err = time.ParseInLocation(time.RFC3339Nano, sdata, parseLoc) x, err = time.ParseInLocation(time.RFC3339Nano, sdata, parseLoc)
session.engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) session.engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
if err != nil { if err != nil {
x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, parseLoc) x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, parseLoc)
session.engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) //session.engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
} }
if err != nil { if err != nil {
x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, parseLoc) x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, parseLoc)
session.engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) //session.engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
} }
} else if len(sdata) == 19 && strings.Contains(sdata, "-") { } else if len(sdata) == 19 && strings.Contains(sdata, "-") {
x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, parseLoc) x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, parseLoc)
session.engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) //session.engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
x, err = time.ParseInLocation("2006-01-02", sdata, parseLoc) x, err = time.ParseInLocation("2006-01-02", sdata, parseLoc)
session.engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) //session.engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
} else if col.SQLType.Name == core.Time { } else if col.SQLType.Name == core.Time {
if strings.Contains(sdata, " ") { if strings.Contains(sdata, " ") {
ssd := strings.Split(sdata, " ") ssd := strings.Split(sdata, " ")
@ -68,7 +68,7 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
st := fmt.Sprintf("2006-01-02 %v", sdata) st := fmt.Sprintf("2006-01-02 %v", sdata)
x, err = time.ParseInLocation("2006-01-02 15:04:05", st, parseLoc) x, err = time.ParseInLocation("2006-01-02 15:04:05", st, parseLoc)
session.engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) //session.engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
} else { } else {
outErr = fmt.Errorf("unsupported time format %v", sdata) outErr = fmt.Errorf("unsupported time format %v", sdata)
return return

View file

@ -10,6 +10,7 @@ import (
"reflect" "reflect"
"github.com/go-xorm/builder" "github.com/go-xorm/builder"
"github.com/go-xorm/core"
) )
// Exist returns true if the record exist otherwise return false // Exist returns true if the record exist otherwise return false
@ -35,10 +36,18 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) {
return false, err return false, err
} }
if session.engine.dialect.DBType() == core.MSSQL {
sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s WHERE %s", tableName, condSQL)
} else {
sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL)
}
args = condArgs args = condArgs
} else {
if session.engine.dialect.DBType() == core.MSSQL {
sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s", tableName)
} else { } else {
sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName)
}
args = []interface{}{} args = []interface{}{}
} }
} else { } else {

View file

@ -5,6 +5,7 @@
package xorm package xorm
import ( import (
"database/sql"
"errors" "errors"
"reflect" "reflect"
"strconv" "strconv"
@ -79,6 +80,13 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea
return false, nil return false, nil
} }
switch bean.(type) {
case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString:
return true, rows.Scan(&bean)
case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString:
return true, rows.Scan(bean)
}
switch beanKind { switch beanKind {
case reflect.Struct: case reflect.Struct:
fields, err := rows.Columns() fields, err := rows.Columns()

View file

@ -400,7 +400,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
return 0, err return 0, err
} }
handleAfterInsertProcessorFunc(bean) defer handleAfterInsertProcessorFunc(bean)
if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache {
session.cacheInsert(table, tableName) session.cacheInsert(table, tableName)
@ -445,7 +445,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
handleAfterInsertProcessorFunc(bean) defer handleAfterInsertProcessorFunc(bean)
if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache {
session.cacheInsert(table, tableName) session.cacheInsert(table, tableName)

View file

@ -8,17 +8,92 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/go-xorm/builder"
"github.com/go-xorm/core" "github.com/go-xorm/core"
) )
func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interface{}, error) {
if len(sqlorArgs) > 0 {
switch sqlorArgs[0].(type) {
case string:
return sqlorArgs[0].(string), sqlorArgs[1:], nil
case *builder.Builder:
return sqlorArgs[0].(*builder.Builder).ToSQL()
case builder.Builder:
bd := sqlorArgs[0].(builder.Builder)
return bd.ToSQL()
default:
return "", nil, ErrUnSupportedType
}
}
if session.statement.RawSQL != "" {
return session.statement.RawSQL, session.statement.RawParams, nil
}
if len(session.statement.TableName()) <= 0 {
return "", nil, ErrTableNotFound
}
var columnStr = session.statement.ColumnStr
if len(session.statement.selectStr) > 0 {
columnStr = session.statement.selectStr
} else {
if session.statement.JoinStr == "" {
if columnStr == "" {
if session.statement.GroupByStr != "" {
columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1))
} else {
columnStr = session.statement.genColumnStr()
}
}
} else {
if columnStr == "" {
if session.statement.GroupByStr != "" {
columnStr = session.statement.Engine.Quote(strings.Replace(session.statement.GroupByStr, ",", session.engine.Quote(","), -1))
} else {
columnStr = "*"
}
}
}
if columnStr == "" {
columnStr = "*"
}
}
condSQL, condArgs, err := builder.ToSQL(session.statement.cond)
if err != nil {
return "", nil, err
}
args := append(session.statement.joinArgs, condArgs...)
sqlStr, err := session.statement.genSelectSQL(columnStr, condSQL)
if err != nil {
return "", nil, err
}
// for mssql and use limit
qs := strings.Count(sqlStr, "?")
if len(args)*2 == qs {
args = append(args, args...)
}
return sqlStr, args, nil
}
// Query runs a raw sql and return records as []map[string][]byte // Query runs a raw sql and return records as []map[string][]byte
func (session *Session) Query(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { func (session *Session) Query(sqlorArgs ...interface{}) ([]map[string][]byte, error) {
if session.isAutoClose { if session.isAutoClose {
defer session.Close() defer session.Close()
} }
sqlStr, args, err := session.genQuerySQL(sqlorArgs...)
if err != nil {
return nil, err
}
return session.queryBytes(sqlStr, args...) return session.queryBytes(sqlStr, args...)
} }
@ -114,11 +189,16 @@ func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error)
} }
// QueryString runs a raw sql and return records as []map[string]string // QueryString runs a raw sql and return records as []map[string]string
func (session *Session) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) {
if session.isAutoClose { if session.isAutoClose {
defer session.Close() defer session.Close()
} }
sqlStr, args, err := session.genQuerySQL(sqlorArgs...)
if err != nil {
return nil, err
}
rows, err := session.queryRows(sqlStr, args...) rows, err := session.queryRows(sqlStr, args...)
if err != nil { if err != nil {
return nil, err return nil, err
@ -162,11 +242,16 @@ func rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, er
} }
// QueryInterface runs a raw sql and return records as []map[string]interface{} // QueryInterface runs a raw sql and return records as []map[string]interface{}
func (session *Session) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { func (session *Session) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) {
if session.isAutoClose { if session.isAutoClose {
defer session.Close() defer session.Close()
} }
sqlStr, args, err := session.genQuerySQL(sqlorArgs...)
if err != nil {
return nil, err
}
rows, err := session.queryRows(sqlStr, args...) rows, err := session.queryRows(sqlStr, args...)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -47,9 +47,16 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row
} }
if session.isAutoCommit { if session.isAutoCommit {
var db *core.DB
if session.engine.engineGroup != nil {
db = session.engine.engineGroup.Slave().DB()
} else {
db = session.DB()
}
if session.prepareStmt { if session.prepareStmt {
// don't clear stmt since session will cache them // don't clear stmt since session will cache them
stmt, err := session.doPrepare(sqlStr) stmt, err := session.doPrepare(db, sqlStr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -61,7 +68,7 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row
return rows, nil return rows, nil
} }
rows, err := session.DB().Query(sqlStr, args...) rows, err := db.Query(sqlStr, args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -171,7 +178,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er
} }
if session.prepareStmt { if session.prepareStmt {
stmt, err := session.doPrepare(sqlStr) stmt, err := session.doPrepare(session.DB(), sqlStr)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -242,11 +242,24 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
var autoCond builder.Cond var autoCond builder.Cond
if !session.statement.noAutoCondition && len(condiBean) > 0 { if !session.statement.noAutoCondition && len(condiBean) > 0 {
if c, ok := condiBean[0].(map[string]interface{}); ok {
autoCond = builder.Eq(c)
} else {
ct := reflect.TypeOf(condiBean[0])
k := ct.Kind()
if k == reflect.Ptr {
k = ct.Elem().Kind()
}
if k == reflect.Struct {
var err error var err error
autoCond, err = session.statement.buildConds(session.statement.RefTable, condiBean[0], true, true, false, true, false) autoCond, err = session.statement.buildConds(session.statement.RefTable, condiBean[0], true, true, false, true, false)
if err != nil { if err != nil {
return 0, err return 0, err
} }
} else {
return 0, ErrConditionType
}
}
} }
st := &session.statement st := &session.statement

View file

@ -160,6 +160,9 @@ func (statement *Statement) And(query interface{}, args ...interface{}) *Stateme
case string: case string:
cond := builder.Expr(query.(string), args...) cond := builder.Expr(query.(string), args...)
statement.cond = statement.cond.And(cond) statement.cond = statement.cond.And(cond)
case map[string]interface{}:
cond := builder.Eq(query.(map[string]interface{}))
statement.cond = statement.cond.And(cond)
case builder.Cond: case builder.Cond:
cond := query.(builder.Cond) cond := query.(builder.Cond)
statement.cond = statement.cond.And(cond) statement.cond = statement.cond.And(cond)
@ -181,6 +184,9 @@ func (statement *Statement) Or(query interface{}, args ...interface{}) *Statemen
case string: case string:
cond := builder.Expr(query.(string), args...) cond := builder.Expr(query.(string), args...)
statement.cond = statement.cond.Or(cond) statement.cond = statement.cond.Or(cond)
case map[string]interface{}:
cond := builder.Eq(query.(map[string]interface{}))
statement.cond = statement.cond.Or(cond)
case builder.Cond: case builder.Cond:
cond := query.(builder.Cond) cond := query.(builder.Cond)
statement.cond = statement.cond.Or(cond) statement.cond = statement.cond.Or(cond)
@ -901,8 +907,12 @@ func (statement *Statement) genDelIndexSQL() []string {
func (statement *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) { func (statement *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) {
quote := statement.Engine.Quote quote := statement.Engine.Quote
sql := fmt.Sprintf("ALTER TABLE %v ADD %v;", quote(statement.TableName()), sql := fmt.Sprintf("ALTER TABLE %v ADD %v", quote(statement.TableName()),
col.String(statement.Engine.dialect)) col.String(statement.Engine.dialect))
if statement.Engine.dialect.DBType() == core.MYSQL && len(col.Comment) > 0 {
sql += " COMMENT '" + col.Comment + "'"
}
sql += ";"
return sql, []interface{}{} return sql, []interface{}{}
} }

18
vendor/vendor.json vendored
View file

@ -462,16 +462,16 @@
"revisionTime": "2016-11-01T11:13:14Z" "revisionTime": "2016-11-01T11:13:14Z"
}, },
{ {
"checksumSHA1": "9SXbj96wb1PgppBZzxMIN0axbFQ=", "checksumSHA1": "HsUSlgz1VKEEiZdkXY5qdLzexWU=",
"path": "github.com/go-xorm/builder", "path": "github.com/go-xorm/builder",
"revision": "c8871c857d2555fbfbd8524f895be5386d3d8836", "revision": "488224409dd8aa2ce7a5baf8d10d55764a913738",
"revisionTime": "2017-05-19T03:21:30Z" "revisionTime": "2018-01-16T06:54:19Z"
}, },
{ {
"checksumSHA1": "HMavuxvDhKOwmbbFnYt9hfT6jE0=", "checksumSHA1": "7JjlvSpGfLa49MHElks8NGBUfFA=",
"path": "github.com/go-xorm/core", "path": "github.com/go-xorm/core",
"revision": "da1adaf7a28ca792961721a34e6e04945200c890", "revision": "cb1d0ca71f42d3ee1bf4aba7daa16099bc31a7e9",
"revisionTime": "2017-09-09T08:56:53Z" "revisionTime": "2017-12-21T01:38:49Z"
}, },
{ {
"checksumSHA1": "k52lEKLp8j5M+jFpe+3u+bIFpxQ=", "checksumSHA1": "k52lEKLp8j5M+jFpe+3u+bIFpxQ=",
@ -480,10 +480,10 @@
"revisionTime": "2016-08-11T02:11:45Z" "revisionTime": "2016-08-11T02:11:45Z"
}, },
{ {
"checksumSHA1": "+KmPfckyKvrUZPIHBYHylg/7V8o=", "checksumSHA1": "eGBz6F3I/0naVUclZ6GZWc3EzQo=",
"path": "github.com/go-xorm/xorm", "path": "github.com/go-xorm/xorm",
"revision": "29d4a0330a00b9be468b70e3fb0f74109348c358", "revision": "d4149d1eee0c2c488a74a5863fd9caf13d60fd03",
"revisionTime": "2017-09-30T01:26:13Z" "revisionTime": "2018-01-22T13:32:35Z"
}, },
{ {
"checksumSHA1": "1ft/4j5MFa7C9dPI9whL03HSUzk=", "checksumSHA1": "1ft/4j5MFa7C9dPI9whL03HSUzk=",