From 682b827c7ea60480fc38d27f8416b4f596349ac0 Mon Sep 17 00:00:00 2001 From: Oleh Herych Date: Tue, 11 Jul 2017 04:16:07 +0300 Subject: [PATCH 01/12] More clear log (#638) I don't see `nil` in log. I suggest the next changes *Before* ``` INSERT INTO `dish` (`dish_id`,`restaurant_id`,`name`,`source`,`entry_id`,`created_by`,`menu_id`,`category`,`description`,`price`,`score`,`flog_count`,`approve`,`activated`,`created_at`,`updatated_at`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [59639fb56d9d9437b707a434 58a313719fee7b336da70c45 Chocolate Mousse Torte fsqi 51339970 Dessert Seasonal berry sauce 0 0 0 1 false 2017-07-10 18:39:33 2017-07-10 18:39:33] ``` *After* ``` INSERT INTO `dish` (`dish_id`,`restaurant_id`,`name`,`source`,`entry_id`,`created_by`,`menu_id`,`category`,`description`,`price`,`score`,`flog_count`,`approve`,`activated`,`created_at`,`updatated_at`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) []interface {}{"59639ec16d9d943782ce2f6c", "58a313719fee7b336da70c45", "Chocolate Mousse Torte", "fsqi", "51339970", "", "", "Dessert", "Seasonal berry sauce", 0, 0, 0, 1, false, "2017-07-10 18:35:29", "2017-07-10 18:35:29"} ``` --- engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine.go b/engine.go index b1478a24..5c48710e 100644 --- a/engine.go +++ b/engine.go @@ -267,7 +267,7 @@ func (engine *Engine) Ping() error { func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) { if engine.showSQL && !engine.showExecTime { if len(sqlArgs) > 0 { - engine.logger.Infof("[SQL] %v %v", sqlStr, sqlArgs) + engine.logger.Infof("[SQL] %v %#v", sqlStr, sqlArgs) } else { engine.logger.Infof("[SQL] %v", sqlStr) } From 78795d8f1385c80d5229435a13ef71d4e30435a6 Mon Sep 17 00:00:00 2001 From: Yang Luo Date: Tue, 11 Jul 2017 22:53:39 +0800 Subject: [PATCH 02/12] add Xorm Adapter for Casbin as cases (#639) --- README.md | 2 ++ README_CN.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 8b1c35b3..e366a634 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,8 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e")) * [Docker.cn](https://docker.cn/) +* [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter) + * [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) * [Gorevel](http://gorevel.cn/) - [github.com/goofcc/gorevel](http://github.com/goofcc/gorevel) diff --git a/README_CN.md b/README_CN.md index d74f6763..66a3c015 100644 --- a/README_CN.md +++ b/README_CN.md @@ -269,6 +269,8 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e")) * [Docker.cn](https://docker.cn/) +* [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter) + * [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) * [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) From 774f83c1bc04aca2a136270778d70ba0212d25d5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 14 Jul 2017 09:20:13 +0800 Subject: [PATCH 03/12] add Exist functions (#640) --- README.md | 51 ++++++++++++++++--------- README_CN.md | 52 +++++++++++++++++--------- engine.go | 7 ++++ session_exist.go | 87 +++++++++++++++++++++++++++++++++++++++++++ session_exist_test.go | 76 +++++++++++++++++++++++++++++++++++++ xorm.go | 2 +- 6 files changed, 238 insertions(+), 37 deletions(-) create mode 100644 session_exist.go create mode 100644 session_exist_test.go diff --git a/README.md b/README.md index e366a634..797df536 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,6 @@ Xorm is a simple and powerful ORM for Go. [![](https://goreportcard.com/badge/github.com/go-xorm/xorm)](https://goreportcard.com/report/github.com/go-xorm/xorm) [![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3) -# Notice - -The last master version is not backwards compatible. You should use `engine.ShowSQL()` and `engine.Logger().SetLevel()` instead of `engine.ShowSQL = `, `engine.ShowInfo = ` and so on. - # Features * Struct <-> Table Mapping Support @@ -52,6 +48,14 @@ Drivers for Go's sql package which currently support database/sql includes: # Changelog +* **v0.6.3** + * merge tests to main project + * add `Exist` function + * add `SumInt` function + * Mysql now support read and create column comment. + * fix time related bugs. + * fix some other bugs. + * **v0.6.2** * refactor tag parse methods * add Scan features to Get @@ -68,18 +72,6 @@ methods can use `builder.Cond` as parameter * logging interface changed * some bugs fixed -* **v0.4.5** - * many bugs fixed - * extends support unlimited deepth - * Delete Limit support - -* **v0.4.4** - * ql database expriment support - * tidb database expriment support - * sql.NullString and etc. field support - * select ForUpdate support - * many bugs fixed - [More changes ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16) # Installation @@ -168,6 +160,25 @@ has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) // SELECT col1, col2, col3 FROM user WHERE id = ? ``` +* Check if one record exist on table + +```Go +has, err := testEngine.Exist(new(RecordExist)) +// SELECT * FROM record_exist LIMIT 1 +has, err = testEngine.Exist(&RecordExist{ + Name: "test1", + }) +// SELECT * FROM record_exist WHERE name = ? LIMIT 1 +has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) +// SELECT * FROM record_exist WHERE name = ? LIMIT 1 +has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist() +// select * from record_exist where name = ? +has, err = testEngine.Table("record_exist").Exist() +// SELECT * FROM record_exist LIMIT 1 +has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() +// SELECT * FROM record_exist WHERE name = ? LIMIT 1 +``` + * Query multiple records from database, also you can use join and extends ```Go @@ -260,6 +271,12 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e")) # Cases +* [Gitea](http://gitea.io) - [github.com/go-gitea/gitea](http://github.com/go-gitea/gitea) + +* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) + +* [grafana](https://grafana.com/) - [github.com/grafana/grafana](http://github.com/grafana/grafana) + * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) * [Wego](http://github.com/go-tango/wego) @@ -268,8 +285,6 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e")) * [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter) -* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) - * [Gorevel](http://gorevel.cn/) - [github.com/goofcc/gorevel](http://github.com/goofcc/gorevel) * [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) diff --git a/README_CN.md b/README_CN.md index 66a3c015..53cc50af 100644 --- a/README_CN.md +++ b/README_CN.md @@ -8,10 +8,6 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 [![](https://goreportcard.com/badge/github.com/go-xorm/xorm)](https://goreportcard.com/report/github.com/go-xorm/xorm) [![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3) -# 注意 - -最新的版本有不兼容的更新,您必须使用 `engine.ShowSQL()` 和 `engine.Logger().SetLevel()` 来替代 `engine.ShowSQL = `, `engine.ShowInfo = ` 等等。 - ## 特性 * 支持Struct和数据库表之间的灵活映射,并支持自动同步 @@ -56,6 +52,15 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 更新日志 +* **v0.6.3** + * 合并单元测试到主工程 + * 新增`Exist`方法 + * 新增`SumInt`方法 + * Mysql新增读取和创建字段注释支持 + * 新增`SetConnMaxLifetime`方法 + * 修正了时间相关的Bug + * 修复了一些其它Bug + * **v0.6.2** * 重构Tag解析方式 * Get方法新增类似Scan的特性 @@ -72,18 +77,6 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * logging接口进行不兼容改变 * Bug修正 -* **v0.4.5** - * bug修正 - * extends 支持无限级 - * Delete Limit 支持 - -* **v0.4.4** - * Tidb 数据库支持 - * QL 试验性支持 - * sql.NullString支持 - * ForUpdate 支持 - * bug修正 - [更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16) ## 安装 @@ -172,6 +165,25 @@ has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) // SELECT col1, col2, col3 FROM user WHERE id = ? ``` +* 检测记录是否存在 + +```Go +has, err := testEngine.Exist(new(RecordExist)) +// SELECT * FROM record_exist LIMIT 1 +has, err = testEngine.Exist(&RecordExist{ + Name: "test1", + }) +// SELECT * FROM record_exist WHERE name = ? LIMIT 1 +has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) +// SELECT * FROM record_exist WHERE name = ? LIMIT 1 +has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist() +// select * from record_exist where name = ? +has, err = testEngine.Table("record_exist").Exist() +// SELECT * FROM record_exist LIMIT 1 +has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() +// SELECT * FROM record_exist WHERE name = ? LIMIT 1 +``` + * 查询多条记录,当然可以使用Join和extends来组合使用 ```Go @@ -263,6 +275,12 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e")) # 案例 +* [Gitea](http://gitea.io) - [github.com/go-gitea/gitea](http://github.com/go-gitea/gitea) + +* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) + +* [grafana](https://grafana.com/) - [github.com/grafana/grafana](http://github.com/grafana/grafana) + * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) * [Wego](http://github.com/go-tango/wego) @@ -271,8 +289,6 @@ err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e")) * [Xorm Adapter](https://github.com/casbin/xorm-adapter) for [Casbin](https://github.com/casbin/casbin) - [github.com/casbin/xorm-adapter](https://github.com/casbin/xorm-adapter) -* [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) - * [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) * [Gobuild.io](http://gobuild.io) - [github.com/shxsun/gobuild](http://github.com/shxsun/gobuild) diff --git a/engine.go b/engine.go index 5c48710e..84e9206d 100644 --- a/engine.go +++ b/engine.go @@ -1426,6 +1426,13 @@ func (engine *Engine) Get(bean interface{}) (bool, error) { return session.Get(bean) } +// Exist returns true if the record exist otherwise return false +func (engine *Engine) Exist(bean ...interface{}) (bool, error) { + session := engine.NewSession() + defer session.Close() + return session.Exist(bean...) +} + // Find retrieve records from table, condiBeans's non-empty fields // are conditions. beans could be []Struct, []*Struct, map[int64]Struct // map[int64]*Struct diff --git a/session_exist.go b/session_exist.go new file mode 100644 index 00000000..6f895c1e --- /dev/null +++ b/session_exist.go @@ -0,0 +1,87 @@ +// 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 ( + "errors" + "fmt" + "reflect" + + "github.com/go-xorm/builder" + "github.com/go-xorm/core" +) + +// Exist returns true if the record exist otherwise return false +func (session *Session) Exist(bean ...interface{}) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + var sqlStr string + var args []interface{} + var err error + + if session.Statement.RawSQL == "" { + if len(bean) == 0 { + tableName := session.Statement.TableName() + if len(tableName) <= 0 { + return false, ErrTableNotFound + } + + if session.Statement.cond.IsValid() { + condSQL, condArgs, err := builder.ToSQL(session.Statement.cond) + if err != nil { + return false, err + } + + sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) + args = condArgs + } else { + sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) + args = []interface{}{} + } + } else { + beanValue := reflect.ValueOf(bean[0]) + if beanValue.Kind() != reflect.Ptr { + return false, errors.New("needs a pointer") + } + + if beanValue.Elem().Kind() == reflect.Struct { + if err := session.Statement.setRefValue(beanValue.Elem()); err != nil { + return false, err + } + } + + if len(session.Statement.TableName()) <= 0 { + return false, ErrTableNotFound + } + session.Statement.Limit(1) + sqlStr, args, err = session.Statement.genGetSQL(bean[0]) + if err != nil { + return false, err + } + } + } else { + sqlStr = session.Statement.RawSQL + args = session.Statement.RawParams + } + + session.queryPreprocess(&sqlStr, args...) + + var rawRows *core.Rows + if session.IsAutoCommit { + _, rawRows, err = session.innerQuery(sqlStr, args...) + } else { + rawRows, err = session.Tx.Query(sqlStr, args...) + } + if err != nil { + return false, err + } + + defer rawRows.Close() + + return rawRows.Next(), nil +} diff --git a/session_exist_test.go b/session_exist_test.go new file mode 100644 index 00000000..857bf4a1 --- /dev/null +++ b/session_exist_test.go @@ -0,0 +1,76 @@ +// 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 ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestExistStruct(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type RecordExist struct { + Id int64 + Name string + } + + assertSync(t, new(RecordExist)) + + has, err := testEngine.Exist(new(RecordExist)) + assert.NoError(t, err) + assert.False(t, has) + + cnt, err := testEngine.Insert(&RecordExist{ + Name: "test1", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + has, err = testEngine.Exist(new(RecordExist)) + assert.NoError(t, err) + assert.True(t, has) + + has, err = testEngine.Exist(&RecordExist{ + Name: "test1", + }) + assert.NoError(t, err) + assert.True(t, has) + + has, err = testEngine.Exist(&RecordExist{ + Name: "test2", + }) + assert.NoError(t, err) + assert.False(t, has) + + has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) + assert.NoError(t, err) + assert.True(t, has) + + has, err = testEngine.Where("name = ?", "test2").Exist(&RecordExist{}) + assert.NoError(t, err) + assert.False(t, has) + + has, err = testEngine.SQL("select * from record_exist where name = ?", "test1").Exist() + assert.NoError(t, err) + assert.True(t, has) + + has, err = testEngine.SQL("select * from record_exist where name = ?", "test2").Exist() + assert.NoError(t, err) + assert.False(t, has) + + has, err = testEngine.Table("record_exist").Exist() + assert.NoError(t, err) + assert.True(t, has) + + has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() + assert.NoError(t, err) + assert.True(t, has) + + has, err = testEngine.Table("record_exist").Where("name = ?", "test2").Exist() + assert.NoError(t, err) + assert.False(t, has) +} diff --git a/xorm.go b/xorm.go index 8b2fd6c6..36895a9d 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( const ( // Version show the xorm's version - Version string = "0.6.2.0605" + Version string = "0.6.3.0713" ) func regDrvsNDialects() bool { From 305eb27bcf7e63198716d32af0cf74a6afcb2b37 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 21 Jul 2017 16:42:23 +0800 Subject: [PATCH 04/12] improve update error when no conent to be updated (#648) --- session_update.go | 4 ++++ session_update_test.go | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/session_update.go b/session_update.go index 792fb574..a1d25bab 100644 --- a/session_update.go +++ b/session_update.go @@ -330,6 +330,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } + if len(colNames) <= 0 { + return 0, errors.New("No content found to be updated") + } + sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v", top, session.Engine.Quote(session.Statement.TableName()), diff --git a/session_update_test.go b/session_update_test.go index ef4c1e70..d4bf437e 100644 --- a/session_update_test.go +++ b/session_update_test.go @@ -1093,3 +1093,23 @@ func TestBool(t *testing.T) { } } } + +func TestNoUpdate(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type NoUpdate struct { + Id int64 + Content string + } + + assertSync(t, new(NoUpdate)) + + _, err := testEngine.Insert(&NoUpdate{ + Content: "test", + }) + assert.NoError(t, err) + + _, err = testEngine.Id(1).Update(&NoUpdate{}) + assert.Error(t, err) + assert.EqualValues(t, "No content found to be updated", err.Error()) +} From c412be236502ca71315dd61dbd324f3706010f0c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 23 Jul 2017 09:15:44 +0800 Subject: [PATCH 05/12] support reuse session to transaction (#650) --- session_tx.go | 2 ++ session_tx_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/session_tx.go b/session_tx.go index 302bc104..c8f0777c 100644 --- a/session_tx.go +++ b/session_tx.go @@ -24,6 +24,7 @@ func (session *Session) Rollback() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { session.saveLastSQL(session.Engine.dialect.RollBackStr()) session.IsCommitedOrRollbacked = true + session.IsAutoCommit = true return session.Tx.Rollback() } return nil @@ -34,6 +35,7 @@ func (session *Session) Commit() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { session.saveLastSQL("COMMIT") session.IsCommitedOrRollbacked = true + session.IsAutoCommit = true var err error if err = session.Tx.Commit(); err == nil { // handle processors after tx committed diff --git a/session_tx_test.go b/session_tx_test.go index 3e71bb40..adb11c66 100644 --- a/session_tx_test.go +++ b/session_tx_test.go @@ -190,3 +190,34 @@ func TestCombineTransactionSameMapper(t *testing.T) { panic(err) } } + +func TestReuseTransaction(t *testing.T) { + assert.NoError(t, prepareEngine()) + sess := testEngine.NewSession() + defer sess.Close() + + type ReuseTx struct { + Id int64 + Name string + } + + assertSync(t, new(ReuseTx)) + + records := []ReuseTx{ + { + Name: "1", + }, + { + Name: "3", + }, + { + Name: "2", + }, + } + for _, r := range records { + assert.NoError(t, sess.Begin()) + _, err := sess.Insert(&r) + assert.NoError(t, err) + assert.NoError(t, sess.Commit()) + } +} From 1aa1846afbf091c9b8bebb53340eff568a9b87bd Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 23 Jul 2017 09:52:08 +0800 Subject: [PATCH 06/12] Revert "support reuse session to transaction (#650)" (#651) This reverts commit c412be236502ca71315dd61dbd324f3706010f0c. --- session_tx.go | 2 -- session_tx_test.go | 31 ------------------------------- 2 files changed, 33 deletions(-) diff --git a/session_tx.go b/session_tx.go index c8f0777c..302bc104 100644 --- a/session_tx.go +++ b/session_tx.go @@ -24,7 +24,6 @@ func (session *Session) Rollback() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { session.saveLastSQL(session.Engine.dialect.RollBackStr()) session.IsCommitedOrRollbacked = true - session.IsAutoCommit = true return session.Tx.Rollback() } return nil @@ -35,7 +34,6 @@ func (session *Session) Commit() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { session.saveLastSQL("COMMIT") session.IsCommitedOrRollbacked = true - session.IsAutoCommit = true var err error if err = session.Tx.Commit(); err == nil { // handle processors after tx committed diff --git a/session_tx_test.go b/session_tx_test.go index adb11c66..3e71bb40 100644 --- a/session_tx_test.go +++ b/session_tx_test.go @@ -190,34 +190,3 @@ func TestCombineTransactionSameMapper(t *testing.T) { panic(err) } } - -func TestReuseTransaction(t *testing.T) { - assert.NoError(t, prepareEngine()) - sess := testEngine.NewSession() - defer sess.Close() - - type ReuseTx struct { - Id int64 - Name string - } - - assertSync(t, new(ReuseTx)) - - records := []ReuseTx{ - { - Name: "1", - }, - { - Name: "3", - }, - { - Name: "2", - }, - } - for _, r := range records { - assert.NoError(t, sess.Begin()) - _, err := sess.Insert(&r) - assert.NoError(t, err) - assert.NoError(t, sess.Commit()) - } -} From 8cfde0eb4bd4450c97367dac171155efcb2ad7e3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 24 Jul 2017 21:26:14 +0800 Subject: [PATCH 07/12] fix Conds bug on Find and add test for FindAndCount (#652) --- session_cond.go | 2 +- session_cond_test.go | 32 ++++++++++++++++++++++++++++++++ session_find.go | 3 ++- statement.go | 21 +++++++++++++-------- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/session_cond.go b/session_cond.go index 948a90bc..69775187 100644 --- a/session_cond.go +++ b/session_cond.go @@ -64,7 +64,7 @@ func (session *Session) NotIn(column string, args ...interface{}) *Session { return session } -// Conds returns session query conditions +// Conds returns session query conditions except auto bean conditions func (session *Session) Conds() builder.Cond { return session.Statement.cond } diff --git a/session_cond_test.go b/session_cond_test.go index d90fbc2f..7b9e8a07 100644 --- a/session_cond_test.go +++ b/session_cond_test.go @@ -260,3 +260,35 @@ func TestIn(t *testing.T) { panic(err) } } + +func TestFindAndCount(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type FindAndCount struct { + Id int64 + Name string + } + + assert.NoError(t, testEngine.Sync2(new(FindAndCount))) + + _, err := testEngine.Insert([]FindAndCount{ + { + Name: "test1", + }, + { + Name: "test2", + }, + }) + assert.NoError(t, err) + + var results []FindAndCount + sess := testEngine.Where("name = ?", "test1") + conds := sess.Conds() + err = sess.Find(&results) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(results)) + + total, err := testEngine.Where(conds).Count(new(FindAndCount)) + assert.NoError(t, err) + assert.EqualValues(t, 1, total) +} diff --git a/session_find.go b/session_find.go index b711991e..2518af42 100644 --- a/session_find.go +++ b/session_find.go @@ -119,7 +119,8 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } } - condSQL, condArgs, err := builder.ToSQL(session.Statement.cond.And(autoCond)) + session.Statement.cond = session.Statement.cond.And(autoCond) + condSQL, condArgs, err := builder.ToSQL(session.Statement.cond) if err != nil { return err } diff --git a/statement.go b/statement.go index 6e360bb3..0f90f64d 100644 --- a/statement.go +++ b/statement.go @@ -890,17 +890,24 @@ func (statement *Statement) buildConds(table *core.Table, bean interface{}, incl statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName) } -func (statement *Statement) genConds(bean interface{}) (string, []interface{}, error) { +func (statement *Statement) mergeConds(bean interface{}) error { if !statement.noAutoCondition { var addedTableName = (len(statement.JoinStr) > 0) autoCond, err := statement.buildConds(statement.RefTable, bean, true, true, false, true, addedTableName) if err != nil { - return "", nil, err + return err } statement.cond = statement.cond.And(autoCond) } if err := statement.processIDParam(); err != nil { + return err + } + return nil +} + +func (statement *Statement) genConds(bean interface{}) (string, []interface{}, error) { + if err := statement.mergeConds(bean); err != nil { return "", nil, err } @@ -940,14 +947,12 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, columnStr = "*" } - var condSQL string - var condArgs []interface{} - var err error if isStruct { - condSQL, condArgs, err = statement.genConds(bean) - } else { - condSQL, condArgs, err = builder.ToSQL(statement.cond) + if err := statement.mergeConds(bean); err != nil { + return "", nil, err + } } + condSQL, condArgs, err := builder.ToSQL(statement.cond) if err != nil { return "", nil, err } From dbc493df5e34bf9a5b111c6fbe5e626d99a87134 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 24 Jul 2017 22:09:47 +0800 Subject: [PATCH 08/12] multiple Cols support (#653) --- session_cols_test.go | 32 ++++++++++++++++++++++++++++++++ statement.go | 18 +++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/session_cols_test.go b/session_cols_test.go index 33105281..f742a86a 100644 --- a/session_cols_test.go +++ b/session_cols_test.go @@ -35,3 +35,35 @@ func TestSetExpr(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) } + +func TestCols(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type ColsTable struct { + Id int64 + Col1 string + Col2 string + } + + assertSync(t, new(ColsTable)) + + _, err := testEngine.Insert(&ColsTable{ + Col1: "1", + Col2: "2", + }) + assert.NoError(t, err) + + sess := testEngine.ID(1) + _, err = sess.Cols("col1").Cols("col2").Update(&ColsTable{ + Col1: "", + Col2: "", + }) + assert.NoError(t, err) + + var tb ColsTable + has, err := testEngine.ID(1).Get(&tb) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "", tb.Col1) + assert.EqualValues(t, "", tb.Col2) +} diff --git a/statement.go b/statement.go index 0f90f64d..c97dea50 100644 --- a/statement.go +++ b/statement.go @@ -592,6 +592,22 @@ func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { return newColumns } +func (statement *Statement) colmap2NewColsWithQuote() []string { + newColumns := make([]string, 0, len(statement.columnMap)) + for col := range statement.columnMap { + fields := strings.Split(strings.TrimSpace(col), ".") + if len(fields) == 1 { + newColumns = append(newColumns, statement.Engine.quote(fields[0])) + } else if len(fields) == 2 { + newColumns = append(newColumns, statement.Engine.quote(fields[0])+"."+ + statement.Engine.quote(fields[1])) + } else { + panic(errors.New("unwanted colnames")) + } + } + return newColumns +} + // Distinct generates "DISTINCT col1, col2 " statement func (statement *Statement) Distinct(columns ...string) *Statement { statement.IsDistinct = true @@ -618,7 +634,7 @@ func (statement *Statement) Cols(columns ...string) *Statement { statement.columnMap[strings.ToLower(nc)] = true } - newColumns := statement.col2NewColsWithQuote(columns...) + newColumns := statement.colmap2NewColsWithQuote() statement.ColumnStr = strings.Join(newColumns, ", ") statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.quote("*"), "*", -1) return statement From fbf37fc795df623c45a0a5234906f52fdb70b943 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Jul 2017 16:50:20 +0800 Subject: [PATCH 09/12] improve count usage (#654) --- session_sum.go => session_stats.go | 5 +---- session_sum_test.go => session_stats_test.go | 8 ++++++++ statement.go | 14 ++++++++++---- 3 files changed, 19 insertions(+), 8 deletions(-) rename session_sum.go => session_stats.go (97%) rename session_sum_test.go => session_stats_test.go (93%) diff --git a/session_sum.go b/session_stats.go similarity index 97% rename from session_sum.go rename to session_stats.go index 2d5ba6bd..49ef0560 100644 --- a/session_sum.go +++ b/session_stats.go @@ -18,10 +18,7 @@ func (session *Session) Count(bean ...interface{}) (int64, error) { var args []interface{} var err error if session.Statement.RawSQL == "" { - if len(bean) == 0 { - return 0, ErrTableNotFound - } - sqlStr, args, err = session.Statement.genCountSQL(bean[0]) + sqlStr, args, err = session.Statement.genCountSQL(bean...) if err != nil { return 0, err } diff --git a/session_sum_test.go b/session_stats_test.go similarity index 93% rename from session_sum_test.go rename to session_stats_test.go index 2d2ad9b2..73f30e1d 100644 --- a/session_sum_test.go +++ b/session_stats_test.go @@ -127,6 +127,14 @@ func TestCount(t *testing.T) { total, err = testEngine.Where(cond).Count(new(UserinfoCount)) assert.NoError(t, err) assert.EqualValues(t, 1, total) + + total, err = testEngine.Where(cond).Table("userinfo_count").Count() + assert.NoError(t, err) + assert.EqualValues(t, 1, total) + + total, err = testEngine.Table("userinfo_count").Count() + assert.NoError(t, err) + assert.EqualValues(t, 1, total) } func TestSQLCount(t *testing.T) { diff --git a/statement.go b/statement.go index c97dea50..b4d2d877 100644 --- a/statement.go +++ b/statement.go @@ -981,10 +981,16 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}, return sqlStr, append(statement.joinArgs, condArgs...), nil } -func (statement *Statement) genCountSQL(bean interface{}) (string, []interface{}, error) { - statement.setRefValue(rValue(bean)) - - condSQL, condArgs, err := statement.genConds(bean) +func (statement *Statement) genCountSQL(beans ...interface{}) (string, []interface{}, error) { + var condSQL string + var condArgs []interface{} + var err error + if len(beans) > 0 { + statement.setRefValue(rValue(beans[0])) + condSQL, condArgs, err = statement.genConds(beans[0]) + } else { + condSQL, condArgs, err = builder.ToSQL(statement.cond) + } if err != nil { return "", nil, err } From 463b4d8da724ba2ad3e5a095474d1ede3fd49575 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Jul 2017 17:09:10 +0800 Subject: [PATCH 10/12] add more tests for Delete (#655) --- session_delete_test.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/session_delete_test.go b/session_delete_test.go index 27e61321..ad817bd1 100644 --- a/session_delete_test.go +++ b/session_delete_test.go @@ -26,7 +26,7 @@ func TestDelete(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - cnt, err = testEngine.Delete(&UserinfoDelete{Uid: 1}) + cnt, err = testEngine.Delete(&UserinfoDelete{Uid: user.Uid}) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) @@ -35,6 +35,20 @@ func TestDelete(t *testing.T) { has, err := testEngine.Id(1).Get(&user) assert.NoError(t, err) assert.False(t, has) + + cnt, err = testEngine.Insert(&user) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + cnt, err = testEngine.Where("id=?", user.Uid).Delete(&UserinfoDelete{}) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + user.Uid = 0 + user.IsMan = true + has, err = testEngine.Id(2).Get(&user) + assert.NoError(t, err) + assert.False(t, has) } func TestDeleted(t *testing.T) { From a5a917d477c27830276b8dc88163170436789943 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Jul 2017 17:51:20 +0800 Subject: [PATCH 11/12] add mymysql tests (#656) --- circle.yml | 2 +- session_get_test.go | 21 ++++++++++++--------- test_mymysql.sh | 1 + xorm_test.go | 1 + 4 files changed, 15 insertions(+), 10 deletions(-) create mode 100755 test_mymysql.sh diff --git a/circle.yml b/circle.yml index 3063ac9d..e81bdb0e 100644 --- a/circle.yml +++ b/circle.yml @@ -21,7 +21,7 @@ database: test: override: # './...' is a relative pattern which means all subdirectories - - go test -v -race -db="sqlite3::mysql::postgres" -conn_str="./test.db::root:@/xorm_test::dbname=xorm_test sslmode=disable" -coverprofile=coverage.txt -covermode=atomic + - go test -v -race -db="sqlite3::mysql::mymysql::postgres" -conn_str="./test.db::root:@/xorm_test::xorm_test/root/::dbname=xorm_test sslmode=disable" -coverprofile=coverage.txt -covermode=atomic - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./sqlite3.sh - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./mysql.sh - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./postgres.sh diff --git a/session_get_test.go b/session_get_test.go index e3887118..24139c9f 100644 --- a/session_get_test.go +++ b/session_get_test.go @@ -71,15 +71,18 @@ func TestGetVar(t *testing.T) { assert.Equal(t, "28", valuesString["age"]) assert.Equal(t, "1.5", valuesString["money"]) - var valuesInter = make(map[string]interface{}) - has, err = testEngine.Table("get_var").Where("id = ?", 1).Select("*").Get(&valuesInter) - assert.NoError(t, err) - assert.Equal(t, true, has) - assert.Equal(t, 5, len(valuesInter)) - assert.EqualValues(t, 1, valuesInter["id"]) - assert.Equal(t, "hi", fmt.Sprintf("%s", valuesInter["msg"])) - assert.EqualValues(t, 28, valuesInter["age"]) - assert.Equal(t, "1.5", fmt.Sprintf("%v", valuesInter["money"])) + // for mymysql driver, interface{} will be []byte, so ignore it currently + if testEngine.dialect.DriverName() != "mymysql" { + var valuesInter = make(map[string]interface{}) + has, err = testEngine.Table("get_var").Where("id = ?", 1).Select("*").Get(&valuesInter) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.Equal(t, 5, len(valuesInter)) + assert.EqualValues(t, 1, valuesInter["id"]) + assert.Equal(t, "hi", fmt.Sprintf("%s", valuesInter["msg"])) + assert.EqualValues(t, 28, valuesInter["age"]) + assert.Equal(t, "1.5", fmt.Sprintf("%v", valuesInter["money"])) + } var valuesSliceString = make([]string, 5) has, err = testEngine.Table("get_var").Get(&valuesSliceString) diff --git a/test_mymysql.sh b/test_mymysql.sh new file mode 100755 index 00000000..f7780d14 --- /dev/null +++ b/test_mymysql.sh @@ -0,0 +1 @@ +go test -db=mymysql -conn_str="xorm_test/root/" \ No newline at end of file diff --git a/xorm_test.go b/xorm_test.go index 2e722f86..e7deec11 100644 --- a/xorm_test.go +++ b/xorm_test.go @@ -12,6 +12,7 @@ import ( "github.com/go-xorm/core" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" + _ "github.com/ziutek/mymysql/godrv" ) var ( From 7c2d9247948f0e49fab931ef14bc3638b92e8738 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Jul 2017 22:25:23 +0800 Subject: [PATCH 12/12] tests for close and add IsClosed() method (#659) --- session.go | 6 +++++- session_test.go | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 session_test.go diff --git a/session.go b/session.go index 76d7cb28..0bef19a0 100644 --- a/session.go +++ b/session.go @@ -93,11 +93,15 @@ func (session *Session) Close() { } session.Tx = nil session.stmtCache = nil - session.Init() session.db = nil } } +// IsClosed returns if session is closed +func (session *Session) IsClosed() bool { + return session.db == nil +} + func (session *Session) resetStatement() { if session.AutoResetStatement { session.Statement.Init() diff --git a/session_test.go b/session_test.go new file mode 100644 index 00000000..d003274d --- /dev/null +++ b/session_test.go @@ -0,0 +1,23 @@ +// 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 ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestClose(t *testing.T) { + assert.NoError(t, prepareEngine()) + + sess1 := testEngine.NewSession() + sess1.Close() + assert.True(t, sess1.IsClosed()) + + sess2 := testEngine.Where("a = ?", 1) + sess2.Close() + assert.True(t, sess2.IsClosed()) +}