From 8079cad870b67a5eb0ed7fbcfb91b58fc2297756 Mon Sep 17 00:00:00 2001 From: xormplus Date: Thu, 4 May 2017 20:24:48 +0800 Subject: [PATCH 01/31] fix bug on formatTime --- engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine.go b/engine.go index a788c117..e21738be 100644 --- a/engine.go +++ b/engine.go @@ -1572,7 +1572,7 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T } else if engine.dialect.DBType() == "sqlite3" { v = t.UTC().Format("2006-01-02 15:04:05") } else { - v = t.Format("2006-01-02 15:04:05") + v = t.Format("2006-01-02 15:04:05.999") } case core.TimeStampz: if engine.dialect.DBType() == core.MSSQL { From 85ba0388d71bd9848ddc5af1ceed85ea0a5dc205 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Thu, 4 May 2017 20:32:54 +0800 Subject: [PATCH 02/31] fix bug on formatTime fix bug on formatTime --- engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine.go b/engine.go index a788c117..e21738be 100644 --- a/engine.go +++ b/engine.go @@ -1572,7 +1572,7 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T } else if engine.dialect.DBType() == "sqlite3" { v = t.UTC().Format("2006-01-02 15:04:05") } else { - v = t.Format("2006-01-02 15:04:05") + v = t.Format("2006-01-02 15:04:05.999") } case core.TimeStampz: if engine.dialect.DBType() == core.MSSQL { From 5f152c90b5a99d786b4b44307dade2f54abddce4 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Thu, 4 May 2017 22:36:51 +0800 Subject: [PATCH 03/31] add test for formatTime add test for formatTime --- session_insert_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/session_insert_test.go b/session_insert_test.go index b232d3f7..b421e1b3 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -26,3 +26,19 @@ func TestInsertOne(t *testing.T) { _, err := testEngine.InsertOne(data) assert.NoError(t, err) } + +func TestInsertOne2(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type Test2 struct { + Id int64 `xorm:"autoincr pk"` + Msg string `xorm:"varchar(255)"` + Created time.Time `xorm:"datetime created"` + } + + assert.NoError(t, testEngine.Sync2(new(Test2))) + + data := Test2{Msg: "hi"} + _, err := testEngine.InsertOne(data) + assert.NoError(t, err) +} From 8c9ffa956bec4149aa6f6961ab1afe93ff93b2eb Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Thu, 4 May 2017 22:40:32 +0800 Subject: [PATCH 04/31] add test for formatTime add test for formatTime --- session_update_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/session_update_test.go b/session_update_test.go index 9eeb6186..49009c98 100644 --- a/session_update_test.go +++ b/session_update_test.go @@ -72,3 +72,27 @@ func TestUpdateLimit(t *testing.T) { assert.EqualValues(t, 35, uts[0].Age) assert.EqualValues(t, 30, uts[1].Age) } + +func TestUpdate(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type UpdateTable2 struct { + Id int64 `xorm:"autoincr pk"` + Msg string `xorm:"varchar(255)"` + Created time.Time `xorm:"datetime updated"` + } + + assert.NoError(t, testEngine.Sync2(new(UpdateTable2))) + + data := UpdateTable2{Msg: "test1"} + + cnt, err := testEngine.Insert(&data) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + cnt, err = testEngine.Where("id = ?", data.Id).Update(&UpdateTable2{ + Msg: "test2", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) +} From a764de26dba155f38cd111b231c4bbe75b04193a Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Sat, 23 Sep 2017 20:16:19 +0800 Subject: [PATCH 05/31] revert --- engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine.go b/engine.go index e21738be..a788c117 100644 --- a/engine.go +++ b/engine.go @@ -1572,7 +1572,7 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T } else if engine.dialect.DBType() == "sqlite3" { v = t.UTC().Format("2006-01-02 15:04:05") } else { - v = t.Format("2006-01-02 15:04:05.999") + v = t.Format("2006-01-02 15:04:05") } case core.TimeStampz: if engine.dialect.DBType() == core.MSSQL { From 5017cabec61433ce8393fb8c3b78a9d482c1f295 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Sat, 23 Sep 2017 20:50:11 +0800 Subject: [PATCH 06/31] time bug fix --- engine.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/engine.go b/engine.go index 4539f7aa..f4459cf1 100644 --- a/engine.go +++ b/engine.go @@ -1545,17 +1545,10 @@ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{} case core.Date: v = t.Format("2006-01-02") case core.DateTime, core.TimeStamp: -<<<<<<< HEAD - v = t.Format("2006-01-02 15:04:05") -======= - if engine.dialect.DBType() == "ql" { - v = t - } else if engine.dialect.DBType() == "sqlite3" { - v = t.UTC().Format("2006-01-02 15:04:05") - } else { - v = t.Format("2006-01-02 15:04:05.999") + v = t.Format("2006-01-02 15:04:05.999") + if engine.dialect.DBType() == "sqlite3" { + v = t.UTC().Format("2006-01-02 15:04:05.999") } ->>>>>>> 8c9ffa956bec4149aa6f6961ab1afe93ff93b2eb case core.TimeStampz: if engine.dialect.DBType() == core.MSSQL { v = t.Format("2006-01-02T15:04:05.9999999Z07:00") From e9f95a9f8b41ff98e3844b9ded60b0ba457b3bbe Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Sat, 23 Sep 2017 20:51:25 +0800 Subject: [PATCH 07/31] time bug fix --- engine.go | 1 + 1 file changed, 1 insertion(+) diff --git a/engine.go b/engine.go index f4459cf1..2b1408bf 100644 --- a/engine.go +++ b/engine.go @@ -1546,6 +1546,7 @@ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{} v = t.Format("2006-01-02") case core.DateTime, core.TimeStamp: v = t.Format("2006-01-02 15:04:05.999") + if engine.dialect.DBType() == "sqlite3" { v = t.UTC().Format("2006-01-02 15:04:05.999") } From a21b0243bd092d6803c97f632cec8dc12af1052e Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Mon, 25 Sep 2017 20:32:49 +0800 Subject: [PATCH 08/31] add support group engine --- ge_session.go | 772 ++++++++++++++++++++++++++++++++++++++++++++++++ group_engine.go | 744 ++++++++++++++++++++++++++++++++++++++++++++++ session_cond.go | 2 +- xorm.go | 17 ++ 4 files changed, 1534 insertions(+), 1 deletion(-) create mode 100644 ge_session.go create mode 100644 group_engine.go diff --git a/ge_session.go b/ge_session.go new file mode 100644 index 00000000..3c8d066b --- /dev/null +++ b/ge_session.go @@ -0,0 +1,772 @@ +package xorm + +import ( + "database/sql" + + "github.com/go-xorm/builder" +) + +type GESession struct { + ge *GroupEngine + operation []string + args map[string]interface{} + err error +} + +func (ges *GESession) operate(session *Session) *Session { + for _, v := range ges.operation { + switch v { + case "Before": + args := ges.args["Before"].(BeforeArgs) + session = session.Before(args.closures) + case "After": + args := ges.args["After"].(AfterArgs) + session = session.After(args.closures) + case "Table": + args := ges.args["Table"].(TableArgs) + session = session.Table(args.tableNameOrBean) + case "Alias": + args := ges.args["Alias"].(AliasArgs) + session = session.Alias(args.alias) + case "NoCascade": + session = session.NoCascade() + case "ForUpdate": + session = session.ForUpdate() + case "NoAutoCondition": + args := ges.args["NoAutoCondition"].(NoAutoConditionArgs) + session = session.NoAutoCondition(args.no...) + case "Limit": + args := ges.args["Limit"].(LimitArgs) + session = session.Limit(args.limit, args.start...) + case "OrderBy": + args := ges.args["OrderBy"].(OrderByArgs) + session = session.OrderBy(args.order) + case "Desc": + args := ges.args["Desc"].(DescArgs) + session = session.Desc(args.colNames...) + case "Asc": + args := ges.args["Asc"].(AscArgs) + session = session.Asc(args.colNames...) + case "StoreEngine": + args := ges.args["StoreEngine"].(StoreEngineArgs) + session = session.StoreEngine(args.storeEngine) + case "Charset": + args := ges.args["Charset"].(CharsetArgs) + session = session.Charset(args.charset) + case "Cascade": + args := ges.args["Cascade"].(CascadeArgs) + session = session.Cascade(args.trueOrFalse...) + case "NoCache": + session = session.NoCache() + case "Join": + args := ges.args["Join"].(JoinArgs) + session = session.Join(args.joinOperator, args.tablename, args.condition, args.args...) + case "GroupBy": + args := ges.args["GroupBy"].(GroupByArgs) + session = session.GroupBy(args.keys) + case "Having": + args := ges.args["Having"].(HavingArgs) + session = session.Having(args.conditions) + case "Unscoped": + session = session.Unscoped() + case "Incr": + args := ges.args["Incr"].(IncrArgs) + session = session.Incr(args.column, args.args...) + case "Decr": + args := ges.args["Decr"].(DecrArgs) + session = session.Decr(args.column, args.args...) + case "SetExpr": + args := ges.args["SetExpr"].(SetExprArgs) + session = session.SetExpr(args.column, args.expression) + case "Select": + args := ges.args["Select"].(SelectArgs) + session = session.Select(args.str) + case "Cols": + args := ges.args["Cols"].(ColsArgs) + session = session.Cols(args.columns...) + case "AllCols": + session = session.AllCols() + case "MustCols": + args := ges.args["MustCols"].(MustColsArgs) + session = session.MustCols(args.columns...) + case "UseBool": + args := ges.args["UseBool"].(UseBoolArgs) + session = session.UseBool(args.columns...) + case "Distinct": + args := ges.args["Distinct"].(DistinctArgs) + session = session.Distinct(args.columns...) + case "Omit": + args := ges.args["Omit"].(OmitArgs) + session = session.Omit(args.columns...) + case "Nullable": + args := ges.args["Nullable"].(NullableArgs) + session = session.Nullable(args.columns...) + case "NoAutoTime": + session = session.NoAutoTime() + case "Sql": + args := ges.args["Sql"].(SqlArgs) + session = session.Sql(args.query, args.args...) + case "SQL": + args := ges.args["SQL"].(SqlArgs) + session = session.SQL(args.query, args.args...) + case "Where": + args := ges.args["Where"].(WhereArgs) + session = session.Where(args.query, args.args...) + case "And": + args := ges.args["And"].(AndArgs) + session = session.And(args.query, args.args...) + case "Or": + args := ges.args["Or"].(OrArgs) + session = session.Or(args.query, args.args...) + case "Id": + args := ges.args["Id"].(IdArgs) + session = session.Id(args.id) + case "ID": + args := ges.args["ID"].(IDArgs) + session = session.ID(args.id) + case "In": + args := ges.args["In"].(InArgs) + session = session.In(args.column, args.args...) + case "NotIn": + args := ges.args["NotIn"].(NotInArgs) + session = session.NotIn(args.column, args.args...) + case "BufferSize": + args := ges.args["BufferSize"].(BufferSizeArgs) + session = session.BufferSize(args.size) + } + } + return session +} + +// Before Apply before Processor, affected bean is passed to closure arg +func (ges *GESession) Before(closures func(interface{})) *GESession { + ges.operation = append(ges.operation, "Before") + args := BeforeArgs{ + closures: closures, + } + ges.args["Before"] = args + return ges +} + +// After Apply after Processor, affected bean is passed to closure arg +func (ges *GESession) After(closures func(interface{})) *GESession { + ges.operation = append(ges.operation, "After") + args := AfterArgs{ + closures: closures, + } + ges.args["After"] = args + return ges +} + +// Table can input a string or pointer to struct for special a table to operate. +func (ges *GESession) Table(tableNameOrBean interface{}) *GESession { + ges.operation = append(ges.operation, "Table") + args := TableArgs{ + tableNameOrBean: tableNameOrBean, + } + ges.args["Table"] = args + return ges +} + +// Alias set the table alias +func (ges *GESession) Alias(alias string) *GESession { + ges.operation = append(ges.operation, "Alias") + args := AliasArgs{ + alias: alias, + } + ges.args["Alias"] = args + return ges +} + +// NoCascade indicate that no cascade load child object +func (ges *GESession) NoCascade() *GESession { + ges.operation = append(ges.operation, "NoCascade") + return ges +} + +// ForUpdate Set Read/Write locking for UPDATE +func (ges *GESession) ForUpdate() *GESession { + ges.operation = append(ges.operation, "ForUpdate") + return ges +} + +// NoAutoCondition disable generate SQL condition from beans +func (ges *GESession) NoAutoCondition(no ...bool) *GESession { + ges.operation = append(ges.operation, "NoAutoCondition") + args := NoAutoConditionArgs{ + no: no, + } + ges.args["NoAutoCondition"] = args + return ges +} + +// Limit provide limit and offset query condition +func (ges *GESession) Limit(limit int, start ...int) *GESession { + ges.operation = append(ges.operation, "Limit") + args := LimitArgs{ + limit: limit, + start: start, + } + ges.args["Limit"] = args + return ges +} + +// OrderBy provide order by query condition, the input parameter is the content +// after order by on a sql statement. +func (ges *GESession) OrderBy(order string) *GESession { + ges.operation = append(ges.operation, "OrderBy") + args := OrderByArgs{ + order: order, + } + ges.args["OrderBy"] = args + return ges +} + +// Desc provide desc order by query condition, the input parameters are columns. +func (ges *GESession) Desc(colNames ...string) *GESession { + ges.operation = append(ges.operation, "Desc") + args := DescArgs{ + colNames: colNames, + } + ges.args["Desc"] = args + return ges +} + +// Asc provide asc order by query condition, the input parameters are columns. +func (ges *GESession) Asc(colNames ...string) *GESession { + ges.operation = append(ges.operation, "Asc") + args := AscArgs{ + colNames: colNames, + } + ges.args["Asc"] = args + return ges +} + +// StoreEngine is only avialble mysql dialect currently +func (ges *GESession) StoreEngine(storeEngine string) *GESession { + ges.operation = append(ges.operation, "StoreEngine") + args := StoreEngineArgs{ + storeEngine: storeEngine, + } + ges.args["StoreEngine"] = args + return ges +} + +// Charset is only avialble mysql dialect currently +func (ges *GESession) Charset(charset string) *GESession { + ges.operation = append(ges.operation, "Charset") + args := CharsetArgs{ + charset: charset, + } + ges.args["Charset"] = args + return ges +} + +// Cascade indicates if loading sub Struct +func (ges *GESession) Cascade(trueOrFalse ...bool) *GESession { + ges.operation = append(ges.operation, "Cascade") + args := CascadeArgs{ + trueOrFalse: trueOrFalse, + } + ges.args["Cascade"] = args + return ges +} + +// NoCache ask this session do not retrieve data from cache system and +// get data from database directly. +func (ges *GESession) NoCache() *GESession { + ges.operation = append(ges.operation, "NoCache") + return ges +} + +// Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN +func (ges *GESession) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "Join") + joinArgs := JoinArgs{ + joinOperator: joinOperator, + tablename: tablename, + condition: condition, + args: args, + } + ges.args["Join"] = joinArgs + return ges +} + +// GroupBy Generate Group By statement +func (ges *GESession) GroupBy(keys string) *GESession { + ges.operation = append(ges.operation, "GroupBy") + args := GroupByArgs{ + keys: keys, + } + ges.args["GroupBy"] = args + return ges +} + +// Having Generate Having statement +func (ges *GESession) Having(conditions string) *GESession { + ges.operation = append(ges.operation, "Having") + args := HavingArgs{ + conditions: conditions, + } + ges.args["Having"] = args + return ges +} + +// Unscoped always disable struct tag "deleted" +func (ges *GESession) Unscoped() *GESession { + ges.operation = append(ges.operation, "Unscoped") + return ges +} + +// Incr provides a query string like "count = count + 1" +func (ges *GESession) Incr(column string, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "Incr") + incrArgs := IncrArgs{ + column: column, + args: args, + } + ges.args["Incr"] = incrArgs + return ges +} + +// Decr provides a query string like "count = count - 1" +func (ges *GESession) Decr(column string, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "Decr") + decrArgs := DecrArgs{ + column: column, + args: args, + } + ges.args["Decr"] = decrArgs + return ges +} + +// SetExpr provides a query string like "column = {expression}" +func (ges *GESession) SetExpr(column string, expression string) *GESession { + ges.operation = append(ges.operation, "SetExpr") + args := SetExprArgs{ + column: column, + expression: expression, + } + ges.args["SetExpr"] = args + return ges +} + +// Select provides some columns to special +func (ges *GESession) Select(str string) *GESession { + ges.operation = append(ges.operation, "Select") + args := SelectArgs{ + str: str, + } + ges.args["Select"] = args + return ges +} + +// Cols provides some columns to special +func (ges *GESession) Cols(columns ...string) *GESession { + ges.operation = append(ges.operation, "Cols") + args := ColsArgs{ + columns: columns, + } + ges.args["Cols"] = args + return ges +} + +// AllCols ask all columns +func (ges *GESession) AllCols() *GESession { + ges.operation = append(ges.operation, "AllCols") + return ges +} + +// MustCols specify some columns must use even if they are empty +func (ges *GESession) MustCols(columns ...string) *GESession { + ges.operation = append(ges.operation, "MustCols") + args := MustColsArgs{ + columns: columns, + } + ges.args["MustCols"] = args + return ges +} + +// UseBool automatically retrieve condition according struct, but +// if struct has bool field, it will ignore them. So use UseBool +// to tell system to do not ignore them. +// If no parameters, it will use all the bool field of struct, or +// it will use parameters's columns +func (ges *GESession) UseBool(columns ...string) *GESession { + ges.operation = append(ges.operation, "UseBool") + args := UseBoolArgs{ + columns: columns, + } + ges.args["UseBool"] = args + return ges +} + +// Distinct use for distinct columns. Caution: when you are using cache, +// distinct will not be cached because cache system need id, +// but distinct will not provide id +func (ges *GESession) Distinct(columns ...string) *GESession { + ges.operation = append(ges.operation, "Distinct") + args := DistinctArgs{ + columns: columns, + } + ges.args["Distinct"] = args + return ges +} + +// Omit Only not use the parameters as select or update columns +func (ges *GESession) Omit(columns ...string) *GESession { + ges.operation = append(ges.operation, "Omit") + args := OmitArgs{ + columns: columns, + } + ges.args["Omit"] = args + return ges +} + +// Nullable Set null when column is zero-value and nullable for update +func (ges *GESession) Nullable(columns ...string) *GESession { + ges.operation = append(ges.operation, "Nullable") + args := NullableArgs{ + columns: columns, + } + ges.args["Nullable"] = args + return ges +} + +// NoAutoTime means do not automatically give created field and updated field +// the current time on the current session temporarily +func (ges *GESession) NoAutoTime() *GESession { + ges.operation = append(ges.operation, "NoAutoTime") + return ges +} + +// Sql provides raw sql input parameter. When you have a complex SQL statement +// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. +// +// Deprecated: use SQL instead. +func (ges *GESession) Sql(query interface{}, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "Sql") + sqlArgs := SqlArgs{ + query: query, + args: args, + } + ges.args["Sql"] = sqlArgs + return ges +} + +// SQL provides raw sql input parameter. When you have a complex SQL statement +// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. +func (ges *GESession) SQL(query interface{}, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "SQL") + sqlArgs := SqlArgs{ + query: query, + args: args, + } + ges.args["SQL"] = sqlArgs + return ges +} + +// Where provides custom query condition. +func (ges *GESession) Where(query interface{}, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "Where") + whereArgs := WhereArgs{ + query: query, + args: args, + } + ges.args["Where"] = whereArgs + return ges +} + +type AndArgs struct { + query interface{} + args []interface{} +} + +// And provides custom query condition. +func (ges *GESession) And(query interface{}, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "And") + andArgs := AndArgs{ + query: query, + args: args, + } + ges.args["And"] = andArgs + return ges +} + +type OrArgs struct { + query interface{} + args []interface{} +} + +// Or provides custom query condition. +func (ges *GESession) Or(query interface{}, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "Or") + orArgs := OrArgs{ + query: query, + args: args, + } + ges.args["Or"] = orArgs + return ges +} + +// Id provides converting id as a query condition +// +// Deprecated: use ID instead +func (ges *GESession) Id(id interface{}) *GESession { + ges.operation = append(ges.operation, "Id") + args := IdArgs{ + id: id, + } + ges.args["Id"] = args + return ges +} + +// ID provides converting id as a query condition +func (ges *GESession) ID(id interface{}) *GESession { + ges.operation = append(ges.operation, "ID") + args := IDArgs{ + id: id, + } + ges.args["ID"] = args + return ges +} + +// In provides a query string like "id in (1, 2, 3)" +func (ges *GESession) In(column string, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "In") + inArgs := InArgs{ + column: column, + args: args, + } + ges.args["In"] = inArgs + return ges +} + +// NotIn provides a query string like "id in (1, 2, 3)" +func (ges *GESession) NotIn(column string, args ...interface{}) *GESession { + ges.operation = append(ges.operation, "NotIn") + notInArgs := NotInArgs{ + column: column, + args: args, + } + ges.args["NotIn"] = notInArgs + return ges +} + +//TODO 还需要分析如何实现 +// Conds returns session query conditions except auto bean conditions +func (ges *GESession) Conds() builder.Cond { + return ges.ge.Master().NewSession().Conds() +} + +//TODO 缺少前置session操作链 +// Delete records, bean's non-empty fields are conditions +func (ges *GESession) Delete(bean interface{}) (int64, error) { + session := ges.ge.Master().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Delete(bean) +} + +//TODO 缺少前置session操作链 +// Exist returns true if the record exist otherwise return false +func (ges *GESession) Exist(bean ...interface{}) (bool, error) { + session := ges.ge.Master().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Exist(bean...) +} + +//TODO 缺少前置session操作链 +// Find retrieve records from table, condiBeans's non-empty fields +// are conditions. beans could be []Struct, []*Struct, map[int64]Struct +// map[int64]*Struct +func (ges *GESession) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Find(rowsSlicePtr, condiBean...) +} + +//TODO 缺少前置session操作链 +// Get retrieve one record from database, bean's non-empty fields +// will be as conditions +func (ges *GESession) Get(bean interface{}) (bool, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Get(bean) +} + +//TODO 缺少前置session操作链 +// Insert insert one or more beans +func (ges *GESession) Insert(beans ...interface{}) (int64, error) { + session := ges.ge.Master().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Insert(beans...) +} + +// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields +// are conditions. +func (ges *GESession) Rows(bean interface{}) (*Rows, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Rows(bean) +} + +// Iterate record by record handle records from table, condiBeans's non-empty fields +// are conditions. beans could be []Struct, []*Struct, map[int64]Struct +// map[int64]*Struct +func (ges *GESession) Iterate(bean interface{}, fun IterFunc) error { + return ges.ge.Slave().Iterate(bean, fun) +} + +// BufferSize sets the buffersize for iterate +func (ges *GESession) BufferSize(size int) *GESession { + ges.operation = append(ges.operation, "BufferSize") + args := BufferSizeArgs{ + size: size, + } + ges.args["BufferSize"] = args + return ges +} + +//TODO 缺少前置session操作链 +// Query runs a raw sql and return records as []map[string][]byte +func (ges *GESession) Query(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Query(sqlStr, args...) +} + +//TODO 缺少前置session操作链 +// QueryString runs a raw sql and return records as []map[string]string +func (ges *GESession) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.QueryString(sqlStr, args...) +} + +// QueryInterface runs a raw sql and return records as []map[string]interface{} +func (ges *GESession) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.QueryInterface(sqlStr, args...) +} + +// Exec raw sql +func (ges *GESession) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { + session := ges.ge.Master().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Exec(sqlStr, args...) +} + +// CreateTable create a table according a bean +func (ges *GESession) CreateTable(bean interface{}) error { + session := ges.ge.Master().NewSession() + defer session.Close() + return session.CreateTable(bean) +} + +// CreateIndexes create indexes +func (ges *GESession) CreateIndexes(bean interface{}) error { + return ges.ge.Master().CreateIndexes(bean) +} + +// CreateUniques create uniques +func (ges *GESession) CreateUniques(bean interface{}) error { + return ges.ge.Master().CreateUniques(bean) +} + +// DropIndexes drop indexes +func (ges *GESession) DropIndexes(bean interface{}) error { + return ges.ge.Master().DropIndexes(bean) +} + +// DropTable drop table will drop table if exist, if drop failed, it will return error +func (ges *GESession) DropTable(beanOrTableName interface{}) error { + session := ges.ge.Master().NewSession() + defer session.Close() + return session.DropTable(beanOrTableName) +} + +// IsTableExist if a table is exist +func (ges *GESession) IsTableExist(beanOrTableName interface{}) (bool, error) { + return ges.ge.Master().IsTableExist(beanOrTableName) +} + +// IsTableEmpty if table have any records +func (ges *GESession) IsTableEmpty(bean interface{}) (bool, error) { + return ges.ge.Master().IsTableEmpty(bean) +} + +// Sync2 synchronize structs to database tables +func (ges *GESession) Sync2(beans ...interface{}) error { + return ges.ge.Master().Sync2(beans...) +} + +//TODO 缺少前置session操作链 +// Count counts the records. bean's non-empty fields +// are conditions. +func (ges *GESession) Count(bean ...interface{}) (int64, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Count(bean...) +} + +//TODO 缺少前置session操作链 +// sum call sum some column. bean's non-empty fields are conditions. +func (ges *GESession) Sum(bean interface{}, columnName string) (res float64, err error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Sum(bean, columnName) +} + +//TODO 缺少前置session操作链 +// SumInt call sum some column. bean's non-empty fields are conditions. +func (ges *GESession) SumInt(bean interface{}, columnName string) (res int64, err error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.SumInt(bean, columnName) +} + +//TODO 缺少前置session操作链 +// Sums call sum some columns. bean's non-empty fields are conditions. +func (ges *GESession) Sums(bean interface{}, columnNames ...string) ([]float64, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Sums(bean, columnNames...) +} + +//TODO 缺少前置session操作链 +// SumsInt sum specify columns and return as []int64 instead of []float64 +func (ges *GESession) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { + session := ges.ge.Slave().NewSession() + defer session.Close() + session = ges.operate(session) + return session.SumsInt(bean, columnNames...) +} + +// Update records, bean's non-empty fields are updated contents, +// condiBean' non-empty filds are conditions +// CAUTION: +// 1.bool will defaultly be updated content nor conditions +// You should call UseBool if you have bool to use. +// 2.float32 & float64 may be not inexact as conditions +func (ges *GESession) Update(bean interface{}, condiBean ...interface{}) (int64, error) { + session := ges.ge.Master().NewSession() + defer session.Close() + session = ges.operate(session) + return session.Update(bean, condiBean...) + +} diff --git a/group_engine.go b/group_engine.go new file mode 100644 index 00000000..dff4f368 --- /dev/null +++ b/group_engine.go @@ -0,0 +1,744 @@ +// Copyright 2015 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" + "io" + "reflect" + "sync/atomic" + "time" + + "github.com/go-xorm/builder" + "github.com/go-xorm/core" +) + +type GroupEngine struct { + engines []*Engine + count uint64 +} + +func (ge *GroupEngine) Master() *Engine { + return ge.engines[0] +} + +// Slave returns one of the physical databases which is a slave +func (ge *GroupEngine) Slave() *Engine { + return ge.engines[ge.slave(len(ge.engines))] +} + +func (ge *GroupEngine) GetEngine(i int) *Engine { + if i >= len(ge.engines) { + return ge.engines[0] + } + return ge.engines[i] +} + +func (ge *GroupEngine) slave(n int) int { + if n <= 1 { + return 0 + } + return int(1 + (atomic.AddUint64(&ge.count, 1) % uint64(n-1))) +} + +// ShowSQL show SQL statement or not on logger if log level is great than INFO +func (ge *GroupEngine) ShowSQL(show ...bool) { + for i, _ := range ge.engines { + ge.engines[i].ShowSQL(show...) + } +} + +// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO +func (ge *GroupEngine) ShowExecTime(show ...bool) { + for i, _ := range ge.engines { + ge.engines[i].ShowExecTime(show...) + } +} + +// SetMapper set the name mapping rules +func (ge *GroupEngine) SetMapper(mapper core.IMapper) { + for i, _ := range ge.engines { + ge.engines[i].SetTableMapper(mapper) + ge.engines[i].SetColumnMapper(mapper) + } +} + +// SetTableMapper set the table name mapping rule +func (ge *GroupEngine) SetTableMapper(mapper core.IMapper) { + for i, _ := range ge.engines { + ge.engines[i].TableMapper = mapper + } +} + +// SetColumnMapper set the column name mapping rule +func (ge *GroupEngine) SetColumnMapper(mapper core.IMapper) { + for i, _ := range ge.engines { + ge.engines[i].ColumnMapper = mapper + } +} + +// SetMaxOpenConns is only available for go 1.2+ +func (ge *GroupEngine) SetMaxOpenConns(conns int) { + for i, _ := range ge.engines { + ge.engines[i].db.SetMaxOpenConns(conns) + } +} + +// SetMaxIdleConns set the max idle connections on pool, default is 2 +func (ge *GroupEngine) SetMaxIdleConns(conns int) { + for i, _ := range ge.engines { + ge.engines[i].db.SetMaxIdleConns(conns) + } +} + +// NoCascade If you do not want to auto cascade load object +func (ge *GroupEngine) NoCascade() *GESession { + ges := ge.NewGESession() + return ges.NoCascade() +} + +// Close the engine +func (ge *GroupEngine) Close() error { + for i, _ := range ge.engines { + err := ge.engines[i].db.Close() + if err != nil { + return err + } + } + return nil +} + +// Ping tests if database is alive +func (ge *GroupEngine) Ping() error { + return scatter(len(ge.engines), func(i int) error { + return ge.engines[i].Ping() + }) +} + +// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. +func (ge *GroupEngine) SetConnMaxLifetime(d time.Duration) { + for i, _ := range ge.engines { + ge.engines[i].db.SetConnMaxLifetime(d) + } +} + +func scatter(n int, fn func(i int) error) error { + errors := make(chan error, n) + + var i int + for i = 0; i < n; i++ { + go func(i int) { errors <- fn(i) }(i) + } + + var err, innerErr error + for i = 0; i < cap(errors); i++ { + if innerErr = <-errors; innerErr != nil { + err = innerErr + } + } + + return err +} + +// SqlType will be deprecated, please use SQLType instead +// +// Deprecated: use SQLType instead +func (ge *GroupEngine) SqlType(c *core.Column) string { + return ge.Master().SQLType(c) +} + +// SQLType A simple wrapper to dialect's core.SqlType method +func (ge *GroupEngine) SQLType(c *core.Column) string { + return ge.Master().dialect.SqlType(c) +} + +// NewSession New a session +func (ge *GroupEngine) NewSession() *Session { + return ge.Master().NewSession() +} + +// NewSession New a session +func (ge *GroupEngine) NewGESession() *GESession { + args := make(map[string]interface{}) + ges := &GESession{ge: ge, operation: []string{}, args: args} + return ges +} + +type SqlArgs struct { + query interface{} + args []interface{} +} + +func (ge *GroupEngine) Sql(query interface{}, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.Sql(query, args...) +} + +func (ge *GroupEngine) SQL(query interface{}, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.SQL(query, args...) +} + +// NoAutoTime Default if your struct has "created" or "updated" filed tag, the fields +// will automatically be filled with current time when Insert or Update +// invoked. Call NoAutoTime if you dont' want to fill automatically. +func (ge *GroupEngine) NoAutoTime() *GESession { + ges := ge.NewGESession() + return ges.NoAutoTime() +} + +type NoAutoConditionArgs struct { + no []bool +} + +// NoAutoCondition disable auto generate Where condition from bean or not +func (ge *GroupEngine) NoAutoCondition(no ...bool) *GESession { + ges := ge.NewGESession() + return ges.NoAutoCondition(no...) +} + +// DBMetas Retrieve all tables, columns, indexes' informations from database. +func (ge *GroupEngine) DBMetas() ([]*core.Table, error) { + return ge.Master().DBMetas() +} + +// DumpAllToFile dump database all table structs and data to a file +func (ge *GroupEngine) DumpAllToFile(fp string, tp ...core.DbType) error { + return ge.Master().DumpAllToFile(fp, tp...) +} + +// DumpAll dump database all table structs and data to w +func (ge *GroupEngine) DumpAll(w io.Writer, tp ...core.DbType) error { + return ge.Master().DumpAll(w, tp...) +} + +// DumpTablesToFile dump specified tables to SQL file. +func (ge *GroupEngine) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error { + return ge.Master().DumpTablesToFile(tables, fp, tp...) +} + +// DumpTables dump specify tables to io.Writer +func (ge *GroupEngine) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error { + return ge.Master().DumpTables(tables, w, tp...) +} + +type CascadeArgs struct { + trueOrFalse []bool +} + +// Cascade use cascade or not +func (ge *GroupEngine) Cascade(trueOrFalse ...bool) *GESession { + ges := ge.NewGESession() + return ges.Cascade(trueOrFalse...) +} + +type WhereArgs struct { + query interface{} + args []interface{} +} + +// Where method provide a condition query +func (ge *GroupEngine) Where(query interface{}, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.Where(query, args...) +} + +type IdArgs struct { + id interface{} +} + +// Id will be deprecated, please use ID instead +func (ge *GroupEngine) Id(id interface{}) *GESession { + ges := ge.NewGESession() + return ges.Id(id) +} + +type IDArgs struct { + id interface{} +} + +// ID method provoide a condition as (id) = ? +func (ge *GroupEngine) ID(id interface{}) *GESession { + ges := ge.NewGESession() + ges.operation = append(ges.operation, "ID") + args := IDArgs{ + id: id, + } + ges.args["ID"] = args + return ges +} + +type BeforeArgs struct { + closures func(interface{}) +} + +// Before apply before Processor, affected bean is passed to closure arg +func (ge *GroupEngine) Before(closures func(interface{})) *GESession { + ges := ge.NewGESession() + return ges.Before(closures) +} + +type AfterArgs struct { + closures func(interface{}) +} + +// After apply after insert Processor, affected bean is passed to closure arg +func (ge *GroupEngine) After(closures func(interface{})) *GESession { + ges := ge.NewGESession() + return ges.After(closures) +} + +type CharsetArgs struct { + charset string +} + +// Charset set charset when create table, only support mysql now +func (ge *GroupEngine) Charset(charset string) *GESession { + ges := ge.NewGESession() + return ges.Charset(charset) +} + +type StoreEngineArgs struct { + storeEngine string +} + +// StoreEngine set store engine when create table, only support mysql now +func (ge *GroupEngine) StoreEngine(storeEngine string) *GESession { + ges := ge.NewGESession() + return ges.StoreEngine(storeEngine) +} + +type DistinctArgs struct { + columns []string +} + +// Distinct use for distinct columns. Caution: when you are using cache, +// distinct will not be cached because cache system need id, +// but distinct will not provide id +func (ge *GroupEngine) Distinct(columns ...string) *GESession { + ges := ge.NewGESession() + return ges.Distinct(columns...) +} + +type SelectArgs struct { + str string +} + +// Select customerize your select columns or contents +func (ge *GroupEngine) Select(str string) *GESession { + ges := ge.NewGESession() + return ges.Select(str) +} + +type ColsArgs struct { + columns []string +} + +// Cols only use the parameters as select or update columns +func (ge *GroupEngine) Cols(columns ...string) *GESession { + ges := ge.NewGESession() + return ges.Cols(columns...) +} + +// AllCols indicates that all columns should be use +func (ge *GroupEngine) AllCols() *GESession { + ges := ge.NewGESession() + return ges.AllCols() +} + +type MustColsArgs struct { + columns []string +} + +// MustCols specify some columns must use even if they are empty +func (ge *GroupEngine) MustCols(columns ...string) *GESession { + ges := ge.NewGESession() + return ges.MustCols(columns...) +} + +type UseBoolArgs struct { + columns []string +} + +// UseBool xorm automatically retrieve condition according struct, but +// if struct has bool field, it will ignore them. So use UseBool +// to tell system to do not ignore them. +// If no parameters, it will use all the bool field of struct, or +// it will use parameters's columns +func (ge *GroupEngine) UseBool(columns ...string) *GESession { + ges := ge.NewGESession() + return ges.UseBool(columns...) +} + +type OmitArgs struct { + columns []string +} + +// Omit only not use the parameters as select or update columns +func (ge *GroupEngine) Omit(columns ...string) *GESession { + ges := ge.NewGESession() + return ges.Omit(columns...) +} + +type NullableArgs struct { + columns []string +} + +// Nullable set null when column is zero-value and nullable for update +func (ge *GroupEngine) Nullable(columns ...string) *GESession { + ges := ge.NewGESession() + return ges.Nullable(columns...) +} + +type InArgs struct { + column string + args []interface{} +} + +// In will generate "column IN (?, ?)" +func (ge *GroupEngine) In(column string, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.In(column, args...) +} + +type NotInArgs struct { + column string + args []interface{} +} + +// NotIn will generate "column NOT IN (?, ?)" +func (ge *GroupEngine) NotIn(column string, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.NotIn(column, args...) +} + +type IncrArgs struct { + column string + args []interface{} +} + +// Incr provides a update string like "column = column + ?" +func (ge *GroupEngine) Incr(column string, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.Incr(column, args...) +} + +type DecrArgs struct { + column string + args []interface{} +} + +// Decr provides a update string like "column = column - ?" +func (ge *GroupEngine) Decr(column string, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.Decr(column, args...) +} + +type SetExprArgs struct { + column string + expression string +} + +// SetExpr provides a update string like "column = {expression}" +func (ge *GroupEngine) SetExpr(column string, expression string) *GESession { + ges := ge.NewGESession() + return ges.SetExpr(column, expression) +} + +type TableArgs struct { + tableNameOrBean interface{} +} + +// Table temporarily change the Get, Find, Update's table +func (ge *GroupEngine) Table(tableNameOrBean interface{}) *GESession { + ges := ge.NewGESession() + return ges.Table(tableNameOrBean) +} + +type AliasArgs struct { + alias string +} + +// Alias set the table alias +func (ge *GroupEngine) Alias(alias string) *GESession { + ges := ge.NewGESession() + return ges.Alias(alias) +} + +type LimitArgs struct { + limit int + start []int +} + +// Limit will generate "LIMIT start, limit" +func (ge *GroupEngine) Limit(limit int, start ...int) *GESession { + ges := ge.NewGESession() + return ges.Limit(limit, start...) +} + +type DescArgs struct { + colNames []string +} + +// Desc will generate "ORDER BY column1 DESC, column2 DESC" +func (ge *GroupEngine) Desc(colNames ...string) *GESession { + ges := ge.NewGESession() + return ges.Desc(colNames...) +} + +type AscArgs struct { + colNames []string +} + +// Asc will generate "ORDER BY column1,column2 Asc" +// This method can chainable use. +// +// engine.Desc("name").Asc("age").Find(&users) +// // SELECT * FROM user ORDER BY name DESC, age ASC +// +func (ge *GroupEngine) Asc(colNames ...string) *GESession { + ges := ge.NewGESession() + return ges.Asc(colNames...) +} + +type OrderByArgs struct { + order string +} + +// OrderBy will generate "ORDER BY order" +func (ge *GroupEngine) OrderBy(order string) *GESession { + ges := ge.NewGESession() + return ges.OrderBy(order) +} + +type JoinArgs struct { + joinOperator string + tablename interface{} + condition string + args []interface{} +} + +// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN +func (ge *GroupEngine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *GESession { + ges := ge.NewGESession() + return ges.Join(joinOperator, tablename, condition, args...) +} + +type GroupByArgs struct { + keys string +} + +// GroupBy generate group by statement +func (ge *GroupEngine) GroupBy(keys string) *GESession { + ges := ge.NewGESession() + return ges.GroupBy(keys) +} + +type HavingArgs struct { + conditions string +} + +// Having generate having statement +func (ge *GroupEngine) Having(conditions string) *GESession { + ges := ge.NewGESession() + return ges.Having(conditions) +} + +// IdOf get id from one struct +// +// Deprecated: use IDOf instead. +func (ge *GroupEngine) IdOf(bean interface{}) core.PK { + return ge.Master().IdOf(bean) +} + +// IDOf get id from one struct +func (ge *GroupEngine) IDOf(bean interface{}) core.PK { + return ge.Master().IDOf(bean) +} + +// IdOfV get id from one value of struct +// +// Deprecated: use IDOfV instead. +func (ge *GroupEngine) IdOfV(rv reflect.Value) core.PK { + return ge.Master().IdOfV(rv) +} + +// IDOfV get id from one value of struct +func (ge *GroupEngine) IDOfV(rv reflect.Value) core.PK { + return ge.Master().IDOfV(rv) +} + +// CreateIndexes create indexes +func (ge *GroupEngine) CreateIndexes(bean interface{}) error { + return ge.Master().CreateIndexes(bean) +} + +// CreateUniques create uniques +func (ge *GroupEngine) CreateUniques(bean interface{}) error { + return ge.Master().CreateUniques(bean) +} + +// Sync the new struct changes to database, this method will automatically add +// table, column, index, unique. but will not delete or change anything. +// If you change some field, you should change the database manually. +func (ge *GroupEngine) Sync(beans ...interface{}) error { + return ge.Master().Sync(beans...) +} + +// Sync2 synchronize structs to database tables +func (ge *GroupEngine) Sync2(beans ...interface{}) error { + return ge.Master().Sync2(beans...) +} + +// CreateTables create tabls according bean +func (ge *GroupEngine) CreateTables(beans ...interface{}) error { + return ge.Master().CreateTables(beans...) +} + +// DropTables drop specify tables +func (ge *GroupEngine) DropTables(beans ...interface{}) error { + return ge.Master().DropTables(beans...) +} + +// DropIndexes drop indexes of a table +func (ge *GroupEngine) DropIndexes(bean interface{}) error { + return ge.Master().DropIndexes(bean) +} + +func (ge *GroupEngine) Exec(sql string, args ...interface{}) (sql.Result, error) { + return ge.Master().Exec(sql, args...) +} + +// Query a raw sql and return records as []map[string][]byte +func (ge *GroupEngine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { + return ge.Slave().Query(sql, paramStr...) +} + +// QueryString runs a raw sql and return records as []map[string]string +func (ge *GroupEngine) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { + return ge.Slave().QueryString(sqlStr, args...) +} + +// QueryInterface runs a raw sql and return records as []map[string]interface{} +func (ge *GroupEngine) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { + return ge.Slave().QueryInterface(sqlStr, args...) +} + +// Insert one or more records +func (ge *GroupEngine) Insert(beans ...interface{}) (int64, error) { + return ge.Master().Insert(beans...) +} + +// InsertOne insert only one record +func (ge *GroupEngine) InsertOne(bean interface{}) (int64, error) { + return ge.Master().InsertOne(bean) +} + +// IsTableEmpty if a table has any reocrd +func (ge *GroupEngine) IsTableEmpty(bean interface{}) (bool, error) { + return ge.Master().IsTableEmpty(bean) +} + +// IsTableExist if a table is exist +func (ge *GroupEngine) IsTableExist(beanOrTableName interface{}) (bool, error) { + return ge.Master().IsTableExist(beanOrTableName) +} + +func (ge *GroupEngine) Update(bean interface{}, condiBeans ...interface{}) (int64, error) { + return ge.Master().Update(bean, condiBeans...) +} + +// Delete records, bean's non-empty fields are conditions +func (ge *GroupEngine) Delete(bean interface{}) (int64, error) { + return ge.Master().Delete(bean) +} + +// Get retrieve one record from table, bean's non-empty fields +// are conditions +func (ge *GroupEngine) Get(bean interface{}) (bool, error) { + return ge.Slave().Get(bean) +} + +// Exist returns true if the record exist otherwise return false +func (ge *GroupEngine) Exist(bean ...interface{}) (bool, error) { + return ge.Slave().Exist(bean...) +} + +// Iterate record by record handle records from table, bean's non-empty fields +// are conditions. +func (ge *GroupEngine) Iterate(bean interface{}, fun IterFunc) error { + return ge.Master().Iterate(bean, fun) +} + +func (ge *GroupEngine) Find(beans interface{}, condiBeans ...interface{}) error { + return ge.Slave().Find(beans, condiBeans...) +} + +// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields +// are conditions. +func (ge *GroupEngine) Rows(bean interface{}) (*Rows, error) { + return ge.Slave().Rows(bean) +} + +// Count counts the records. bean's non-empty fields are conditions. +func (ge *GroupEngine) Count(bean ...interface{}) (int64, error) { + return ge.Slave().Count(bean...) +} + +// Sum sum the records by some column. bean's non-empty fields are conditions. +func (ge *GroupEngine) Sum(bean interface{}, colName string) (float64, error) { + return ge.Slave().Sum(bean, colName) +} + +// SumInt sum the records by some column. bean's non-empty fields are conditions. +func (ge *GroupEngine) SumInt(bean interface{}, colName string) (int64, error) { + return ge.Slave().SumInt(bean, colName) +} + +// Sums sum the records by some columns. bean's non-empty fields are conditions. +func (ge *GroupEngine) Sums(bean interface{}, colNames ...string) ([]float64, error) { + return ge.Slave().Sums(bean, colNames...) +} + +// SumsInt like Sums but return slice of int64 instead of float64. +func (ge *GroupEngine) SumsInt(bean interface{}, colNames ...string) ([]int64, error) { + return ge.Slave().SumsInt(bean, colNames...) +} + +// ImportFile SQL DDL file +func (ge *GroupEngine) ImportFile(ddlPath string) ([]sql.Result, error) { + return ge.Master().ImportFile(ddlPath) +} + +// Import SQL DDL from io.Reader +func (ge *GroupEngine) Import(r io.Reader) ([]sql.Result, error) { + return ge.Master().Import(r) +} + +// NowTime2 return current time +func (ge *GroupEngine) NowTime2(sqlTypeName string) (interface{}, time.Time) { + return ge.Master().NowTime2(sqlTypeName) +} + +// Unscoped always disable struct tag "deleted" +func (ge *GroupEngine) Unscoped() *GESession { + ges := ge.NewGESession() + return ges.Unscoped() +} + +// CondDeleted returns the conditions whether a record is soft deleted. +func (ge *GroupEngine) CondDeleted(colName string) builder.Cond { + return ge.Master().CondDeleted(colName) +} + +type BufferSizeArgs struct { + size int +} + +// BufferSize sets buffer size for iterate +func (ge *GroupEngine) BufferSize(size int) *GESession { + ges := ge.NewGESession() + return ges.BufferSize(size) +} diff --git a/session_cond.go b/session_cond.go index e1d528f2..15d036d2 100644 --- a/session_cond.go +++ b/session_cond.go @@ -10,7 +10,7 @@ import "github.com/go-xorm/builder" // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. // // Deprecated: use SQL instead. -func (session *Session) Sql(query string, args ...interface{}) *Session { +func (session *Session) Sql(query interface{}, args ...interface{}) *Session { return session.SQL(query, args...) } diff --git a/xorm.go b/xorm.go index 4fdadf2f..47f27175 100644 --- a/xorm.go +++ b/xorm.go @@ -9,6 +9,7 @@ import ( "os" "reflect" "runtime" + "strings" "sync" "time" @@ -54,6 +55,22 @@ func init() { regDrvsNDialects() } +func NewGroupEngine(driverName string, dataSourceNames string) (*GroupEngine, error) { + conns := strings.Split(dataSourceNames, ";") + engines := make([]*Engine, len(conns)) + for i, _ := range conns { + engine, err := NewEngine(driverName, conns[i]) + if err != nil { + return nil, err + } + engines[i] = engine + } + ge := &GroupEngine{ + engines: engines, + } + return ge, nil +} + // NewEngine new a db manager according to the parameter. Currently support four // drivers func NewEngine(driverName string, dataSourceName string) (*Engine, error) { From e60236dae9ad4b147ae0c420550234c33350f97e Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Mon, 25 Sep 2017 21:24:51 +0800 Subject: [PATCH 09/31] revert code --- engine.go | 6 +----- ge_session.go | 12 +++++++----- group_engine.go | 4 ++-- session_cond.go | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/engine.go b/engine.go index 2b1408bf..17d16063 100644 --- a/engine.go +++ b/engine.go @@ -1545,11 +1545,7 @@ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{} case core.Date: v = t.Format("2006-01-02") case core.DateTime, core.TimeStamp: - v = t.Format("2006-01-02 15:04:05.999") - - if engine.dialect.DBType() == "sqlite3" { - v = t.UTC().Format("2006-01-02 15:04:05.999") - } + v = t.Format("2006-01-02 15:04:05") case core.TimeStampz: if engine.dialect.DBType() == core.MSSQL { v = t.Format("2006-01-02T15:04:05.9999999Z07:00") diff --git a/ge_session.go b/ge_session.go index 3c8d066b..d8dabbd1 100644 --- a/ge_session.go +++ b/ge_session.go @@ -444,7 +444,7 @@ func (ges *GESession) NoAutoTime() *GESession { // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. // // Deprecated: use SQL instead. -func (ges *GESession) Sql(query interface{}, args ...interface{}) *GESession { +func (ges *GESession) Sql(query string, args ...interface{}) *GESession { ges.operation = append(ges.operation, "Sql") sqlArgs := SqlArgs{ query: query, @@ -454,11 +454,16 @@ func (ges *GESession) Sql(query interface{}, args ...interface{}) *GESession { return ges } +type SQLArgs struct { + query interface{} + args []interface{} +} + // SQL provides raw sql input parameter. When you have a complex SQL statement // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. func (ges *GESession) SQL(query interface{}, args ...interface{}) *GESession { ges.operation = append(ges.operation, "SQL") - sqlArgs := SqlArgs{ + sqlArgs := SQLArgs{ query: query, args: args, } @@ -577,7 +582,6 @@ func (ges *GESession) Exist(bean ...interface{}) (bool, error) { return session.Exist(bean...) } -//TODO 缺少前置session操作链 // Find retrieve records from table, condiBeans's non-empty fields // are conditions. beans could be []Struct, []*Struct, map[int64]Struct // map[int64]*Struct @@ -588,7 +592,6 @@ func (ges *GESession) Find(rowsSlicePtr interface{}, condiBean ...interface{}) e return session.Find(rowsSlicePtr, condiBean...) } -//TODO 缺少前置session操作链 // Get retrieve one record from database, bean's non-empty fields // will be as conditions func (ges *GESession) Get(bean interface{}) (bool, error) { @@ -598,7 +601,6 @@ func (ges *GESession) Get(bean interface{}) (bool, error) { return session.Get(bean) } -//TODO 缺少前置session操作链 // Insert insert one or more beans func (ges *GESession) Insert(beans ...interface{}) (int64, error) { session := ges.ge.Master().NewSession() diff --git a/group_engine.go b/group_engine.go index dff4f368..1c667f95 100644 --- a/group_engine.go +++ b/group_engine.go @@ -167,11 +167,11 @@ func (ge *GroupEngine) NewGESession() *GESession { } type SqlArgs struct { - query interface{} + query string args []interface{} } -func (ge *GroupEngine) Sql(query interface{}, args ...interface{}) *GESession { +func (ge *GroupEngine) Sql(query string, args ...interface{}) *GESession { ges := ge.NewGESession() return ges.Sql(query, args...) } diff --git a/session_cond.go b/session_cond.go index 15d036d2..e1d528f2 100644 --- a/session_cond.go +++ b/session_cond.go @@ -10,7 +10,7 @@ import "github.com/go-xorm/builder" // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. // // Deprecated: use SQL instead. -func (session *Session) Sql(query interface{}, args ...interface{}) *Session { +func (session *Session) Sql(query string, args ...interface{}) *Session { return session.SQL(query, args...) } From ab0e589093c18a2f579f5cd882f1f2d062fc5705 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Mon, 25 Sep 2017 21:54:40 +0800 Subject: [PATCH 10/31] add NewGroup function --- group_engine.go | 38 ++++++++++++++++++++++++++++++++++++++ xorm.go | 17 ----------------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/group_engine.go b/group_engine.go index 1c667f95..93a8e0d2 100644 --- a/group_engine.go +++ b/group_engine.go @@ -8,6 +8,7 @@ import ( "database/sql" "io" "reflect" + "strings" "sync/atomic" "time" @@ -20,6 +21,36 @@ type GroupEngine struct { count uint64 } +func NewGroupEngine(driverName string, dataSourceNames string) (*GroupEngine, error) { + conns := strings.Split(dataSourceNames, ";") + engines := make([]*Engine, len(conns)) + for i, _ := range conns { + engine, err := NewEngine(driverName, conns[i]) + if err != nil { + return nil, err + } + engines[i] = engine + } + ge := &GroupEngine{ + engines: engines, + count: uint64(len(engines)), + } + return ge, nil +} + +func NewGroup(Master *Engine, Slaves []*Engine, policy int) (*GroupEngine, error) { + engines := make([]*Engine, 0) + engines = append(engines, Master) + for i, _ := range Slaves { + engines = append(engines, Slaves[i]) + } + ge := &GroupEngine{ + engines: engines, + count: uint64(len(engines)), + } + return ge, nil +} + func (ge *GroupEngine) Master() *Engine { return ge.engines[0] } @@ -36,6 +67,13 @@ func (ge *GroupEngine) GetEngine(i int) *Engine { return ge.engines[i] } +func (ge *GroupEngine) GetSlaves() []*Engine { + if len(ge.engines) == 1 { + return ge.engines + } + return ge.engines[1:] +} + func (ge *GroupEngine) slave(n int) int { if n <= 1 { return 0 diff --git a/xorm.go b/xorm.go index 47f27175..4fdadf2f 100644 --- a/xorm.go +++ b/xorm.go @@ -9,7 +9,6 @@ import ( "os" "reflect" "runtime" - "strings" "sync" "time" @@ -55,22 +54,6 @@ func init() { regDrvsNDialects() } -func NewGroupEngine(driverName string, dataSourceNames string) (*GroupEngine, error) { - conns := strings.Split(dataSourceNames, ";") - engines := make([]*Engine, len(conns)) - for i, _ := range conns { - engine, err := NewEngine(driverName, conns[i]) - if err != nil { - return nil, err - } - engines[i] = engine - } - ge := &GroupEngine{ - engines: engines, - } - return ge, nil -} - // NewEngine new a db manager according to the parameter. Currently support four // drivers func NewEngine(driverName string, dataSourceName string) (*Engine, error) { From 5ead594155c98508cac18b2e44f8be714106abf8 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Tue, 26 Sep 2017 09:45:51 +0800 Subject: [PATCH 11/31] add engine group policy --- engine.go | 5 + engine_group_policy.go | 130 +++++++++ ge_session.go | 540 +++++++++++++++++----------------- group_engine.go | 647 ++++++++++++++++++++++++----------------- 4 files changed, 776 insertions(+), 546 deletions(-) create mode 100644 engine_group_policy.go diff --git a/engine.go b/engine.go index 17d16063..5fccf6c1 100644 --- a/engine.go +++ b/engine.go @@ -1581,3 +1581,8 @@ func (engine *Engine) BufferSize(size int) *Session { session.isAutoClose = true return session.BufferSize(size) } + +//Stats return the number of open connections to the database +func (engine *Engine) Stats() int { + return engine.DB().Stats().OpenConnections +} diff --git a/engine_group_policy.go b/engine_group_policy.go new file mode 100644 index 00000000..1d55bde1 --- /dev/null +++ b/engine_group_policy.go @@ -0,0 +1,130 @@ +// Copyright 2015 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" + + "time" +) + +const ( + ENGINE_GROUP_POLICY_RANDOM = iota + ENGINE_GROUP_POLICY_WEIGHTRANDOM + ENGINE_GROUP_POLICY_ROUNDROBIN + ENGINE_GROUP_POLICY_WEIGHTROUNDROBIN + ENGINE_GROUP_POLICY_LEASTCONNECTIONS +) + +type Policy interface { + Slave() int + SetEngineGroup(*EngineGroup) +} + +type XormEngineGroupPolicy struct { + pos int + slaves []int + eg *EngineGroup + r *rand.Rand +} + +func (xgep *XormEngineGroupPolicy) SetEngineGroup(eg *EngineGroup) { + xgep.r = rand.New(rand.NewSource(time.Now().UnixNano())) + xgep.eg = eg +} + +func (xgep *XormEngineGroupPolicy) SetWeight() { + for i, _ := range xgep.eg.weight { + w := xgep.eg.weight[i] + for n := 0; n < w; n++ { + xgep.slaves = append(xgep.slaves, i) + } + } +} + +func (xgep *XormEngineGroupPolicy) Slave() int { + switch xgep.eg.p { + case ENGINE_GROUP_POLICY_RANDOM: + return xgep.Random() + case ENGINE_GROUP_POLICY_WEIGHTRANDOM: + return xgep.WeightRandom() + case ENGINE_GROUP_POLICY_ROUNDROBIN: + return xgep.RoundRobin() + case ENGINE_GROUP_POLICY_WEIGHTROUNDROBIN: + return xgep.WeightRoundRobin() + case ENGINE_GROUP_POLICY_LEASTCONNECTIONS: + return xgep.LeastConnections() + default: + return xgep.Random() + } + +} + +func (xgep *XormEngineGroupPolicy) Random() int { + if xgep.eg.s_count <= 1 { + return 0 + } + + rnd := xgep.r.Intn(xgep.eg.s_count) + return rnd +} + +func (xgep *XormEngineGroupPolicy) WeightRandom() int { + if xgep.eg.s_count <= 1 { + return 0 + } + + xgep.SetWeight() + s := len(xgep.slaves) + rnd := xgep.r.Intn(s) + return xgep.slaves[rnd] +} + +func (xgep *XormEngineGroupPolicy) RoundRobin() int { + if xgep.eg.s_count <= 1 { + return 0 + } + + if xgep.pos >= xgep.eg.s_count { + xgep.pos = 0 + } + xgep.pos++ + + return xgep.pos - 1 +} + +func (xgep *XormEngineGroupPolicy) WeightRoundRobin() int { + if xgep.eg.s_count <= 1 { + return 0 + } + + xgep.SetWeight() + count := len(xgep.slaves) + if xgep.pos >= count { + xgep.pos = 0 + } + xgep.pos++ + + return xgep.slaves[xgep.pos-1] +} + +func (xgep *XormEngineGroupPolicy) LeastConnections() int { + if xgep.eg.s_count <= 1 { + return 0 + } + connections := 0 + slave := 0 + for i, _ := range xgep.eg.slaves { + open_connections := xgep.eg.slaves[i].Stats() + if i == 0 { + connections = open_connections + slave = i + } else if open_connections <= connections { + slave = i + connections = open_connections + } + } + return slave +} diff --git a/ge_session.go b/ge_session.go index d8dabbd1..0f333cb1 100644 --- a/ge_session.go +++ b/ge_session.go @@ -6,132 +6,132 @@ import ( "github.com/go-xorm/builder" ) -type GESession struct { - ge *GroupEngine +type EGSession struct { + eg *EngineGroup operation []string args map[string]interface{} err error } -func (ges *GESession) operate(session *Session) *Session { - for _, v := range ges.operation { +func (egs *EGSession) operate(session *Session) *Session { + for _, v := range egs.operation { switch v { case "Before": - args := ges.args["Before"].(BeforeArgs) + args := egs.args["Before"].(BeforeArgs) session = session.Before(args.closures) case "After": - args := ges.args["After"].(AfterArgs) + args := egs.args["After"].(AfterArgs) session = session.After(args.closures) case "Table": - args := ges.args["Table"].(TableArgs) + args := egs.args["Table"].(TableArgs) session = session.Table(args.tableNameOrBean) case "Alias": - args := ges.args["Alias"].(AliasArgs) + args := egs.args["Alias"].(AliasArgs) session = session.Alias(args.alias) case "NoCascade": session = session.NoCascade() case "ForUpdate": session = session.ForUpdate() case "NoAutoCondition": - args := ges.args["NoAutoCondition"].(NoAutoConditionArgs) + args := egs.args["NoAutoCondition"].(NoAutoConditionArgs) session = session.NoAutoCondition(args.no...) case "Limit": - args := ges.args["Limit"].(LimitArgs) + args := egs.args["Limit"].(LimitArgs) session = session.Limit(args.limit, args.start...) case "OrderBy": - args := ges.args["OrderBy"].(OrderByArgs) + args := egs.args["OrderBy"].(OrderByArgs) session = session.OrderBy(args.order) case "Desc": - args := ges.args["Desc"].(DescArgs) + args := egs.args["Desc"].(DescArgs) session = session.Desc(args.colNames...) case "Asc": - args := ges.args["Asc"].(AscArgs) + args := egs.args["Asc"].(AscArgs) session = session.Asc(args.colNames...) case "StoreEngine": - args := ges.args["StoreEngine"].(StoreEngineArgs) + args := egs.args["StoreEngine"].(StoreEngineArgs) session = session.StoreEngine(args.storeEngine) case "Charset": - args := ges.args["Charset"].(CharsetArgs) + args := egs.args["Charset"].(CharsetArgs) session = session.Charset(args.charset) case "Cascade": - args := ges.args["Cascade"].(CascadeArgs) + args := egs.args["Cascade"].(CascadeArgs) session = session.Cascade(args.trueOrFalse...) case "NoCache": session = session.NoCache() case "Join": - args := ges.args["Join"].(JoinArgs) + args := egs.args["Join"].(JoinArgs) session = session.Join(args.joinOperator, args.tablename, args.condition, args.args...) case "GroupBy": - args := ges.args["GroupBy"].(GroupByArgs) + args := egs.args["GroupBy"].(GroupByArgs) session = session.GroupBy(args.keys) case "Having": - args := ges.args["Having"].(HavingArgs) + args := egs.args["Having"].(HavingArgs) session = session.Having(args.conditions) case "Unscoped": session = session.Unscoped() case "Incr": - args := ges.args["Incr"].(IncrArgs) + args := egs.args["Incr"].(IncrArgs) session = session.Incr(args.column, args.args...) case "Decr": - args := ges.args["Decr"].(DecrArgs) + args := egs.args["Decr"].(DecrArgs) session = session.Decr(args.column, args.args...) case "SetExpr": - args := ges.args["SetExpr"].(SetExprArgs) + args := egs.args["SetExpr"].(SetExprArgs) session = session.SetExpr(args.column, args.expression) case "Select": - args := ges.args["Select"].(SelectArgs) + args := egs.args["Select"].(SelectArgs) session = session.Select(args.str) case "Cols": - args := ges.args["Cols"].(ColsArgs) + args := egs.args["Cols"].(ColsArgs) session = session.Cols(args.columns...) case "AllCols": session = session.AllCols() case "MustCols": - args := ges.args["MustCols"].(MustColsArgs) + args := egs.args["MustCols"].(MustColsArgs) session = session.MustCols(args.columns...) case "UseBool": - args := ges.args["UseBool"].(UseBoolArgs) + args := egs.args["UseBool"].(UseBoolArgs) session = session.UseBool(args.columns...) case "Distinct": - args := ges.args["Distinct"].(DistinctArgs) + args := egs.args["Distinct"].(DistinctArgs) session = session.Distinct(args.columns...) case "Omit": - args := ges.args["Omit"].(OmitArgs) + args := egs.args["Omit"].(OmitArgs) session = session.Omit(args.columns...) case "Nullable": - args := ges.args["Nullable"].(NullableArgs) + args := egs.args["Nullable"].(NullableArgs) session = session.Nullable(args.columns...) case "NoAutoTime": session = session.NoAutoTime() case "Sql": - args := ges.args["Sql"].(SqlArgs) + args := egs.args["Sql"].(SqlArgs) session = session.Sql(args.query, args.args...) case "SQL": - args := ges.args["SQL"].(SqlArgs) + args := egs.args["SQL"].(SqlArgs) session = session.SQL(args.query, args.args...) case "Where": - args := ges.args["Where"].(WhereArgs) + args := egs.args["Where"].(WhereArgs) session = session.Where(args.query, args.args...) case "And": - args := ges.args["And"].(AndArgs) + args := egs.args["And"].(AndArgs) session = session.And(args.query, args.args...) case "Or": - args := ges.args["Or"].(OrArgs) + args := egs.args["Or"].(OrArgs) session = session.Or(args.query, args.args...) case "Id": - args := ges.args["Id"].(IdArgs) + args := egs.args["Id"].(IdArgs) session = session.Id(args.id) case "ID": - args := ges.args["ID"].(IDArgs) + args := egs.args["ID"].(IDArgs) session = session.ID(args.id) case "In": - args := ges.args["In"].(InArgs) + args := egs.args["In"].(InArgs) session = session.In(args.column, args.args...) case "NotIn": - args := ges.args["NotIn"].(NotInArgs) + args := egs.args["NotIn"].(NotInArgs) session = session.NotIn(args.column, args.args...) case "BufferSize": - args := ges.args["BufferSize"].(BufferSizeArgs) + args := egs.args["BufferSize"].(BufferSizeArgs) session = session.BufferSize(args.size) } } @@ -139,252 +139,252 @@ func (ges *GESession) operate(session *Session) *Session { } // Before Apply before Processor, affected bean is passed to closure arg -func (ges *GESession) Before(closures func(interface{})) *GESession { - ges.operation = append(ges.operation, "Before") +func (egs *EGSession) Before(closures func(interface{})) *EGSession { + egs.operation = append(egs.operation, "Before") args := BeforeArgs{ closures: closures, } - ges.args["Before"] = args - return ges + egs.args["Before"] = args + return egs } // After Apply after Processor, affected bean is passed to closure arg -func (ges *GESession) After(closures func(interface{})) *GESession { - ges.operation = append(ges.operation, "After") +func (egs *EGSession) After(closures func(interface{})) *EGSession { + egs.operation = append(egs.operation, "After") args := AfterArgs{ closures: closures, } - ges.args["After"] = args - return ges + egs.args["After"] = args + return egs } // Table can input a string or pointer to struct for special a table to operate. -func (ges *GESession) Table(tableNameOrBean interface{}) *GESession { - ges.operation = append(ges.operation, "Table") +func (egs *EGSession) Table(tableNameOrBean interface{}) *EGSession { + egs.operation = append(egs.operation, "Table") args := TableArgs{ tableNameOrBean: tableNameOrBean, } - ges.args["Table"] = args - return ges + egs.args["Table"] = args + return egs } // Alias set the table alias -func (ges *GESession) Alias(alias string) *GESession { - ges.operation = append(ges.operation, "Alias") +func (egs *EGSession) Alias(alias string) *EGSession { + egs.operation = append(egs.operation, "Alias") args := AliasArgs{ alias: alias, } - ges.args["Alias"] = args - return ges + egs.args["Alias"] = args + return egs } // NoCascade indicate that no cascade load child object -func (ges *GESession) NoCascade() *GESession { - ges.operation = append(ges.operation, "NoCascade") - return ges +func (egs *EGSession) NoCascade() *EGSession { + egs.operation = append(egs.operation, "NoCascade") + return egs } // ForUpdate Set Read/Write locking for UPDATE -func (ges *GESession) ForUpdate() *GESession { - ges.operation = append(ges.operation, "ForUpdate") - return ges +func (egs *EGSession) ForUpdate() *EGSession { + egs.operation = append(egs.operation, "ForUpdate") + return egs } // NoAutoCondition disable generate SQL condition from beans -func (ges *GESession) NoAutoCondition(no ...bool) *GESession { - ges.operation = append(ges.operation, "NoAutoCondition") +func (egs *EGSession) NoAutoCondition(no ...bool) *EGSession { + egs.operation = append(egs.operation, "NoAutoCondition") args := NoAutoConditionArgs{ no: no, } - ges.args["NoAutoCondition"] = args - return ges + egs.args["NoAutoCondition"] = args + return egs } // Limit provide limit and offset query condition -func (ges *GESession) Limit(limit int, start ...int) *GESession { - ges.operation = append(ges.operation, "Limit") +func (egs *EGSession) Limit(limit int, start ...int) *EGSession { + egs.operation = append(egs.operation, "Limit") args := LimitArgs{ limit: limit, start: start, } - ges.args["Limit"] = args - return ges + egs.args["Limit"] = args + return egs } // OrderBy provide order by query condition, the input parameter is the content // after order by on a sql statement. -func (ges *GESession) OrderBy(order string) *GESession { - ges.operation = append(ges.operation, "OrderBy") +func (egs *EGSession) OrderBy(order string) *EGSession { + egs.operation = append(egs.operation, "OrderBy") args := OrderByArgs{ order: order, } - ges.args["OrderBy"] = args - return ges + egs.args["OrderBy"] = args + return egs } // Desc provide desc order by query condition, the input parameters are columns. -func (ges *GESession) Desc(colNames ...string) *GESession { - ges.operation = append(ges.operation, "Desc") +func (egs *EGSession) Desc(colNames ...string) *EGSession { + egs.operation = append(egs.operation, "Desc") args := DescArgs{ colNames: colNames, } - ges.args["Desc"] = args - return ges + egs.args["Desc"] = args + return egs } // Asc provide asc order by query condition, the input parameters are columns. -func (ges *GESession) Asc(colNames ...string) *GESession { - ges.operation = append(ges.operation, "Asc") +func (egs *EGSession) Asc(colNames ...string) *EGSession { + egs.operation = append(egs.operation, "Asc") args := AscArgs{ colNames: colNames, } - ges.args["Asc"] = args - return ges + egs.args["Asc"] = args + return egs } // StoreEngine is only avialble mysql dialect currently -func (ges *GESession) StoreEngine(storeEngine string) *GESession { - ges.operation = append(ges.operation, "StoreEngine") +func (egs *EGSession) StoreEngine(storeEngine string) *EGSession { + egs.operation = append(egs.operation, "StoreEngine") args := StoreEngineArgs{ storeEngine: storeEngine, } - ges.args["StoreEngine"] = args - return ges + egs.args["StoreEngine"] = args + return egs } // Charset is only avialble mysql dialect currently -func (ges *GESession) Charset(charset string) *GESession { - ges.operation = append(ges.operation, "Charset") +func (egs *EGSession) Charset(charset string) *EGSession { + egs.operation = append(egs.operation, "Charset") args := CharsetArgs{ charset: charset, } - ges.args["Charset"] = args - return ges + egs.args["Charset"] = args + return egs } // Cascade indicates if loading sub Struct -func (ges *GESession) Cascade(trueOrFalse ...bool) *GESession { - ges.operation = append(ges.operation, "Cascade") +func (egs *EGSession) Cascade(trueOrFalse ...bool) *EGSession { + egs.operation = append(egs.operation, "Cascade") args := CascadeArgs{ trueOrFalse: trueOrFalse, } - ges.args["Cascade"] = args - return ges + egs.args["Cascade"] = args + return egs } // NoCache ask this session do not retrieve data from cache system and // get data from database directly. -func (ges *GESession) NoCache() *GESession { - ges.operation = append(ges.operation, "NoCache") - return ges +func (egs *EGSession) NoCache() *EGSession { + egs.operation = append(egs.operation, "NoCache") + return egs } // Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (ges *GESession) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "Join") +func (egs *EGSession) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "Join") joinArgs := JoinArgs{ joinOperator: joinOperator, tablename: tablename, condition: condition, args: args, } - ges.args["Join"] = joinArgs - return ges + egs.args["Join"] = joinArgs + return egs } // GroupBy Generate Group By statement -func (ges *GESession) GroupBy(keys string) *GESession { - ges.operation = append(ges.operation, "GroupBy") +func (egs *EGSession) GroupBy(keys string) *EGSession { + egs.operation = append(egs.operation, "GroupBy") args := GroupByArgs{ keys: keys, } - ges.args["GroupBy"] = args - return ges + egs.args["GroupBy"] = args + return egs } // Having Generate Having statement -func (ges *GESession) Having(conditions string) *GESession { - ges.operation = append(ges.operation, "Having") +func (egs *EGSession) Having(conditions string) *EGSession { + egs.operation = append(egs.operation, "Having") args := HavingArgs{ conditions: conditions, } - ges.args["Having"] = args - return ges + egs.args["Having"] = args + return egs } // Unscoped always disable struct tag "deleted" -func (ges *GESession) Unscoped() *GESession { - ges.operation = append(ges.operation, "Unscoped") - return ges +func (egs *EGSession) Unscoped() *EGSession { + egs.operation = append(egs.operation, "Unscoped") + return egs } // Incr provides a query string like "count = count + 1" -func (ges *GESession) Incr(column string, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "Incr") +func (egs *EGSession) Incr(column string, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "Incr") incrArgs := IncrArgs{ column: column, args: args, } - ges.args["Incr"] = incrArgs - return ges + egs.args["Incr"] = incrArgs + return egs } // Decr provides a query string like "count = count - 1" -func (ges *GESession) Decr(column string, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "Decr") +func (egs *EGSession) Decr(column string, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "Decr") decrArgs := DecrArgs{ column: column, args: args, } - ges.args["Decr"] = decrArgs - return ges + egs.args["Decr"] = decrArgs + return egs } // SetExpr provides a query string like "column = {expression}" -func (ges *GESession) SetExpr(column string, expression string) *GESession { - ges.operation = append(ges.operation, "SetExpr") +func (egs *EGSession) SetExpr(column string, expression string) *EGSession { + egs.operation = append(egs.operation, "SetExpr") args := SetExprArgs{ column: column, expression: expression, } - ges.args["SetExpr"] = args - return ges + egs.args["SetExpr"] = args + return egs } // Select provides some columns to special -func (ges *GESession) Select(str string) *GESession { - ges.operation = append(ges.operation, "Select") +func (egs *EGSession) Select(str string) *EGSession { + egs.operation = append(egs.operation, "Select") args := SelectArgs{ str: str, } - ges.args["Select"] = args - return ges + egs.args["Select"] = args + return egs } // Cols provides some columns to special -func (ges *GESession) Cols(columns ...string) *GESession { - ges.operation = append(ges.operation, "Cols") +func (egs *EGSession) Cols(columns ...string) *EGSession { + egs.operation = append(egs.operation, "Cols") args := ColsArgs{ columns: columns, } - ges.args["Cols"] = args - return ges + egs.args["Cols"] = args + return egs } // AllCols ask all columns -func (ges *GESession) AllCols() *GESession { - ges.operation = append(ges.operation, "AllCols") - return ges +func (egs *EGSession) AllCols() *EGSession { + egs.operation = append(egs.operation, "AllCols") + return egs } // MustCols specify some columns must use even if they are empty -func (ges *GESession) MustCols(columns ...string) *GESession { - ges.operation = append(ges.operation, "MustCols") +func (egs *EGSession) MustCols(columns ...string) *EGSession { + egs.operation = append(egs.operation, "MustCols") args := MustColsArgs{ columns: columns, } - ges.args["MustCols"] = args - return ges + egs.args["MustCols"] = args + return egs } // UseBool automatically retrieve condition according struct, but @@ -392,66 +392,66 @@ func (ges *GESession) MustCols(columns ...string) *GESession { // to tell system to do not ignore them. // If no parameters, it will use all the bool field of struct, or // it will use parameters's columns -func (ges *GESession) UseBool(columns ...string) *GESession { - ges.operation = append(ges.operation, "UseBool") +func (egs *EGSession) UseBool(columns ...string) *EGSession { + egs.operation = append(egs.operation, "UseBool") args := UseBoolArgs{ columns: columns, } - ges.args["UseBool"] = args - return ges + egs.args["UseBool"] = args + return egs } // Distinct use for distinct columns. Caution: when you are using cache, // distinct will not be cached because cache system need id, // but distinct will not provide id -func (ges *GESession) Distinct(columns ...string) *GESession { - ges.operation = append(ges.operation, "Distinct") +func (egs *EGSession) Distinct(columns ...string) *EGSession { + egs.operation = append(egs.operation, "Distinct") args := DistinctArgs{ columns: columns, } - ges.args["Distinct"] = args - return ges + egs.args["Distinct"] = args + return egs } // Omit Only not use the parameters as select or update columns -func (ges *GESession) Omit(columns ...string) *GESession { - ges.operation = append(ges.operation, "Omit") +func (egs *EGSession) Omit(columns ...string) *EGSession { + egs.operation = append(egs.operation, "Omit") args := OmitArgs{ columns: columns, } - ges.args["Omit"] = args - return ges + egs.args["Omit"] = args + return egs } // Nullable Set null when column is zero-value and nullable for update -func (ges *GESession) Nullable(columns ...string) *GESession { - ges.operation = append(ges.operation, "Nullable") +func (egs *EGSession) Nullable(columns ...string) *EGSession { + egs.operation = append(egs.operation, "Nullable") args := NullableArgs{ columns: columns, } - ges.args["Nullable"] = args - return ges + egs.args["Nullable"] = args + return egs } // NoAutoTime means do not automatically give created field and updated field // the current time on the current session temporarily -func (ges *GESession) NoAutoTime() *GESession { - ges.operation = append(ges.operation, "NoAutoTime") - return ges +func (egs *EGSession) NoAutoTime() *EGSession { + egs.operation = append(egs.operation, "NoAutoTime") + return egs } // Sql provides raw sql input parameter. When you have a complex SQL statement // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. // // Deprecated: use SQL instead. -func (ges *GESession) Sql(query string, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "Sql") +func (egs *EGSession) Sql(query string, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "Sql") sqlArgs := SqlArgs{ query: query, args: args, } - ges.args["Sql"] = sqlArgs - return ges + egs.args["Sql"] = sqlArgs + return egs } type SQLArgs struct { @@ -461,25 +461,25 @@ type SQLArgs struct { // SQL provides raw sql input parameter. When you have a complex SQL statement // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. -func (ges *GESession) SQL(query interface{}, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "SQL") +func (egs *EGSession) SQL(query interface{}, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "SQL") sqlArgs := SQLArgs{ query: query, args: args, } - ges.args["SQL"] = sqlArgs - return ges + egs.args["SQL"] = sqlArgs + return egs } // Where provides custom query condition. -func (ges *GESession) Where(query interface{}, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "Where") +func (egs *EGSession) Where(query interface{}, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "Where") whereArgs := WhereArgs{ query: query, args: args, } - ges.args["Where"] = whereArgs - return ges + egs.args["Where"] = whereArgs + return egs } type AndArgs struct { @@ -488,14 +488,14 @@ type AndArgs struct { } // And provides custom query condition. -func (ges *GESession) And(query interface{}, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "And") +func (egs *EGSession) And(query interface{}, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "And") andArgs := AndArgs{ query: query, args: args, } - ges.args["And"] = andArgs - return ges + egs.args["And"] = andArgs + return egs } type OrArgs struct { @@ -504,258 +504,248 @@ type OrArgs struct { } // Or provides custom query condition. -func (ges *GESession) Or(query interface{}, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "Or") +func (egs *EGSession) Or(query interface{}, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "Or") orArgs := OrArgs{ query: query, args: args, } - ges.args["Or"] = orArgs - return ges + egs.args["Or"] = orArgs + return egs } // Id provides converting id as a query condition // // Deprecated: use ID instead -func (ges *GESession) Id(id interface{}) *GESession { - ges.operation = append(ges.operation, "Id") +func (egs *EGSession) Id(id interface{}) *EGSession { + egs.operation = append(egs.operation, "Id") args := IdArgs{ id: id, } - ges.args["Id"] = args - return ges + egs.args["Id"] = args + return egs } // ID provides converting id as a query condition -func (ges *GESession) ID(id interface{}) *GESession { - ges.operation = append(ges.operation, "ID") +func (egs *EGSession) ID(id interface{}) *EGSession { + egs.operation = append(egs.operation, "ID") args := IDArgs{ id: id, } - ges.args["ID"] = args - return ges + egs.args["ID"] = args + return egs } // In provides a query string like "id in (1, 2, 3)" -func (ges *GESession) In(column string, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "In") +func (egs *EGSession) In(column string, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "In") inArgs := InArgs{ column: column, args: args, } - ges.args["In"] = inArgs - return ges + egs.args["In"] = inArgs + return egs } // NotIn provides a query string like "id in (1, 2, 3)" -func (ges *GESession) NotIn(column string, args ...interface{}) *GESession { - ges.operation = append(ges.operation, "NotIn") +func (egs *EGSession) NotIn(column string, args ...interface{}) *EGSession { + egs.operation = append(egs.operation, "NotIn") notInArgs := NotInArgs{ column: column, args: args, } - ges.args["NotIn"] = notInArgs - return ges + egs.args["NotIn"] = notInArgs + return egs } -//TODO 还需要分析如何实现 // Conds returns session query conditions except auto bean conditions -func (ges *GESession) Conds() builder.Cond { - return ges.ge.Master().NewSession().Conds() +func (egs *EGSession) Conds() builder.Cond { + return egs.eg.Master().NewSession().Conds() } -//TODO 缺少前置session操作链 // Delete records, bean's non-empty fields are conditions -func (ges *GESession) Delete(bean interface{}) (int64, error) { - session := ges.ge.Master().NewSession() +func (egs *EGSession) Delete(bean interface{}) (int64, error) { + session := egs.eg.Master().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Delete(bean) } -//TODO 缺少前置session操作链 // Exist returns true if the record exist otherwise return false -func (ges *GESession) Exist(bean ...interface{}) (bool, error) { - session := ges.ge.Master().NewSession() +func (egs *EGSession) Exist(bean ...interface{}) (bool, error) { + session := egs.eg.Master().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) 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 -func (ges *GESession) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Find(rowsSlicePtr, condiBean...) } // Get retrieve one record from database, bean's non-empty fields // will be as conditions -func (ges *GESession) Get(bean interface{}) (bool, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) Get(bean interface{}) (bool, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Get(bean) } // Insert insert one or more beans -func (ges *GESession) Insert(beans ...interface{}) (int64, error) { - session := ges.ge.Master().NewSession() +func (egs *EGSession) Insert(beans ...interface{}) (int64, error) { + session := egs.eg.Master().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Insert(beans...) } // Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields // are conditions. -func (ges *GESession) Rows(bean interface{}) (*Rows, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) Rows(bean interface{}) (*Rows, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Rows(bean) } // Iterate record by record handle records from table, condiBeans's non-empty fields // are conditions. beans could be []Struct, []*Struct, map[int64]Struct // map[int64]*Struct -func (ges *GESession) Iterate(bean interface{}, fun IterFunc) error { - return ges.ge.Slave().Iterate(bean, fun) +func (egs *EGSession) Iterate(bean interface{}, fun IterFunc) error { + return egs.eg.Slave().Iterate(bean, fun) } // BufferSize sets the buffersize for iterate -func (ges *GESession) BufferSize(size int) *GESession { - ges.operation = append(ges.operation, "BufferSize") +func (egs *EGSession) BufferSize(size int) *EGSession { + egs.operation = append(egs.operation, "BufferSize") args := BufferSizeArgs{ size: size, } - ges.args["BufferSize"] = args - return ges + egs.args["BufferSize"] = args + return egs } -//TODO 缺少前置session操作链 // Query runs a raw sql and return records as []map[string][]byte -func (ges *GESession) Query(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) Query(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Query(sqlStr, args...) } -//TODO 缺少前置session操作链 // QueryString runs a raw sql and return records as []map[string]string -func (ges *GESession) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.QueryString(sqlStr, args...) } // QueryInterface runs a raw sql and return records as []map[string]interface{} -func (ges *GESession) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.QueryInterface(sqlStr, args...) } // Exec raw sql -func (ges *GESession) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { - session := ges.ge.Master().NewSession() +func (egs *EGSession) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { + session := egs.eg.Master().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Exec(sqlStr, args...) } // CreateTable create a table according a bean -func (ges *GESession) CreateTable(bean interface{}) error { - session := ges.ge.Master().NewSession() +func (egs *EGSession) CreateTable(bean interface{}) error { + session := egs.eg.Master().NewSession() defer session.Close() return session.CreateTable(bean) } // CreateIndexes create indexes -func (ges *GESession) CreateIndexes(bean interface{}) error { - return ges.ge.Master().CreateIndexes(bean) +func (egs *EGSession) CreateIndexes(bean interface{}) error { + return egs.eg.Master().CreateIndexes(bean) } // CreateUniques create uniques -func (ges *GESession) CreateUniques(bean interface{}) error { - return ges.ge.Master().CreateUniques(bean) +func (egs *EGSession) CreateUniques(bean interface{}) error { + return egs.eg.Master().CreateUniques(bean) } // DropIndexes drop indexes -func (ges *GESession) DropIndexes(bean interface{}) error { - return ges.ge.Master().DropIndexes(bean) +func (egs *EGSession) DropIndexes(bean interface{}) error { + return egs.eg.Master().DropIndexes(bean) } // DropTable drop table will drop table if exist, if drop failed, it will return error -func (ges *GESession) DropTable(beanOrTableName interface{}) error { - session := ges.ge.Master().NewSession() +func (egs *EGSession) DropTable(beanOrTableName interface{}) error { + session := egs.eg.Master().NewSession() defer session.Close() return session.DropTable(beanOrTableName) } // IsTableExist if a table is exist -func (ges *GESession) IsTableExist(beanOrTableName interface{}) (bool, error) { - return ges.ge.Master().IsTableExist(beanOrTableName) +func (egs *EGSession) IsTableExist(beanOrTableName interface{}) (bool, error) { + return egs.eg.Master().IsTableExist(beanOrTableName) } // IsTableEmpty if table have any records -func (ges *GESession) IsTableEmpty(bean interface{}) (bool, error) { - return ges.ge.Master().IsTableEmpty(bean) +func (egs *EGSession) IsTableEmpty(bean interface{}) (bool, error) { + return egs.eg.Master().IsTableEmpty(bean) } // Sync2 synchronize structs to database tables -func (ges *GESession) Sync2(beans ...interface{}) error { - return ges.ge.Master().Sync2(beans...) +func (egs *EGSession) Sync2(beans ...interface{}) error { + return egs.eg.Master().Sync2(beans...) } -//TODO 缺少前置session操作链 // Count counts the records. bean's non-empty fields // are conditions. -func (ges *GESession) Count(bean ...interface{}) (int64, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) Count(bean ...interface{}) (int64, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Count(bean...) } -//TODO 缺少前置session操作链 // sum call sum some column. bean's non-empty fields are conditions. -func (ges *GESession) Sum(bean interface{}, columnName string) (res float64, err error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) Sum(bean interface{}, columnName string) (res float64, err error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Sum(bean, columnName) } -//TODO 缺少前置session操作链 // SumInt call sum some column. bean's non-empty fields are conditions. -func (ges *GESession) SumInt(bean interface{}, columnName string) (res int64, err error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) SumInt(bean interface{}, columnName string) (res int64, err error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.SumInt(bean, columnName) } -//TODO 缺少前置session操作链 // Sums call sum some columns. bean's non-empty fields are conditions. -func (ges *GESession) Sums(bean interface{}, columnNames ...string) ([]float64, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) Sums(bean interface{}, columnNames ...string) ([]float64, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Sums(bean, columnNames...) } -//TODO 缺少前置session操作链 // SumsInt sum specify columns and return as []int64 instead of []float64 -func (ges *GESession) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { - session := ges.ge.Slave().NewSession() +func (egs *EGSession) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { + session := egs.eg.Slave().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.SumsInt(bean, columnNames...) } @@ -765,10 +755,10 @@ func (ges *GESession) SumsInt(bean interface{}, columnNames ...string) ([]int64, // 1.bool will defaultly be updated content nor conditions // You should call UseBool if you have bool to use. // 2.float32 & float64 may be not inexact as conditions -func (ges *GESession) Update(bean interface{}, condiBean ...interface{}) (int64, error) { - session := ges.ge.Master().NewSession() +func (egs *EGSession) Update(bean interface{}, condiBean ...interface{}) (int64, error) { + session := egs.eg.Master().NewSession() defer session.Close() - session = ges.operate(session) + session = egs.operate(session) return session.Update(bean, condiBean...) } diff --git a/group_engine.go b/group_engine.go index 93a8e0d2..341de2c7 100644 --- a/group_engine.go +++ b/group_engine.go @@ -9,19 +9,38 @@ import ( "io" "reflect" "strings" - "sync/atomic" "time" "github.com/go-xorm/builder" "github.com/go-xorm/core" ) -type GroupEngine struct { - engines []*Engine - count uint64 +type EngineGroup struct { + master *Engine + slaves []*Engine + weight []int + count int + s_count int + policy Policy + p int } -func NewGroupEngine(driverName string, dataSourceNames string) (*GroupEngine, error) { +func NewGroup(args1 interface{}, args2 interface{}, policy ...Policy) (*EngineGroup, error) { + driverName, ok1 := args1.(string) + dataSourceNames, ok2 := args2.(string) + if ok1 && ok2 { + return newGroup1(driverName, dataSourceNames, policy...) + } + + Master, ok3 := args1.(*Engine) + Slaves, ok4 := args2.([]*Engine) + if ok3 && ok4 { + return newGroup2(Master, Slaves, policy...) + } + return nil, ErrParamsType +} + +func newGroup1(driverName string, dataSourceNames string, policy ...Policy) (*EngineGroup, error) { conns := strings.Split(dataSourceNames, ";") engines := make([]*Engine, len(conns)) for i, _ := range conns { @@ -31,116 +50,205 @@ func NewGroupEngine(driverName string, dataSourceNames string) (*GroupEngine, er } engines[i] = engine } - ge := &GroupEngine{ - engines: engines, - count: uint64(len(engines)), + + n := len(policy) + if n > 1 { + return nil, ErrParamsType + } else if n == 1 { + eg := &EngineGroup{ + master: engines[0], + slaves: engines[1:], + count: len(engines), + s_count: len(engines[1:]), + policy: policy[0], + } + eg.policy.SetEngineGroup(eg) + return eg, nil + } else { + xPolicy := new(XormEngineGroupPolicy) + eg := &EngineGroup{ + master: engines[0], + slaves: engines[1:], + count: len(engines), + s_count: len(engines[1:]), + policy: xPolicy, + } + xPolicy.SetEngineGroup(eg) + return eg, nil } - return ge, nil + } -func NewGroup(Master *Engine, Slaves []*Engine, policy int) (*GroupEngine, error) { - engines := make([]*Engine, 0) - engines = append(engines, Master) - for i, _ := range Slaves { - engines = append(engines, Slaves[i]) +func newGroup2(Master *Engine, Slaves []*Engine, policy ...Policy) (*EngineGroup, error) { + n := len(policy) + if n > 1 { + return nil, ErrParamsType + } else if n == 1 { + eg := &EngineGroup{ + master: Master, + slaves: Slaves, + count: 1 + len(Slaves), + s_count: len(Slaves), + policy: policy[0], + } + eg.policy.SetEngineGroup(eg) + return eg, nil + } else { + xPolicy := new(XormEngineGroupPolicy) + eg := &EngineGroup{ + master: Master, + slaves: Slaves, + count: 1 + len(Slaves), + s_count: len(Slaves), + policy: xPolicy, + } + xPolicy.SetEngineGroup(eg) + return eg, nil } - ge := &GroupEngine{ - engines: engines, - count: uint64(len(engines)), - } - return ge, nil } -func (ge *GroupEngine) Master() *Engine { - return ge.engines[0] +func (eg *EngineGroup) SetPolicy(policy Policy) *EngineGroup { + eg.policy = policy + return eg +} + +func (eg *EngineGroup) UsePolicy(policy int) *EngineGroup { + eg.p = policy + return eg +} + +func (eg *EngineGroup) SetWeight(weight ...interface{}) *EngineGroup { + l := len(weight) + if l == 1 { + switch weight[0].(type) { + case []int: + eg.weight = weight[0].([]int) + } + } else if l > 1 { + s := make([]int, 0) + for i, _ := range weight { + switch weight[i].(type) { + case int: + s = append(s, weight[i].(int)) + default: + s = append(s, 1) + } + } + eg.weight = s + } + + return eg +} + +func (eg *EngineGroup) Master() *Engine { + return eg.master } // Slave returns one of the physical databases which is a slave -func (ge *GroupEngine) Slave() *Engine { - return ge.engines[ge.slave(len(ge.engines))] +func (eg *EngineGroup) Slave() *Engine { + if eg.count == 1 { + return eg.master + } + return eg.slaves[eg.policy.Slave()] } -func (ge *GroupEngine) GetEngine(i int) *Engine { - if i >= len(ge.engines) { - return ge.engines[0] +func (eg *EngineGroup) Slaves() []*Engine { + if eg.count == 1 { + return []*Engine{eg.master} } - return ge.engines[i] + return eg.slaves } -func (ge *GroupEngine) GetSlaves() []*Engine { - if len(ge.engines) == 1 { - return ge.engines +func (eg *EngineGroup) GetSlave(i int) *Engine { + if eg.count == 1 || i == 0 { + return eg.master } - return ge.engines[1:] + if i > eg.s_count { + return eg.slaves[0] + } + return eg.slaves[i] } -func (ge *GroupEngine) slave(n int) int { - if n <= 1 { - return 0 +func (eg *EngineGroup) GetEngine(i int) *Engine { + if i >= eg.count || i == 0 { + return eg.master } - return int(1 + (atomic.AddUint64(&ge.count, 1) % uint64(n-1))) + return eg.slaves[i-1] } // ShowSQL show SQL statement or not on logger if log level is great than INFO -func (ge *GroupEngine) ShowSQL(show ...bool) { - for i, _ := range ge.engines { - ge.engines[i].ShowSQL(show...) +func (eg *EngineGroup) ShowSQL(show ...bool) { + eg.master.ShowSQL(show...) + for i, _ := range eg.slaves { + eg.slaves[i].ShowSQL(show...) } } // ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO -func (ge *GroupEngine) ShowExecTime(show ...bool) { - for i, _ := range ge.engines { - ge.engines[i].ShowExecTime(show...) +func (eg *EngineGroup) ShowExecTime(show ...bool) { + eg.master.ShowExecTime(show...) + for i, _ := range eg.slaves { + eg.slaves[i].ShowExecTime(show...) } } // SetMapper set the name mapping rules -func (ge *GroupEngine) SetMapper(mapper core.IMapper) { - for i, _ := range ge.engines { - ge.engines[i].SetTableMapper(mapper) - ge.engines[i].SetColumnMapper(mapper) +func (eg *EngineGroup) SetMapper(mapper core.IMapper) { + eg.master.SetTableMapper(mapper) + eg.master.SetColumnMapper(mapper) + for i, _ := range eg.slaves { + eg.slaves[i].SetTableMapper(mapper) + eg.slaves[i].SetColumnMapper(mapper) } } // SetTableMapper set the table name mapping rule -func (ge *GroupEngine) SetTableMapper(mapper core.IMapper) { - for i, _ := range ge.engines { - ge.engines[i].TableMapper = mapper +func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) { + eg.master.TableMapper = mapper + for i, _ := range eg.slaves { + eg.slaves[i].TableMapper = mapper } } // SetColumnMapper set the column name mapping rule -func (ge *GroupEngine) SetColumnMapper(mapper core.IMapper) { - for i, _ := range ge.engines { - ge.engines[i].ColumnMapper = mapper +func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { + eg.master.ColumnMapper = mapper + for i, _ := range eg.slaves { + eg.slaves[i].ColumnMapper = mapper } } // SetMaxOpenConns is only available for go 1.2+ -func (ge *GroupEngine) SetMaxOpenConns(conns int) { - for i, _ := range ge.engines { - ge.engines[i].db.SetMaxOpenConns(conns) +func (eg *EngineGroup) SetMaxOpenConns(conns int) { + eg.master.db.SetMaxOpenConns(conns) + for i, _ := range eg.slaves { + eg.slaves[i].db.SetMaxOpenConns(conns) } } // SetMaxIdleConns set the max idle connections on pool, default is 2 -func (ge *GroupEngine) SetMaxIdleConns(conns int) { - for i, _ := range ge.engines { - ge.engines[i].db.SetMaxIdleConns(conns) +func (eg *EngineGroup) SetMaxIdleConns(conns int) { + eg.master.db.SetMaxIdleConns(conns) + for i, _ := range eg.slaves { + eg.slaves[i].db.SetMaxIdleConns(conns) } } // NoCascade If you do not want to auto cascade load object -func (ge *GroupEngine) NoCascade() *GESession { - ges := ge.NewGESession() - return ges.NoCascade() +func (eg *EngineGroup) NoCascade() *EGSession { + egs := eg.NewEGSession() + return egs.NoCascade() } // Close the engine -func (ge *GroupEngine) Close() error { - for i, _ := range ge.engines { - err := ge.engines[i].db.Close() +func (eg *EngineGroup) Close() error { + err := eg.master.db.Close() + if err != nil { + return err + } + + for i, _ := range eg.slaves { + err := eg.slaves[i].db.Close() if err != nil { return err } @@ -149,16 +257,18 @@ func (ge *GroupEngine) Close() error { } // Ping tests if database is alive -func (ge *GroupEngine) Ping() error { - return scatter(len(ge.engines), func(i int) error { - return ge.engines[i].Ping() +func (eg *EngineGroup) Ping() error { + eg.master.Ping() + return scatter(eg.s_count, func(i int) error { + return eg.slaves[i].Ping() }) } // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. -func (ge *GroupEngine) SetConnMaxLifetime(d time.Duration) { - for i, _ := range ge.engines { - ge.engines[i].db.SetConnMaxLifetime(d) +func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { + eg.master.db.SetConnMaxLifetime(d) + for i, _ := range eg.slaves { + eg.slaves[i].db.SetConnMaxLifetime(d) } } @@ -183,25 +293,25 @@ func scatter(n int, fn func(i int) error) error { // SqlType will be deprecated, please use SQLType instead // // Deprecated: use SQLType instead -func (ge *GroupEngine) SqlType(c *core.Column) string { - return ge.Master().SQLType(c) +func (eg *EngineGroup) SqlType(c *core.Column) string { + return eg.Master().SQLType(c) } // SQLType A simple wrapper to dialect's core.SqlType method -func (ge *GroupEngine) SQLType(c *core.Column) string { - return ge.Master().dialect.SqlType(c) +func (eg *EngineGroup) SQLType(c *core.Column) string { + return eg.Master().dialect.SqlType(c) } // NewSession New a session -func (ge *GroupEngine) NewSession() *Session { - return ge.Master().NewSession() +func (eg *EngineGroup) NewSession() *Session { + return eg.Master().NewSession() } // NewSession New a session -func (ge *GroupEngine) NewGESession() *GESession { +func (eg *EngineGroup) NewEGSession() *EGSession { args := make(map[string]interface{}) - ges := &GESession{ge: ge, operation: []string{}, args: args} - return ges + egs := &EGSession{eg: eg, operation: []string{}, args: args} + return egs } type SqlArgs struct { @@ -209,22 +319,22 @@ type SqlArgs struct { args []interface{} } -func (ge *GroupEngine) Sql(query string, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.Sql(query, args...) +func (eg *EngineGroup) Sql(query string, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.Sql(query, args...) } -func (ge *GroupEngine) SQL(query interface{}, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.SQL(query, args...) +func (eg *EngineGroup) SQL(query interface{}, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.SQL(query, args...) } // NoAutoTime Default if your struct has "created" or "updated" filed tag, the fields // will automatically be filled with current time when Insert or Update // invoked. Call NoAutoTime if you dont' want to fill automatically. -func (ge *GroupEngine) NoAutoTime() *GESession { - ges := ge.NewGESession() - return ges.NoAutoTime() +func (eg *EngineGroup) NoAutoTime() *EGSession { + egs := eg.NewEGSession() + return egs.NoAutoTime() } type NoAutoConditionArgs struct { @@ -232,34 +342,34 @@ type NoAutoConditionArgs struct { } // NoAutoCondition disable auto generate Where condition from bean or not -func (ge *GroupEngine) NoAutoCondition(no ...bool) *GESession { - ges := ge.NewGESession() - return ges.NoAutoCondition(no...) +func (eg *EngineGroup) NoAutoCondition(no ...bool) *EGSession { + egs := eg.NewEGSession() + return egs.NoAutoCondition(no...) } // DBMetas Retrieve all tables, columns, indexes' informations from database. -func (ge *GroupEngine) DBMetas() ([]*core.Table, error) { - return ge.Master().DBMetas() +func (eg *EngineGroup) DBMetas() ([]*core.Table, error) { + return eg.Master().DBMetas() } // DumpAllToFile dump database all table structs and data to a file -func (ge *GroupEngine) DumpAllToFile(fp string, tp ...core.DbType) error { - return ge.Master().DumpAllToFile(fp, tp...) +func (eg *EngineGroup) DumpAllToFile(fp string, tp ...core.DbType) error { + return eg.Master().DumpAllToFile(fp, tp...) } // DumpAll dump database all table structs and data to w -func (ge *GroupEngine) DumpAll(w io.Writer, tp ...core.DbType) error { - return ge.Master().DumpAll(w, tp...) +func (eg *EngineGroup) DumpAll(w io.Writer, tp ...core.DbType) error { + return eg.Master().DumpAll(w, tp...) } // DumpTablesToFile dump specified tables to SQL file. -func (ge *GroupEngine) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error { - return ge.Master().DumpTablesToFile(tables, fp, tp...) +func (eg *EngineGroup) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error { + return eg.Master().DumpTablesToFile(tables, fp, tp...) } // DumpTables dump specify tables to io.Writer -func (ge *GroupEngine) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error { - return ge.Master().DumpTables(tables, w, tp...) +func (eg *EngineGroup) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error { + return eg.Master().DumpTables(tables, w, tp...) } type CascadeArgs struct { @@ -267,9 +377,9 @@ type CascadeArgs struct { } // Cascade use cascade or not -func (ge *GroupEngine) Cascade(trueOrFalse ...bool) *GESession { - ges := ge.NewGESession() - return ges.Cascade(trueOrFalse...) +func (eg *EngineGroup) Cascade(trueOrFalse ...bool) *EGSession { + egs := eg.NewEGSession() + return egs.Cascade(trueOrFalse...) } type WhereArgs struct { @@ -278,9 +388,9 @@ type WhereArgs struct { } // Where method provide a condition query -func (ge *GroupEngine) Where(query interface{}, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.Where(query, args...) +func (eg *EngineGroup) Where(query interface{}, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.Where(query, args...) } type IdArgs struct { @@ -288,9 +398,9 @@ type IdArgs struct { } // Id will be deprecated, please use ID instead -func (ge *GroupEngine) Id(id interface{}) *GESession { - ges := ge.NewGESession() - return ges.Id(id) +func (eg *EngineGroup) Id(id interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.Id(id) } type IDArgs struct { @@ -298,14 +408,9 @@ type IDArgs struct { } // ID method provoide a condition as (id) = ? -func (ge *GroupEngine) ID(id interface{}) *GESession { - ges := ge.NewGESession() - ges.operation = append(ges.operation, "ID") - args := IDArgs{ - id: id, - } - ges.args["ID"] = args - return ges +func (eg *EngineGroup) ID(id interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.ID(id) } type BeforeArgs struct { @@ -313,9 +418,9 @@ type BeforeArgs struct { } // Before apply before Processor, affected bean is passed to closure arg -func (ge *GroupEngine) Before(closures func(interface{})) *GESession { - ges := ge.NewGESession() - return ges.Before(closures) +func (eg *EngineGroup) Before(closures func(interface{})) *EGSession { + egs := eg.NewEGSession() + return egs.Before(closures) } type AfterArgs struct { @@ -323,9 +428,9 @@ type AfterArgs struct { } // After apply after insert Processor, affected bean is passed to closure arg -func (ge *GroupEngine) After(closures func(interface{})) *GESession { - ges := ge.NewGESession() - return ges.After(closures) +func (eg *EngineGroup) After(closures func(interface{})) *EGSession { + egs := eg.NewEGSession() + return egs.After(closures) } type CharsetArgs struct { @@ -333,9 +438,9 @@ type CharsetArgs struct { } // Charset set charset when create table, only support mysql now -func (ge *GroupEngine) Charset(charset string) *GESession { - ges := ge.NewGESession() - return ges.Charset(charset) +func (eg *EngineGroup) Charset(charset string) *EGSession { + egs := eg.NewEGSession() + return egs.Charset(charset) } type StoreEngineArgs struct { @@ -343,9 +448,9 @@ type StoreEngineArgs struct { } // StoreEngine set store engine when create table, only support mysql now -func (ge *GroupEngine) StoreEngine(storeEngine string) *GESession { - ges := ge.NewGESession() - return ges.StoreEngine(storeEngine) +func (eg *EngineGroup) StoreEngine(storeEngine string) *EGSession { + egs := eg.NewEGSession() + return egs.StoreEngine(storeEngine) } type DistinctArgs struct { @@ -355,9 +460,9 @@ type DistinctArgs struct { // Distinct use for distinct columns. Caution: when you are using cache, // distinct will not be cached because cache system need id, // but distinct will not provide id -func (ge *GroupEngine) Distinct(columns ...string) *GESession { - ges := ge.NewGESession() - return ges.Distinct(columns...) +func (eg *EngineGroup) Distinct(columns ...string) *EGSession { + egs := eg.NewEGSession() + return egs.Distinct(columns...) } type SelectArgs struct { @@ -365,9 +470,9 @@ type SelectArgs struct { } // Select customerize your select columns or contents -func (ge *GroupEngine) Select(str string) *GESession { - ges := ge.NewGESession() - return ges.Select(str) +func (eg *EngineGroup) Select(str string) *EGSession { + egs := eg.NewEGSession() + return egs.Select(str) } type ColsArgs struct { @@ -375,15 +480,15 @@ type ColsArgs struct { } // Cols only use the parameters as select or update columns -func (ge *GroupEngine) Cols(columns ...string) *GESession { - ges := ge.NewGESession() - return ges.Cols(columns...) +func (eg *EngineGroup) Cols(columns ...string) *EGSession { + egs := eg.NewEGSession() + return egs.Cols(columns...) } // AllCols indicates that all columns should be use -func (ge *GroupEngine) AllCols() *GESession { - ges := ge.NewGESession() - return ges.AllCols() +func (eg *EngineGroup) AllCols() *EGSession { + egs := eg.NewEGSession() + return egs.AllCols() } type MustColsArgs struct { @@ -391,9 +496,9 @@ type MustColsArgs struct { } // MustCols specify some columns must use even if they are empty -func (ge *GroupEngine) MustCols(columns ...string) *GESession { - ges := ge.NewGESession() - return ges.MustCols(columns...) +func (eg *EngineGroup) MustCols(columns ...string) *EGSession { + egs := eg.NewEGSession() + return egs.MustCols(columns...) } type UseBoolArgs struct { @@ -405,9 +510,9 @@ type UseBoolArgs struct { // to tell system to do not ignore them. // If no parameters, it will use all the bool field of struct, or // it will use parameters's columns -func (ge *GroupEngine) UseBool(columns ...string) *GESession { - ges := ge.NewGESession() - return ges.UseBool(columns...) +func (eg *EngineGroup) UseBool(columns ...string) *EGSession { + egs := eg.NewEGSession() + return egs.UseBool(columns...) } type OmitArgs struct { @@ -415,9 +520,9 @@ type OmitArgs struct { } // Omit only not use the parameters as select or update columns -func (ge *GroupEngine) Omit(columns ...string) *GESession { - ges := ge.NewGESession() - return ges.Omit(columns...) +func (eg *EngineGroup) Omit(columns ...string) *EGSession { + egs := eg.NewEGSession() + return egs.Omit(columns...) } type NullableArgs struct { @@ -425,9 +530,9 @@ type NullableArgs struct { } // Nullable set null when column is zero-value and nullable for update -func (ge *GroupEngine) Nullable(columns ...string) *GESession { - ges := ge.NewGESession() - return ges.Nullable(columns...) +func (eg *EngineGroup) Nullable(columns ...string) *EGSession { + egs := eg.NewEGSession() + return egs.Nullable(columns...) } type InArgs struct { @@ -436,9 +541,9 @@ type InArgs struct { } // In will generate "column IN (?, ?)" -func (ge *GroupEngine) In(column string, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.In(column, args...) +func (eg *EngineGroup) In(column string, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.In(column, args...) } type NotInArgs struct { @@ -447,9 +552,9 @@ type NotInArgs struct { } // NotIn will generate "column NOT IN (?, ?)" -func (ge *GroupEngine) NotIn(column string, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.NotIn(column, args...) +func (eg *EngineGroup) NotIn(column string, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.NotIn(column, args...) } type IncrArgs struct { @@ -458,9 +563,9 @@ type IncrArgs struct { } // Incr provides a update string like "column = column + ?" -func (ge *GroupEngine) Incr(column string, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.Incr(column, args...) +func (eg *EngineGroup) Incr(column string, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.Incr(column, args...) } type DecrArgs struct { @@ -469,9 +574,9 @@ type DecrArgs struct { } // Decr provides a update string like "column = column - ?" -func (ge *GroupEngine) Decr(column string, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.Decr(column, args...) +func (eg *EngineGroup) Decr(column string, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.Decr(column, args...) } type SetExprArgs struct { @@ -480,9 +585,9 @@ type SetExprArgs struct { } // SetExpr provides a update string like "column = {expression}" -func (ge *GroupEngine) SetExpr(column string, expression string) *GESession { - ges := ge.NewGESession() - return ges.SetExpr(column, expression) +func (eg *EngineGroup) SetExpr(column string, expression string) *EGSession { + egs := eg.NewEGSession() + return egs.SetExpr(column, expression) } type TableArgs struct { @@ -490,9 +595,9 @@ type TableArgs struct { } // Table temporarily change the Get, Find, Update's table -func (ge *GroupEngine) Table(tableNameOrBean interface{}) *GESession { - ges := ge.NewGESession() - return ges.Table(tableNameOrBean) +func (eg *EngineGroup) Table(tableNameOrBean interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.Table(tableNameOrBean) } type AliasArgs struct { @@ -500,9 +605,9 @@ type AliasArgs struct { } // Alias set the table alias -func (ge *GroupEngine) Alias(alias string) *GESession { - ges := ge.NewGESession() - return ges.Alias(alias) +func (eg *EngineGroup) Alias(alias string) *EGSession { + egs := eg.NewEGSession() + return egs.Alias(alias) } type LimitArgs struct { @@ -511,9 +616,9 @@ type LimitArgs struct { } // Limit will generate "LIMIT start, limit" -func (ge *GroupEngine) Limit(limit int, start ...int) *GESession { - ges := ge.NewGESession() - return ges.Limit(limit, start...) +func (eg *EngineGroup) Limit(limit int, start ...int) *EGSession { + egs := eg.NewEGSession() + return egs.Limit(limit, start...) } type DescArgs struct { @@ -521,9 +626,9 @@ type DescArgs struct { } // Desc will generate "ORDER BY column1 DESC, column2 DESC" -func (ge *GroupEngine) Desc(colNames ...string) *GESession { - ges := ge.NewGESession() - return ges.Desc(colNames...) +func (eg *EngineGroup) Desc(colNames ...string) *EGSession { + egs := eg.NewEGSession() + return egs.Desc(colNames...) } type AscArgs struct { @@ -536,9 +641,9 @@ type AscArgs struct { // engine.Desc("name").Asc("age").Find(&users) // // SELECT * FROM user ORDER BY name DESC, age ASC // -func (ge *GroupEngine) Asc(colNames ...string) *GESession { - ges := ge.NewGESession() - return ges.Asc(colNames...) +func (eg *EngineGroup) Asc(colNames ...string) *EGSession { + egs := eg.NewEGSession() + return egs.Asc(colNames...) } type OrderByArgs struct { @@ -546,9 +651,9 @@ type OrderByArgs struct { } // OrderBy will generate "ORDER BY order" -func (ge *GroupEngine) OrderBy(order string) *GESession { - ges := ge.NewGESession() - return ges.OrderBy(order) +func (eg *EngineGroup) OrderBy(order string) *EGSession { + egs := eg.NewEGSession() + return egs.OrderBy(order) } type JoinArgs struct { @@ -559,9 +664,9 @@ type JoinArgs struct { } // Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (ge *GroupEngine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *GESession { - ges := ge.NewGESession() - return ges.Join(joinOperator, tablename, condition, args...) +func (eg *EngineGroup) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *EGSession { + egs := eg.NewEGSession() + return egs.Join(joinOperator, tablename, condition, args...) } type GroupByArgs struct { @@ -569,9 +674,9 @@ type GroupByArgs struct { } // GroupBy generate group by statement -func (ge *GroupEngine) GroupBy(keys string) *GESession { - ges := ge.NewGESession() - return ges.GroupBy(keys) +func (eg *EngineGroup) GroupBy(keys string) *EGSession { + egs := eg.NewEGSession() + return egs.GroupBy(keys) } type HavingArgs struct { @@ -579,196 +684,196 @@ type HavingArgs struct { } // Having generate having statement -func (ge *GroupEngine) Having(conditions string) *GESession { - ges := ge.NewGESession() - return ges.Having(conditions) +func (eg *EngineGroup) Having(conditions string) *EGSession { + egs := eg.NewEGSession() + return egs.Having(conditions) } // IdOf get id from one struct // // Deprecated: use IDOf instead. -func (ge *GroupEngine) IdOf(bean interface{}) core.PK { - return ge.Master().IdOf(bean) +func (eg *EngineGroup) IdOf(bean interface{}) core.PK { + return eg.Master().IdOf(bean) } // IDOf get id from one struct -func (ge *GroupEngine) IDOf(bean interface{}) core.PK { - return ge.Master().IDOf(bean) +func (eg *EngineGroup) IDOf(bean interface{}) core.PK { + return eg.Master().IDOf(bean) } // IdOfV get id from one value of struct // // Deprecated: use IDOfV instead. -func (ge *GroupEngine) IdOfV(rv reflect.Value) core.PK { - return ge.Master().IdOfV(rv) +func (eg *EngineGroup) IdOfV(rv reflect.Value) core.PK { + return eg.Master().IdOfV(rv) } // IDOfV get id from one value of struct -func (ge *GroupEngine) IDOfV(rv reflect.Value) core.PK { - return ge.Master().IDOfV(rv) +func (eg *EngineGroup) IDOfV(rv reflect.Value) core.PK { + return eg.Master().IDOfV(rv) } // CreateIndexes create indexes -func (ge *GroupEngine) CreateIndexes(bean interface{}) error { - return ge.Master().CreateIndexes(bean) +func (eg *EngineGroup) CreateIndexes(bean interface{}) error { + return eg.Master().CreateIndexes(bean) } // CreateUniques create uniques -func (ge *GroupEngine) CreateUniques(bean interface{}) error { - return ge.Master().CreateUniques(bean) +func (eg *EngineGroup) CreateUniques(bean interface{}) error { + return eg.Master().CreateUniques(bean) } // Sync the new struct changes to database, this method will automatically add // table, column, index, unique. but will not delete or change anything. // If you change some field, you should change the database manually. -func (ge *GroupEngine) Sync(beans ...interface{}) error { - return ge.Master().Sync(beans...) +func (eg *EngineGroup) Sync(beans ...interface{}) error { + return eg.Master().Sync(beans...) } // Sync2 synchronize structs to database tables -func (ge *GroupEngine) Sync2(beans ...interface{}) error { - return ge.Master().Sync2(beans...) +func (eg *EngineGroup) Sync2(beans ...interface{}) error { + return eg.Master().Sync2(beans...) } // CreateTables create tabls according bean -func (ge *GroupEngine) CreateTables(beans ...interface{}) error { - return ge.Master().CreateTables(beans...) +func (eg *EngineGroup) CreateTables(beans ...interface{}) error { + return eg.Master().CreateTables(beans...) } // DropTables drop specify tables -func (ge *GroupEngine) DropTables(beans ...interface{}) error { - return ge.Master().DropTables(beans...) +func (eg *EngineGroup) DropTables(beans ...interface{}) error { + return eg.Master().DropTables(beans...) } // DropIndexes drop indexes of a table -func (ge *GroupEngine) DropIndexes(bean interface{}) error { - return ge.Master().DropIndexes(bean) +func (eg *EngineGroup) DropIndexes(bean interface{}) error { + return eg.Master().DropIndexes(bean) } -func (ge *GroupEngine) Exec(sql string, args ...interface{}) (sql.Result, error) { - return ge.Master().Exec(sql, args...) +func (eg *EngineGroup) Exec(sql string, args ...interface{}) (sql.Result, error) { + return eg.Master().Exec(sql, args...) } // Query a raw sql and return records as []map[string][]byte -func (ge *GroupEngine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { - return ge.Slave().Query(sql, paramStr...) +func (eg *EngineGroup) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { + return eg.Slave().Query(sql, paramStr...) } // QueryString runs a raw sql and return records as []map[string]string -func (ge *GroupEngine) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { - return ge.Slave().QueryString(sqlStr, args...) +func (eg *EngineGroup) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { + return eg.Slave().QueryString(sqlStr, args...) } // QueryInterface runs a raw sql and return records as []map[string]interface{} -func (ge *GroupEngine) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { - return ge.Slave().QueryInterface(sqlStr, args...) +func (eg *EngineGroup) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { + return eg.Slave().QueryInterface(sqlStr, args...) } // Insert one or more records -func (ge *GroupEngine) Insert(beans ...interface{}) (int64, error) { - return ge.Master().Insert(beans...) +func (eg *EngineGroup) Insert(beans ...interface{}) (int64, error) { + return eg.Master().Insert(beans...) } // InsertOne insert only one record -func (ge *GroupEngine) InsertOne(bean interface{}) (int64, error) { - return ge.Master().InsertOne(bean) +func (eg *EngineGroup) InsertOne(bean interface{}) (int64, error) { + return eg.Master().InsertOne(bean) } // IsTableEmpty if a table has any reocrd -func (ge *GroupEngine) IsTableEmpty(bean interface{}) (bool, error) { - return ge.Master().IsTableEmpty(bean) +func (eg *EngineGroup) IsTableEmpty(bean interface{}) (bool, error) { + return eg.Master().IsTableEmpty(bean) } // IsTableExist if a table is exist -func (ge *GroupEngine) IsTableExist(beanOrTableName interface{}) (bool, error) { - return ge.Master().IsTableExist(beanOrTableName) +func (eg *EngineGroup) IsTableExist(beanOrTableName interface{}) (bool, error) { + return eg.Master().IsTableExist(beanOrTableName) } -func (ge *GroupEngine) Update(bean interface{}, condiBeans ...interface{}) (int64, error) { - return ge.Master().Update(bean, condiBeans...) +func (eg *EngineGroup) Update(bean interface{}, condiBeans ...interface{}) (int64, error) { + return eg.Master().Update(bean, condiBeans...) } // Delete records, bean's non-empty fields are conditions -func (ge *GroupEngine) Delete(bean interface{}) (int64, error) { - return ge.Master().Delete(bean) +func (eg *EngineGroup) Delete(bean interface{}) (int64, error) { + return eg.Master().Delete(bean) } // Get retrieve one record from table, bean's non-empty fields // are conditions -func (ge *GroupEngine) Get(bean interface{}) (bool, error) { - return ge.Slave().Get(bean) +func (eg *EngineGroup) Get(bean interface{}) (bool, error) { + return eg.Slave().Get(bean) } // Exist returns true if the record exist otherwise return false -func (ge *GroupEngine) Exist(bean ...interface{}) (bool, error) { - return ge.Slave().Exist(bean...) +func (eg *EngineGroup) Exist(bean ...interface{}) (bool, error) { + return eg.Slave().Exist(bean...) } // Iterate record by record handle records from table, bean's non-empty fields // are conditions. -func (ge *GroupEngine) Iterate(bean interface{}, fun IterFunc) error { - return ge.Master().Iterate(bean, fun) +func (eg *EngineGroup) Iterate(bean interface{}, fun IterFunc) error { + return eg.Master().Iterate(bean, fun) } -func (ge *GroupEngine) Find(beans interface{}, condiBeans ...interface{}) error { - return ge.Slave().Find(beans, condiBeans...) +func (eg *EngineGroup) Find(beans interface{}, condiBeans ...interface{}) error { + return eg.Slave().Find(beans, condiBeans...) } // Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields // are conditions. -func (ge *GroupEngine) Rows(bean interface{}) (*Rows, error) { - return ge.Slave().Rows(bean) +func (eg *EngineGroup) Rows(bean interface{}) (*Rows, error) { + return eg.Slave().Rows(bean) } // Count counts the records. bean's non-empty fields are conditions. -func (ge *GroupEngine) Count(bean ...interface{}) (int64, error) { - return ge.Slave().Count(bean...) +func (eg *EngineGroup) Count(bean ...interface{}) (int64, error) { + return eg.Slave().Count(bean...) } // Sum sum the records by some column. bean's non-empty fields are conditions. -func (ge *GroupEngine) Sum(bean interface{}, colName string) (float64, error) { - return ge.Slave().Sum(bean, colName) +func (eg *EngineGroup) Sum(bean interface{}, colName string) (float64, error) { + return eg.Slave().Sum(bean, colName) } // SumInt sum the records by some column. bean's non-empty fields are conditions. -func (ge *GroupEngine) SumInt(bean interface{}, colName string) (int64, error) { - return ge.Slave().SumInt(bean, colName) +func (eg *EngineGroup) SumInt(bean interface{}, colName string) (int64, error) { + return eg.Slave().SumInt(bean, colName) } // Sums sum the records by some columns. bean's non-empty fields are conditions. -func (ge *GroupEngine) Sums(bean interface{}, colNames ...string) ([]float64, error) { - return ge.Slave().Sums(bean, colNames...) +func (eg *EngineGroup) Sums(bean interface{}, colNames ...string) ([]float64, error) { + return eg.Slave().Sums(bean, colNames...) } // SumsInt like Sums but return slice of int64 instead of float64. -func (ge *GroupEngine) SumsInt(bean interface{}, colNames ...string) ([]int64, error) { - return ge.Slave().SumsInt(bean, colNames...) +func (eg *EngineGroup) SumsInt(bean interface{}, colNames ...string) ([]int64, error) { + return eg.Slave().SumsInt(bean, colNames...) } // ImportFile SQL DDL file -func (ge *GroupEngine) ImportFile(ddlPath string) ([]sql.Result, error) { - return ge.Master().ImportFile(ddlPath) +func (eg *EngineGroup) ImportFile(ddlPath string) ([]sql.Result, error) { + return eg.Master().ImportFile(ddlPath) } // Import SQL DDL from io.Reader -func (ge *GroupEngine) Import(r io.Reader) ([]sql.Result, error) { - return ge.Master().Import(r) +func (eg *EngineGroup) Import(r io.Reader) ([]sql.Result, error) { + return eg.Master().Import(r) } // NowTime2 return current time -func (ge *GroupEngine) NowTime2(sqlTypeName string) (interface{}, time.Time) { - return ge.Master().NowTime2(sqlTypeName) +func (eg *EngineGroup) NowTime2(sqlTypeName string) (interface{}, time.Time) { + return eg.Master().NowTime2(sqlTypeName) } // Unscoped always disable struct tag "deleted" -func (ge *GroupEngine) Unscoped() *GESession { - ges := ge.NewGESession() - return ges.Unscoped() +func (eg *EngineGroup) Unscoped() *EGSession { + egs := eg.NewEGSession() + return egs.Unscoped() } // CondDeleted returns the conditions whether a record is soft deleted. -func (ge *GroupEngine) CondDeleted(colName string) builder.Cond { - return ge.Master().CondDeleted(colName) +func (eg *EngineGroup) CondDeleted(colName string) builder.Cond { + return eg.Master().CondDeleted(colName) } type BufferSizeArgs struct { @@ -776,7 +881,7 @@ type BufferSizeArgs struct { } // BufferSize sets buffer size for iterate -func (ge *GroupEngine) BufferSize(size int) *GESession { - ges := ge.NewGESession() - return ges.BufferSize(size) +func (eg *EngineGroup) BufferSize(size int) *EGSession { + egs := eg.NewEGSession() + return egs.BufferSize(size) } From 6bef89a47b0943923bc5e5589a4e139895ab0077 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Tue, 26 Sep 2017 09:59:03 +0800 Subject: [PATCH 12/31] rename file name --- group_engine.go => engine_group.go | 0 ge_session.go => engine_group_session.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename group_engine.go => engine_group.go (100%) rename ge_session.go => engine_group_session.go (100%) diff --git a/group_engine.go b/engine_group.go similarity index 100% rename from group_engine.go rename to engine_group.go diff --git a/ge_session.go b/engine_group_session.go similarity index 100% rename from ge_session.go rename to engine_group_session.go From 4d30c6865af38e23fa6842d791e065639d2d81bb Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Tue, 26 Sep 2017 10:22:13 +0800 Subject: [PATCH 13/31] modify policy interface --- engine_group.go | 10 +++++----- engine_group_policy.go | 12 ++++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/engine_group.go b/engine_group.go index 341de2c7..9bbe854a 100644 --- a/engine_group.go +++ b/engine_group.go @@ -62,7 +62,7 @@ func newGroup1(driverName string, dataSourceNames string, policy ...Policy) (*En s_count: len(engines[1:]), policy: policy[0], } - eg.policy.SetEngineGroup(eg) + eg.policy.Init() return eg, nil } else { xPolicy := new(XormEngineGroupPolicy) @@ -73,7 +73,7 @@ func newGroup1(driverName string, dataSourceNames string, policy ...Policy) (*En s_count: len(engines[1:]), policy: xPolicy, } - xPolicy.SetEngineGroup(eg) + xPolicy.Init() return eg, nil } @@ -91,7 +91,7 @@ func newGroup2(Master *Engine, Slaves []*Engine, policy ...Policy) (*EngineGroup s_count: len(Slaves), policy: policy[0], } - eg.policy.SetEngineGroup(eg) + eg.policy.Init() return eg, nil } else { xPolicy := new(XormEngineGroupPolicy) @@ -102,7 +102,7 @@ func newGroup2(Master *Engine, Slaves []*Engine, policy ...Policy) (*EngineGroup s_count: len(Slaves), policy: xPolicy, } - xPolicy.SetEngineGroup(eg) + xPolicy.Init() return eg, nil } } @@ -149,7 +149,7 @@ func (eg *EngineGroup) Slave() *Engine { if eg.count == 1 { return eg.master } - return eg.slaves[eg.policy.Slave()] + return eg.policy.Slave(eg) } func (eg *EngineGroup) Slaves() []*Engine { diff --git a/engine_group_policy.go b/engine_group_policy.go index 1d55bde1..2219d9f5 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -19,8 +19,8 @@ const ( ) type Policy interface { - Slave() int - SetEngineGroup(*EngineGroup) + Init() + Slave(*EngineGroup) *Engine } type XormEngineGroupPolicy struct { @@ -30,9 +30,13 @@ type XormEngineGroupPolicy struct { r *rand.Rand } -func (xgep *XormEngineGroupPolicy) SetEngineGroup(eg *EngineGroup) { +func (xgep *XormEngineGroupPolicy) Init() { xgep.r = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +func (xgep *XormEngineGroupPolicy) Slave(eg *EngineGroup) *Engine { xgep.eg = eg + return eg.slaves[xgep.slave()] } func (xgep *XormEngineGroupPolicy) SetWeight() { @@ -44,7 +48,7 @@ func (xgep *XormEngineGroupPolicy) SetWeight() { } } -func (xgep *XormEngineGroupPolicy) Slave() int { +func (xgep *XormEngineGroupPolicy) slave() int { switch xgep.eg.p { case ENGINE_GROUP_POLICY_RANDOM: return xgep.Random() From 8517e7abd1674107204d6795cccfa1722df1f033 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Tue, 26 Sep 2017 11:02:24 +0800 Subject: [PATCH 14/31] remove Init function from policy interface --- engine_group.go | 2 -- engine_group_policy.go | 1 - 2 files changed, 3 deletions(-) diff --git a/engine_group.go b/engine_group.go index 9bbe854a..b8cdc9c8 100644 --- a/engine_group.go +++ b/engine_group.go @@ -62,7 +62,6 @@ func newGroup1(driverName string, dataSourceNames string, policy ...Policy) (*En s_count: len(engines[1:]), policy: policy[0], } - eg.policy.Init() return eg, nil } else { xPolicy := new(XormEngineGroupPolicy) @@ -91,7 +90,6 @@ func newGroup2(Master *Engine, Slaves []*Engine, policy ...Policy) (*EngineGroup s_count: len(Slaves), policy: policy[0], } - eg.policy.Init() return eg, nil } else { xPolicy := new(XormEngineGroupPolicy) diff --git a/engine_group_policy.go b/engine_group_policy.go index 2219d9f5..311ba67f 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -19,7 +19,6 @@ const ( ) type Policy interface { - Init() Slave(*EngineGroup) *Engine } From 694a7f1d9416b8cd09603d37c072d8bdf533df64 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 11:26:06 +0800 Subject: [PATCH 15/31] refactor Group Policy --- engine_group.go | 110 ++++++------------------ engine_group_policy.go | 186 +++++++++++++++++++++-------------------- 2 files changed, 120 insertions(+), 176 deletions(-) diff --git a/engine_group.go b/engine_group.go index 9bbe854a..fe51f30f 100644 --- a/engine_group.go +++ b/engine_group.go @@ -25,22 +25,29 @@ type EngineGroup struct { p int } -func NewGroup(args1 interface{}, args2 interface{}, policy ...Policy) (*EngineGroup, error) { +func NewGroup(args1 interface{}, args2 interface{}, policies ...Policy) (*EngineGroup, error) { + var policy Policy + if len(policies) > 0 { + policy = policies[0] + } else { + policy = NewRandomPolicy() + } + driverName, ok1 := args1.(string) dataSourceNames, ok2 := args2.(string) if ok1 && ok2 { - return newGroup1(driverName, dataSourceNames, policy...) + return newGroup1(driverName, dataSourceNames, policy) } Master, ok3 := args1.(*Engine) Slaves, ok4 := args2.([]*Engine) if ok3 && ok4 { - return newGroup2(Master, Slaves, policy...) + return newGroup2(Master, Slaves, policy) } return nil, ErrParamsType } -func newGroup1(driverName string, dataSourceNames string, policy ...Policy) (*EngineGroup, error) { +func newGroup1(driverName string, dataSourceNames string, policy Policy) (*EngineGroup, error) { conns := strings.Split(dataSourceNames, ";") engines := make([]*Engine, len(conns)) for i, _ := range conns { @@ -51,60 +58,23 @@ func newGroup1(driverName string, dataSourceNames string, policy ...Policy) (*En engines[i] = engine } - n := len(policy) - if n > 1 { - return nil, ErrParamsType - } else if n == 1 { - eg := &EngineGroup{ - master: engines[0], - slaves: engines[1:], - count: len(engines), - s_count: len(engines[1:]), - policy: policy[0], - } - eg.policy.Init() - return eg, nil - } else { - xPolicy := new(XormEngineGroupPolicy) - eg := &EngineGroup{ - master: engines[0], - slaves: engines[1:], - count: len(engines), - s_count: len(engines[1:]), - policy: xPolicy, - } - xPolicy.Init() - return eg, nil - } - + return &EngineGroup{ + master: engines[0], + slaves: engines[1:], + count: len(engines), + s_count: len(engines[1:]), + policy: policy, + }, nil } -func newGroup2(Master *Engine, Slaves []*Engine, policy ...Policy) (*EngineGroup, error) { - n := len(policy) - if n > 1 { - return nil, ErrParamsType - } else if n == 1 { - eg := &EngineGroup{ - master: Master, - slaves: Slaves, - count: 1 + len(Slaves), - s_count: len(Slaves), - policy: policy[0], - } - eg.policy.Init() - return eg, nil - } else { - xPolicy := new(XormEngineGroupPolicy) - eg := &EngineGroup{ - master: Master, - slaves: Slaves, - count: 1 + len(Slaves), - s_count: len(Slaves), - policy: xPolicy, - } - xPolicy.Init() - return eg, nil - } +func newGroup2(Master *Engine, Slaves []*Engine, policy Policy) (*EngineGroup, error) { + return &EngineGroup{ + master: Master, + slaves: Slaves, + count: 1 + len(Slaves), + s_count: len(Slaves), + policy: policy, + }, nil } func (eg *EngineGroup) SetPolicy(policy Policy) *EngineGroup { @@ -112,34 +82,6 @@ func (eg *EngineGroup) SetPolicy(policy Policy) *EngineGroup { return eg } -func (eg *EngineGroup) UsePolicy(policy int) *EngineGroup { - eg.p = policy - return eg -} - -func (eg *EngineGroup) SetWeight(weight ...interface{}) *EngineGroup { - l := len(weight) - if l == 1 { - switch weight[0].(type) { - case []int: - eg.weight = weight[0].([]int) - } - } else if l > 1 { - s := make([]int, 0) - for i, _ := range weight { - switch weight[i].(type) { - case int: - s = append(s, weight[i].(int)) - default: - s = append(s, 1) - } - } - eg.weight = s - } - - return eg -} - func (eg *EngineGroup) Master() *Engine { return eg.master } diff --git a/engine_group_policy.go b/engine_group_policy.go index 2219d9f5..bd4ba4ae 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -6,129 +6,131 @@ package xorm import ( "math/rand" + "sync" "time" ) -const ( - ENGINE_GROUP_POLICY_RANDOM = iota - ENGINE_GROUP_POLICY_WEIGHTRANDOM - ENGINE_GROUP_POLICY_ROUNDROBIN - ENGINE_GROUP_POLICY_WEIGHTROUNDROBIN - ENGINE_GROUP_POLICY_LEASTCONNECTIONS -) - type Policy interface { - Init() Slave(*EngineGroup) *Engine } -type XormEngineGroupPolicy struct { - pos int - slaves []int - eg *EngineGroup - r *rand.Rand +type RandomPolicy struct { + r *rand.Rand } -func (xgep *XormEngineGroupPolicy) Init() { - xgep.r = rand.New(rand.NewSource(time.Now().UnixNano())) +func NewRandomPolicy() *RandomPolicy { + return &RandomPolicy{ + r: rand.New(rand.NewSource(time.Now().UnixNano())), + } } -func (xgep *XormEngineGroupPolicy) Slave(eg *EngineGroup) *Engine { - xgep.eg = eg - return eg.slaves[xgep.slave()] +func (policy *RandomPolicy) Slave(g *EngineGroup) *Engine { + return g.Slaves()[policy.r.Intn(len(g.Slaves()))] } -func (xgep *XormEngineGroupPolicy) SetWeight() { - for i, _ := range xgep.eg.weight { - w := xgep.eg.weight[i] - for n := 0; n < w; n++ { - xgep.slaves = append(xgep.slaves, i) +type WeightRandomPolicy struct { + weights []int + rands []int + r *rand.Rand +} + +func NewWeightRandomPolicy(weights []int) *WeightRandomPolicy { + 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) } } + + return &WeightRandomPolicy{ + weights: weights, + rands: rands, + r: rand.New(rand.NewSource(time.Now().UnixNano())), + } } -func (xgep *XormEngineGroupPolicy) slave() int { - switch xgep.eg.p { - case ENGINE_GROUP_POLICY_RANDOM: - return xgep.Random() - case ENGINE_GROUP_POLICY_WEIGHTRANDOM: - return xgep.WeightRandom() - case ENGINE_GROUP_POLICY_ROUNDROBIN: - return xgep.RoundRobin() - case ENGINE_GROUP_POLICY_WEIGHTROUNDROBIN: - return xgep.WeightRoundRobin() - case ENGINE_GROUP_POLICY_LEASTCONNECTIONS: - return xgep.LeastConnections() - default: - return xgep.Random() +func (policy *WeightRandomPolicy) Slave(g *EngineGroup) *Engine { + var slaves = g.Slaves() + idx := policy.rands[policy.r.Intn(len(policy.rands))] + if idx >= len(slaves) { + idx = len(slaves) - 1 } - + return slaves[idx] } -func (xgep *XormEngineGroupPolicy) Random() int { - if xgep.eg.s_count <= 1 { - return 0 - } - - rnd := xgep.r.Intn(xgep.eg.s_count) - return rnd +type RoundRobinPolicy struct { + pos int + lock sync.Mutex } -func (xgep *XormEngineGroupPolicy) WeightRandom() int { - if xgep.eg.s_count <= 1 { - return 0 - } - - xgep.SetWeight() - s := len(xgep.slaves) - rnd := xgep.r.Intn(s) - return xgep.slaves[rnd] +func NewRoundRobinPolicy() *RoundRobinPolicy { + return &RoundRobinPolicy{pos: -1} } -func (xgep *XormEngineGroupPolicy) RoundRobin() int { - if xgep.eg.s_count <= 1 { - return 0 +func (policy *RoundRobinPolicy) Slave(g *EngineGroup) *Engine { + var pos int + policy.lock.Lock() + policy.pos++ + if policy.pos >= len(g.Slaves()) { + policy.pos = 0 } + pos = policy.pos + policy.lock.Unlock() - if xgep.pos >= xgep.eg.s_count { - xgep.pos = 0 - } - xgep.pos++ - - return xgep.pos - 1 + return g.Slaves()[pos] } -func (xgep *XormEngineGroupPolicy) WeightRoundRobin() int { - if xgep.eg.s_count <= 1 { - return 0 - } - - xgep.SetWeight() - count := len(xgep.slaves) - if xgep.pos >= count { - xgep.pos = 0 - } - xgep.pos++ - - return xgep.slaves[xgep.pos-1] +type WeightRoundRobin struct { + weights []int + rands []int + r *rand.Rand + lock sync.Mutex + pos int } -func (xgep *XormEngineGroupPolicy) LeastConnections() int { - if xgep.eg.s_count <= 1 { - return 0 - } - connections := 0 - slave := 0 - for i, _ := range xgep.eg.slaves { - open_connections := xgep.eg.slaves[i].Stats() - if i == 0 { - connections = open_connections - slave = i - } else if open_connections <= connections { - slave = i - connections = open_connections +func NewWeightRoundRobin(weights []int) *WeightRoundRobin { + 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) } } - return slave + + return &WeightRoundRobin{ + weights: weights, + rands: rands, + r: rand.New(rand.NewSource(time.Now().UnixNano())), + pos: -1, + } +} + +func (policy *WeightRoundRobin) Slave(g *EngineGroup) *Engine { + var slaves = g.Slaves() + var pos int + policy.lock.Lock() + policy.pos++ + if policy.pos >= len(g.Slaves()) { + policy.pos = 0 + } + pos = policy.pos + policy.lock.Unlock() + + idx := policy.rands[pos] + if idx >= len(slaves) { + idx = len(slaves) - 1 + } + return slaves[idx] +} + +type LeastConnPolicy struct { +} + +func NewLeastConnPolicy() *LeastConnPolicy { + return &LeastConnPolicy{} +} + +func (policy *LeastConnPolicy) Slave(g *EngineGroup) *Engine { + panic("not implementation") + return nil } From 8353ce81e901e6dbf057f8c540d34e82e3308f08 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 11:29:41 +0800 Subject: [PATCH 16/31] rename and comments --- engine_group.go | 14 +++++++------- engine_group_policy.go | 5 ++--- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/engine_group.go b/engine_group.go index fe51f30f..88d0db0f 100644 --- a/engine_group.go +++ b/engine_group.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. +// 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. @@ -21,12 +21,12 @@ type EngineGroup struct { weight []int count int s_count int - policy Policy + policy GroupPolicy p int } -func NewGroup(args1 interface{}, args2 interface{}, policies ...Policy) (*EngineGroup, error) { - var policy Policy +func NewGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) { + var policy GroupPolicy if len(policies) > 0 { policy = policies[0] } else { @@ -47,7 +47,7 @@ func NewGroup(args1 interface{}, args2 interface{}, policies ...Policy) (*Engine return nil, ErrParamsType } -func newGroup1(driverName string, dataSourceNames string, policy Policy) (*EngineGroup, error) { +func newGroup1(driverName string, dataSourceNames string, policy GroupPolicy) (*EngineGroup, error) { conns := strings.Split(dataSourceNames, ";") engines := make([]*Engine, len(conns)) for i, _ := range conns { @@ -67,7 +67,7 @@ func newGroup1(driverName string, dataSourceNames string, policy Policy) (*Engin }, nil } -func newGroup2(Master *Engine, Slaves []*Engine, policy Policy) (*EngineGroup, error) { +func newGroup2(Master *Engine, Slaves []*Engine, policy GroupPolicy) (*EngineGroup, error) { return &EngineGroup{ master: Master, slaves: Slaves, @@ -77,7 +77,7 @@ func newGroup2(Master *Engine, Slaves []*Engine, policy Policy) (*EngineGroup, e }, nil } -func (eg *EngineGroup) SetPolicy(policy Policy) *EngineGroup { +func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup { eg.policy = policy return eg } diff --git a/engine_group_policy.go b/engine_group_policy.go index bd4ba4ae..f2efca40 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. +// 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. @@ -7,11 +7,10 @@ package xorm import ( "math/rand" "sync" - "time" ) -type Policy interface { +type GroupPolicy interface { Slave(*EngineGroup) *Engine } From 3f4ec27ef7d90f4a47a65e5e24701b2cca9f193e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 11:36:12 +0800 Subject: [PATCH 17/31] rename and bug fix for WeightRoundRobinPolicy --- engine_group_policy.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engine_group_policy.go b/engine_group_policy.go index f2efca40..88412eaf 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -80,7 +80,7 @@ func (policy *RoundRobinPolicy) Slave(g *EngineGroup) *Engine { return g.Slaves()[pos] } -type WeightRoundRobin struct { +type WeightRoundRobinPolicy struct { weights []int rands []int r *rand.Rand @@ -88,7 +88,7 @@ type WeightRoundRobin struct { pos int } -func NewWeightRoundRobin(weights []int) *WeightRoundRobin { +func NewWeightRoundRobinPolicy(weights []int) *WeightRoundRobinPolicy { var rands = make([]int, 0, len(weights)) for i := 0; i < len(weights); i++ { for n := 0; n < weights[i]; n++ { @@ -96,7 +96,7 @@ func NewWeightRoundRobin(weights []int) *WeightRoundRobin { } } - return &WeightRoundRobin{ + return &WeightRoundRobinPolicy{ weights: weights, rands: rands, r: rand.New(rand.NewSource(time.Now().UnixNano())), @@ -104,12 +104,12 @@ func NewWeightRoundRobin(weights []int) *WeightRoundRobin { } } -func (policy *WeightRoundRobin) Slave(g *EngineGroup) *Engine { +func (policy *WeightRoundRobinPolicy) Slave(g *EngineGroup) *Engine { var slaves = g.Slaves() var pos int policy.lock.Lock() policy.pos++ - if policy.pos >= len(g.Slaves()) { + if policy.pos >= len(policy.rands) { policy.pos = 0 } pos = policy.pos From 52a84b29b7e97dcda44aef768fc289a81530c76a Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Tue, 26 Sep 2017 12:07:49 +0800 Subject: [PATCH 18/31] modify Slave function --- engine_group_policy.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/engine_group_policy.go b/engine_group_policy.go index 88412eaf..e05663bc 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -25,6 +25,9 @@ func NewRandomPolicy() *RandomPolicy { } func (policy *RandomPolicy) Slave(g *EngineGroup) *Engine { + if g.s_count == 1 { + return g.Slaves()[0] + } return g.Slaves()[policy.r.Intn(len(g.Slaves()))] } @@ -51,6 +54,9 @@ func NewWeightRandomPolicy(weights []int) *WeightRandomPolicy { func (policy *WeightRandomPolicy) Slave(g *EngineGroup) *Engine { var slaves = g.Slaves() + if g.s_count == 1 { + return slaves[0] + } idx := policy.rands[policy.r.Intn(len(policy.rands))] if idx >= len(slaves) { idx = len(slaves) - 1 @@ -68,10 +74,14 @@ func NewRoundRobinPolicy() *RoundRobinPolicy { } func (policy *RoundRobinPolicy) Slave(g *EngineGroup) *Engine { + if g.s_count == 1 { + return g.Slaves()[0] + } + var pos int policy.lock.Lock() policy.pos++ - if policy.pos >= len(g.Slaves()) { + if policy.pos >= g.s_count { policy.pos = 0 } pos = policy.pos @@ -106,6 +116,10 @@ func NewWeightRoundRobinPolicy(weights []int) *WeightRoundRobinPolicy { func (policy *WeightRoundRobinPolicy) Slave(g *EngineGroup) *Engine { var slaves = g.Slaves() + if g.s_count == 1 { + return slaves[0] + } + var pos int policy.lock.Lock() policy.pos++ From d8947626190d9abb8d3aadb39cf30a48fc460a33 Mon Sep 17 00:00:00 2001 From: WhiteBatman Date: Tue, 26 Sep 2017 12:32:21 +0800 Subject: [PATCH 19/31] modify Slave function and add LeastConnPolicy --- engine_group.go | 3 +++ engine_group_policy.go | 30 ++++++++++++++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/engine_group.go b/engine_group.go index 88d0db0f..093d48aa 100644 --- a/engine_group.go +++ b/engine_group.go @@ -91,6 +91,9 @@ func (eg *EngineGroup) Slave() *Engine { if eg.count == 1 { return eg.master } + if eg.s_count == 1 { + return eg.slaves[0] + } return eg.policy.Slave(eg) } diff --git a/engine_group_policy.go b/engine_group_policy.go index e05663bc..c4fdcb0d 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -25,9 +25,6 @@ func NewRandomPolicy() *RandomPolicy { } func (policy *RandomPolicy) Slave(g *EngineGroup) *Engine { - if g.s_count == 1 { - return g.Slaves()[0] - } return g.Slaves()[policy.r.Intn(len(g.Slaves()))] } @@ -54,9 +51,6 @@ func NewWeightRandomPolicy(weights []int) *WeightRandomPolicy { func (policy *WeightRandomPolicy) Slave(g *EngineGroup) *Engine { var slaves = g.Slaves() - if g.s_count == 1 { - return slaves[0] - } idx := policy.rands[policy.r.Intn(len(policy.rands))] if idx >= len(slaves) { idx = len(slaves) - 1 @@ -74,10 +68,6 @@ func NewRoundRobinPolicy() *RoundRobinPolicy { } func (policy *RoundRobinPolicy) Slave(g *EngineGroup) *Engine { - if g.s_count == 1 { - return g.Slaves()[0] - } - var pos int policy.lock.Lock() policy.pos++ @@ -116,10 +106,6 @@ func NewWeightRoundRobinPolicy(weights []int) *WeightRoundRobinPolicy { func (policy *WeightRoundRobinPolicy) Slave(g *EngineGroup) *Engine { var slaves = g.Slaves() - if g.s_count == 1 { - return slaves[0] - } - var pos int policy.lock.Lock() policy.pos++ @@ -144,6 +130,18 @@ func NewLeastConnPolicy() *LeastConnPolicy { } func (policy *LeastConnPolicy) Slave(g *EngineGroup) *Engine { - panic("not implementation") - return nil + var slaves = g.Slaves() + connections := 0 + idx := 0 + for i, _ := range slaves { + open_connections := slaves[i].Stats() + if i == 0 { + connections = open_connections + idx = i + } else if open_connections <= connections { + connections = open_connections + idx = i + } + } + return slaves[idx] } From e131ff6355c2cf8b908a07f5b1573408b31abc17 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 12:50:49 +0800 Subject: [PATCH 20/31] use original Engine and Session --- engine.go | 2 + engine_group.go | 754 +++------------------------------------ engine_group_session.go | 764 ---------------------------------------- session.go | 4 +- session_raw.go | 13 +- 5 files changed, 67 insertions(+), 1470 deletions(-) delete mode 100644 engine_group_session.go diff --git a/engine.go b/engine.go index 5fccf6c1..9e7053ab 100644 --- a/engine.go +++ b/engine.go @@ -47,6 +47,8 @@ type Engine struct { disableGlobalCache bool tagHandlers map[string]tagHandler + + engineGroup *EngineGroup } // ShowSQL show SQL statement or not on logger if log level is great than INFO diff --git a/engine_group.go b/engine_group.go index 88d0db0f..dd1e6380 100644 --- a/engine_group.go +++ b/engine_group.go @@ -5,122 +5,88 @@ package xorm import ( - "database/sql" - "io" - "reflect" "strings" "time" - "github.com/go-xorm/builder" "github.com/go-xorm/core" ) type EngineGroup struct { - master *Engine - slaves []*Engine - weight []int - count int - s_count int - policy GroupPolicy - p int + *Engine + slaves []*Engine + policy GroupPolicy } func NewGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) { - var policy GroupPolicy + var eg EngineGroup if len(policies) > 0 { - policy = policies[0] + eg.policy = policies[0] } else { - policy = NewRandomPolicy() + eg.policy = NewRandomPolicy() } driverName, ok1 := args1.(string) dataSourceNames, ok2 := args2.(string) if ok1 && ok2 { - return newGroup1(driverName, dataSourceNames, policy) + conns := strings.Split(dataSourceNames, ";") + 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) + master, ok3 := args1.(*Engine) + slaves, ok4 := args2.([]*Engine) if ok3 && ok4 { - return newGroup2(Master, Slaves, policy) + master.engineGroup = &eg + for i := 0; i < len(slaves); i++ { + slaves[i].engineGroup = &eg + } + return &eg, nil } return nil, ErrParamsType } -func newGroup1(driverName string, dataSourceNames string, policy GroupPolicy) (*EngineGroup, error) { - conns := strings.Split(dataSourceNames, ";") - engines := make([]*Engine, len(conns)) - for i, _ := range conns { - engine, err := NewEngine(driverName, conns[i]) - if err != nil { - return nil, err - } - engines[i] = engine - } - - return &EngineGroup{ - master: engines[0], - slaves: engines[1:], - count: len(engines), - s_count: len(engines[1:]), - policy: policy, - }, nil -} - -func newGroup2(Master *Engine, Slaves []*Engine, policy GroupPolicy) (*EngineGroup, error) { - return &EngineGroup{ - master: Master, - slaves: Slaves, - count: 1 + len(Slaves), - s_count: len(Slaves), - policy: policy, - }, nil -} - func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup { eg.policy = policy return eg } func (eg *EngineGroup) Master() *Engine { - return eg.master + return eg.Engine } // Slave returns one of the physical databases which is a slave func (eg *EngineGroup) Slave() *Engine { - if eg.count == 1 { - return eg.master + switch len(eg.slaves) { + case 0: + return eg.Engine + case 1: + return eg.slaves[0] } return eg.policy.Slave(eg) } func (eg *EngineGroup) Slaves() []*Engine { - if eg.count == 1 { - return []*Engine{eg.master} - } return eg.slaves } func (eg *EngineGroup) GetSlave(i int) *Engine { - if eg.count == 1 || i == 0 { - return eg.master - } - if i > eg.s_count { - return eg.slaves[0] - } return eg.slaves[i] } -func (eg *EngineGroup) GetEngine(i int) *Engine { - if i >= eg.count || i == 0 { - return eg.master - } - return eg.slaves[i-1] -} - // ShowSQL show SQL statement or not on logger if log level is great than INFO func (eg *EngineGroup) ShowSQL(show ...bool) { - eg.master.ShowSQL(show...) + eg.Engine.ShowSQL(show...) for i, _ := range eg.slaves { eg.slaves[i].ShowSQL(show...) } @@ -128,7 +94,7 @@ func (eg *EngineGroup) ShowSQL(show ...bool) { // 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.master.ShowExecTime(show...) + eg.Engine.ShowExecTime(show...) for i, _ := range eg.slaves { eg.slaves[i].ShowExecTime(show...) } @@ -136,8 +102,8 @@ func (eg *EngineGroup) ShowExecTime(show ...bool) { // SetMapper set the name mapping rules func (eg *EngineGroup) SetMapper(mapper core.IMapper) { - eg.master.SetTableMapper(mapper) - eg.master.SetColumnMapper(mapper) + eg.Engine.SetTableMapper(mapper) + eg.Engine.SetColumnMapper(mapper) for i, _ := range eg.slaves { eg.slaves[i].SetTableMapper(mapper) eg.slaves[i].SetColumnMapper(mapper) @@ -146,7 +112,7 @@ func (eg *EngineGroup) SetMapper(mapper core.IMapper) { // SetTableMapper set the table name mapping rule func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) { - eg.master.TableMapper = mapper + eg.Engine.TableMapper = mapper for i, _ := range eg.slaves { eg.slaves[i].TableMapper = mapper } @@ -154,7 +120,7 @@ func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) { // SetColumnMapper set the column name mapping rule func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { - eg.master.ColumnMapper = mapper + eg.Engine.ColumnMapper = mapper for i, _ := range eg.slaves { eg.slaves[i].ColumnMapper = mapper } @@ -162,7 +128,7 @@ func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { // SetMaxOpenConns is only available for go 1.2+ func (eg *EngineGroup) SetMaxOpenConns(conns int) { - eg.master.db.SetMaxOpenConns(conns) + eg.Engine.db.SetMaxOpenConns(conns) for i, _ := range eg.slaves { eg.slaves[i].db.SetMaxOpenConns(conns) } @@ -170,21 +136,15 @@ func (eg *EngineGroup) SetMaxOpenConns(conns int) { // SetMaxIdleConns set the max idle connections on pool, default is 2 func (eg *EngineGroup) SetMaxIdleConns(conns int) { - eg.master.db.SetMaxIdleConns(conns) + eg.Engine.db.SetMaxIdleConns(conns) for i, _ := range eg.slaves { eg.slaves[i].db.SetMaxIdleConns(conns) } } -// NoCascade If you do not want to auto cascade load object -func (eg *EngineGroup) NoCascade() *EGSession { - egs := eg.NewEGSession() - return egs.NoCascade() -} - // Close the engine func (eg *EngineGroup) Close() error { - err := eg.master.db.Close() + err := eg.Engine.db.Close() if err != nil { return err } @@ -200,630 +160,22 @@ func (eg *EngineGroup) Close() error { // Ping tests if database is alive func (eg *EngineGroup) Ping() error { - eg.master.Ping() - return scatter(eg.s_count, func(i int) error { - return eg.slaves[i].Ping() - }) + if err := eg.Engine.Ping(); err != nil { + return err + } + + for _, slave := range eg.slaves { + if err := slave.Ping(); err != nil { + return err + } + } + return nil } // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { - eg.master.db.SetConnMaxLifetime(d) + eg.Engine.db.SetConnMaxLifetime(d) for i, _ := range eg.slaves { eg.slaves[i].db.SetConnMaxLifetime(d) } } - -func scatter(n int, fn func(i int) error) error { - errors := make(chan error, n) - - var i int - for i = 0; i < n; i++ { - go func(i int) { errors <- fn(i) }(i) - } - - var err, innerErr error - for i = 0; i < cap(errors); i++ { - if innerErr = <-errors; innerErr != nil { - err = innerErr - } - } - - return err -} - -// SqlType will be deprecated, please use SQLType instead -// -// Deprecated: use SQLType instead -func (eg *EngineGroup) SqlType(c *core.Column) string { - return eg.Master().SQLType(c) -} - -// SQLType A simple wrapper to dialect's core.SqlType method -func (eg *EngineGroup) SQLType(c *core.Column) string { - return eg.Master().dialect.SqlType(c) -} - -// NewSession New a session -func (eg *EngineGroup) NewSession() *Session { - return eg.Master().NewSession() -} - -// NewSession New a session -func (eg *EngineGroup) NewEGSession() *EGSession { - args := make(map[string]interface{}) - egs := &EGSession{eg: eg, operation: []string{}, args: args} - return egs -} - -type SqlArgs struct { - query string - args []interface{} -} - -func (eg *EngineGroup) Sql(query string, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.Sql(query, args...) -} - -func (eg *EngineGroup) SQL(query interface{}, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.SQL(query, args...) -} - -// NoAutoTime Default if your struct has "created" or "updated" filed tag, the fields -// will automatically be filled with current time when Insert or Update -// invoked. Call NoAutoTime if you dont' want to fill automatically. -func (eg *EngineGroup) NoAutoTime() *EGSession { - egs := eg.NewEGSession() - return egs.NoAutoTime() -} - -type NoAutoConditionArgs struct { - no []bool -} - -// NoAutoCondition disable auto generate Where condition from bean or not -func (eg *EngineGroup) NoAutoCondition(no ...bool) *EGSession { - egs := eg.NewEGSession() - return egs.NoAutoCondition(no...) -} - -// DBMetas Retrieve all tables, columns, indexes' informations from database. -func (eg *EngineGroup) DBMetas() ([]*core.Table, error) { - return eg.Master().DBMetas() -} - -// DumpAllToFile dump database all table structs and data to a file -func (eg *EngineGroup) DumpAllToFile(fp string, tp ...core.DbType) error { - return eg.Master().DumpAllToFile(fp, tp...) -} - -// DumpAll dump database all table structs and data to w -func (eg *EngineGroup) DumpAll(w io.Writer, tp ...core.DbType) error { - return eg.Master().DumpAll(w, tp...) -} - -// DumpTablesToFile dump specified tables to SQL file. -func (eg *EngineGroup) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error { - return eg.Master().DumpTablesToFile(tables, fp, tp...) -} - -// DumpTables dump specify tables to io.Writer -func (eg *EngineGroup) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error { - return eg.Master().DumpTables(tables, w, tp...) -} - -type CascadeArgs struct { - trueOrFalse []bool -} - -// Cascade use cascade or not -func (eg *EngineGroup) Cascade(trueOrFalse ...bool) *EGSession { - egs := eg.NewEGSession() - return egs.Cascade(trueOrFalse...) -} - -type WhereArgs struct { - query interface{} - args []interface{} -} - -// Where method provide a condition query -func (eg *EngineGroup) Where(query interface{}, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.Where(query, args...) -} - -type IdArgs struct { - id interface{} -} - -// Id will be deprecated, please use ID instead -func (eg *EngineGroup) Id(id interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.Id(id) -} - -type IDArgs struct { - id interface{} -} - -// ID method provoide a condition as (id) = ? -func (eg *EngineGroup) ID(id interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.ID(id) -} - -type BeforeArgs struct { - closures func(interface{}) -} - -// Before apply before Processor, affected bean is passed to closure arg -func (eg *EngineGroup) Before(closures func(interface{})) *EGSession { - egs := eg.NewEGSession() - return egs.Before(closures) -} - -type AfterArgs struct { - closures func(interface{}) -} - -// After apply after insert Processor, affected bean is passed to closure arg -func (eg *EngineGroup) After(closures func(interface{})) *EGSession { - egs := eg.NewEGSession() - return egs.After(closures) -} - -type CharsetArgs struct { - charset string -} - -// Charset set charset when create table, only support mysql now -func (eg *EngineGroup) Charset(charset string) *EGSession { - egs := eg.NewEGSession() - return egs.Charset(charset) -} - -type StoreEngineArgs struct { - storeEngine string -} - -// StoreEngine set store engine when create table, only support mysql now -func (eg *EngineGroup) StoreEngine(storeEngine string) *EGSession { - egs := eg.NewEGSession() - return egs.StoreEngine(storeEngine) -} - -type DistinctArgs struct { - columns []string -} - -// Distinct use for distinct columns. Caution: when you are using cache, -// distinct will not be cached because cache system need id, -// but distinct will not provide id -func (eg *EngineGroup) Distinct(columns ...string) *EGSession { - egs := eg.NewEGSession() - return egs.Distinct(columns...) -} - -type SelectArgs struct { - str string -} - -// Select customerize your select columns or contents -func (eg *EngineGroup) Select(str string) *EGSession { - egs := eg.NewEGSession() - return egs.Select(str) -} - -type ColsArgs struct { - columns []string -} - -// Cols only use the parameters as select or update columns -func (eg *EngineGroup) Cols(columns ...string) *EGSession { - egs := eg.NewEGSession() - return egs.Cols(columns...) -} - -// AllCols indicates that all columns should be use -func (eg *EngineGroup) AllCols() *EGSession { - egs := eg.NewEGSession() - return egs.AllCols() -} - -type MustColsArgs struct { - columns []string -} - -// MustCols specify some columns must use even if they are empty -func (eg *EngineGroup) MustCols(columns ...string) *EGSession { - egs := eg.NewEGSession() - return egs.MustCols(columns...) -} - -type UseBoolArgs struct { - columns []string -} - -// UseBool xorm automatically retrieve condition according struct, but -// if struct has bool field, it will ignore them. So use UseBool -// to tell system to do not ignore them. -// If no parameters, it will use all the bool field of struct, or -// it will use parameters's columns -func (eg *EngineGroup) UseBool(columns ...string) *EGSession { - egs := eg.NewEGSession() - return egs.UseBool(columns...) -} - -type OmitArgs struct { - columns []string -} - -// Omit only not use the parameters as select or update columns -func (eg *EngineGroup) Omit(columns ...string) *EGSession { - egs := eg.NewEGSession() - return egs.Omit(columns...) -} - -type NullableArgs struct { - columns []string -} - -// Nullable set null when column is zero-value and nullable for update -func (eg *EngineGroup) Nullable(columns ...string) *EGSession { - egs := eg.NewEGSession() - return egs.Nullable(columns...) -} - -type InArgs struct { - column string - args []interface{} -} - -// In will generate "column IN (?, ?)" -func (eg *EngineGroup) In(column string, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.In(column, args...) -} - -type NotInArgs struct { - column string - args []interface{} -} - -// NotIn will generate "column NOT IN (?, ?)" -func (eg *EngineGroup) NotIn(column string, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.NotIn(column, args...) -} - -type IncrArgs struct { - column string - args []interface{} -} - -// Incr provides a update string like "column = column + ?" -func (eg *EngineGroup) Incr(column string, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.Incr(column, args...) -} - -type DecrArgs struct { - column string - args []interface{} -} - -// Decr provides a update string like "column = column - ?" -func (eg *EngineGroup) Decr(column string, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.Decr(column, args...) -} - -type SetExprArgs struct { - column string - expression string -} - -// SetExpr provides a update string like "column = {expression}" -func (eg *EngineGroup) SetExpr(column string, expression string) *EGSession { - egs := eg.NewEGSession() - return egs.SetExpr(column, expression) -} - -type TableArgs struct { - tableNameOrBean interface{} -} - -// Table temporarily change the Get, Find, Update's table -func (eg *EngineGroup) Table(tableNameOrBean interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.Table(tableNameOrBean) -} - -type AliasArgs struct { - alias string -} - -// Alias set the table alias -func (eg *EngineGroup) Alias(alias string) *EGSession { - egs := eg.NewEGSession() - return egs.Alias(alias) -} - -type LimitArgs struct { - limit int - start []int -} - -// Limit will generate "LIMIT start, limit" -func (eg *EngineGroup) Limit(limit int, start ...int) *EGSession { - egs := eg.NewEGSession() - return egs.Limit(limit, start...) -} - -type DescArgs struct { - colNames []string -} - -// Desc will generate "ORDER BY column1 DESC, column2 DESC" -func (eg *EngineGroup) Desc(colNames ...string) *EGSession { - egs := eg.NewEGSession() - return egs.Desc(colNames...) -} - -type AscArgs struct { - colNames []string -} - -// Asc will generate "ORDER BY column1,column2 Asc" -// This method can chainable use. -// -// engine.Desc("name").Asc("age").Find(&users) -// // SELECT * FROM user ORDER BY name DESC, age ASC -// -func (eg *EngineGroup) Asc(colNames ...string) *EGSession { - egs := eg.NewEGSession() - return egs.Asc(colNames...) -} - -type OrderByArgs struct { - order string -} - -// OrderBy will generate "ORDER BY order" -func (eg *EngineGroup) OrderBy(order string) *EGSession { - egs := eg.NewEGSession() - return egs.OrderBy(order) -} - -type JoinArgs struct { - joinOperator string - tablename interface{} - condition string - args []interface{} -} - -// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (eg *EngineGroup) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *EGSession { - egs := eg.NewEGSession() - return egs.Join(joinOperator, tablename, condition, args...) -} - -type GroupByArgs struct { - keys string -} - -// GroupBy generate group by statement -func (eg *EngineGroup) GroupBy(keys string) *EGSession { - egs := eg.NewEGSession() - return egs.GroupBy(keys) -} - -type HavingArgs struct { - conditions string -} - -// Having generate having statement -func (eg *EngineGroup) Having(conditions string) *EGSession { - egs := eg.NewEGSession() - return egs.Having(conditions) -} - -// IdOf get id from one struct -// -// Deprecated: use IDOf instead. -func (eg *EngineGroup) IdOf(bean interface{}) core.PK { - return eg.Master().IdOf(bean) -} - -// IDOf get id from one struct -func (eg *EngineGroup) IDOf(bean interface{}) core.PK { - return eg.Master().IDOf(bean) -} - -// IdOfV get id from one value of struct -// -// Deprecated: use IDOfV instead. -func (eg *EngineGroup) IdOfV(rv reflect.Value) core.PK { - return eg.Master().IdOfV(rv) -} - -// IDOfV get id from one value of struct -func (eg *EngineGroup) IDOfV(rv reflect.Value) core.PK { - return eg.Master().IDOfV(rv) -} - -// CreateIndexes create indexes -func (eg *EngineGroup) CreateIndexes(bean interface{}) error { - return eg.Master().CreateIndexes(bean) -} - -// CreateUniques create uniques -func (eg *EngineGroup) CreateUniques(bean interface{}) error { - return eg.Master().CreateUniques(bean) -} - -// Sync the new struct changes to database, this method will automatically add -// table, column, index, unique. but will not delete or change anything. -// If you change some field, you should change the database manually. -func (eg *EngineGroup) Sync(beans ...interface{}) error { - return eg.Master().Sync(beans...) -} - -// Sync2 synchronize structs to database tables -func (eg *EngineGroup) Sync2(beans ...interface{}) error { - return eg.Master().Sync2(beans...) -} - -// CreateTables create tabls according bean -func (eg *EngineGroup) CreateTables(beans ...interface{}) error { - return eg.Master().CreateTables(beans...) -} - -// DropTables drop specify tables -func (eg *EngineGroup) DropTables(beans ...interface{}) error { - return eg.Master().DropTables(beans...) -} - -// DropIndexes drop indexes of a table -func (eg *EngineGroup) DropIndexes(bean interface{}) error { - return eg.Master().DropIndexes(bean) -} - -func (eg *EngineGroup) Exec(sql string, args ...interface{}) (sql.Result, error) { - return eg.Master().Exec(sql, args...) -} - -// Query a raw sql and return records as []map[string][]byte -func (eg *EngineGroup) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { - return eg.Slave().Query(sql, paramStr...) -} - -// QueryString runs a raw sql and return records as []map[string]string -func (eg *EngineGroup) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { - return eg.Slave().QueryString(sqlStr, args...) -} - -// QueryInterface runs a raw sql and return records as []map[string]interface{} -func (eg *EngineGroup) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { - return eg.Slave().QueryInterface(sqlStr, args...) -} - -// Insert one or more records -func (eg *EngineGroup) Insert(beans ...interface{}) (int64, error) { - return eg.Master().Insert(beans...) -} - -// InsertOne insert only one record -func (eg *EngineGroup) InsertOne(bean interface{}) (int64, error) { - return eg.Master().InsertOne(bean) -} - -// IsTableEmpty if a table has any reocrd -func (eg *EngineGroup) IsTableEmpty(bean interface{}) (bool, error) { - return eg.Master().IsTableEmpty(bean) -} - -// IsTableExist if a table is exist -func (eg *EngineGroup) IsTableExist(beanOrTableName interface{}) (bool, error) { - return eg.Master().IsTableExist(beanOrTableName) -} - -func (eg *EngineGroup) Update(bean interface{}, condiBeans ...interface{}) (int64, error) { - return eg.Master().Update(bean, condiBeans...) -} - -// Delete records, bean's non-empty fields are conditions -func (eg *EngineGroup) Delete(bean interface{}) (int64, error) { - return eg.Master().Delete(bean) -} - -// Get retrieve one record from table, bean's non-empty fields -// are conditions -func (eg *EngineGroup) Get(bean interface{}) (bool, error) { - return eg.Slave().Get(bean) -} - -// Exist returns true if the record exist otherwise return false -func (eg *EngineGroup) Exist(bean ...interface{}) (bool, error) { - return eg.Slave().Exist(bean...) -} - -// Iterate record by record handle records from table, bean's non-empty fields -// are conditions. -func (eg *EngineGroup) Iterate(bean interface{}, fun IterFunc) error { - return eg.Master().Iterate(bean, fun) -} - -func (eg *EngineGroup) Find(beans interface{}, condiBeans ...interface{}) error { - return eg.Slave().Find(beans, condiBeans...) -} - -// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields -// are conditions. -func (eg *EngineGroup) Rows(bean interface{}) (*Rows, error) { - return eg.Slave().Rows(bean) -} - -// Count counts the records. bean's non-empty fields are conditions. -func (eg *EngineGroup) Count(bean ...interface{}) (int64, error) { - return eg.Slave().Count(bean...) -} - -// Sum sum the records by some column. bean's non-empty fields are conditions. -func (eg *EngineGroup) Sum(bean interface{}, colName string) (float64, error) { - return eg.Slave().Sum(bean, colName) -} - -// SumInt sum the records by some column. bean's non-empty fields are conditions. -func (eg *EngineGroup) SumInt(bean interface{}, colName string) (int64, error) { - return eg.Slave().SumInt(bean, colName) -} - -// Sums sum the records by some columns. bean's non-empty fields are conditions. -func (eg *EngineGroup) Sums(bean interface{}, colNames ...string) ([]float64, error) { - return eg.Slave().Sums(bean, colNames...) -} - -// SumsInt like Sums but return slice of int64 instead of float64. -func (eg *EngineGroup) SumsInt(bean interface{}, colNames ...string) ([]int64, error) { - return eg.Slave().SumsInt(bean, colNames...) -} - -// ImportFile SQL DDL file -func (eg *EngineGroup) ImportFile(ddlPath string) ([]sql.Result, error) { - return eg.Master().ImportFile(ddlPath) -} - -// Import SQL DDL from io.Reader -func (eg *EngineGroup) Import(r io.Reader) ([]sql.Result, error) { - return eg.Master().Import(r) -} - -// NowTime2 return current time -func (eg *EngineGroup) NowTime2(sqlTypeName string) (interface{}, time.Time) { - return eg.Master().NowTime2(sqlTypeName) -} - -// Unscoped always disable struct tag "deleted" -func (eg *EngineGroup) Unscoped() *EGSession { - egs := eg.NewEGSession() - return egs.Unscoped() -} - -// CondDeleted returns the conditions whether a record is soft deleted. -func (eg *EngineGroup) CondDeleted(colName string) builder.Cond { - return eg.Master().CondDeleted(colName) -} - -type BufferSizeArgs struct { - size int -} - -// BufferSize sets buffer size for iterate -func (eg *EngineGroup) BufferSize(size int) *EGSession { - egs := eg.NewEGSession() - return egs.BufferSize(size) -} diff --git a/engine_group_session.go b/engine_group_session.go deleted file mode 100644 index 0f333cb1..00000000 --- a/engine_group_session.go +++ /dev/null @@ -1,764 +0,0 @@ -package xorm - -import ( - "database/sql" - - "github.com/go-xorm/builder" -) - -type EGSession struct { - eg *EngineGroup - operation []string - args map[string]interface{} - err error -} - -func (egs *EGSession) operate(session *Session) *Session { - for _, v := range egs.operation { - switch v { - case "Before": - args := egs.args["Before"].(BeforeArgs) - session = session.Before(args.closures) - case "After": - args := egs.args["After"].(AfterArgs) - session = session.After(args.closures) - case "Table": - args := egs.args["Table"].(TableArgs) - session = session.Table(args.tableNameOrBean) - case "Alias": - args := egs.args["Alias"].(AliasArgs) - session = session.Alias(args.alias) - case "NoCascade": - session = session.NoCascade() - case "ForUpdate": - session = session.ForUpdate() - case "NoAutoCondition": - args := egs.args["NoAutoCondition"].(NoAutoConditionArgs) - session = session.NoAutoCondition(args.no...) - case "Limit": - args := egs.args["Limit"].(LimitArgs) - session = session.Limit(args.limit, args.start...) - case "OrderBy": - args := egs.args["OrderBy"].(OrderByArgs) - session = session.OrderBy(args.order) - case "Desc": - args := egs.args["Desc"].(DescArgs) - session = session.Desc(args.colNames...) - case "Asc": - args := egs.args["Asc"].(AscArgs) - session = session.Asc(args.colNames...) - case "StoreEngine": - args := egs.args["StoreEngine"].(StoreEngineArgs) - session = session.StoreEngine(args.storeEngine) - case "Charset": - args := egs.args["Charset"].(CharsetArgs) - session = session.Charset(args.charset) - case "Cascade": - args := egs.args["Cascade"].(CascadeArgs) - session = session.Cascade(args.trueOrFalse...) - case "NoCache": - session = session.NoCache() - case "Join": - args := egs.args["Join"].(JoinArgs) - session = session.Join(args.joinOperator, args.tablename, args.condition, args.args...) - case "GroupBy": - args := egs.args["GroupBy"].(GroupByArgs) - session = session.GroupBy(args.keys) - case "Having": - args := egs.args["Having"].(HavingArgs) - session = session.Having(args.conditions) - case "Unscoped": - session = session.Unscoped() - case "Incr": - args := egs.args["Incr"].(IncrArgs) - session = session.Incr(args.column, args.args...) - case "Decr": - args := egs.args["Decr"].(DecrArgs) - session = session.Decr(args.column, args.args...) - case "SetExpr": - args := egs.args["SetExpr"].(SetExprArgs) - session = session.SetExpr(args.column, args.expression) - case "Select": - args := egs.args["Select"].(SelectArgs) - session = session.Select(args.str) - case "Cols": - args := egs.args["Cols"].(ColsArgs) - session = session.Cols(args.columns...) - case "AllCols": - session = session.AllCols() - case "MustCols": - args := egs.args["MustCols"].(MustColsArgs) - session = session.MustCols(args.columns...) - case "UseBool": - args := egs.args["UseBool"].(UseBoolArgs) - session = session.UseBool(args.columns...) - case "Distinct": - args := egs.args["Distinct"].(DistinctArgs) - session = session.Distinct(args.columns...) - case "Omit": - args := egs.args["Omit"].(OmitArgs) - session = session.Omit(args.columns...) - case "Nullable": - args := egs.args["Nullable"].(NullableArgs) - session = session.Nullable(args.columns...) - case "NoAutoTime": - session = session.NoAutoTime() - case "Sql": - args := egs.args["Sql"].(SqlArgs) - session = session.Sql(args.query, args.args...) - case "SQL": - args := egs.args["SQL"].(SqlArgs) - session = session.SQL(args.query, args.args...) - case "Where": - args := egs.args["Where"].(WhereArgs) - session = session.Where(args.query, args.args...) - case "And": - args := egs.args["And"].(AndArgs) - session = session.And(args.query, args.args...) - case "Or": - args := egs.args["Or"].(OrArgs) - session = session.Or(args.query, args.args...) - case "Id": - args := egs.args["Id"].(IdArgs) - session = session.Id(args.id) - case "ID": - args := egs.args["ID"].(IDArgs) - session = session.ID(args.id) - case "In": - args := egs.args["In"].(InArgs) - session = session.In(args.column, args.args...) - case "NotIn": - args := egs.args["NotIn"].(NotInArgs) - session = session.NotIn(args.column, args.args...) - case "BufferSize": - args := egs.args["BufferSize"].(BufferSizeArgs) - session = session.BufferSize(args.size) - } - } - return session -} - -// Before Apply before Processor, affected bean is passed to closure arg -func (egs *EGSession) Before(closures func(interface{})) *EGSession { - egs.operation = append(egs.operation, "Before") - args := BeforeArgs{ - closures: closures, - } - egs.args["Before"] = args - return egs -} - -// After Apply after Processor, affected bean is passed to closure arg -func (egs *EGSession) After(closures func(interface{})) *EGSession { - egs.operation = append(egs.operation, "After") - args := AfterArgs{ - closures: closures, - } - egs.args["After"] = args - return egs -} - -// Table can input a string or pointer to struct for special a table to operate. -func (egs *EGSession) Table(tableNameOrBean interface{}) *EGSession { - egs.operation = append(egs.operation, "Table") - args := TableArgs{ - tableNameOrBean: tableNameOrBean, - } - egs.args["Table"] = args - return egs -} - -// Alias set the table alias -func (egs *EGSession) Alias(alias string) *EGSession { - egs.operation = append(egs.operation, "Alias") - args := AliasArgs{ - alias: alias, - } - egs.args["Alias"] = args - return egs -} - -// NoCascade indicate that no cascade load child object -func (egs *EGSession) NoCascade() *EGSession { - egs.operation = append(egs.operation, "NoCascade") - return egs -} - -// ForUpdate Set Read/Write locking for UPDATE -func (egs *EGSession) ForUpdate() *EGSession { - egs.operation = append(egs.operation, "ForUpdate") - return egs -} - -// NoAutoCondition disable generate SQL condition from beans -func (egs *EGSession) NoAutoCondition(no ...bool) *EGSession { - egs.operation = append(egs.operation, "NoAutoCondition") - args := NoAutoConditionArgs{ - no: no, - } - egs.args["NoAutoCondition"] = args - return egs -} - -// Limit provide limit and offset query condition -func (egs *EGSession) Limit(limit int, start ...int) *EGSession { - egs.operation = append(egs.operation, "Limit") - args := LimitArgs{ - limit: limit, - start: start, - } - egs.args["Limit"] = args - return egs -} - -// OrderBy provide order by query condition, the input parameter is the content -// after order by on a sql statement. -func (egs *EGSession) OrderBy(order string) *EGSession { - egs.operation = append(egs.operation, "OrderBy") - args := OrderByArgs{ - order: order, - } - egs.args["OrderBy"] = args - return egs -} - -// Desc provide desc order by query condition, the input parameters are columns. -func (egs *EGSession) Desc(colNames ...string) *EGSession { - egs.operation = append(egs.operation, "Desc") - args := DescArgs{ - colNames: colNames, - } - egs.args["Desc"] = args - return egs -} - -// Asc provide asc order by query condition, the input parameters are columns. -func (egs *EGSession) Asc(colNames ...string) *EGSession { - egs.operation = append(egs.operation, "Asc") - args := AscArgs{ - colNames: colNames, - } - egs.args["Asc"] = args - return egs -} - -// StoreEngine is only avialble mysql dialect currently -func (egs *EGSession) StoreEngine(storeEngine string) *EGSession { - egs.operation = append(egs.operation, "StoreEngine") - args := StoreEngineArgs{ - storeEngine: storeEngine, - } - egs.args["StoreEngine"] = args - return egs -} - -// Charset is only avialble mysql dialect currently -func (egs *EGSession) Charset(charset string) *EGSession { - egs.operation = append(egs.operation, "Charset") - args := CharsetArgs{ - charset: charset, - } - egs.args["Charset"] = args - return egs -} - -// Cascade indicates if loading sub Struct -func (egs *EGSession) Cascade(trueOrFalse ...bool) *EGSession { - egs.operation = append(egs.operation, "Cascade") - args := CascadeArgs{ - trueOrFalse: trueOrFalse, - } - egs.args["Cascade"] = args - return egs -} - -// NoCache ask this session do not retrieve data from cache system and -// get data from database directly. -func (egs *EGSession) NoCache() *EGSession { - egs.operation = append(egs.operation, "NoCache") - return egs -} - -// Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (egs *EGSession) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "Join") - joinArgs := JoinArgs{ - joinOperator: joinOperator, - tablename: tablename, - condition: condition, - args: args, - } - egs.args["Join"] = joinArgs - return egs -} - -// GroupBy Generate Group By statement -func (egs *EGSession) GroupBy(keys string) *EGSession { - egs.operation = append(egs.operation, "GroupBy") - args := GroupByArgs{ - keys: keys, - } - egs.args["GroupBy"] = args - return egs -} - -// Having Generate Having statement -func (egs *EGSession) Having(conditions string) *EGSession { - egs.operation = append(egs.operation, "Having") - args := HavingArgs{ - conditions: conditions, - } - egs.args["Having"] = args - return egs -} - -// Unscoped always disable struct tag "deleted" -func (egs *EGSession) Unscoped() *EGSession { - egs.operation = append(egs.operation, "Unscoped") - return egs -} - -// Incr provides a query string like "count = count + 1" -func (egs *EGSession) Incr(column string, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "Incr") - incrArgs := IncrArgs{ - column: column, - args: args, - } - egs.args["Incr"] = incrArgs - return egs -} - -// Decr provides a query string like "count = count - 1" -func (egs *EGSession) Decr(column string, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "Decr") - decrArgs := DecrArgs{ - column: column, - args: args, - } - egs.args["Decr"] = decrArgs - return egs -} - -// SetExpr provides a query string like "column = {expression}" -func (egs *EGSession) SetExpr(column string, expression string) *EGSession { - egs.operation = append(egs.operation, "SetExpr") - args := SetExprArgs{ - column: column, - expression: expression, - } - egs.args["SetExpr"] = args - return egs -} - -// Select provides some columns to special -func (egs *EGSession) Select(str string) *EGSession { - egs.operation = append(egs.operation, "Select") - args := SelectArgs{ - str: str, - } - egs.args["Select"] = args - return egs -} - -// Cols provides some columns to special -func (egs *EGSession) Cols(columns ...string) *EGSession { - egs.operation = append(egs.operation, "Cols") - args := ColsArgs{ - columns: columns, - } - egs.args["Cols"] = args - return egs -} - -// AllCols ask all columns -func (egs *EGSession) AllCols() *EGSession { - egs.operation = append(egs.operation, "AllCols") - return egs -} - -// MustCols specify some columns must use even if they are empty -func (egs *EGSession) MustCols(columns ...string) *EGSession { - egs.operation = append(egs.operation, "MustCols") - args := MustColsArgs{ - columns: columns, - } - egs.args["MustCols"] = args - return egs -} - -// UseBool automatically retrieve condition according struct, but -// if struct has bool field, it will ignore them. So use UseBool -// to tell system to do not ignore them. -// If no parameters, it will use all the bool field of struct, or -// it will use parameters's columns -func (egs *EGSession) UseBool(columns ...string) *EGSession { - egs.operation = append(egs.operation, "UseBool") - args := UseBoolArgs{ - columns: columns, - } - egs.args["UseBool"] = args - return egs -} - -// Distinct use for distinct columns. Caution: when you are using cache, -// distinct will not be cached because cache system need id, -// but distinct will not provide id -func (egs *EGSession) Distinct(columns ...string) *EGSession { - egs.operation = append(egs.operation, "Distinct") - args := DistinctArgs{ - columns: columns, - } - egs.args["Distinct"] = args - return egs -} - -// Omit Only not use the parameters as select or update columns -func (egs *EGSession) Omit(columns ...string) *EGSession { - egs.operation = append(egs.operation, "Omit") - args := OmitArgs{ - columns: columns, - } - egs.args["Omit"] = args - return egs -} - -// Nullable Set null when column is zero-value and nullable for update -func (egs *EGSession) Nullable(columns ...string) *EGSession { - egs.operation = append(egs.operation, "Nullable") - args := NullableArgs{ - columns: columns, - } - egs.args["Nullable"] = args - return egs -} - -// NoAutoTime means do not automatically give created field and updated field -// the current time on the current session temporarily -func (egs *EGSession) NoAutoTime() *EGSession { - egs.operation = append(egs.operation, "NoAutoTime") - return egs -} - -// Sql provides raw sql input parameter. When you have a complex SQL statement -// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. -// -// Deprecated: use SQL instead. -func (egs *EGSession) Sql(query string, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "Sql") - sqlArgs := SqlArgs{ - query: query, - args: args, - } - egs.args["Sql"] = sqlArgs - return egs -} - -type SQLArgs struct { - query interface{} - args []interface{} -} - -// SQL provides raw sql input parameter. When you have a complex SQL statement -// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. -func (egs *EGSession) SQL(query interface{}, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "SQL") - sqlArgs := SQLArgs{ - query: query, - args: args, - } - egs.args["SQL"] = sqlArgs - return egs -} - -// Where provides custom query condition. -func (egs *EGSession) Where(query interface{}, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "Where") - whereArgs := WhereArgs{ - query: query, - args: args, - } - egs.args["Where"] = whereArgs - return egs -} - -type AndArgs struct { - query interface{} - args []interface{} -} - -// And provides custom query condition. -func (egs *EGSession) And(query interface{}, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "And") - andArgs := AndArgs{ - query: query, - args: args, - } - egs.args["And"] = andArgs - return egs -} - -type OrArgs struct { - query interface{} - args []interface{} -} - -// Or provides custom query condition. -func (egs *EGSession) Or(query interface{}, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "Or") - orArgs := OrArgs{ - query: query, - args: args, - } - egs.args["Or"] = orArgs - return egs -} - -// Id provides converting id as a query condition -// -// Deprecated: use ID instead -func (egs *EGSession) Id(id interface{}) *EGSession { - egs.operation = append(egs.operation, "Id") - args := IdArgs{ - id: id, - } - egs.args["Id"] = args - return egs -} - -// ID provides converting id as a query condition -func (egs *EGSession) ID(id interface{}) *EGSession { - egs.operation = append(egs.operation, "ID") - args := IDArgs{ - id: id, - } - egs.args["ID"] = args - return egs -} - -// In provides a query string like "id in (1, 2, 3)" -func (egs *EGSession) In(column string, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "In") - inArgs := InArgs{ - column: column, - args: args, - } - egs.args["In"] = inArgs - return egs -} - -// NotIn provides a query string like "id in (1, 2, 3)" -func (egs *EGSession) NotIn(column string, args ...interface{}) *EGSession { - egs.operation = append(egs.operation, "NotIn") - notInArgs := NotInArgs{ - column: column, - args: args, - } - egs.args["NotIn"] = notInArgs - return egs -} - -// Conds returns session query conditions except auto bean conditions -func (egs *EGSession) Conds() builder.Cond { - return egs.eg.Master().NewSession().Conds() -} - -// Delete records, bean's non-empty fields are conditions -func (egs *EGSession) Delete(bean interface{}) (int64, error) { - session := egs.eg.Master().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Delete(bean) -} - -// Exist returns true if the record exist otherwise return false -func (egs *EGSession) Exist(bean ...interface{}) (bool, error) { - session := egs.eg.Master().NewSession() - defer session.Close() - session = egs.operate(session) - 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 -func (egs *EGSession) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Find(rowsSlicePtr, condiBean...) -} - -// Get retrieve one record from database, bean's non-empty fields -// will be as conditions -func (egs *EGSession) Get(bean interface{}) (bool, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Get(bean) -} - -// Insert insert one or more beans -func (egs *EGSession) Insert(beans ...interface{}) (int64, error) { - session := egs.eg.Master().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Insert(beans...) -} - -// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields -// are conditions. -func (egs *EGSession) Rows(bean interface{}) (*Rows, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Rows(bean) -} - -// Iterate record by record handle records from table, condiBeans's non-empty fields -// are conditions. beans could be []Struct, []*Struct, map[int64]Struct -// map[int64]*Struct -func (egs *EGSession) Iterate(bean interface{}, fun IterFunc) error { - return egs.eg.Slave().Iterate(bean, fun) -} - -// BufferSize sets the buffersize for iterate -func (egs *EGSession) BufferSize(size int) *EGSession { - egs.operation = append(egs.operation, "BufferSize") - args := BufferSizeArgs{ - size: size, - } - egs.args["BufferSize"] = args - return egs -} - -// Query runs a raw sql and return records as []map[string][]byte -func (egs *EGSession) Query(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Query(sqlStr, args...) -} - -// QueryString runs a raw sql and return records as []map[string]string -func (egs *EGSession) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.QueryString(sqlStr, args...) -} - -// QueryInterface runs a raw sql and return records as []map[string]interface{} -func (egs *EGSession) QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.QueryInterface(sqlStr, args...) -} - -// Exec raw sql -func (egs *EGSession) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { - session := egs.eg.Master().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Exec(sqlStr, args...) -} - -// CreateTable create a table according a bean -func (egs *EGSession) CreateTable(bean interface{}) error { - session := egs.eg.Master().NewSession() - defer session.Close() - return session.CreateTable(bean) -} - -// CreateIndexes create indexes -func (egs *EGSession) CreateIndexes(bean interface{}) error { - return egs.eg.Master().CreateIndexes(bean) -} - -// CreateUniques create uniques -func (egs *EGSession) CreateUniques(bean interface{}) error { - return egs.eg.Master().CreateUniques(bean) -} - -// DropIndexes drop indexes -func (egs *EGSession) DropIndexes(bean interface{}) error { - return egs.eg.Master().DropIndexes(bean) -} - -// DropTable drop table will drop table if exist, if drop failed, it will return error -func (egs *EGSession) DropTable(beanOrTableName interface{}) error { - session := egs.eg.Master().NewSession() - defer session.Close() - return session.DropTable(beanOrTableName) -} - -// IsTableExist if a table is exist -func (egs *EGSession) IsTableExist(beanOrTableName interface{}) (bool, error) { - return egs.eg.Master().IsTableExist(beanOrTableName) -} - -// IsTableEmpty if table have any records -func (egs *EGSession) IsTableEmpty(bean interface{}) (bool, error) { - return egs.eg.Master().IsTableEmpty(bean) -} - -// Sync2 synchronize structs to database tables -func (egs *EGSession) Sync2(beans ...interface{}) error { - return egs.eg.Master().Sync2(beans...) -} - -// Count counts the records. bean's non-empty fields -// are conditions. -func (egs *EGSession) Count(bean ...interface{}) (int64, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Count(bean...) -} - -// sum call sum some column. bean's non-empty fields are conditions. -func (egs *EGSession) Sum(bean interface{}, columnName string) (res float64, err error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Sum(bean, columnName) -} - -// SumInt call sum some column. bean's non-empty fields are conditions. -func (egs *EGSession) SumInt(bean interface{}, columnName string) (res int64, err error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.SumInt(bean, columnName) -} - -// Sums call sum some columns. bean's non-empty fields are conditions. -func (egs *EGSession) Sums(bean interface{}, columnNames ...string) ([]float64, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Sums(bean, columnNames...) -} - -// SumsInt sum specify columns and return as []int64 instead of []float64 -func (egs *EGSession) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { - session := egs.eg.Slave().NewSession() - defer session.Close() - session = egs.operate(session) - return session.SumsInt(bean, columnNames...) -} - -// Update records, bean's non-empty fields are updated contents, -// condiBean' non-empty filds are conditions -// CAUTION: -// 1.bool will defaultly be updated content nor conditions -// You should call UseBool if you have bool to use. -// 2.float32 & float64 may be not inexact as conditions -func (egs *EGSession) Update(bean interface{}, condiBean ...interface{}) (int64, error) { - session := egs.eg.Master().NewSession() - defer session.Close() - session = egs.operate(session) - return session.Update(bean, condiBean...) - -} diff --git a/session.go b/session.go index c69ac9e5..fe2b9746 100644 --- a/session.go +++ b/session.go @@ -258,13 +258,13 @@ func (session *Session) canCache() bool { 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)) // TODO try hash(sqlStr+len(sqlStr)) var has bool stmt, has = session.stmtCache[crc] if !has { - stmt, err = session.DB().Prepare(sqlStr) + stmt, err = db.Prepare(sqlStr) if err != nil { return nil, err } diff --git a/session_raw.go b/session_raw.go index c225598e..69bf9b3c 100644 --- a/session_raw.go +++ b/session_raw.go @@ -47,9 +47,16 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row } 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 { // don't clear stmt since session will cache them - stmt, err := session.doPrepare(sqlStr) + stmt, err := session.doPrepare(db, sqlStr) if err != nil { return nil, err } @@ -61,7 +68,7 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row return rows, nil } - rows, err := session.DB().Query(sqlStr, args...) + rows, err := db.Query(sqlStr, args...) if err != nil { return nil, err } @@ -171,7 +178,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er } if session.prepareStmt { - stmt, err := session.doPrepare(sqlStr) + stmt, err := session.doPrepare(session.DB(), sqlStr) if err != nil { return nil, err } From 0e47f0d3c74253c449d7727274dcf5eccba91f93 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 12:54:13 +0800 Subject: [PATCH 21/31] remove unused count variables --- engine_group.go | 5 +---- engine_group_policy.go | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/engine_group.go b/engine_group.go index 8b8118be..5e659aaf 100644 --- a/engine_group.go +++ b/engine_group.go @@ -17,7 +17,7 @@ type EngineGroup struct { policy GroupPolicy } -func NewGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) { +func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) { var eg EngineGroup if len(policies) > 0 { eg.policy = policies[0] @@ -73,9 +73,6 @@ func (eg *EngineGroup) Slave() *Engine { case 1: return eg.slaves[0] } - if eg.s_count == 1 { - return eg.slaves[0] - } return eg.policy.Slave(eg) } diff --git a/engine_group_policy.go b/engine_group_policy.go index c4fdcb0d..c3e528ad 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -68,16 +68,17 @@ func NewRoundRobinPolicy() *RoundRobinPolicy { } func (policy *RoundRobinPolicy) Slave(g *EngineGroup) *Engine { + var slaves = g.Slaves() var pos int policy.lock.Lock() policy.pos++ - if policy.pos >= g.s_count { + if policy.pos >= len(slaves) { policy.pos = 0 } pos = policy.pos policy.lock.Unlock() - return g.Slaves()[pos] + return slaves[pos] } type WeightRoundRobinPolicy struct { From 8abcd18bbdd5a3c9dd505bd8b9664c3cc1c373bf Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 13:19:33 +0800 Subject: [PATCH 22/31] fix bug on NewEngineGroup --- engine_group.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine_group.go b/engine_group.go index 5e659aaf..734056c2 100644 --- a/engine_group.go +++ b/engine_group.go @@ -51,6 +51,8 @@ func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolic for i := 0; i < len(slaves); i++ { slaves[i].engineGroup = &eg } + eg.Engine = master + eg.slaves = slaves return &eg, nil } return nil, ErrParamsType From b354379b9cf09cb7e60a23bb7772cfd9254f1796 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 13:25:50 +0800 Subject: [PATCH 23/31] remove unused method --- engine.go | 5 ----- engine_group_policy.go | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/engine.go b/engine.go index 9e7053ab..61748adf 100644 --- a/engine.go +++ b/engine.go @@ -1583,8 +1583,3 @@ func (engine *Engine) BufferSize(size int) *Session { session.isAutoClose = true return session.BufferSize(size) } - -//Stats return the number of open connections to the database -func (engine *Engine) Stats() int { - return engine.DB().Stats().OpenConnections -} diff --git a/engine_group_policy.go b/engine_group_policy.go index c3e528ad..175f8e39 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -135,7 +135,7 @@ func (policy *LeastConnPolicy) Slave(g *EngineGroup) *Engine { connections := 0 idx := 0 for i, _ := range slaves { - open_connections := slaves[i].Stats() + open_connections := slaves[i].DB().Stats().OpenConnections if i == 0 { connections = open_connections idx = i From 741af4a3156e013dbe0bbaf596983f4e5d89f25f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 13:41:48 +0800 Subject: [PATCH 24/31] improve range and refactor --- engine_group.go | 45 +++++++++++++++++++++------------------------ engine_maxlife.go | 8 ++++++++ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/engine_group.go b/engine_group.go index 734056c2..b3b6a6c9 100644 --- a/engine_group.go +++ b/engine_group.go @@ -6,7 +6,6 @@ package xorm import ( "strings" - "time" "github.com/go-xorm/core" ) @@ -67,7 +66,7 @@ func (eg *EngineGroup) Master() *Engine { return eg.Engine } -// Slave returns one of the physical databases which is a slave +// 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: @@ -89,7 +88,7 @@ func (eg *EngineGroup) GetSlave(i int) *Engine { // 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, _ := range eg.slaves { + for i := 0; i < len(eg.slaves); i++ { eg.slaves[i].ShowSQL(show...) } } @@ -97,25 +96,31 @@ func (eg *EngineGroup) ShowSQL(show ...bool) { // 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, _ := range eg.slaves { + for i := 0; i < len(eg.slaves); i++ { eg.slaves[i].ShowExecTime(show...) } } // SetMapper set the name mapping rules func (eg *EngineGroup) SetMapper(mapper core.IMapper) { - eg.Engine.SetTableMapper(mapper) - eg.Engine.SetColumnMapper(mapper) - for i, _ := range eg.slaves { - eg.slaves[i].SetTableMapper(mapper) - eg.slaves[i].SetColumnMapper(mapper) + eg.Engine.SetMapper(mapper) + for i := 0; i < len(eg.slaves); i++ { + eg.slaves[i].SetMapper(mapper) + } +} + +// 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) } } // SetTableMapper set the table name mapping rule func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) { eg.Engine.TableMapper = mapper - for i, _ := range eg.slaves { + for i := 0; i < len(eg.slaves); i++ { eg.slaves[i].TableMapper = mapper } } @@ -123,7 +128,7 @@ func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) { // SetColumnMapper set the column name mapping rule func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { eg.Engine.ColumnMapper = mapper - for i, _ := range eg.slaves { + for i := 0; i < len(eg.slaves); i++ { eg.slaves[i].ColumnMapper = mapper } } @@ -131,7 +136,7 @@ func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { // SetMaxOpenConns is only available for go 1.2+ func (eg *EngineGroup) SetMaxOpenConns(conns int) { eg.Engine.db.SetMaxOpenConns(conns) - for i, _ := range eg.slaves { + for i := 0; i < len(eg.slaves); i++ { eg.slaves[i].db.SetMaxOpenConns(conns) } } @@ -139,20 +144,20 @@ func (eg *EngineGroup) SetMaxOpenConns(conns int) { // SetMaxIdleConns set the max idle connections on pool, default is 2 func (eg *EngineGroup) SetMaxIdleConns(conns int) { eg.Engine.db.SetMaxIdleConns(conns) - for i, _ := range eg.slaves { + for i := 0; i < len(eg.slaves); i++ { eg.slaves[i].db.SetMaxIdleConns(conns) } } // Close the engine func (eg *EngineGroup) Close() error { - err := eg.Engine.db.Close() + err := eg.Engine.Close() if err != nil { return err } - for i, _ := range eg.slaves { - err := eg.slaves[i].db.Close() + for i := 0; i < len(eg.slaves); i++ { + err := eg.slaves[i].Close() if err != nil { return err } @@ -173,11 +178,3 @@ func (eg *EngineGroup) Ping() error { } return nil } - -// SetConnMaxLifetime sets the maximum amount of time a connection may be reused. -func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { - eg.Engine.db.SetConnMaxLifetime(d) - for i, _ := range eg.slaves { - eg.slaves[i].db.SetConnMaxLifetime(d) - } -} diff --git a/engine_maxlife.go b/engine_maxlife.go index 21daeaa1..22666c5f 100644 --- a/engine_maxlife.go +++ b/engine_maxlife.go @@ -12,3 +12,11 @@ import "time" func (engine *Engine) SetConnMaxLifetime(d time.Duration) { 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) + } +} From 8f73a779a8749beeeb812e872ace07756ef7453b Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 19:58:43 +0800 Subject: [PATCH 25/31] add some comments and refactor --- engine_group_policy.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/engine_group_policy.go b/engine_group_policy.go index 175f8e39..5ac89f71 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -10,24 +10,32 @@ import ( "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 + +// RandomPolicy implmentes randomly chose the slave of slaves type RandomPolicy struct { r *rand.Rand } +// NewRandomPolicy creates a RandomPolicy func NewRandomPolicy() *RandomPolicy { return &RandomPolicy{ r: rand.New(rand.NewSource(time.Now().UnixNano())), } } +// Slave randomly choses the slave of slaves func (policy *RandomPolicy) Slave(g *EngineGroup) *Engine { return g.Slaves()[policy.r.Intn(len(g.Slaves()))] } +// WeightRandomPolicy implmentes randomly chose the slave of slaves type WeightRandomPolicy struct { weights []int rands []int @@ -123,14 +131,8 @@ func (policy *WeightRoundRobinPolicy) Slave(g *EngineGroup) *Engine { return slaves[idx] } -type LeastConnPolicy struct { -} - -func NewLeastConnPolicy() *LeastConnPolicy { - return &LeastConnPolicy{} -} - -func (policy *LeastConnPolicy) Slave(g *EngineGroup) *Engine { +// LeastConnPolicy implements GroupPolicy, every time will get the least connections slave +var LeastConnPolicy GroupPolicyHandler = func(g *EngineGroup) *Engine { var slaves = g.Slaves() connections := 0 idx := 0 From 2066723e714e1cc52b6a7991d0bfd3749b3a6203 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 20:05:07 +0800 Subject: [PATCH 26/31] implement GroupPolicy of GroupPolicyHandler --- engine_group_policy.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine_group_policy.go b/engine_group_policy.go index 5ac89f71..e79f7d52 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -18,6 +18,11 @@ type GroupPolicy interface { // 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 type RandomPolicy struct { r *rand.Rand From 71beaa0190f41e376a2de6081b85d60cf74d0ad8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 20:13:49 +0800 Subject: [PATCH 27/31] refactor --- engine_group_policy.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/engine_group_policy.go b/engine_group_policy.go index e79f7d52..7db41c20 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -137,19 +137,21 @@ func (policy *WeightRoundRobinPolicy) Slave(g *EngineGroup) *Engine { } // LeastConnPolicy implements GroupPolicy, every time will get the least connections slave -var LeastConnPolicy GroupPolicyHandler = func(g *EngineGroup) *Engine { - var slaves = g.Slaves() - connections := 0 - idx := 0 - for i, _ := range slaves { - open_connections := slaves[i].DB().Stats().OpenConnections - if i == 0 { - connections = open_connections - idx = i - } else if open_connections <= connections { - connections = open_connections - idx = i +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] } - return slaves[idx] } From 7b36611d51df8731d592698cce20d370e48238ff Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 26 Sep 2017 20:41:34 +0800 Subject: [PATCH 28/31] simple code --- engine_group.go | 2 +- engine_group_policy.go | 127 ++++++++++++++--------------------------- 2 files changed, 44 insertions(+), 85 deletions(-) diff --git a/engine_group.go b/engine_group.go index b3b6a6c9..1c42a556 100644 --- a/engine_group.go +++ b/engine_group.go @@ -21,7 +21,7 @@ func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolic if len(policies) > 0 { eg.policy = policies[0] } else { - eg.policy = NewRandomPolicy() + eg.policy = RandomPolicy() } driverName, ok1 := args1.(string) diff --git a/engine_group_policy.go b/engine_group_policy.go index 7db41c20..5b56e899 100644 --- a/engine_group_policy.go +++ b/engine_group_policy.go @@ -24,116 +24,75 @@ func (h GroupPolicyHandler) Slave(eg *EngineGroup) *Engine { } // RandomPolicy implmentes randomly chose the slave of slaves -type RandomPolicy struct { - r *rand.Rand -} - -// NewRandomPolicy creates a RandomPolicy -func NewRandomPolicy() *RandomPolicy { - return &RandomPolicy{ - r: rand.New(rand.NewSource(time.Now().UnixNano())), +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()))] } } -// Slave randomly choses the slave of slaves -func (policy *RandomPolicy) Slave(g *EngineGroup) *Engine { - return g.Slaves()[policy.r.Intn(len(g.Slaves()))] -} - // WeightRandomPolicy implmentes randomly chose the slave of slaves -type WeightRandomPolicy struct { - weights []int - rands []int - r *rand.Rand -} - -func NewWeightRandomPolicy(weights []int) *WeightRandomPolicy { +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 &WeightRandomPolicy{ - weights: weights, - rands: rands, - 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 (policy *WeightRandomPolicy) Slave(g *EngineGroup) *Engine { - var slaves = g.Slaves() - idx := policy.rands[policy.r.Intn(len(policy.rands))] - if idx >= len(slaves) { - idx = len(slaves) - 1 +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] } - return slaves[idx] } -type RoundRobinPolicy struct { - pos int - lock sync.Mutex -} - -func NewRoundRobinPolicy() *RoundRobinPolicy { - return &RoundRobinPolicy{pos: -1} -} - -func (policy *RoundRobinPolicy) Slave(g *EngineGroup) *Engine { - var slaves = g.Slaves() - var pos int - policy.lock.Lock() - policy.pos++ - if policy.pos >= len(slaves) { - policy.pos = 0 - } - pos = policy.pos - policy.lock.Unlock() - - return slaves[pos] -} - -type WeightRoundRobinPolicy struct { - weights []int - rands []int - r *rand.Rand - lock sync.Mutex - pos int -} - -func NewWeightRoundRobinPolicy(weights []int) *WeightRoundRobinPolicy { +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 &WeightRoundRobinPolicy{ - weights: weights, - rands: rands, - r: rand.New(rand.NewSource(time.Now().UnixNano())), - pos: -1, - } -} + return func(g *EngineGroup) *Engine { + var slaves = g.Slaves() + lock.Lock() + defer lock.Unlock() + pos++ + if pos >= len(rands) { + pos = 0 + } -func (policy *WeightRoundRobinPolicy) Slave(g *EngineGroup) *Engine { - var slaves = g.Slaves() - var pos int - policy.lock.Lock() - policy.pos++ - if policy.pos >= len(policy.rands) { - policy.pos = 0 + idx := rands[pos] + if idx >= len(slaves) { + idx = len(slaves) - 1 + } + return slaves[idx] } - pos = policy.pos - policy.lock.Unlock() - - idx := policy.rands[pos] - if idx >= len(slaves) { - idx = len(slaves) - 1 - } - return slaves[idx] } // LeastConnPolicy implements GroupPolicy, every time will get the least connections slave From 71529c2e6f866bd52fd9c30a18a56598d03861ab Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 28 Sep 2017 10:38:52 +0800 Subject: [PATCH 29/31] add tests support for EngineGroup & fix some bugs --- cache_test.go | 6 +- engine.go | 73 +++++++++++---- engine_group.go | 201 ++++++++++++++++++++++------------------- interface.go | 99 ++++++++++++++++++++ session.go | 1 + session_cols_test.go | 2 +- session_cond_test.go | 4 +- session_delete_test.go | 14 +-- session_find_test.go | 42 ++++----- session_get_test.go | 10 +- session_raw_test.go | 2 +- session_schema_test.go | 4 +- session_stats_test.go | 6 +- session_tx_test.go | 6 +- statement_test.go | 20 +++- tag_extends_test.go | 20 ++-- tag_id_test.go | 12 +-- tag_test.go | 8 +- time_test.go | 20 ++-- types_test.go | 6 +- xorm_test.go | 13 ++- 21 files changed, 372 insertions(+), 197 deletions(-) create mode 100644 interface.go diff --git a/cache_test.go b/cache_test.go index 5f138f24..26d7ac68 100644 --- a/cache_test.go +++ b/cache_test.go @@ -20,7 +20,7 @@ func TestCacheFind(t *testing.T) { Password string } - oldCacher := testEngine.Cacher + oldCacher := testEngine.GetDefaultCacher() cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) @@ -95,7 +95,7 @@ func TestCacheFind2(t *testing.T) { Password string } - oldCacher := testEngine.Cacher + oldCacher := testEngine.GetDefaultCacher() cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) @@ -146,7 +146,7 @@ func TestCacheGet(t *testing.T) { Password string } - oldCacher := testEngine.Cacher + oldCacher := testEngine.GetDefaultCacher() cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) diff --git a/engine.go b/engine.go index 61748adf..3b7fd257 100644 --- a/engine.go +++ b/engine.go @@ -51,6 +51,21 @@ type Engine struct { 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 func (engine *Engine) ShowSQL(show ...bool) { engine.logger.ShowSQL(show...) @@ -81,6 +96,11 @@ func (engine *Engine) SetLogger(logger core.ILogger) { 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 func (engine *Engine) SetDisableGlobalCache(disable bool) { if engine.disableGlobalCache != disable { @@ -203,6 +223,11 @@ func (engine *Engine) SetDefaultCacher(cacher core.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, // you can use NoCache() func (engine *Engine) NoCache() *Session { @@ -759,7 +784,8 @@ func (engine *Engine) Having(conditions string) *Session { 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() defer engine.mutex.Unlock() delete(engine.Tables, t) @@ -1562,24 +1588,39 @@ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{} 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" func (engine *Engine) Unscoped() *Session { session := engine.NewSession() session.isAutoClose = true 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) -} diff --git a/engine_group.go b/engine_group.go index 1c42a556..223eb985 100644 --- a/engine_group.go +++ b/engine_group.go @@ -10,12 +10,14 @@ 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 { @@ -57,98 +59,6 @@ func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolic return nil, ErrParamsType } -func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup { - eg.policy = policy - return eg -} - -func (eg *EngineGroup) Master() *Engine { - return eg.Engine -} - -// 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) -} - -func (eg *EngineGroup) Slaves() []*Engine { - return eg.slaves -} - -func (eg *EngineGroup) GetSlave(i int) *Engine { - return eg.slaves[i] -} - -// 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...) - } -} - -// 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...) - } -} - -// 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) - } -} - -// 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) - } -} - -// 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 - } -} - -// 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 - } -} - -// 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) - } -} - -// 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) - } -} - // Close the engine func (eg *EngineGroup) Close() error { err := eg.Engine.Close() @@ -165,6 +75,11 @@ func (eg *EngineGroup) Close() error { 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 { @@ -178,3 +93,105 @@ func (eg *EngineGroup) Ping() error { } 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 +} diff --git a/interface.go b/interface.go new file mode 100644 index 00000000..31180486 --- /dev/null +++ b/interface.go @@ -0,0 +1,99 @@ +// 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 + 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 + 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(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) + QueryInterface(sqlStr string, args ...interface{}) ([]map[string]interface{}, error) + QueryString(sqlStr string, args ...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) + 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) + Sync2(...interface{}) error + StoreEngine(storeEngine string) *Session + TableInfo(bean interface{}) *Table + UnMapType(reflect.Type) +} + +var ( + _ Interface = &Session{} + _ EngineInterface = &Engine{} + _ EngineInterface = &EngineGroup{} +) diff --git a/session.go b/session.go index fe2b9746..09b887d6 100644 --- a/session.go +++ b/session.go @@ -74,6 +74,7 @@ func (session *Session) Init() { session.afterDeleteBeans = make(map[interface{}]*[]func(interface{}), 0) session.beforeClosures = make([]func(interface{}), 0) session.afterClosures = make([]func(interface{}), 0) + session.stmtCache = make(map[uint32]*core.Stmt) session.lastSQL = "" session.lastSQLArgs = []interface{}{} diff --git a/session_cols_test.go b/session_cols_test.go index 43854723..6ec17130 100644 --- a/session_cols_test.go +++ b/session_cols_test.go @@ -28,7 +28,7 @@ func TestSetExpr(t *testing.T) { assert.EqualValues(t, 1, cnt) var not = "NOT" - if testEngine.dialect.DBType() == core.MSSQL { + if testEngine.Dialect().DBType() == core.MSSQL { not = "~" } cnt, err = testEngine.SetExpr("show", not+" `show`").ID(1).Update(new(UserExpr)) diff --git a/session_cond_test.go b/session_cond_test.go index 5f8716f0..a80e7d03 100644 --- a/session_cond_test.go +++ b/session_cond_test.go @@ -183,7 +183,7 @@ func TestIn(t *testing.T) { idsInterface = append(idsInterface, id) } - department := "`" + testEngine.ColumnMapper.Obj2Table("Departname") + "`" + department := "`" + testEngine.GetColumnMapper().Obj2Table("Departname") + "`" err = testEngine.Where(department+" = ?", "dev").In("(id)", idsInterface...).Find(&users) if err != nil { t.Error(err) @@ -205,7 +205,7 @@ func TestIn(t *testing.T) { } } - dev := testEngine.ColumnMapper.Obj2Table("Dev") + dev := testEngine.GetColumnMapper().Obj2Table("Dev") err = testEngine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) diff --git a/session_delete_test.go b/session_delete_test.go index adabb269..916dab46 100644 --- a/session_delete_test.go +++ b/session_delete_test.go @@ -77,7 +77,7 @@ func TestDeleted(t *testing.T) { // Test normal Find() var records1 []Deleted - err = testEngine.Where("`"+testEngine.ColumnMapper.Obj2Table("Id")+"` > 0").Find(&records1, &Deleted{}) + err = testEngine.Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").Find(&records1, &Deleted{}) assert.EqualValues(t, 3, len(records1)) // Test normal Get() @@ -96,7 +96,7 @@ func TestDeleted(t *testing.T) { assert.False(t, has) var records2 []Deleted - err = testEngine.Where("`" + testEngine.ColumnMapper.Obj2Table("Id") + "` > 0").Find(&records2) + err = testEngine.Where("`" + testEngine.GetColumnMapper().Obj2Table("Id") + "` > 0").Find(&records2) assert.NoError(t, err) assert.EqualValues(t, 2, len(records2)) @@ -117,7 +117,7 @@ func TestDeleted(t *testing.T) { // Test find all records whatever `deleted`. var unscopedRecords1 []Deleted - err = testEngine.Unscoped().Where("`"+testEngine.ColumnMapper.Obj2Table("Id")+"` > 0").Find(&unscopedRecords1, &Deleted{}) + err = testEngine.Unscoped().Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").Find(&unscopedRecords1, &Deleted{}) assert.NoError(t, err) assert.EqualValues(t, 3, len(unscopedRecords1)) @@ -127,13 +127,13 @@ func TestDeleted(t *testing.T) { assert.EqualValues(t, 1, affected) var unscopedRecords2 []Deleted - err = testEngine.Unscoped().Where("`"+testEngine.ColumnMapper.Obj2Table("Id")+"` > 0").Find(&unscopedRecords2, &Deleted{}) + err = testEngine.Unscoped().Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").Find(&unscopedRecords2, &Deleted{}) assert.NoError(t, err) assert.EqualValues(t, 2, len(unscopedRecords2)) var records3 []Deleted - err = testEngine.Where("`"+testEngine.ColumnMapper.Obj2Table("Id")+"` > 0").And("`"+testEngine.ColumnMapper.Obj2Table("Id")+"`> 1"). - Or("`"+testEngine.ColumnMapper.Obj2Table("Id")+"` = ?", 3).Find(&records3) + err = testEngine.Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").And("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"`> 1"). + Or("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` = ?", 3).Find(&records3) assert.NoError(t, err) assert.EqualValues(t, 2, len(records3)) } @@ -141,7 +141,7 @@ func TestDeleted(t *testing.T) { func TestCacheDelete(t *testing.T) { assert.NoError(t, prepareEngine()) - oldCacher := testEngine.Cacher + oldCacher := testEngine.GetDefaultCacher() cacher := NewLRUCacher(NewMemoryStore(), 1000) testEngine.SetDefaultCacher(cacher) diff --git a/session_find_test.go b/session_find_test.go index 9739bc44..393e4621 100644 --- a/session_find_test.go +++ b/session_find_test.go @@ -64,7 +64,7 @@ func TestJoinLimit(t *testing.T) { func assertSync(t *testing.T, beans ...interface{}) { for _, bean := range beans { assert.NoError(t, testEngine.DropTables(bean)) - assert.NoError(t, testEngine.Sync(bean)) + assert.NoError(t, testEngine.Sync2(bean)) } } @@ -105,8 +105,8 @@ func TestFind(t *testing.T) { } users2 := make([]Userinfo, 0) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") - err = testEngine.Sql("select * from " + testEngine.Quote(userinfo)).Find(&users2) + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") + err = testEngine.SQL("select * from " + testEngine.Quote(userinfo)).Find(&users2) if err != nil { t.Error(err) panic(err) @@ -199,7 +199,7 @@ func TestDistinct(t *testing.T) { assert.NoError(t, err) users := make([]Userinfo, 0) - departname := testEngine.TableMapper.Obj2Table("Departname") + departname := testEngine.GetTableMapper().Obj2Table("Departname") err = testEngine.Distinct(departname).Find(&users) if err != nil { t.Error(err) @@ -273,13 +273,13 @@ func TestHaving(t *testing.T) { func TestOrderSameMapper(t *testing.T) { assert.NoError(t, prepareEngine()) - testEngine.unMapType(rValue(new(Userinfo)).Type()) + testEngine.UnMapType(rValue(new(Userinfo)).Type()) - mapper := testEngine.TableMapper + mapper := testEngine.GetTableMapper() testEngine.SetMapper(core.SameMapper{}) defer func() { - testEngine.unMapType(rValue(new(Userinfo)).Type()) + testEngine.UnMapType(rValue(new(Userinfo)).Type()) testEngine.SetMapper(mapper) }() @@ -304,12 +304,12 @@ func TestOrderSameMapper(t *testing.T) { func TestHavingSameMapper(t *testing.T) { assert.NoError(t, prepareEngine()) - testEngine.unMapType(rValue(new(Userinfo)).Type()) + testEngine.UnMapType(rValue(new(Userinfo)).Type()) - mapper := testEngine.TableMapper + mapper := testEngine.GetTableMapper() testEngine.SetMapper(core.SameMapper{}) defer func() { - testEngine.unMapType(rValue(new(Userinfo)).Type()) + testEngine.UnMapType(rValue(new(Userinfo)).Type()) testEngine.SetMapper(mapper) }() assertSync(t, new(Userinfo)) @@ -326,7 +326,7 @@ func TestFindInts(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") var idsInt64 []int64 err := testEngine.Table(userinfo).Cols("id").Desc("id").Find(&idsInt64) if err != nil { @@ -367,8 +367,8 @@ func TestFindInts(t *testing.T) { func TestFindStrings(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") - username := testEngine.ColumnMapper.Obj2Table("Username") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") + username := testEngine.GetColumnMapper().Obj2Table("Username") var idsString []string err := testEngine.Table(userinfo).Cols(username).Desc("id").Find(&idsString) if err != nil { @@ -380,8 +380,8 @@ func TestFindStrings(t *testing.T) { func TestFindMyString(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") - username := testEngine.ColumnMapper.Obj2Table("Username") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") + username := testEngine.GetColumnMapper().Obj2Table("Username") var idsMyString []MyString err := testEngine.Table(userinfo).Cols(username).Desc("id").Find(&idsMyString) @@ -395,8 +395,8 @@ func TestFindInterface(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") - username := testEngine.ColumnMapper.Obj2Table("Username") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") + username := testEngine.GetColumnMapper().Obj2Table("Username") var idsInterface []interface{} err := testEngine.Table(userinfo).Cols(username).Desc("id").Find(&idsInterface) if err != nil { @@ -409,7 +409,7 @@ func TestFindSliceBytes(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") var ids [][][]byte err := testEngine.Table(userinfo).Desc("id").Find(&ids) if err != nil { @@ -424,7 +424,7 @@ func TestFindSlicePtrString(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") var ids [][]*string err := testEngine.Table(userinfo).Desc("id").Find(&ids) if err != nil { @@ -439,7 +439,7 @@ func TestFindMapBytes(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") var ids []map[string][]byte err := testEngine.Table(userinfo).Desc("id").Find(&ids) if err != nil { @@ -454,7 +454,7 @@ func TestFindMapPtrString(t *testing.T) { assert.NoError(t, prepareEngine()) assertSync(t, new(Userinfo)) - userinfo := testEngine.TableMapper.Obj2Table("Userinfo") + userinfo := testEngine.GetTableMapper().Obj2Table("Userinfo") var ids []map[string]*string err := testEngine.Table(userinfo).Desc("id").Find(&ids) assert.NoError(t, err) diff --git a/session_get_test.go b/session_get_test.go index 91006365..73b23a81 100644 --- a/session_get_test.go +++ b/session_get_test.go @@ -72,7 +72,7 @@ func TestGetVar(t *testing.T) { assert.Equal(t, "1.5", valuesString["money"]) // for mymysql driver, interface{} will be []byte, so ignore it currently - if testEngine.dialect.DriverName() != "mymysql" { + 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) @@ -121,10 +121,10 @@ func TestGetStruct(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync(new(UserinfoGet))) + assert.NoError(t, testEngine.Sync2(new(UserinfoGet))) var err error - if testEngine.dialect.DBType() == core.MSSQL { + if testEngine.Dialect().DBType() == core.MSSQL { _, err = testEngine.Exec("SET IDENTITY_INSERT userinfo_get ON") assert.NoError(t, err) } @@ -143,9 +143,9 @@ func TestGetStruct(t *testing.T) { Total int64 } - assert.NoError(t, testEngine.Sync(&NoIdUser{})) + assert.NoError(t, testEngine.Sync2(&NoIdUser{})) - userCol := testEngine.ColumnMapper.Obj2Table("User") + userCol := testEngine.GetColumnMapper().Obj2Table("User") _, err = testEngine.Where("`"+userCol+"` = ?", "xlw").Delete(&NoIdUser{}) assert.NoError(t, err) diff --git a/session_raw_test.go b/session_raw_test.go index cf381974..f52db7d3 100644 --- a/session_raw_test.go +++ b/session_raw_test.go @@ -19,7 +19,7 @@ func TestQuery(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync(new(UserinfoQuery))) + assert.NoError(t, testEngine.Sync2(new(UserinfoQuery))) res, err := testEngine.Exec("INSERT INTO `userinfo_query` (uid, name) VALUES (?, ?)", 1, "user") assert.NoError(t, err) diff --git a/session_schema_test.go b/session_schema_test.go index be999ce3..fa2fa7eb 100644 --- a/session_schema_test.go +++ b/session_schema_test.go @@ -126,13 +126,13 @@ func TestIsTableEmpty(t *testing.T) { assert.NoError(t, testEngine.DropTables(&PictureEmpty{}, &NumericEmpty{})) - assert.NoError(t, testEngine.Sync(new(PictureEmpty), new(NumericEmpty))) + assert.NoError(t, testEngine.Sync2(new(PictureEmpty), new(NumericEmpty))) isEmpty, err := testEngine.IsTableEmpty(&PictureEmpty{}) assert.NoError(t, err) assert.True(t, isEmpty) - tbName := testEngine.TableMapper.Obj2Table("PictureEmpty") + tbName := testEngine.GetTableMapper().Obj2Table("PictureEmpty") isEmpty, err = testEngine.IsTableEmpty(tbName) assert.NoError(t, err) assert.True(t, isEmpty) diff --git a/session_stats_test.go b/session_stats_test.go index 17eaf6dc..ec5cace1 100644 --- a/session_stats_test.go +++ b/session_stats_test.go @@ -46,8 +46,8 @@ func TestSum(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 3, cnt) - colInt := testEngine.ColumnMapper.Obj2Table("Int") - colFloat := testEngine.ColumnMapper.Obj2Table("Float") + colInt := testEngine.GetColumnMapper().Obj2Table("Int") + colFloat := testEngine.GetColumnMapper().Obj2Table("Float") sumInt, err := testEngine.Sum(new(SumStruct), colInt) assert.NoError(t, err) @@ -109,7 +109,7 @@ func TestCount(t *testing.T) { } assert.NoError(t, testEngine.Sync2(new(UserinfoCount))) - colName := testEngine.ColumnMapper.Obj2Table("Departname") + colName := testEngine.GetColumnMapper().Obj2Table("Departname") var cond builder.Cond = builder.Eq{ "`" + colName + "`": "dev", } diff --git a/session_tx_test.go b/session_tx_test.go index 3e71bb40..7102f5c7 100644 --- a/session_tx_test.go +++ b/session_tx_test.go @@ -128,11 +128,11 @@ func TestCombineTransaction(t *testing.T) { func TestCombineTransactionSameMapper(t *testing.T) { assert.NoError(t, prepareEngine()) - oldMapper := testEngine.ColumnMapper - testEngine.unMapType(rValue(new(Userinfo)).Type()) + oldMapper := testEngine.GetColumnMapper() + testEngine.UnMapType(rValue(new(Userinfo)).Type()) testEngine.SetMapper(core.SameMapper{}) defer func() { - testEngine.unMapType(rValue(new(Userinfo)).Type()) + testEngine.UnMapType(rValue(new(Userinfo)).Type()) testEngine.SetMapper(oldMapper) }() diff --git a/statement_test.go b/statement_test.go index 594aa4f3..758c2759 100644 --- a/statement_test.go +++ b/statement_test.go @@ -163,10 +163,20 @@ func (TestType) TableName() string { } func createTestStatement() *Statement { - statement := &Statement{} - statement.Init() - statement.Engine = testEngine - statement.setRefValue(reflect.ValueOf(TestType{})) + if engine, ok := testEngine.(*Engine); ok { + statement := &Statement{} + statement.Init() + statement.Engine = engine + statement.setRefValue(reflect.ValueOf(TestType{})) - return statement + return statement + } else if eg, ok := testEngine.(*EngineGroup); ok { + statement := &Statement{} + statement.Init() + statement.Engine = eg.Engine + statement.setRefValue(reflect.ValueOf(TestType{})) + + return statement + } + return nil } diff --git a/tag_extends_test.go b/tag_extends_test.go index 61a61e9e..b70eefe3 100644 --- a/tag_extends_test.go +++ b/tag_extends_test.go @@ -202,13 +202,13 @@ func TestExtends(t *testing.T) { var info UserAndDetail qt := testEngine.Quote - ui := testEngine.TableMapper.Obj2Table("Userinfo") - ud := testEngine.TableMapper.Obj2Table("Userdetail") - uiid := testEngine.TableMapper.Obj2Table("Id") + ui := testEngine.GetTableMapper().Obj2Table("Userinfo") + ud := testEngine.GetTableMapper().Obj2Table("Userdetail") + uiid := testEngine.GetTableMapper().Obj2Table("Id") udid := "detail_id" sql := fmt.Sprintf("select * from %s, %s where %s.%s = %s.%s", qt(ui), qt(ud), qt(ui), qt(udid), qt(ud), qt(uiid)) - b, err := testEngine.Sql(sql).NoCascade().Get(&info) + b, err := testEngine.SQL(sql).NoCascade().Get(&info) if err != nil { t.Error(err) panic(err) @@ -329,7 +329,7 @@ func TestExtends2(t *testing.T) { Uid: sender.Id, ToUid: receiver.Id, } - if testEngine.dialect.DBType() == core.MSSQL { + if testEngine.Dialect().DBType() == core.MSSQL { _, err = testEngine.Exec("SET IDENTITY_INSERT message ON") assert.NoError(t, err) } @@ -340,7 +340,7 @@ func TestExtends2(t *testing.T) { panic(err) } - var mapper = testEngine.TableMapper.Obj2Table + var mapper = testEngine.GetTableMapper().Obj2Table userTableName := mapper("MessageUser") typeTableName := mapper("MessageType") msgTableName := mapper("Message") @@ -401,7 +401,7 @@ func TestExtends3(t *testing.T) { Uid: sender.Id, ToUid: receiver.Id, } - if testEngine.dialect.DBType() == core.MSSQL { + if testEngine.Dialect().DBType() == core.MSSQL { _, err = testEngine.Exec("SET IDENTITY_INSERT message ON") assert.NoError(t, err) } @@ -411,7 +411,7 @@ func TestExtends3(t *testing.T) { panic(err) } - var mapper = testEngine.TableMapper.Obj2Table + var mapper = testEngine.GetTableMapper().Obj2Table userTableName := mapper("MessageUser") typeTableName := mapper("MessageType") msgTableName := mapper("Message") @@ -488,7 +488,7 @@ func TestExtends4(t *testing.T) { Content: "test", Uid: sender.Id, } - if testEngine.dialect.DBType() == core.MSSQL { + if testEngine.Dialect().DBType() == core.MSSQL { _, err = testEngine.Exec("SET IDENTITY_INSERT message ON") assert.NoError(t, err) } @@ -498,7 +498,7 @@ func TestExtends4(t *testing.T) { panic(err) } - var mapper = testEngine.TableMapper.Obj2Table + var mapper = testEngine.GetTableMapper().Obj2Table userTableName := mapper("MessageUser") typeTableName := mapper("MessageType") msgTableName := mapper("Message") diff --git a/tag_id_test.go b/tag_id_test.go index d22cc7b1..a53fe6bd 100644 --- a/tag_id_test.go +++ b/tag_id_test.go @@ -18,11 +18,11 @@ type IDGonicMapper struct { func TestGonicMapperID(t *testing.T) { assert.NoError(t, prepareEngine()) - oldMapper := testEngine.ColumnMapper - testEngine.unMapType(rValue(new(IDGonicMapper)).Type()) + oldMapper := testEngine.GetColumnMapper() + testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) testEngine.SetMapper(core.LintGonicMapper) defer func() { - testEngine.unMapType(rValue(new(IDGonicMapper)).Type()) + testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) testEngine.SetMapper(oldMapper) }() @@ -55,11 +55,11 @@ type IDSameMapper struct { func TestSameMapperID(t *testing.T) { assert.NoError(t, prepareEngine()) - oldMapper := testEngine.ColumnMapper - testEngine.unMapType(rValue(new(IDSameMapper)).Type()) + oldMapper := testEngine.GetColumnMapper() + testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) testEngine.SetMapper(core.SameMapper{}) defer func() { - testEngine.unMapType(rValue(new(IDSameMapper)).Type()) + testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) testEngine.SetMapper(oldMapper) }() diff --git a/tag_test.go b/tag_test.go index ef5028f6..c9b76048 100644 --- a/tag_test.go +++ b/tag_test.go @@ -123,7 +123,7 @@ func TestCreatedUpdated(t *testing.T) { Updated time.Time `xorm:"updated"` } - err := testEngine.Sync(&CreatedUpdated{}) + err := testEngine.Sync2(&CreatedUpdated{}) assert.NoError(t, err) c := &CreatedUpdated{Name: "test"} @@ -178,7 +178,7 @@ type Lowercase struct { func TestLowerCase(t *testing.T) { assert.NoError(t, prepareEngine()) - err := testEngine.Sync(&Lowercase{}) + err := testEngine.Sync2(&Lowercase{}) _, err = testEngine.Where("(id) > 0").Delete(&Lowercase{}) if err != nil { t.Error(err) @@ -255,7 +255,7 @@ func TestAutoIncrTag(t *testing.T) { func TestTagComment(t *testing.T) { assert.NoError(t, prepareEngine()) // FIXME: only support mysql - if testEngine.dialect.DriverName() != core.MYSQL { + if testEngine.Dialect().DriverName() != core.MYSQL { return } @@ -371,7 +371,7 @@ func TestTagTime(t *testing.T) { assertSync(t, new(TagUTCStruct)) - assert.EqualValues(t, time.Local.String(), testEngine.TZLocation.String()) + assert.EqualValues(t, time.Local.String(), testEngine.GetTZLocation().String()) s := TagUTCStruct{ Name: "utc", diff --git a/time_test.go b/time_test.go index 15b20c37..b7e4d12b 100644 --- a/time_test.go +++ b/time_test.go @@ -47,10 +47,10 @@ func TestTimeUserTimeDiffLoc(t *testing.T) { assert.NoError(t, prepareEngine()) loc, err := time.LoadLocation("Asia/Shanghai") assert.NoError(t, err) - testEngine.TZLocation = loc + testEngine.SetTZLocation(loc) dbLoc, err := time.LoadLocation("America/New_York") assert.NoError(t, err) - testEngine.DatabaseTZ = dbLoc + testEngine.SetTZDatabase(dbLoc) type TimeUser2 struct { Id string @@ -112,10 +112,10 @@ func TestTimeUserCreatedDiffLoc(t *testing.T) { assert.NoError(t, prepareEngine()) loc, err := time.LoadLocation("Asia/Shanghai") assert.NoError(t, err) - testEngine.TZLocation = loc + testEngine.SetTZLocation(loc) dbLoc, err := time.LoadLocation("America/New_York") assert.NoError(t, err) - testEngine.DatabaseTZ = dbLoc + testEngine.SetTZDatabase(dbLoc) type UserCreated2 struct { Id string @@ -198,10 +198,10 @@ func TestTimeUserUpdatedDiffLoc(t *testing.T) { assert.NoError(t, prepareEngine()) loc, err := time.LoadLocation("Asia/Shanghai") assert.NoError(t, err) - testEngine.TZLocation = loc + testEngine.SetTZLocation(loc) dbLoc, err := time.LoadLocation("America/New_York") assert.NoError(t, err) - testEngine.DatabaseTZ = dbLoc + testEngine.SetTZDatabase(dbLoc) type UserUpdated2 struct { Id string @@ -302,10 +302,10 @@ func TestTimeUserDeletedDiffLoc(t *testing.T) { assert.NoError(t, prepareEngine()) loc, err := time.LoadLocation("Asia/Shanghai") assert.NoError(t, err) - testEngine.TZLocation = loc + testEngine.SetTZLocation(loc) dbLoc, err := time.LoadLocation("America/New_York") assert.NoError(t, err) - testEngine.DatabaseTZ = dbLoc + testEngine.SetTZDatabase(dbLoc) type UserDeleted2 struct { Id string @@ -426,10 +426,10 @@ func TestCustomTimeUserDeletedDiffLoc(t *testing.T) { assert.NoError(t, prepareEngine()) loc, err := time.LoadLocation("Asia/Shanghai") assert.NoError(t, err) - testEngine.TZLocation = loc + testEngine.SetTZLocation(loc) dbLoc, err := time.LoadLocation("America/New_York") assert.NoError(t, err) - testEngine.DatabaseTZ = dbLoc + testEngine.SetTZDatabase(dbLoc) type UserDeleted4 struct { Id string diff --git a/types_test.go b/types_test.go index df4ee70e..3dc1cf9d 100644 --- a/types_test.go +++ b/types_test.go @@ -154,7 +154,7 @@ func TestConversion(t *testing.T) { c := new(ConvStruct) assert.NoError(t, testEngine.DropTables(c)) - assert.NoError(t, testEngine.Sync(c)) + assert.NoError(t, testEngine.Sync2(c)) var s ConvString = "sssss" c.Conv = "tttt" @@ -304,7 +304,7 @@ func TestCustomType2(t *testing.T) { err := testEngine.CreateTables(&UserCus{}) assert.NoError(t, err) - tableName := testEngine.TableMapper.Obj2Table("UserCus") + tableName := testEngine.GetTableMapper().Obj2Table("UserCus") _, err = testEngine.Exec("delete from " + testEngine.Quote(tableName)) assert.NoError(t, err) @@ -327,7 +327,7 @@ func TestCustomType2(t *testing.T) { fmt.Println(user) users := make([]UserCus, 0) - err = testEngine.Where("`"+testEngine.ColumnMapper.Obj2Table("Status")+"` = ?", "Registed").Find(&users) + err = testEngine.Where("`"+testEngine.GetColumnMapper().Obj2Table("Status")+"` = ?", "Registed").Find(&users) assert.NoError(t, err) assert.EqualValues(t, 1, len(users)) diff --git a/xorm_test.go b/xorm_test.go index 1a757d3f..c201b8c9 100644 --- a/xorm_test.go +++ b/xorm_test.go @@ -16,7 +16,7 @@ import ( ) var ( - testEngine *Engine + testEngine EngineInterface dbType string connString string @@ -25,18 +25,25 @@ var ( ptrConnStr = flag.String("conn_str", "./test.db?cache=shared&mode=rwc", "test database connection string") mapType = flag.String("map_type", "snake", "indicate the name mapping") cache = flag.Bool("cache", false, "if enable cache") + cluster = flag.Bool("cluster", false, "if this is a cluster") ) func createEngine(dbType, connStr string) error { if testEngine == nil { var err error - testEngine, err = NewEngine(dbType, connStr) + + if !*cluster { + testEngine, err = NewEngine(dbType, connStr) + + } else { + testEngine, err = NewEngineGroup(dbType, connStr) + } if err != nil { return err } testEngine.ShowSQL(*showSQL) - testEngine.logger.SetLevel(core.LOG_DEBUG) + testEngine.SetLogLevel(core.LOG_DEBUG) if *cache { cacher := NewLRUCacher(NewMemoryStore(), 100000) testEngine.SetDefaultCacher(cacher) From 51acb3530a1b79c4c6a3c48b8990a0b068880724 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 28 Sep 2017 17:30:00 +0800 Subject: [PATCH 30/31] improve the NewEngineGroup interface --- engine_group.go | 5 +---- xorm_test.go | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/engine_group.go b/engine_group.go index 223eb985..4d346231 100644 --- a/engine_group.go +++ b/engine_group.go @@ -5,8 +5,6 @@ package xorm import ( - "strings" - "github.com/go-xorm/core" ) @@ -27,9 +25,8 @@ func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolic } driverName, ok1 := args1.(string) - dataSourceNames, ok2 := args2.(string) + conns, ok2 := args2.([]string) if ok1 && ok2 { - conns := strings.Split(dataSourceNames, ";") engines := make([]*Engine, len(conns)) for i, conn := range conns { engine, err := NewEngine(driverName, conn) diff --git a/xorm_test.go b/xorm_test.go index c201b8c9..569bc681 100644 --- a/xorm_test.go +++ b/xorm_test.go @@ -26,6 +26,7 @@ var ( mapType = flag.String("map_type", "snake", "indicate the name mapping") cache = flag.Bool("cache", false, "if enable cache") cluster = flag.Bool("cluster", false, "if this is a cluster") + splitter = flag.String("splitter", ";", "the splitter on connstr for cluster") ) func createEngine(dbType, connStr string) error { @@ -36,7 +37,7 @@ func createEngine(dbType, connStr string) error { testEngine, err = NewEngine(dbType, connStr) } else { - testEngine, err = NewEngineGroup(dbType, connStr) + testEngine, err = NewEngineGroup(dbType, strings.Split(connStr, *splitter)) } if err != nil { return err From caf55f1ca375b4c1fcab85bead8ae254d36a3406 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 28 Sep 2017 17:37:39 +0800 Subject: [PATCH 31/31] change the default policy of engine group --- engine_group.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine_group.go b/engine_group.go index 4d346231..1de425f3 100644 --- a/engine_group.go +++ b/engine_group.go @@ -21,7 +21,7 @@ func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolic if len(policies) > 0 { eg.policy = policies[0] } else { - eg.policy = RandomPolicy() + eg.policy = RoundRobinPolicy() } driverName, ok1 := args1.(string)