diff --git a/README.md b/README.md index 6a224f33..2f22bc09 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,15 @@ Drivers for Go's sql package which currently support database/sql includes: * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) -* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (experiment) - # Changelog +* **v0.6.0** + * remove support for ql + * add query condition builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder), so Where, And, Or +methods can use `builder.Cond` as parameter + * add Sum, SumInt, SumInt64 and NotIn methods + * some bugs fixed + * **v0.5.0** * logging interface changed * some bugs fixed diff --git a/README_CN.md b/README_CN.md index ac6aa410..4d135075 100644 --- a/README_CN.md +++ b/README_CN.md @@ -52,10 +52,15 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持) -* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (试验性支持) - ## 更新日志 +* **v0.6.0** + * 去除对 ql 的支持 + * 新增条件查询分析器 [github.com/go-xorm/builder](https://github.com/go-xorm/builder), 从因此 `Where, And, Or` 函数 +将可以用 `builder.Cond` 作为条件组合 + * 新增 Sum, SumInt, SumInt64 和 NotIn 函数 + * Bug修正 + * **v0.5.0** * logging接口进行不兼容改变 * Bug修正 diff --git a/VERSION b/VERSION index a4fb7795..b91f308b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.5.5.0916 +xorm v0.6.0.0917Beta diff --git a/engine.go b/engine.go index 7170fce5..812bd7c6 100644 --- a/engine.go +++ b/engine.go @@ -641,10 +641,10 @@ func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { } // Where method provide a condition query -func (engine *Engine) Where(querystring string, args ...interface{}) *Session { +func (engine *Engine) Where(query interface{}, args ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true - return session.Where(querystring, args...) + return session.Where(query, args...) } // Id will be depracated, please use ID instead diff --git a/session.go b/session.go index cbeee8d4..a58b6417 100644 --- a/session.go +++ b/session.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/go-xorm/builder" "github.com/go-xorm/core" ) @@ -112,7 +113,7 @@ func (session *Session) Prepare() *Session { return session } -// Sql will be deprecated, please use SQL instead. +// Sql !DEPRECIATED! will be deprecated, please use SQL instead. func (session *Session) Sql(querystring string, args ...interface{}) *Session { session.Statement.Sql(querystring, args...) return session @@ -126,20 +127,20 @@ func (session *Session) SQL(querystring string, args ...interface{}) *Session { } // Where provides custom query condition. -func (session *Session) Where(querystring string, args ...interface{}) *Session { - session.Statement.Where(querystring, args...) +func (session *Session) Where(query interface{}, args ...interface{}) *Session { + session.Statement.Where(query, args...) return session } // And provides custom query condition. -func (session *Session) And(querystring string, args ...interface{}) *Session { - session.Statement.And(querystring, args...) +func (session *Session) And(query interface{}, args ...interface{}) *Session { + session.Statement.And(query, args...) return session } // Or provides custom query condition. -func (session *Session) Or(querystring string, args ...interface{}) *Session { - session.Statement.Or(querystring, args...) +func (session *Session) Or(query interface{}, args ...interface{}) *Session { + session.Statement.Or(query, args...) return session } @@ -189,6 +190,12 @@ func (session *Session) In(column string, args ...interface{}) *Session { return session } +// NotIn provides a query string like "id in (1, 2, 3)" +func (session *Session) NotIn(column string, args ...interface{}) *Session { + session.Statement.NotIn(column, args...) + return session +} + // Incr provides a query string like "count = count + 1" func (session *Session) Incr(column string, arg ...interface{}) *Session { session.Statement.Incr(column, arg...) @@ -892,15 +899,14 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in ff = append(ff, ie[0]) } - newSession.In(table.PrimaryKeys[0], ff...) + newSession.In("`"+table.PrimaryKeys[0]+"`", ff...) } else { - var kn = make([]string, 0) - for _, name := range table.PrimaryKeys { - kn = append(kn, name+" = ?") - } - condi := "(" + strings.Join(kn, " AND ") + ")" for _, ie := range ides { - newSession.Or(condi, ie...) + cond := builder.NewCond() + for i, name := range table.PrimaryKeys { + cond = cond.And(builder.Eq{"`" + name + "`": ie[i]}) + } + newSession.Or(cond) } } @@ -1240,10 +1246,13 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) var table = session.Statement.RefTable var addedTableName = (len(session.Statement.JoinStr) > 0) + var autoCond builder.Cond if !session.Statement.noAutoCondition && len(condiBean) > 0 { - colNames, args := session.Statement.buildConditions(table, condiBean[0], true, true, false, true, addedTableName) - session.Statement.ConditionStr = strings.Join(colNames, " AND ") - session.Statement.BeanArgs = args + var err error + autoCond, err = session.Statement.buildConds(table, condiBean[0], true, true, false, true, addedTableName) + if err != nil { + panic(err) + } } else { // !oinume! Add " IS NULL" to WHERE whatever condiBean is given. // See https://github.com/go-xorm/xorm/issues/179 @@ -1256,8 +1265,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } colName = session.Engine.Quote(nm) + "." + colName } - session.Statement.ConditionStr = fmt.Sprintf("(%v IS NULL OR %v = '0001-01-01 00:00:00')", - colName, colName) + autoCond = builder.IsNull{colName}.Or(builder.Eq{colName: "0001-01-01 00:00:00"}) } } @@ -1291,12 +1299,10 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } } - session.Statement.attachInSql() - session.Statement.Params = append(append(append(session.Statement.joinArgs, session.Statement.Params...), - session.Statement.BeanArgs...), session.Statement.inParams...) + condSQL, condArgs, _ := builder.ToSQL(session.Statement.cond.And(autoCond)) - sqlStr = session.Statement.genSelectSQL(columnStr) - args = session.Statement.Params + args = append(session.Statement.joinArgs, condArgs...) + sqlStr = session.Statement.genSelectSQL(columnStr, condSQL) // for mssql and use limit qs := strings.Count(sqlStr, "?") if len(args)*2 == qs { @@ -3567,97 +3573,68 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+v.expr) } - var condiColNames []string - var condiArgs []interface{} + session.Statement.processIdParam() + var autoCond builder.Cond if !session.Statement.noAutoCondition && len(condiBean) > 0 { - condiColNames, condiArgs = session.Statement.buildConditions(session.Statement.RefTable, condiBean[0], true, true, false, true, false) + var err error + autoCond, err = session.Statement.buildConds(session.Statement.RefTable, condiBean[0], true, true, false, true, false) + if err != nil { + return 0, err + } } - var condition = "" - session.Statement.processIdParam() st := session.Statement defer session.resetStatement() - if st.WhereStr != "" { - condition = fmt.Sprintf("%v", st.WhereStr) - } - if condition == "" { - if len(condiColNames) > 0 { - condition = fmt.Sprintf("%v", strings.Join(condiColNames, " "+session.Engine.Dialect().AndStr()+" ")) - } - } else { - if len(condiColNames) > 0 { - condition = fmt.Sprintf("(%v) %v (%v)", condition, - session.Engine.Dialect().AndStr(), strings.Join(condiColNames, " "+session.Engine.Dialect().AndStr()+" ")) - } - } + var sqlStr string + var condArgs []interface{} + var condSQL string + cond := session.Statement.cond.And(autoCond) - var sqlStr, inSQL string - var inArgs []interface{} doIncVer := false var verValue *reflect.Value if table != nil && table.Version != "" && session.Statement.checkVersion { - if condition != "" { - condition = fmt.Sprintf("WHERE (%v) %v %v = ?", condition, session.Engine.Dialect().AndStr(), - session.Engine.Quote(table.Version)) - } else { - condition = fmt.Sprintf("WHERE %v = ?", session.Engine.Quote(table.Version)) + verValue, err = table.VersionColumn().ValueOf(bean) + if err != nil { + return 0, err } - inSQL, inArgs = session.Statement.genInSql() - if len(inSQL) > 0 { - if condition != "" { - condition += " " + session.Engine.Dialect().AndStr() + " " + inSQL - } else { - condition = "WHERE " + inSQL - } + + cond = cond.And(builder.Eq{session.Engine.Quote(table.Version): verValue.Interface()}) + condSQL, condArgs, _ = builder.ToSQL(cond) + + if len(condSQL) > 0 { + condSQL = "WHERE " + condSQL } if st.LimitN > 0 { - condition = condition + fmt.Sprintf(" LIMIT %d", st.LimitN) + condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) } sqlStr = fmt.Sprintf("UPDATE %v SET %v, %v %v", session.Engine.Quote(session.Statement.TableName()), strings.Join(colNames, ", "), session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1", - condition) + condSQL) - verValue, err = table.VersionColumn().ValueOf(bean) - if err != nil { - return 0, err - } - - condiArgs = append(condiArgs, verValue.Interface()) doIncVer = true } else { - if condition != "" { - condition = "WHERE " + condition - } - inSQL, inArgs = session.Statement.genInSql() - if len(inSQL) > 0 { - if condition != "" { - condition += " " + session.Engine.Dialect().AndStr() + " " + inSQL - } else { - condition = "WHERE " + inSQL - } + condSQL, condArgs, _ = builder.ToSQL(cond) + if len(condSQL) > 0 { + condSQL = "WHERE " + condSQL } if st.LimitN > 0 { - condition = condition + fmt.Sprintf(" LIMIT %d", st.LimitN) + condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) } sqlStr = fmt.Sprintf("UPDATE %v SET %v %v", session.Engine.Quote(session.Statement.TableName()), strings.Join(colNames, ", "), - condition) + condSQL) } - args = append(args, st.Params...) - args = append(args, inArgs...) - args = append(args, condiArgs...) - - res, err := session.exec(sqlStr, args...) + res, err := session.exec(sqlStr, append(args, condArgs...)...) if err != nil { return 0, err } else if doIncVer { @@ -3791,41 +3768,27 @@ func (session *Session) Delete(bean interface{}) (int64, error) { } // -- - var colNames []string - var args []interface{} - + var autoCond builder.Cond if !session.Statement.noAutoCondition { - colNames, args = session.Statement.buildConditions(table, bean, true, true, false, true, false) + var err error + autoCond, err = session.Statement.buildConds(table, bean, true, true, false, true, false) + if err != nil { + return 0, err + } } - var condition = "" - var andStr = session.Engine.dialect.AndStr() session.Statement.processIdParam() - if session.Statement.WhereStr != "" { - condition = session.Statement.WhereStr - if len(colNames) > 0 { - condition += " " + andStr + " " + strings.Join(colNames, " "+andStr+" ") - } - } else { - condition = strings.Join(colNames, " "+andStr+" ") - } - inSQL, inArgs := session.Statement.genInSql() - if len(inSQL) > 0 { - if len(condition) > 0 { - condition += " " + andStr + " " - } - condition += inSQL - args = append(args, inArgs...) - } - if len(condition) == 0 && session.Statement.LimitN == 0 { + + condSQL, condArgs, _ := builder.ToSQL(session.Statement.cond.And(autoCond)) + if len(condSQL) == 0 && session.Statement.LimitN == 0 { return 0, ErrNeedDeletedCond } var deleteSQL, realSQL string var tableName = session.Engine.Quote(session.Statement.TableName()) - if len(condition) > 0 { - deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condition) + if len(condSQL) > 0 { + deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL) } else { deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName) } @@ -3842,14 +3805,14 @@ func (session *Session) Delete(bean interface{}) (int64, error) { switch session.Engine.dialect.DBType() { case core.POSTGRES: inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) - if len(condition) > 0 { + if len(condSQL) > 0 { deleteSQL += " AND " + inSQL } else { deleteSQL += " WHERE " + inSQL } case core.SQLITE: inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) - if len(condition) > 0 { + if len(condSQL) > 0 { deleteSQL += " AND " + inSQL } else { deleteSQL += " WHERE " + inSQL @@ -3862,34 +3825,34 @@ func (session *Session) Delete(bean interface{}) (int64, error) { } } - argsForCache := make([]interface{}, 0, len(args)*2) + argsForCache := make([]interface{}, 0, len(condArgs)*2) if session.Statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled realSQL = deleteSQL - copy(argsForCache, args) - argsForCache = append(session.Statement.Params, argsForCache...) + copy(argsForCache, condArgs) + argsForCache = append(condArgs, argsForCache...) } else { // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache. - copy(argsForCache, args) - argsForCache = append(session.Statement.Params, argsForCache...) + copy(argsForCache, condArgs) + argsForCache = append(condArgs, argsForCache...) deletedColumn := table.DeletedColumn() realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v", session.Engine.Quote(session.Statement.TableName()), session.Engine.Quote(deletedColumn.Name), - condition) + condSQL) if len(orderSQL) > 0 { switch session.Engine.dialect.DBType() { case core.POSTGRES: inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) - if len(condition) > 0 { + if len(condSQL) > 0 { realSQL += " AND " + inSQL } else { realSQL += " WHERE " + inSQL } case core.SQLITE: inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) - if len(condition) > 0 { + if len(condSQL) > 0 { realSQL += " AND " + inSQL } else { realSQL += " WHERE " + inSQL @@ -3903,12 +3866,12 @@ func (session *Session) Delete(bean interface{}) (int64, error) { } // !oinume! Insert NowTime to the head of session.Statement.Params - session.Statement.Params = append(session.Statement.Params, "") - paramsLen := len(session.Statement.Params) - copy(session.Statement.Params[1:paramsLen], session.Statement.Params[0:paramsLen-1]) + condArgs = append(condArgs, "") + paramsLen := len(condArgs) + copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1]) val, t := session.Engine.NowTime2(deletedColumn.SQLType.Name) - session.Statement.Params[0] = val + condArgs[0] = val var colName = deletedColumn.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { @@ -3917,13 +3880,11 @@ func (session *Session) Delete(bean interface{}) (int64, error) { }) } - args = append(session.Statement.Params, args...) - if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache { session.cacheDelete(deleteSQL, argsForCache...) } - res, err := session.exec(realSQL, args...) + res, err := session.exec(realSQL, condArgs...) if err != nil { return 0, err } diff --git a/statement.go b/statement.go index 8fee89d7..5536d9cf 100644 --- a/statement.go +++ b/statement.go @@ -14,14 +14,10 @@ import ( "strings" "time" + "github.com/go-xorm/builder" "github.com/go-xorm/core" ) -type inParam struct { - colName string - args []interface{} -} - type incrParam struct { colName string arg interface{} @@ -43,10 +39,7 @@ type Statement struct { Engine *Engine Start int LimitN int - WhereStr string IdParam *core.PK - Params []interface{} - inParams []interface{} OrderStr string JoinStr string joinArgs []interface{} @@ -57,7 +50,6 @@ type Statement struct { columnMap map[string]bool useAllCols bool OmitStr string - ConditionStr string AltTableName string tableName string RawSQL string @@ -66,7 +58,6 @@ type Statement struct { UseAutoJoin bool StoreEngine string Charset string - BeanArgs []interface{} UseCache bool UseAutoTime bool noAutoCondition bool @@ -78,10 +69,10 @@ type Statement struct { unscoped bool mustColumnMap map[string]bool nullableMap map[string]bool - inColumns map[string]*inParam incrColumns map[string]incrParam decrColumns map[string]decrParam exprColumns map[string]exprParam + cond builder.Cond } // Init reset all the statment's fields @@ -89,9 +80,6 @@ func (statement *Statement) Init() { statement.RefTable = nil statement.Start = 0 statement.LimitN = 0 - statement.WhereStr = "" - statement.Params = make([]interface{}, 0) - statement.inParams = make([]interface{}, 0) statement.OrderStr = "" statement.UseCascade = true statement.JoinStr = "" @@ -101,13 +89,11 @@ func (statement *Statement) Init() { statement.ColumnStr = "" statement.OmitStr = "" statement.columnMap = make(map[string]bool) - statement.ConditionStr = "" statement.AltTableName = "" statement.tableName = "" statement.IdParam = nil statement.RawSQL = "" statement.RawParams = make([]interface{}, 0) - statement.BeanArgs = make([]interface{}, 0) statement.UseCache = true statement.UseAutoTime = true statement.noAutoCondition = false @@ -121,10 +107,10 @@ func (statement *Statement) Init() { statement.nullableMap = make(map[string]bool) statement.checkVersion = true statement.unscoped = false - statement.inColumns = make(map[string]*inParam) statement.incrColumns = make(map[string]incrParam) statement.decrColumns = make(map[string]decrParam) statement.exprColumns = make(map[string]exprParam) + statement.cond = builder.NewCond() } // NoAutoCondition if you do not want convert bean's field as query condition, then use this function @@ -136,6 +122,12 @@ func (statement *Statement) NoAutoCondition(no ...bool) *Statement { return statement } +// Alias set the table alias +func (statement *Statement) Alias(alias string) *Statement { + statement.TableAlias = alias + return statement +} + // Sql add the raw sql statement func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement { statement.RawSQL = querystring @@ -143,52 +135,71 @@ func (statement *Statement) Sql(querystring string, args ...interface{}) *Statem return statement } -// Alias set the table alias -func (statement *Statement) Alias(alias string) *Statement { - statement.TableAlias = alias - return statement -} - // Where add Where statment -func (statement *Statement) Where(querystring string, args ...interface{}) *Statement { - // The second where will be triggered as And - if len(statement.WhereStr) > 0 { - return statement.And(querystring, args...) - } - - if !strings.Contains(querystring, statement.Engine.dialect.EqStr()) { - querystring = strings.Replace(querystring, "=", statement.Engine.dialect.EqStr(), -1) - } - statement.WhereStr = querystring - statement.Params = args - return statement +func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement { + return statement.And(query, args...) } // And add Where & and statment -func (statement *Statement) And(querystring string, args ...interface{}) *Statement { - if len(statement.WhereStr) > 0 { - var buf bytes.Buffer - fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr, - statement.Engine.dialect.AndStr(), querystring) - statement.WhereStr = buf.String() - } else { - statement.WhereStr = querystring +func (statement *Statement) And(query interface{}, args ...interface{}) *Statement { + switch query.(type) { + case string: + cond := builder.Expr(query.(string), args...) + statement.cond = statement.cond.And(cond) + case builder.Cond: + cond := query.(builder.Cond) + statement.cond = statement.cond.And(cond) + for _, v := range args { + if vv, ok := v.(builder.Cond); ok { + statement.cond = statement.cond.And(vv) + } + } + default: + // TODO: not support condition type } - statement.Params = append(statement.Params, args...) + return statement } // Or add Where & Or statment -func (statement *Statement) Or(querystring string, args ...interface{}) *Statement { - if len(statement.WhereStr) > 0 { - var buf bytes.Buffer - fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr, - statement.Engine.dialect.OrStr(), querystring) - statement.WhereStr = buf.String() - } else { - statement.WhereStr = querystring +func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement { + switch query.(type) { + case string: + cond := builder.Expr(query.(string), args...) + statement.cond = statement.cond.Or(cond) + case builder.Cond: + cond := query.(builder.Cond) + statement.cond = statement.cond.Or(cond) + for _, v := range args { + if vv, ok := v.(builder.Cond); ok { + statement.cond = statement.cond.Or(vv) + } + } + default: + // TODO: not support condition type } - statement.Params = append(statement.Params, args...) + return statement +} + +// In generate "Where column IN (?) " statment +func (statement *Statement) In(column string, args ...interface{}) *Statement { + if len(args) == 0 { + return statement + } + + in := builder.In(column, args...) + statement.cond = statement.cond.And(in) + return statement +} + +// NotIn generate "Where column IN (?) " statment +func (statement *Statement) NotIn(column string, args ...interface{}) *Statement { + if len(args) == 0 { + return statement + } + + in := builder.NotIn(column, args...) + statement.cond = statement.cond.And(in) return statement } @@ -459,13 +470,11 @@ func (statement *Statement) colName(col *core.Column, tableName string) string { return statement.Engine.Quote(col.Name) } -// Auto generating conditions according a struct -func buildConditions(engine *Engine, table *core.Table, bean interface{}, +func buildConds(engine *Engine, table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool, - mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) ([]string, []interface{}) { - var colNames []string - var args = make([]interface{}, 0) + mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) { + var conds []builder.Cond for _, col := range table.Columns() { if !includeVersion && col.IsVersion { continue @@ -502,8 +511,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, } if col.IsDeleted && !unscoped { // tag "deleted" is enabled - colNames = append(colNames, fmt.Sprintf("(%v IS NULL OR %v = '0001-01-01 00:00:00')", - colName, colName)) + conds = append(conds, builder.IsNull{colName}.Or(builder.Eq{colName: "0001-01-01 00:00:00"})) } fieldValue := *fieldValuePtr @@ -524,8 +532,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, if fieldType.Kind() == reflect.Ptr { if fieldValue.IsNil() { if includeNil { - args = append(args, nil) - colNames = append(colNames, fmt.Sprintf("%v %s ?", colName, engine.dialect.EqStr())) + conds = append(conds, builder.Eq{colName: nil}) } continue } else if !fieldValue.IsValid() { @@ -668,17 +675,10 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, val = fieldValue.Interface() } - args = append(args, val) - var condi string - if col.IsPrimaryKey && engine.dialect.DBType() == "ql" { - condi = "id() == ?" - } else { - condi = fmt.Sprintf("%v %s ?", colName, engine.dialect.EqStr()) - } - colNames = append(colNames, condi) + conds = append(conds, builder.Eq{colName: val}) } - return colNames, args + return builder.And(conds...), nil } // TableName return current tableName @@ -762,70 +762,6 @@ func (statement *Statement) getExpr() map[string]exprParam { return statement.exprColumns } -// In generate "Where column IN (?) " statment -func (statement *Statement) In(column string, args ...interface{}) *Statement { - length := len(args) - if length == 0 { - return statement - } - - k := strings.ToLower(column) - var newargs []interface{} - if length == 1 && - reflect.TypeOf(args[0]).Kind() == reflect.Slice { - newargs = make([]interface{}, 0) - v := reflect.ValueOf(args[0]) - for i := 0; i < v.Len(); i++ { - newargs = append(newargs, v.Index(i).Interface()) - } - } else { - newargs = args - } - - if _, ok := statement.inColumns[k]; ok { - statement.inColumns[k].args = append(statement.inColumns[k].args, newargs...) - } else { - statement.inColumns[k] = &inParam{column, newargs} - } - return statement -} - -func (statement *Statement) genInSql() (string, []interface{}) { - if len(statement.inColumns) == 0 { - return "", []interface{}{} - } - - inStrs := make([]string, len(statement.inColumns), len(statement.inColumns)) - args := make([]interface{}, 0, len(statement.inColumns)) - var buf bytes.Buffer - var i int - for _, params := range statement.inColumns { - buf.Reset() - fmt.Fprintf(&buf, "(%v IN (%v))", - statement.Engine.quoteColumn(params.colName), - strings.Join(makeArray("?", len(params.args)), ",")) - inStrs[i] = buf.String() - i++ - args = append(args, params.args...) - } - - if len(statement.inColumns) == 1 { - return inStrs[0], args - } - return fmt.Sprintf("(%v)", strings.Join(inStrs, " "+statement.Engine.dialect.AndStr()+" ")), args -} - -func (statement *Statement) attachInSql() { - inSql, inArgs := statement.genInSql() - if len(inSql) > 0 { - if len(statement.ConditionStr) > 0 { - statement.ConditionStr += " " + statement.Engine.dialect.AndStr() + " " - } - statement.ConditionStr += inSql - statement.inParams = inArgs - } -} - func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { newColumns := make([]string, 0) for _, col := range columns { @@ -1134,13 +1070,17 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) var table = statement.RefTable var addedTableName = (len(statement.JoinStr) > 0) + var autoCond builder.Cond if !statement.noAutoCondition { - colNames, args := statement.buildConditions(table, bean, true, true, false, true, addedTableName) - - statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") - statement.BeanArgs = args + var err error + autoCond, err = statement.buildConds(table, bean, true, true, false, true, addedTableName) + if err != nil { + panic(err) + } } + statement.processIdParam() + var columnStr = statement.ColumnStr if len(statement.selectStr) > 0 { columnStr = statement.selectStr @@ -1165,9 +1105,9 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) } } - statement.attachInSql() // !admpub! fix bug:Iterate func missing "... IN (...)" - return statement.genSelectSQL(columnStr), append(append(append(statement.joinArgs, statement.Params...), - statement.BeanArgs...), statement.inParams...) + inSQL, inArgs, _ := builder.ToSQL(statement.cond.And(autoCond)) + + return statement.genSelectSQL(columnStr, inSQL), append(statement.joinArgs, inArgs...) } func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) { @@ -1177,55 +1117,53 @@ func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) { return sql, []interface{}{} } -func (statement *Statement) buildConditions(table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) ([]string, []interface{}) { - return buildConditions(statement.Engine, table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols, +func (statement *Statement) buildConds(table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) (builder.Cond, error) { + return buildConds(statement.Engine, table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols, statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName) } func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}) { statement.setRefValue(rValue(bean)) - var addedTableName = (len(statement.JoinStr) > 0) - + var autoCond builder.Cond if !statement.noAutoCondition { - colNames, args := statement.buildConditions(statement.RefTable, bean, true, true, false, true, addedTableName) - - statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") - statement.BeanArgs = args + var err error + var addedTableName = (len(statement.JoinStr) > 0) + autoCond, err = statement.buildConds(statement.RefTable, bean, true, true, false, true, addedTableName) + if err != nil { + panic(err) + } } // count(index fieldname) > count(0) > count(*) - var id = "*" - if statement.Engine.Dialect().DBType() == "ql" { - id = "" - } - statement.attachInSql() - return statement.genSelectSQL(fmt.Sprintf("count(%v)", id)), append(append(append(statement.joinArgs, statement.Params...), - statement.BeanArgs...), statement.inParams...) + condSQL, condArgs, _ := builder.ToSQL(statement.cond.And(autoCond)) + + return statement.genSelectSQL("count(*)", condSQL), append(statement.joinArgs, condArgs...) } func (statement *Statement) genSumSql(bean interface{}, columns ...string) (string, []interface{}) { statement.setRefValue(rValue(bean)) var addedTableName = (len(statement.JoinStr) > 0) - + var autoCond builder.Cond if !statement.noAutoCondition { - colNames, args := statement.buildConditions(statement.RefTable, bean, true, true, false, true, addedTableName) - - statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") - statement.BeanArgs = args + var err error + autoCond, err = statement.buildConds(statement.RefTable, bean, true, true, false, true, addedTableName) + if err != nil { + panic(err) + } } - statement.attachInSql() + condSQL, condArgs, _ := builder.ToSQL(statement.cond.And(autoCond)) + var sumStrs = make([]string, 0, len(columns)) for _, colName := range columns { sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", colName)) } - return statement.genSelectSQL(strings.Join(sumStrs, ", ")), append(append(append(statement.joinArgs, statement.Params...), - statement.BeanArgs...), statement.inParams...) + return statement.genSelectSQL(strings.Join(sumStrs, ", "), condSQL), append(statement.joinArgs, condArgs...) } -func (statement *Statement) genSelectSQL(columnStr string) (a string) { +func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) { var distinct string if statement.IsDistinct { distinct = "DISTINCT " @@ -1239,17 +1177,8 @@ func (statement *Statement) genSelectSQL(columnStr string) (a string) { statement.processIdParam() var buf bytes.Buffer - if len(statement.WhereStr) > 0 { - if len(statement.ConditionStr) > 0 { - fmt.Fprintf(&buf, " WHERE (%v)", statement.WhereStr) - } else { - fmt.Fprintf(&buf, " WHERE %v", statement.WhereStr) - } - if statement.ConditionStr != "" { - fmt.Fprintf(&buf, " %s (%v)", dialect.AndStr(), statement.ConditionStr) - } - } else if len(statement.ConditionStr) > 0 { - fmt.Fprintf(&buf, " WHERE %v", statement.ConditionStr) + if len(condSQL) > 0 { + fmt.Fprintf(&buf, " WHERE %v", condSQL) } var whereStr = buf.String() @@ -1334,20 +1263,12 @@ func (statement *Statement) genSelectSQL(columnStr string) (a string) { func (statement *Statement) processIdParam() { if statement.IdParam != nil { - if statement.Engine.dialect.DBType() != "ql" { - for i, col := range statement.RefTable.PKColumns() { - var colName = statement.colName(col, statement.TableName()) - if i < len(*(statement.IdParam)) { - statement.And(fmt.Sprintf("%v %s ?", colName, - statement.Engine.dialect.EqStr()), (*(statement.IdParam))[i]) - } else { - statement.And(fmt.Sprintf("%v %s ?", colName, - statement.Engine.dialect.EqStr()), "") - } - } - } else { - if len(*(statement.IdParam)) <= 1 { - statement.And("id() == ?", (*(statement.IdParam))[0]) + for i, col := range statement.RefTable.PKColumns() { + var colName = statement.colName(col, statement.TableName()) + if i < len(*(statement.IdParam)) { + statement.cond = statement.cond.And(builder.Eq{colName: (*(statement.IdParam))[i]}) + } else { + statement.cond = statement.cond.And(builder.Eq{colName: ""}) } } } diff --git a/xorm.go b/xorm.go index 8fad97d4..d0edf7cd 100644 --- a/xorm.go +++ b/xorm.go @@ -17,7 +17,7 @@ import ( const ( // Version show the xorm's version - Version string = "0.5.5.0916" + Version string = "0.6.0.0917Beta" ) func regDrvsNDialects() bool {