Browse Source

Merge branch 'builder'

xormplus 9 years ago
parent
commit
77370a86d2
6 changed files with 237 additions and 363 deletions
  1. 2 2
      engine.go
  2. 18 9
      mssql_dialect.go
  3. 1 1
      rows.go
  4. 94 134
      session.go
  5. 121 216
      statement.go
  6. 1 1
      xorm.go

+ 2 - 2
engine.go

@@ -646,10 +646,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

+ 18 - 9
mssql_dialect.go

@@ -330,9 +330,11 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
 
 func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
 	args := []interface{}{}
-	s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale
-from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id
-where a.object_id=object_id('` + tableName + `')`
+	s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable,
+	      replace(replace(isnull(c.text,''),'(',''),')','') as vdefault   
+          from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id 
+          left join  sys.syscomments c  on a.default_object_id=c.id 
+          where a.object_id=object_id('` + tableName + `')`
 	db.LogSQL(s, args)
 
 	rows, err := db.DB().Query(s, args...)
@@ -344,19 +346,26 @@ where a.object_id=object_id('` + tableName + `')`
 	cols := make(map[string]*core.Column)
 	colSeq := make([]string, 0)
 	for rows.Next() {
-		var name, ctype, precision, scale string
-		var maxLen int
-		err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale)
+		var name, ctype, vdefault string
+		var maxLen, precision, scale int
+		var nullable bool
+		err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault)
 		if err != nil {
 			return nil, nil, err
 		}
 
 		col := new(core.Column)
 		col.Indexes = make(map[string]int)
-		col.Length = maxLen
 		col.Name = strings.Trim(name, "` ")
-
+		col.Nullable = nullable
+		col.Default = vdefault
 		ct := strings.ToUpper(ctype)
+		if ct == "DECIMAL" {
+			col.Length = precision
+			col.Length2 = scale
+		} else {
+			col.Length = maxLen
+		}
 		switch ct {
 		case "DATETIMEOFFSET":
 			col.SQLType = core.SQLType{core.TimeStampz, 0, 0}
@@ -458,7 +467,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
 		colName = strings.Trim(colName, "` ")
 
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
-			indexName = indexName[5+len(tableName) : len(indexName)]
+			indexName = indexName[5+len(tableName):]
 		}
 
 		var index *core.Index

+ 1 - 1
rows.go

@@ -41,7 +41,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
 	}
 
 	if rows.session.Statement.RawSQL == "" {
-		sqlStr, args = rows.session.Statement.genGetSql(bean)
+		sqlStr, args = rows.session.Statement.genGetSQL(bean)
 	} else {
 		sqlStr = rows.session.Statement.RawSQL
 		args = rows.session.Statement.RawParams

+ 94 - 134
session.go

@@ -16,6 +16,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/go-xorm/builder"
 	"github.com/xormplus/core"
 )
 
@@ -114,7 +115,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
@@ -128,20 +129,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
 }
 
@@ -191,6 +192,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...)
@@ -690,7 +697,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
 	for _, filter := range session.Engine.dialect.Filters() {
 		sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
 	}
-	newsql := session.Statement.convertIdSql(sqlStr)
+	newsql := session.Statement.convertIDSQL(sqlStr)
 	if newsql == "" {
 		return false, ErrCacheFailed
 	}
@@ -791,7 +798,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
 		sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
 	}
 
-	newsql := session.Statement.convertIdSql(sqlStr)
+	newsql := session.Statement.convertIDSQL(sqlStr)
 	if newsql == "" {
 		return ErrCacheFailed
 	}
@@ -894,15 +901,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)
 			}
 		}
 
@@ -1038,7 +1044,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
 			return false, ErrTableNotFound
 		}
 		session.Statement.Limit(1)
-		sqlStr, args = session.Statement.genGetSql(bean)
+		sqlStr, args = session.Statement.genGetSQL(bean)
 	} else {
 		sqlStr = session.Statement.RawSQL
 		args = session.Statement.RawParams
@@ -1089,7 +1095,7 @@ func (session *Session) Count(bean interface{}) (int64, error) {
 	var sqlStr string
 	var args []interface{}
 	if session.Statement.RawSQL == "" {
-		sqlStr, args = session.Statement.genCountSql(bean)
+		sqlStr, args = session.Statement.genCountSQL(bean)
 	} else {
 		sqlStr = session.Statement.RawSQL
 		args = session.Statement.RawParams
@@ -1121,7 +1127,7 @@ func (session *Session) Sum(bean interface{}, columnName string) (float64, error
 	var sqlStr string
 	var args []interface{}
 	if len(session.Statement.RawSQL) == 0 {
-		sqlStr, args = session.Statement.genSumSql(bean, columnName)
+		sqlStr, args = session.Statement.genSumSQL(bean, columnName)
 	} else {
 		sqlStr = session.Statement.RawSQL
 		args = session.Statement.RawParams
@@ -1153,7 +1159,7 @@ func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64
 	var sqlStr string
 	var args []interface{}
 	if len(session.Statement.RawSQL) == 0 {
-		sqlStr, args = session.Statement.genSumSql(bean, columnNames...)
+		sqlStr, args = session.Statement.genSumSQL(bean, columnNames...)
 	} else {
 		sqlStr = session.Statement.RawSQL
 		args = session.Statement.RawParams
@@ -1185,7 +1191,7 @@ func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int6
 	var sqlStr string
 	var args []interface{}
 	if len(session.Statement.RawSQL) == 0 {
-		sqlStr, args = session.Statement.genSumSql(bean, columnNames...)
+		sqlStr, args = session.Statement.genSumSQL(bean, columnNames...)
 	} else {
 		sqlStr = session.Statement.RawSQL
 		args = session.Statement.RawParams
@@ -1242,10 +1248,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 "<col> IS NULL" to WHERE whatever condiBean is given.
 		// See https://github.com/go-xorm/xorm/issues/179
@@ -1258,8 +1267,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"})
 		}
 	}
 
@@ -1293,12 +1301,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 {
@@ -3177,7 +3183,6 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
 		colPlaces)
 
 	handleAfterInsertProcessorFunc := func(bean interface{}) {
-
 		if session.IsAutoCommit {
 			for _, closure := range session.afterClosures {
 				closure(bean)
@@ -3210,7 +3215,6 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
 	if session.Engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 {
 		//assert table.AutoIncrement != ""
 		res, err := session.query("select seq_atable.currval from dual", args...)
-
 		if err != nil {
 			return 0, err
 		}
@@ -3302,7 +3306,8 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
 		if err != nil {
 			return 0, err
 		}
-		handleAfterInsertProcessorFunc(bean)
+
+		defer handleAfterInsertProcessorFunc(bean)
 
 		if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
 			session.cacheInsert(session.Statement.TableName())
@@ -3589,97 +3594,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)
-
-		verValue, err = table.VersionColumn().ValueOf(bean)
-		if err != nil {
-			return 0, err
-		}
+			condSQL)
 
-		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 {
@@ -3738,7 +3714,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
 		sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
 	}
 
-	newsql := session.Statement.convertIdSql(sqlStr)
+	newsql := session.Statement.convertIDSQL(sqlStr)
 	if newsql == "" {
 		return ErrCacheFailed
 	}
@@ -3813,41 +3789,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)
 	}
@@ -3864,14 +3826,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
@@ -3884,34 +3846,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
@@ -3925,12 +3887,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{}) {
@@ -3939,13 +3901,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
 	}

+ 121 - 216
statement.go

@@ -14,14 +14,10 @@ import (
 	"strings"
 	"time"
 
+	"github.com/go-xorm/builder"
 	"github.com/xormplus/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(query interface{}, args ...interface{}) *Statement {
+	return statement.And(query, args...)
 }
 
-// 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...)
+// And add Where & and statment
+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
 	}
 
-	if !strings.Contains(querystring, statement.Engine.dialect.EqStr()) {
-		querystring = strings.Replace(querystring, "=", statement.Engine.dialect.EqStr(), -1)
+	return statement
+}
+
+// Or add Where & Or statment
+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.WhereStr = querystring
-	statement.Params = args
 	return statement
 }
 
-// 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
+// In generate "Where column IN (?) " statment
+func (statement *Statement) In(column string, args ...interface{}) *Statement {
+	if len(args) == 0 {
+		return statement
 	}
-	statement.Params = append(statement.Params, args...)
+
+	in := builder.In(column, args...)
+	statement.cond = statement.cond.And(in)
 	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
+// NotIn generate "Where column IN (?) " statment
+func (statement *Statement) NotIn(column string, args ...interface{}) *Statement {
+	if len(args) == 0 {
+		return statement
 	}
-	statement.Params = append(statement.Params, args...)
+
+	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 {
@@ -1128,19 +1064,39 @@ func (s *Statement) genDelIndexSQL() []string {
 	return sqls
 }
 
-func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) {
-	statement.setRefValue(rValue(bean))
+func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) {
+	quote := s.Engine.Quote
+	sql := fmt.Sprintf("ALTER TABLE %v ADD %v;", quote(s.TableName()),
+		col.String(s.Engine.dialect))
+	return sql, []interface{}{}
+}
 
+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) genConds(bean interface{}) (string, []interface{}, error) {
 	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 {
+			return "", nil, err
+		}
 	}
 
+	statement.processIdParam()
+
+	return builder.ToSQL(statement.cond.And(autoCond))
+}
+
+func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}) {
+	statement.setRefValue(rValue(bean))
+
 	var columnStr = statement.ColumnStr
 	if len(statement.selectStr) > 0 {
 		columnStr = statement.selectStr
@@ -1165,67 +1121,33 @@ 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...)
-}
-
-func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) {
-	quote := s.Engine.Quote
-	sql := fmt.Sprintf("ALTER TABLE %v ADD %v;", quote(s.TableName()),
-		col.String(s.Engine.dialect))
-	return sql, []interface{}{}
-}
+	condSQL, condArgs, _ := statement.genConds(bean)
 
-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,
-		statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName)
+	return statement.genSelectSQL(columnStr, condSQL), append(statement.joinArgs, condArgs...)
 }
 
-func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}) {
+func (statement *Statement) genCountSQL(bean interface{}) (string, []interface{}) {
 	statement.setRefValue(rValue(bean))
 
-	var addedTableName = (len(statement.JoinStr) > 0)
-
-	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
-	}
+	condSQL, condArgs, _ := statement.genConds(bean)
 
-	// 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...)
+	return statement.genSelectSQL("count(*)", condSQL), append(statement.joinArgs, condArgs...)
 }
 
-func (statement *Statement) genSumSql(bean interface{}, columns ...string) (string, []interface{}) {
+func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (string, []interface{}) {
 	statement.setRefValue(rValue(bean))
 
-	var addedTableName = (len(statement.JoinStr) > 0)
-
-	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
-	}
-
-	statement.attachInSql()
 	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...)
+
+	condSQL, condArgs, _ := statement.genConds(bean)
+
+	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 +1161,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()
 
@@ -1333,27 +1246,21 @@ 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()), "")
-				}
-			}
+	if statement.IdParam == nil {
+		return
+	}
+
+	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 {
-			if len(*(statement.IdParam)) <= 1 {
-				statement.And("id() == ?", (*(statement.IdParam))[0])
-			}
+			statement.cond = statement.cond.And(builder.Eq{colName: ""})
 		}
 	}
 }
 
-func (statement *Statement) JoinColumns(cols []*core.Column, includeTableName bool) string {
+func (statement *Statement) joinColumns(cols []*core.Column, includeTableName bool) string {
 	var colnames = make([]string, len(cols))
 	for i, col := range cols {
 		if includeTableName {
@@ -1366,21 +1273,19 @@ func (statement *Statement) JoinColumns(cols []*core.Column, includeTableName bo
 	return strings.Join(colnames, ", ")
 }
 
-func (statement *Statement) convertIdSql(sqlStr string) string {
+func (statement *Statement) convertIDSQL(sqlStr string) string {
 	if statement.RefTable != nil {
 		cols := statement.RefTable.PKColumns()
 		if len(cols) == 0 {
 			return ""
 		}
 
-		colstrs := statement.JoinColumns(cols, false)
+		colstrs := statement.joinColumns(cols, false)
 		sqls := splitNNoCase(sqlStr, " from ", 2)
 		if len(sqls) != 2 {
 			return ""
 		}
-		if statement.Engine.dialect.DBType() == "ql" {
-			return fmt.Sprintf("SELECT id() FROM %v", sqls[1])
-		}
+
 		return fmt.Sprintf("SELECT %s FROM %v", colstrs, sqls[1])
 	}
 	return ""
@@ -1391,7 +1296,7 @@ func (statement *Statement) convertUpdateSQL(sqlStr string) (string, string) {
 		return "", ""
 	}
 
-	colstrs := statement.JoinColumns(statement.RefTable.PKColumns(), true)
+	colstrs := statement.joinColumns(statement.RefTable.PKColumns(), true)
 	sqls := splitNNoCase(sqlStr, "where", 2)
 	if len(sqls) != 2 {
 		if len(sqls) == 1 {

+ 1 - 1
xorm.go

@@ -17,7 +17,7 @@ import (
 
 const (
 	// Version show the xorm's version
-	Version string = "0.5.5.0907"
+	Version string = "0.6.0.0921"
 )
 
 func regDrvsNDialects() bool {