Przeglądaj źródła

vendor: update xorm version for fix git clone error build with golang 1.8.1 (#4460)

peter zhang 7 lat temu
rodzic
commit
10ee2e0dad
36 zmienionych plików z 2439 dodań i 1807 usunięć
  1. 22 11
      vendor/github.com/go-xorm/xorm/README.md
  2. 21 8
      vendor/github.com/go-xorm/xorm/README_CN.md
  3. 0 1
      vendor/github.com/go-xorm/xorm/VERSION
  4. 336 0
      vendor/github.com/go-xorm/xorm/convert.go
  5. 28 3
      vendor/github.com/go-xorm/xorm/dialect_mssql.go
  6. 92 0
      vendor/github.com/go-xorm/xorm/dialect_mysql.go
  7. 60 0
      vendor/github.com/go-xorm/xorm/dialect_oracle.go
  8. 110 1
      vendor/github.com/go-xorm/xorm/dialect_postgres.go
  9. 10 0
      vendor/github.com/go-xorm/xorm/dialect_sqlite3.go
  10. 31 12
      vendor/github.com/go-xorm/xorm/doc.go
  11. 142 193
      vendor/github.com/go-xorm/xorm/engine.go
  12. 0 42
      vendor/github.com/go-xorm/xorm/goracle_driver.go
  13. 40 23
      vendor/github.com/go-xorm/xorm/helpers.go
  14. 0 65
      vendor/github.com/go-xorm/xorm/mymysql_driver.go
  15. 0 50
      vendor/github.com/go-xorm/xorm/mysql_driver.go
  16. 0 37
      vendor/github.com/go-xorm/xorm/oci8_driver.go
  17. 0 34
      vendor/github.com/go-xorm/xorm/odbc_driver.go
  18. 0 119
      vendor/github.com/go-xorm/xorm/pq_driver.go
  19. 17 10
      vendor/github.com/go-xorm/xorm/rows.go
  20. 114 974
      vendor/github.com/go-xorm/xorm/session.go
  21. 84 0
      vendor/github.com/go-xorm/xorm/session_cols.go
  22. 70 0
      vendor/github.com/go-xorm/xorm/session_cond.go
  23. 673 0
      vendor/github.com/go-xorm/xorm/session_convert.go
  24. 3 1
      vendor/github.com/go-xorm/xorm/session_delete.go
  25. 97 83
      vendor/github.com/go-xorm/xorm/session_find.go
  26. 35 21
      vendor/github.com/go-xorm/xorm/session_get.go
  27. 29 9
      vendor/github.com/go-xorm/xorm/session_insert.go
  28. 14 11
      vendor/github.com/go-xorm/xorm/session_raw.go
  29. 16 5
      vendor/github.com/go-xorm/xorm/session_schema.go
  30. 1 1
      vendor/github.com/go-xorm/xorm/session_sum.go
  31. 42 29
      vendor/github.com/go-xorm/xorm/session_update.go
  32. 0 20
      vendor/github.com/go-xorm/xorm/sqlite3_driver.go
  33. 66 40
      vendor/github.com/go-xorm/xorm/statement.go
  34. 281 0
      vendor/github.com/go-xorm/xorm/tag.go
  35. 2 1
      vendor/github.com/go-xorm/xorm/xorm.go
  36. 3 3
      vendor/vendor.json

+ 22 - 11
vendor/github.com/go-xorm/xorm/README.md

@@ -46,12 +46,15 @@ Drivers for Go's sql package which currently support database/sql includes:
 
 * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb)
 
-* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
-
 * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment)
 
 # Changelog
 
+* **v0.6.2**
+    * refactor tag parse methods
+    * add Scan features to Get
+    * add QueryString method
+
 * **v0.6.0**
     * remove support for ql
     * add query condition builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder), so `Where`, `And`, `Or` 
@@ -79,12 +82,6 @@ methods can use `builder.Cond` as parameter
 
 # Installation
 
-If you have [gopm](https://github.com/gpmgo/gopm) installed,
-
-	gopm get github.com/go-xorm/xorm
-
-Or
-
 	go get github.com/go-xorm/xorm
 
 # Documents
@@ -119,19 +116,21 @@ type User struct {
 err := engine.Sync2(new(User))
 ```
 
-* Query a SQL string, the returned results is []map[string][]byte
+* `Query` runs a SQL string, the returned results is `[]map[string][]byte`, `QueryString` returns `[]map[string]string`.
 
 ```Go
 results, err := engine.Query("select * from user")
+
+results, err := engine.QueryString("select * from user")
 ```
 
-* Execute a SQL string, the returned results
+* `Execute` runs a SQL string, it returns `affetcted` and `error`
 
 ```Go
 affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
 ```
 
-* Insert one or multiple records to database
+* `Insert` one or multiple records to database
 
 ```Go
 affected, err := engine.Insert(&user)
@@ -153,6 +152,18 @@ has, err := engine.Get(&user)
 // SELECT * FROM user LIMIT 1
 has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
 // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+var name string
+has, err := engine.Where("id = ?", id).Cols("name").Get(&name)
+// SELECT name FROM user WHERE id = ?
+var id int64
+has, err := engine.Where("name = ?", name).Cols("id").Get(&id)
+// SELECT id FROM user WHERE name = ?
+var valuesMap = make(map[string]string)
+has, err := engine.Where("id = ?", id).Get(&valuesMap)
+// SELECT * FROM user WHERE id = ?
+var valuesSlice = make([]interface{}, len(cols))
+has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
+// SELECT col1, col2, col3 FROM user WHERE id = ?
 ```
 
 * Query multiple records from database, also you can use join and extends

+ 21 - 8
vendor/github.com/go-xorm/xorm/README_CN.md

@@ -54,6 +54,11 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
 
 ## 更新日志
 
+* **v0.6.2**
+    * 重构Tag解析方式
+    * Get方法新增类似Sacn的特性
+    * 新增 QueryString 方法
+
 * **v0.6.0**
     * 去除对 ql 的支持
     * 新增条件查询分析器 [github.com/go-xorm/builder](https://github.com/go-xorm/builder), 从因此 `Where, And, Or` 函数
@@ -81,12 +86,6 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
 
 ## 安装
 
-推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
-
-	gopm get github.com/go-xorm/xorm
-
-或者您也可以使用go工具进行安装:
-
 	go get github.com/go-xorm/xorm
 
 ## 文档
@@ -121,13 +120,15 @@ type User struct {
 err := engine.Sync2(new(User))
 ```
 
-* 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte
+* `Query` 最原始的也支持SQL语句查询,返回的结果类型为 []map[string][]byte。`QueryString` 返回 []map[string]string
 
 ```Go
 results, err := engine.Query("select * from user")
+
+results, err := engine.QueryString("select * from user")
 ```
 
-* 执行一个SQL语句
+* `Exec` 执行一个SQL语句
 
 ```Go
 affected, err := engine.Exec("update user set age = ? where name = ?", age, name)
@@ -155,6 +156,18 @@ has, err := engine.Get(&user)
 // SELECT * FROM user LIMIT 1
 has, err := engine.Where("name = ?", name).Desc("id").Get(&user)
 // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1
+var name string
+has, err := engine.Where("id = ?", id).Cols("name").Get(&name)
+// SELECT name FROM user WHERE id = ?
+var id int64
+has, err := engine.Where("name = ?", name).Cols("id").Get(&id)
+// SELECT id FROM user WHERE name = ?
+var valuesMap = make(map[string]string)
+has, err := engine.Where("id = ?", id).Get(&valuesMap)
+// SELECT * FROM user WHERE id = ?
+var valuesSlice = make([]interface{}, len(cols))
+has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice)
+// SELECT col1, col2, col3 FROM user WHERE id = ?
 ```
 
 * 查询多条记录,当然可以使用Join和extends来组合使用

+ 0 - 1
vendor/github.com/go-xorm/xorm/VERSION

@@ -1 +0,0 @@
-xorm v0.6.0.1022

+ 336 - 0
vendor/github.com/go-xorm/xorm/convert.go

@@ -0,0 +1,336 @@
+// 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/driver"
+	"errors"
+	"fmt"
+	"reflect"
+	"strconv"
+	"time"
+)
+
+var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
+
+func strconvErr(err error) error {
+	if ne, ok := err.(*strconv.NumError); ok {
+		return ne.Err
+	}
+	return err
+}
+
+func cloneBytes(b []byte) []byte {
+	if b == nil {
+		return nil
+	} else {
+		c := make([]byte, len(b))
+		copy(c, b)
+		return c
+	}
+}
+
+func asString(src interface{}) string {
+	switch v := src.(type) {
+	case string:
+		return v
+	case []byte:
+		return string(v)
+	}
+	rv := reflect.ValueOf(src)
+	switch rv.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return strconv.FormatInt(rv.Int(), 10)
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		return strconv.FormatUint(rv.Uint(), 10)
+	case reflect.Float64:
+		return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
+	case reflect.Float32:
+		return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
+	case reflect.Bool:
+		return strconv.FormatBool(rv.Bool())
+	}
+	return fmt.Sprintf("%v", src)
+}
+
+func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
+	switch rv.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return strconv.AppendInt(buf, rv.Int(), 10), true
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		return strconv.AppendUint(buf, rv.Uint(), 10), true
+	case reflect.Float32:
+		return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
+	case reflect.Float64:
+		return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
+	case reflect.Bool:
+		return strconv.AppendBool(buf, rv.Bool()), true
+	case reflect.String:
+		s := rv.String()
+		return append(buf, s...), true
+	}
+	return
+}
+
+// convertAssign copies to dest the value in src, converting it if possible.
+// An error is returned if the copy would result in loss of information.
+// dest should be a pointer type.
+func convertAssign(dest, src interface{}) error {
+	// Common cases, without reflect.
+	switch s := src.(type) {
+	case string:
+		switch d := dest.(type) {
+		case *string:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = s
+			return nil
+		case *[]byte:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = []byte(s)
+			return nil
+		}
+	case []byte:
+		switch d := dest.(type) {
+		case *string:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = string(s)
+			return nil
+		case *interface{}:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = cloneBytes(s)
+			return nil
+		case *[]byte:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = cloneBytes(s)
+			return nil
+		}
+
+	case time.Time:
+		switch d := dest.(type) {
+		case *string:
+			*d = s.Format(time.RFC3339Nano)
+			return nil
+		case *[]byte:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = []byte(s.Format(time.RFC3339Nano))
+			return nil
+		}
+	case nil:
+		switch d := dest.(type) {
+		case *interface{}:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = nil
+			return nil
+		case *[]byte:
+			if d == nil {
+				return errNilPtr
+			}
+			*d = nil
+			return nil
+		}
+	}
+
+	var sv reflect.Value
+
+	switch d := dest.(type) {
+	case *string:
+		sv = reflect.ValueOf(src)
+		switch sv.Kind() {
+		case reflect.Bool,
+			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+			reflect.Float32, reflect.Float64:
+			*d = asString(src)
+			return nil
+		}
+	case *[]byte:
+		sv = reflect.ValueOf(src)
+		if b, ok := asBytes(nil, sv); ok {
+			*d = b
+			return nil
+		}
+	case *bool:
+		bv, err := driver.Bool.ConvertValue(src)
+		if err == nil {
+			*d = bv.(bool)
+		}
+		return err
+	case *interface{}:
+		*d = src
+		return nil
+	}
+
+	dpv := reflect.ValueOf(dest)
+	if dpv.Kind() != reflect.Ptr {
+		return errors.New("destination not a pointer")
+	}
+	if dpv.IsNil() {
+		return errNilPtr
+	}
+
+	if !sv.IsValid() {
+		sv = reflect.ValueOf(src)
+	}
+
+	dv := reflect.Indirect(dpv)
+	if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
+		switch b := src.(type) {
+		case []byte:
+			dv.Set(reflect.ValueOf(cloneBytes(b)))
+		default:
+			dv.Set(sv)
+		}
+		return nil
+	}
+
+	if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
+		dv.Set(sv.Convert(dv.Type()))
+		return nil
+	}
+
+	switch dv.Kind() {
+	case reflect.Ptr:
+		if src == nil {
+			dv.Set(reflect.Zero(dv.Type()))
+			return nil
+		} else {
+			dv.Set(reflect.New(dv.Type().Elem()))
+			return convertAssign(dv.Interface(), src)
+		}
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		s := asString(src)
+		i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
+		if err != nil {
+			err = strconvErr(err)
+			return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+		}
+		dv.SetInt(i64)
+		return nil
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		s := asString(src)
+		u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
+		if err != nil {
+			err = strconvErr(err)
+			return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+		}
+		dv.SetUint(u64)
+		return nil
+	case reflect.Float32, reflect.Float64:
+		s := asString(src)
+		f64, err := strconv.ParseFloat(s, dv.Type().Bits())
+		if err != nil {
+			err = strconvErr(err)
+			return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+		}
+		dv.SetFloat(f64)
+		return nil
+	case reflect.String:
+		dv.SetString(asString(src))
+		return nil
+	}
+
+	return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
+}
+
+func asKind(vv reflect.Value, tp reflect.Type) (interface{}, error) {
+	switch tp.Kind() {
+	case reflect.Int64:
+		return vv.Int(), nil
+	case reflect.Int:
+		return int(vv.Int()), nil
+	case reflect.Int32:
+		return int32(vv.Int()), nil
+	case reflect.Int16:
+		return int16(vv.Int()), nil
+	case reflect.Int8:
+		return int8(vv.Int()), nil
+	case reflect.Uint64:
+		return vv.Uint(), nil
+	case reflect.Uint:
+		return uint(vv.Uint()), nil
+	case reflect.Uint32:
+		return uint32(vv.Uint()), nil
+	case reflect.Uint16:
+		return uint16(vv.Uint()), nil
+	case reflect.Uint8:
+		return uint8(vv.Uint()), nil
+	case reflect.String:
+		return vv.String(), nil
+	case reflect.Slice:
+		if tp.Elem().Kind() == reflect.Uint8 {
+			v, err := strconv.ParseInt(string(vv.Interface().([]byte)), 10, 64)
+			if err != nil {
+				return nil, err
+			}
+			return v, nil
+		}
+
+	}
+	return nil, fmt.Errorf("unsupported primary key type: %v, %v", tp, vv)
+}
+
+func convertFloat(v interface{}) (float64, error) {
+	switch v.(type) {
+	case float32:
+		return float64(v.(float32)), nil
+	case float64:
+		return v.(float64), nil
+	case string:
+		i, err := strconv.ParseFloat(v.(string), 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	case []byte:
+		i, err := strconv.ParseFloat(string(v.([]byte)), 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	}
+	return 0, fmt.Errorf("unsupported type: %v", v)
+}
+
+func convertInt(v interface{}) (int64, error) {
+	switch v.(type) {
+	case int:
+		return int64(v.(int)), nil
+	case int8:
+		return int64(v.(int8)), nil
+	case int16:
+		return int64(v.(int16)), nil
+	case int32:
+		return int64(v.(int32)), nil
+	case int64:
+		return v.(int64), nil
+	case []byte:
+		i, err := strconv.ParseInt(string(v.([]byte)), 10, 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	case string:
+		i, err := strconv.ParseInt(v.(string), 10, 64)
+		if err != nil {
+			return 0, err
+		}
+		return i, nil
+	}
+	return 0, fmt.Errorf("unsupported type: %v", v)
+}

+ 28 - 3
vendor/github.com/go-xorm/xorm/mssql_dialect.go → vendor/github.com/go-xorm/xorm/dialect_mssql.go

@@ -5,6 +5,7 @@
 package xorm
 
 import (
+	"errors"
 	"fmt"
 	"strconv"
 	"strings"
@@ -215,9 +216,9 @@ func (db *mssql) SqlType(c *core.Column) string {
 	switch t := c.SQLType.Name; t {
 	case core.Bool:
 		res = core.TinyInt
-		if c.Default == "true" {
+		if strings.EqualFold(c.Default, "true") {
 			c.Default = "1"
-		} else if c.Default == "false" {
+		} else {
 			c.Default = "0"
 		}
 	case core.Serial:
@@ -467,9 +468,10 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
 		}
 
 		colName = strings.Trim(colName, "` ")
-
+		var isRegular bool
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 			indexName = indexName[5+len(tableName):]
+			isRegular = true
 		}
 
 		var index *core.Index
@@ -478,6 +480,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =?
 			index = new(core.Index)
 			index.Type = indexType
 			index.Name = indexName
+			index.IsRegular = isRegular
 			indexes[indexName] = index
 		}
 		index.AddColumn(colName)
@@ -526,3 +529,25 @@ func (db *mssql) ForUpdateSql(query string) string {
 func (db *mssql) Filters() []core.Filter {
 	return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}}
 }
+
+type odbcDriver struct {
+}
+
+func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+	kv := strings.Split(dataSourceName, ";")
+	var dbName string
+
+	for _, c := range kv {
+		vv := strings.Split(strings.TrimSpace(c), "=")
+		if len(vv) == 2 {
+			switch strings.ToLower(vv[0]) {
+			case "database":
+				dbName = vv[1]
+			}
+		}
+	}
+	if dbName == "" {
+		return nil, errors.New("no db name provided")
+	}
+	return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil
+}

+ 92 - 0
vendor/github.com/go-xorm/xorm/mysql_dialect.go → vendor/github.com/go-xorm/xorm/dialect_mysql.go

@@ -6,7 +6,9 @@ package xorm
 
 import (
 	"crypto/tls"
+	"errors"
 	"fmt"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -486,3 +488,93 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) {
 func (db *mysql) Filters() []core.Filter {
 	return []core.Filter{&core.IdFilter{}}
 }
+
+type mymysqlDriver struct {
+}
+
+func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+	db := &core.Uri{DbType: core.MYSQL}
+
+	pd := strings.SplitN(dataSourceName, "*", 2)
+	if len(pd) == 2 {
+		// Parse protocol part of URI
+		p := strings.SplitN(pd[0], ":", 2)
+		if len(p) != 2 {
+			return nil, errors.New("Wrong protocol part of URI")
+		}
+		db.Proto = p[0]
+		options := strings.Split(p[1], ",")
+		db.Raddr = options[0]
+		for _, o := range options[1:] {
+			kv := strings.SplitN(o, "=", 2)
+			var k, v string
+			if len(kv) == 2 {
+				k, v = kv[0], kv[1]
+			} else {
+				k, v = o, "true"
+			}
+			switch k {
+			case "laddr":
+				db.Laddr = v
+			case "timeout":
+				to, err := time.ParseDuration(v)
+				if err != nil {
+					return nil, err
+				}
+				db.Timeout = to
+			default:
+				return nil, errors.New("Unknown option: " + k)
+			}
+		}
+		// Remove protocol part
+		pd = pd[1:]
+	}
+	// Parse database part of URI
+	dup := strings.SplitN(pd[0], "/", 3)
+	if len(dup) != 3 {
+		return nil, errors.New("Wrong database part of URI")
+	}
+	db.DbName = dup[0]
+	db.User = dup[1]
+	db.Passwd = dup[2]
+
+	return db, nil
+}
+
+type mysqlDriver struct {
+}
+
+func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+	dsnPattern := regexp.MustCompile(
+		`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
+			`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
+			`\/(?P<dbname>.*?)` + // /dbname
+			`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
+	matches := dsnPattern.FindStringSubmatch(dataSourceName)
+	//tlsConfigRegister := make(map[string]*tls.Config)
+	names := dsnPattern.SubexpNames()
+
+	uri := &core.Uri{DbType: core.MYSQL}
+
+	for i, match := range matches {
+		switch names[i] {
+		case "dbname":
+			uri.DbName = match
+		case "params":
+			if len(match) > 0 {
+				kvs := strings.Split(match, "&")
+				for _, kv := range kvs {
+					splits := strings.Split(kv, "=")
+					if len(splits) == 2 {
+						switch splits[0] {
+						case "charset":
+							uri.Charset = splits[1]
+						}
+					}
+				}
+			}
+
+		}
+	}
+	return uri, nil
+}

+ 60 - 0
vendor/github.com/go-xorm/xorm/oracle_dialect.go → vendor/github.com/go-xorm/xorm/dialect_oracle.go

@@ -5,7 +5,9 @@
 package xorm
 
 import (
+	"errors"
 	"fmt"
+	"regexp"
 	"strconv"
 	"strings"
 
@@ -822,6 +824,12 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
 
 		indexName = strings.Trim(indexName, `" `)
 
+		var isRegular bool
+		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
+			indexName = indexName[5+len(tableName):]
+			isRegular = true
+		}
+
 		if uniqueness == "UNIQUE" {
 			indexType = core.UniqueType
 		} else {
@@ -834,6 +842,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
 			index = new(core.Index)
 			index.Type = indexType
 			index.Name = indexName
+			index.IsRegular = isRegular
 			indexes[indexName] = index
 		}
 		index.AddColumn(colName)
@@ -844,3 +853,54 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) {
 func (db *oracle) Filters() []core.Filter {
 	return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}, &core.IdFilter{}}
 }
+
+type goracleDriver struct {
+}
+
+func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+	db := &core.Uri{DbType: core.ORACLE}
+	dsnPattern := regexp.MustCompile(
+		`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
+			`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
+			`\/(?P<dbname>.*?)` + // /dbname
+			`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
+	matches := dsnPattern.FindStringSubmatch(dataSourceName)
+	//tlsConfigRegister := make(map[string]*tls.Config)
+	names := dsnPattern.SubexpNames()
+
+	for i, match := range matches {
+		switch names[i] {
+		case "dbname":
+			db.DbName = match
+		}
+	}
+	if db.DbName == "" {
+		return nil, errors.New("dbname is empty")
+	}
+	return db, nil
+}
+
+type oci8Driver struct {
+}
+
+//dataSourceName=user/password@ipv4:port/dbname
+//dataSourceName=user/password@[ipv6]:port/dbname
+func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+	db := &core.Uri{DbType: core.ORACLE}
+	dsnPattern := regexp.MustCompile(
+		`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
+			`(?P<net>.*)` + // ip:port
+			`\/(?P<dbname>.*)`) // dbname
+	matches := dsnPattern.FindStringSubmatch(dataSourceName)
+	names := dsnPattern.SubexpNames()
+	for i, match := range matches {
+		switch names[i] {
+		case "dbname":
+			db.DbName = match
+		}
+	}
+	if db.DbName == "" {
+		return nil, errors.New("dbname is empty")
+	}
+	return db, nil
+}

+ 110 - 1
vendor/github.com/go-xorm/xorm/postgres_dialect.go → vendor/github.com/go-xorm/xorm/dialect_postgres.go

@@ -5,7 +5,10 @@
 package xorm
 
 import (
+	"errors"
 	"fmt"
+	"net/url"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -1075,9 +1078,10 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
 		}
 		cs := strings.Split(indexdef, "(")
 		colNames = strings.Split(cs[1][0:len(cs[1])-1], ",")
-
+		var isRegular bool
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 			newIdxName := indexName[5+len(tableName):]
+			isRegular = true
 			if newIdxName != "" {
 				indexName = newIdxName
 			}
@@ -1087,6 +1091,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
 		for _, colName := range colNames {
 			index.Cols = append(index.Cols, strings.Trim(colName, `" `))
 		}
+		index.IsRegular = isRegular
 		indexes[index.Name] = index
 	}
 	return indexes, nil
@@ -1095,3 +1100,107 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error)
 func (db *postgres) Filters() []core.Filter {
 	return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}}
 }
+
+type pqDriver struct {
+}
+
+type values map[string]string
+
+func (vs values) Set(k, v string) {
+	vs[k] = v
+}
+
+func (vs values) Get(k string) (v string) {
+	return vs[k]
+}
+
+func errorf(s string, args ...interface{}) {
+	panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
+}
+
+func parseURL(connstr string) (string, error) {
+	u, err := url.Parse(connstr)
+	if err != nil {
+		return "", err
+	}
+
+	if u.Scheme != "postgresql" && u.Scheme != "postgres" {
+		return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
+	}
+
+	var kvs []string
+	escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
+	accrue := func(k, v string) {
+		if v != "" {
+			kvs = append(kvs, k+"="+escaper.Replace(v))
+		}
+	}
+
+	if u.User != nil {
+		v := u.User.Username()
+		accrue("user", v)
+
+		v, _ = u.User.Password()
+		accrue("password", v)
+	}
+
+	i := strings.Index(u.Host, ":")
+	if i < 0 {
+		accrue("host", u.Host)
+	} else {
+		accrue("host", u.Host[:i])
+		accrue("port", u.Host[i+1:])
+	}
+
+	if u.Path != "" {
+		accrue("dbname", u.Path[1:])
+	}
+
+	q := u.Query()
+	for k := range q {
+		accrue(k, q.Get(k))
+	}
+
+	sort.Strings(kvs) // Makes testing easier (not a performance concern)
+	return strings.Join(kvs, " "), nil
+}
+
+func parseOpts(name string, o values) {
+	if len(name) == 0 {
+		return
+	}
+
+	name = strings.TrimSpace(name)
+
+	ps := strings.Split(name, " ")
+	for _, p := range ps {
+		kv := strings.Split(p, "=")
+		if len(kv) < 2 {
+			errorf("invalid option: %q", p)
+		}
+		o.Set(kv[0], kv[1])
+	}
+}
+
+func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+	db := &core.Uri{DbType: core.POSTGRES}
+	o := make(values)
+	var err error
+	if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
+		dataSourceName, err = parseURL(dataSourceName)
+		if err != nil {
+			return nil, err
+		}
+	}
+	parseOpts(dataSourceName, o)
+
+	db.DbName = o.Get("dbname")
+	if db.DbName == "" {
+		return nil, errors.New("dbname is empty")
+	}
+	/*db.Schema = o.Get("schema")
+	if len(db.Schema) == 0 {
+		db.Schema = "public"
+	}*/
+	return db, nil
+}

+ 10 - 0
vendor/github.com/go-xorm/xorm/sqlite3_dialect.go → vendor/github.com/go-xorm/xorm/dialect_sqlite3.go

@@ -405,8 +405,10 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
 		}
 
 		indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []")
+		var isRegular bool
 		if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) {
 			index.Name = indexName[5+len(tableName):]
+			isRegular = true
 		} else {
 			index.Name = indexName
 		}
@@ -425,6 +427,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
 		for _, col := range colIndexes {
 			index.Cols = append(index.Cols, strings.Trim(col, "` []"))
 		}
+		index.IsRegular = isRegular
 		indexes[index.Name] = index
 	}
 
@@ -434,3 +437,10 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error)
 func (db *sqlite3) Filters() []core.Filter {
 	return []core.Filter{&core.IdFilter{}}
 }
+
+type sqlite3Driver struct {
+}
+
+func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
+	return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil
+}

+ 31 - 12
vendor/github.com/go-xorm/xorm/doc.go

@@ -24,7 +24,7 @@ Generally, one engine for an application is enough. You can set it as package va
 
 Raw Methods
 
-Xorm also support raw sql execution:
+XORM also support raw SQL execution:
 
 1. query a SQL string, the returned results is []map[string][]byte
 
@@ -36,7 +36,7 @@ Xorm also support raw sql execution:
 
 ORM Methods
 
-There are 7 major ORM methods and many helpful methods to use to operate database.
+There are 8 major ORM methods and many helpful methods to use to operate database.
 
 1. Insert one or multiple records to database
 
@@ -58,10 +58,18 @@ There are 7 major ORM methods and many helpful methods to use to operate databas
 
 3. Query multiple records from database
 
-    sliceOfStructs := new(Struct)
-    err := engine.Find(sliceOfStructs)
+    var sliceOfStructs []Struct
+    err := engine.Find(&sliceOfStructs)
     // SELECT * FROM user
 
+    var mapOfStructs = make(map[int64]Struct)
+    err := engine.Find(&mapOfStructs)
+    // SELECT * FROM user
+
+    var int64s []int64
+    err := engine.Table("user").Cols("id").Find(&int64s)
+    // SELECT id FROM user
+
 4. Query multiple records and record by record handle, there two methods, one is Iterate,
 another is Rows
 
@@ -91,20 +99,31 @@ another is Rows
     counts, err := engine.Count(&user)
     // SELECT count(*) AS total FROM user
 
+8. Sum records
+
+    sumFloat64, err := engine.Sum(&user, "id")
+    // SELECT sum(id) from user
+
+    sumFloat64s, err := engine.Sums(&user, "id1", "id2")
+    // SELECT sum(id1), sum(id2) from user
+
+    sumInt64s, err := engine.SumsInt(&user, "id1", "id2")
+    // SELECT sum(id1), sum(id2) from user
+
 Conditions
 
-The above 7 methods could use with condition methods chainable.
-Attention: the above 7 methods should be the last chainable method.
+The above 8 methods could use with condition methods chainable.
+Attention: the above 8 methods should be the last chainable method.
 
-1. Id, In
+1. ID, In
 
-    engine.Id(1).Get(&user) // for single primary key
+    engine.ID(1).Get(&user) // for single primary key
     // SELECT * FROM user WHERE id = 1
-    engine.Id(core.PK{1, 2}).Get(&user) // for composite primary keys
+    engine.ID(core.PK{1, 2}).Get(&user) // for composite primary keys
     // SELECT * FROM user WHERE id1 = 1 AND id2 = 2
     engine.In("id", 1, 2, 3).Find(&users)
     // SELECT * FROM user WHERE id IN (1, 2, 3)
-    engine.In("id", []int{1, 2, 3})
+    engine.In("id", []int{1, 2, 3}).Find(&users)
     // SELECT * FROM user WHERE id IN (1, 2, 3)
 
 2. Where, And, Or
@@ -127,10 +146,10 @@ Attention: the above 7 methods should be the last chainable method.
     // SELECT TOP 5 * FROM user // for mssql
     // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases
 
-5. Sql, let you custom SQL
+5. SQL, let you custom SQL
 
     var users []User
-    engine.Sql("select * from user").Find(&users)
+    engine.SQL("select * from user").Find(&users)
 
 6. Cols, Omit, Distinct
 

+ 142 - 193
vendor/github.com/go-xorm/xorm/engine.go

@@ -44,6 +44,8 @@ type Engine struct {
 	DatabaseTZ *time.Location // The timezone of the database
 
 	disableGlobalCache bool
+
+	tagHandlers map[string]tagHandler
 }
 
 // ShowSQL show SQL statement or not on logger if log level is great than INFO
@@ -215,10 +217,15 @@ func (engine *Engine) NoCascade() *Session {
 }
 
 // MapCacher Set a table use a special cacher
-func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) {
+func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) error {
 	v := rValue(bean)
-	tb := engine.autoMapType(v)
+	tb, err := engine.autoMapType(v)
+	if err != nil {
+		return err
+	}
+
 	tb.Cacher = cacher
+	return nil
 }
 
 // NewDB provides an interface to operate database directly
@@ -260,9 +267,9 @@ func (engine *Engine) Ping() error {
 func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) {
 	if engine.showSQL && !engine.showExecTime {
 		if len(sqlArgs) > 0 {
-			engine.logger.Infof("[sql] %v [args] %v", sqlStr, sqlArgs)
+			engine.logger.Infof("[SQL] %v %v", sqlStr, sqlArgs)
 		} else {
-			engine.logger.Infof("[sql] %v", sqlStr)
+			engine.logger.Infof("[SQL] %v", sqlStr)
 		}
 	}
 }
@@ -273,9 +280,9 @@ func (engine *Engine) logSQLQueryTime(sqlStr string, args []interface{}, executi
 		stmt, res, err := executionBlock()
 		execDuration := time.Since(b4ExecTime)
 		if len(args) > 0 {
-			engine.logger.Infof("[sql] %s [args] %v - took: %v", sqlStr, args, execDuration)
+			engine.logger.Infof("[SQL] %s %v - took: %v", sqlStr, args, execDuration)
 		} else {
-			engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration)
+			engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration)
 		}
 		return stmt, res, err
 	}
@@ -774,13 +781,18 @@ func (engine *Engine) Having(conditions string) *Session {
 	return session.Having(conditions)
 }
 
-func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
+func (engine *Engine) autoMapType(v reflect.Value) (*core.Table, error) {
 	t := v.Type()
 	engine.mutex.Lock()
 	defer engine.mutex.Unlock()
 	table, ok := engine.Tables[t]
 	if !ok {
-		table = engine.mapType(v)
+		var err error
+		table, err = engine.mapType(v)
+		if err != nil {
+			return nil, err
+		}
+
 		engine.Tables[t] = table
 		if engine.Cacher != nil {
 			if v.CanAddr() {
@@ -790,12 +802,11 @@ func (engine *Engine) autoMapType(v reflect.Value) *core.Table {
 			}
 		}
 	}
-	return table
+	return table, nil
 }
 
 // GobRegister register one struct to gob for cache use
 func (engine *Engine) GobRegister(v interface{}) *Engine {
-	//fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v)
 	gob.Register(v)
 	return engine
 }
@@ -806,10 +817,19 @@ type Table struct {
 	Name string
 }
 
+// IsValid if table is valid
+func (t *Table) IsValid() bool {
+	return t.Table != nil && len(t.Name) > 0
+}
+
 // TableInfo get table info according to bean's content
 func (engine *Engine) TableInfo(bean interface{}) *Table {
 	v := rValue(bean)
-	return &Table{engine.autoMapType(v), engine.tbName(v)}
+	tb, err := engine.autoMapType(v)
+	if err != nil {
+		engine.logger.Error(err)
+	}
+	return &Table{tb, engine.tbName(v)}
 }
 
 func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) {
@@ -842,7 +862,7 @@ var (
 	tpTableName = reflect.TypeOf((*TableName)(nil)).Elem()
 )
 
-func (engine *Engine) mapType(v reflect.Value) *core.Table {
+func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) {
 	t := v.Type()
 	table := engine.newTable()
 	if tb, ok := v.Interface().(TableName); ok {
@@ -861,7 +881,6 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
 	table.Type = t
 
 	var idFieldColName string
-	var err error
 	var hasCacheTag, hasNoCacheTag bool
 
 	for i := 0; i < t.NumField(); i++ {
@@ -881,186 +900,95 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
 				if tags[0] == "-" {
 					continue
 				}
+
+				var ctx = tagContext{
+					table:      table,
+					col:        col,
+					fieldValue: fieldValue,
+					indexNames: make(map[string]int),
+					engine:     engine,
+				}
+
 				if strings.ToUpper(tags[0]) == "EXTENDS" {
-					switch fieldValue.Kind() {
-					case reflect.Ptr:
-						f := fieldValue.Type().Elem()
-						if f.Kind() == reflect.Struct {
-							fieldPtr := fieldValue
-							fieldValue = fieldValue.Elem()
-							if !fieldValue.IsValid() || fieldPtr.IsNil() {
-								fieldValue = reflect.New(f).Elem()
-							}
-						}
-						fallthrough
-					case reflect.Struct:
-						parentTable := engine.mapType(fieldValue)
-						for _, col := range parentTable.Columns() {
-							col.FieldName = fmt.Sprintf("%v.%v", t.Field(i).Name, col.FieldName)
-							table.AddColumn(col)
-							for indexName, indexType := range col.Indexes {
-								addIndex(indexName, table, col, indexType)
-							}
-						}
-						continue
-					default:
-						//TODO: warning
+					if err := ExtendsTagHandler(&ctx); err != nil {
+						return nil, err
 					}
+					continue
 				}
 
-				indexNames := make(map[string]int)
-				var isIndex, isUnique bool
-				var preKey string
 				for j, key := range tags {
+					if ctx.ignoreNext {
+						ctx.ignoreNext = false
+						continue
+					}
+
 					k := strings.ToUpper(key)
-					switch {
-					case k == "<-":
-						col.MapType = core.ONLYFROMDB
-					case k == "->":
-						col.MapType = core.ONLYTODB
-					case k == "PK":
-						col.IsPrimaryKey = true
-						col.Nullable = false
-					case k == "NULL":
-						if j == 0 {
-							col.Nullable = true
-						} else {
-							col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT")
-						}
-					// TODO: for postgres how add autoincr?
-					/*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"):
-					col.IsAutoIncrement = true
+					ctx.tagName = k
+					ctx.params = []string{}
 
-					autoStart := k[len("AUTOINCR")+1 : len(k)-1]
-					autoStartInt, err := strconv.Atoi(autoStart)
-					if err != nil {
-						engine.LogError(err)
+					pStart := strings.Index(k, "(")
+					if pStart == 0 {
+						return nil, errors.New("( could not be the first charactor")
 					}
-					col.AutoIncrStart = autoStartInt*/
-					case k == "AUTOINCR":
-						col.IsAutoIncrement = true
-						//col.AutoIncrStart = 1
-					case k == "DEFAULT":
-						col.Default = tags[j+1]
-					case k == "CREATED":
-						col.IsCreated = true
-					case k == "VERSION":
-						col.IsVersion = true
-						col.Default = "1"
-					case k == "UTC":
-						col.TimeZone = time.UTC
-					case k == "LOCAL":
-						col.TimeZone = time.Local
-					case strings.HasPrefix(k, "LOCALE(") && strings.HasSuffix(k, ")"):
-						location := k[len("LOCALE")+1 : len(k)-1]
-						col.TimeZone, err = time.LoadLocation(location)
-						if err != nil {
-							engine.logger.Error(err)
+					if pStart > -1 {
+						if !strings.HasSuffix(k, ")") {
+							return nil, errors.New("cannot match ) charactor")
 						}
-					case k == "UPDATED":
-						col.IsUpdated = true
-					case k == "DELETED":
-						col.IsDeleted = true
-					case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"):
-						indexName := k[len("INDEX")+1 : len(k)-1]
-						indexNames[indexName] = core.IndexType
-					case k == "INDEX":
-						isIndex = true
-					case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"):
-						indexName := k[len("UNIQUE")+1 : len(k)-1]
-						indexNames[indexName] = core.UniqueType
-					case k == "UNIQUE":
-						isUnique = true
-					case k == "NOTNULL":
-						col.Nullable = false
-					case k == "CACHE":
-						if !hasCacheTag {
-							hasCacheTag = true
-						}
-					case k == "NOCACHE":
-						if !hasNoCacheTag {
-							hasNoCacheTag = true
+
+						ctx.tagName = k[:pStart]
+						ctx.params = strings.Split(key[pStart+1:len(k)-1], ",")
+					}
+
+					if j > 0 {
+						ctx.preTag = strings.ToUpper(tags[j-1])
+					}
+					if j < len(tags)-1 {
+						ctx.nextTag = tags[j+1]
+					} else {
+						ctx.nextTag = ""
+					}
+
+					if h, ok := engine.tagHandlers[ctx.tagName]; ok {
+						if err := h(&ctx); err != nil {
+							return nil, err
 						}
-					case k == "NOT":
-					default:
-						if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") {
-							if preKey != "DEFAULT" {
-								col.Name = key[1 : len(key)-1]
-							}
-						} else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") {
-							fs := strings.Split(k, "(")
-
-							if _, ok := core.SqlTypes[fs[0]]; !ok {
-								preKey = k
-								continue
-							}
-							col.SQLType = core.SQLType{Name: fs[0]}
-							if fs[0] == core.Enum && fs[1][0] == '\'' { //enum
-								options := strings.Split(fs[1][0:len(fs[1])-1], ",")
-								col.EnumOptions = make(map[string]int)
-								for k, v := range options {
-									v = strings.TrimSpace(v)
-									v = strings.Trim(v, "'")
-									col.EnumOptions[v] = k
-								}
-							} else if fs[0] == core.Set && fs[1][0] == '\'' { //set
-								options := strings.Split(fs[1][0:len(fs[1])-1], ",")
-								col.SetOptions = make(map[string]int)
-								for k, v := range options {
-									v = strings.TrimSpace(v)
-									v = strings.Trim(v, "'")
-									col.SetOptions[v] = k
-								}
-							} else {
-								fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",")
-								if len(fs2) == 2 {
-									col.Length, err = strconv.Atoi(fs2[0])
-									if err != nil {
-										engine.logger.Error(err)
-									}
-									col.Length2, err = strconv.Atoi(fs2[1])
-									if err != nil {
-										engine.logger.Error(err)
-									}
-								} else if len(fs2) == 1 {
-									col.Length, err = strconv.Atoi(fs2[0])
-									if err != nil {
-										engine.logger.Error(err)
-									}
-								}
-							}
+					} else {
+						if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") {
+							col.Name = key[1 : len(key)-1]
 						} else {
-							if _, ok := core.SqlTypes[k]; ok {
-								col.SQLType = core.SQLType{Name: k}
-							} else if key != col.Default {
-								col.Name = key
-							}
+							col.Name = key
 						}
-						engine.dialect.SqlType(col)
 					}
-					preKey = k
+
+					if ctx.hasCacheTag {
+						hasCacheTag = true
+					}
+					if ctx.hasNoCacheTag {
+						hasNoCacheTag = true
+					}
 				}
+
 				if col.SQLType.Name == "" {
 					col.SQLType = core.Type2SQLType(fieldType)
 				}
+				engine.dialect.SqlType(col)
 				if col.Length == 0 {
 					col.Length = col.SQLType.DefaultLength
 				}
 				if col.Length2 == 0 {
 					col.Length2 = col.SQLType.DefaultLength2
 				}
-
 				if col.Name == "" {
 					col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name)
 				}
 
-				if isUnique {
-					indexNames[col.Name] = core.UniqueType
-				} else if isIndex {
-					indexNames[col.Name] = core.IndexType
+				if ctx.isUnique {
+					ctx.indexNames[col.Name] = core.UniqueType
+				} else if ctx.isIndex {
+					ctx.indexNames[col.Name] = core.IndexType
 				}
 
-				for indexName, indexType := range indexNames {
+				for indexName, indexType := range ctx.indexNames {
 					addIndex(indexName, table, col, indexType)
 				}
 			}
@@ -1114,7 +1042,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table {
 		table.Cacher = nil
 	}
 
-	return table
+	return table, nil
 }
 
 // IsTableEmpty if a table has any reocrd
@@ -1152,8 +1080,21 @@ func (engine *Engine) IdOfV(rv reflect.Value) core.PK {
 
 // IDOfV get id from one value of struct
 func (engine *Engine) IDOfV(rv reflect.Value) core.PK {
+	pk, err := engine.idOfV(rv)
+	if err != nil {
+		engine.logger.Error(err)
+		return nil
+	}
+	return pk
+}
+
+func (engine *Engine) idOfV(rv reflect.Value) (core.PK, error) {
 	v := reflect.Indirect(rv)
-	table := engine.autoMapType(v)
+	table, err := engine.autoMapType(v)
+	if err != nil {
+		return nil, err
+	}
+
 	pk := make([]interface{}, len(table.PrimaryKeys))
 	for i, col := range table.PKColumns() {
 		pkField := v.FieldByName(col.FieldName)
@@ -1166,7 +1107,7 @@ func (engine *Engine) IDOfV(rv reflect.Value) core.PK {
 			pk[i] = pkField.Uint()
 		}
 	}
-	return core.PK(pk)
+	return core.PK(pk), nil
 }
 
 // CreateIndexes create indexes
@@ -1187,13 +1128,6 @@ func (engine *Engine) getCacher2(table *core.Table) core.Cacher {
 	return table.Cacher
 }
 
-func (engine *Engine) getCacher(v reflect.Value) core.Cacher {
-	if table := engine.autoMapType(v); table != nil {
-		return table.Cacher
-	}
-	return engine.Cacher
-}
-
 // ClearCacheBean if enabled cache, clear the cache bean
 func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
 	v := rValue(bean)
@@ -1202,7 +1136,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id string) error {
 		return errors.New("error params")
 	}
 	tableName := engine.tbName(v)
-	table := engine.autoMapType(v)
+	table, err := engine.autoMapType(v)
+	if err != nil {
+		return err
+	}
 	cacher := table.Cacher
 	if cacher == nil {
 		cacher = engine.Cacher
@@ -1223,7 +1160,11 @@ func (engine *Engine) ClearCache(beans ...interface{}) error {
 			return errors.New("error params")
 		}
 		tableName := engine.tbName(v)
-		table := engine.autoMapType(v)
+		table, err := engine.autoMapType(v)
+		if err != nil {
+			return err
+		}
+
 		cacher := table.Cacher
 		if cacher == nil {
 			cacher = engine.Cacher
@@ -1243,7 +1184,10 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 	for _, bean := range beans {
 		v := rValue(bean)
 		tableName := engine.tbName(v)
-		table := engine.autoMapType(v)
+		table, err := engine.autoMapType(v)
+		if err != nil {
+			return err
+		}
 
 		s := engine.NewSession()
 		defer s.Close()
@@ -1279,8 +1223,10 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 				}
 				if !isExist {
 					session := engine.NewSession()
-					session.Statement.setRefValue(v)
 					defer session.Close()
+					if err := session.Statement.setRefValue(v); err != nil {
+						return err
+					}
 					err = session.addColumn(col.Name)
 					if err != nil {
 						return err
@@ -1290,8 +1236,10 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 
 			for name, index := range table.Indexes {
 				session := engine.NewSession()
-				session.Statement.setRefValue(v)
 				defer session.Close()
+				if err := session.Statement.setRefValue(v); err != nil {
+					return err
+				}
 				if index.Type == core.UniqueType {
 					//isExist, err := session.isIndexExist(table.Name, name, true)
 					isExist, err := session.isIndexExist2(tableName, index.Cols, true)
@@ -1300,8 +1248,11 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 					}
 					if !isExist {
 						session := engine.NewSession()
-						session.Statement.setRefValue(v)
 						defer session.Close()
+						if err := session.Statement.setRefValue(v); err != nil {
+							return err
+						}
+
 						err = session.addUnique(tableName, name)
 						if err != nil {
 							return err
@@ -1314,8 +1265,11 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 					}
 					if !isExist {
 						session := engine.NewSession()
-						session.Statement.setRefValue(v)
 						defer session.Close()
+						if err := session.Statement.setRefValue(v); err != nil {
+							return err
+						}
+
 						err = session.addIndex(tableName, name)
 						if err != nil {
 							return err
@@ -1337,18 +1291,6 @@ func (engine *Engine) Sync2(beans ...interface{}) error {
 	return s.Sync2(beans...)
 }
 
-func (engine *Engine) unMap(beans ...interface{}) (e error) {
-	engine.mutex.Lock()
-	defer engine.mutex.Unlock()
-	for _, bean := range beans {
-		t := rType(bean)
-		if _, ok := engine.Tables[t]; ok {
-			delete(engine.Tables, t)
-		}
-	}
-	return
-}
-
 // Drop all mapped table
 func (engine *Engine) dropAll() error {
 	session := engine.NewSession()
@@ -1426,6 +1368,13 @@ func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice [
 	return session.Query(sql, paramStr...)
 }
 
+// QueryString runs a raw sql and return records as []map[string]string
+func (engine *Engine) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) {
+	session := engine.NewSession()
+	defer session.Close()
+	return session.QueryString(sqlStr, args...)
+}
+
 // Insert one or more records
 func (engine *Engine) Insert(beans ...interface{}) (int64, error) {
 	session := engine.NewSession()

+ 0 - 42
vendor/github.com/go-xorm/xorm/goracle_driver.go

@@ -1,42 +0,0 @@
-// 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 (
-	"errors"
-	"regexp"
-
-	"github.com/go-xorm/core"
-)
-
-// func init() {
-// 	core.RegisterDriver("goracle", &goracleDriver{})
-// }
-
-type goracleDriver struct {
-}
-
-func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
-	db := &core.Uri{DbType: core.ORACLE}
-	dsnPattern := regexp.MustCompile(
-		`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
-			`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
-			`\/(?P<dbname>.*?)` + // /dbname
-			`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
-	matches := dsnPattern.FindStringSubmatch(dataSourceName)
-	//tlsConfigRegister := make(map[string]*tls.Config)
-	names := dsnPattern.SubexpNames()
-
-	for i, match := range matches {
-		switch names[i] {
-		case "dbname":
-			db.DbName = match
-		}
-	}
-	if db.DbName == "" {
-		return nil, errors.New("dbname is empty")
-	}
-	return db, nil
-}

+ 40 - 23
vendor/github.com/go-xorm/xorm/helpers.go

@@ -17,74 +17,83 @@ import (
 )
 
 // str2PK convert string value to primary key value according to tp
-func str2PK(s string, tp reflect.Type) (interface{}, error) {
+func str2PKValue(s string, tp reflect.Type) (reflect.Value, error) {
 	var err error
 	var result interface{}
+	var defReturn = reflect.Zero(tp)
+
 	switch tp.Kind() {
 	case reflect.Int:
 		result, err = strconv.Atoi(s)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as int: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as int: %s", s, err.Error())
 		}
 	case reflect.Int8:
 		x, err := strconv.Atoi(s)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as int16: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as int8: %s", s, err.Error())
 		}
 		result = int8(x)
 	case reflect.Int16:
 		x, err := strconv.Atoi(s)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as int16: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as int16: %s", s, err.Error())
 		}
 		result = int16(x)
 	case reflect.Int32:
 		x, err := strconv.Atoi(s)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as int32: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as int32: %s", s, err.Error())
 		}
 		result = int32(x)
 	case reflect.Int64:
 		result, err = strconv.ParseInt(s, 10, 64)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as int64: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as int64: %s", s, err.Error())
 		}
 	case reflect.Uint:
 		x, err := strconv.ParseUint(s, 10, 64)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as uint: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as uint: %s", s, err.Error())
 		}
 		result = uint(x)
 	case reflect.Uint8:
 		x, err := strconv.ParseUint(s, 10, 64)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as uint8: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as uint8: %s", s, err.Error())
 		}
 		result = uint8(x)
 	case reflect.Uint16:
 		x, err := strconv.ParseUint(s, 10, 64)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as uint16: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as uint16: %s", s, err.Error())
 		}
 		result = uint16(x)
 	case reflect.Uint32:
 		x, err := strconv.ParseUint(s, 10, 64)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as uint32: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as uint32: %s", s, err.Error())
 		}
 		result = uint32(x)
 	case reflect.Uint64:
 		result, err = strconv.ParseUint(s, 10, 64)
 		if err != nil {
-			return nil, errors.New("convert " + s + " as uint64: " + err.Error())
+			return defReturn, fmt.Errorf("convert %s as uint64: %s", s, err.Error())
 		}
 	case reflect.String:
 		result = s
 	default:
-		panic("unsupported convert type")
+		return defReturn, errors.New("unsupported convert type")
 	}
-	result = reflect.ValueOf(result).Convert(tp).Interface()
-	return result, nil
+	return reflect.ValueOf(result).Convert(tp), nil
+}
+
+func str2PK(s string, tp reflect.Type) (interface{}, error) {
+	v, err := str2PKValue(s, tp)
+	if err != nil {
+		return nil, err
+	}
+	return v.Interface(), nil
 }
 
 func splitTag(tag string) (tags []string) {
@@ -171,6 +180,20 @@ func isStructZero(v reflect.Value) bool {
 	return true
 }
 
+func isArrayValueZero(v reflect.Value) bool {
+	if !v.IsValid() || v.Len() == 0 {
+		return true
+	}
+
+	for i := 0; i < v.Len(); i++ {
+		if !isZero(v.Index(i).Interface()) {
+			return false
+		}
+	}
+
+	return true
+}
+
 func int64ToIntValue(id int64, tp reflect.Type) reflect.Value {
 	var v interface{}
 	switch tp.Kind() {
@@ -429,7 +452,7 @@ func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string,
 	return result, nil
 }
 
-func txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) {
+func txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) ([]map[string]string, error) {
 	rows, err := tx.Query(sqlStr, params...)
 	if err != nil {
 		return nil, err
@@ -439,13 +462,8 @@ func txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice [
 	return rows2Strings(rows)
 }
 
-func query2(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) {
-	s, err := db.Prepare(sqlStr)
-	if err != nil {
-		return nil, err
-	}
-	defer s.Close()
-	rows, err := s.Query(params...)
+func query2(db *core.DB, sqlStr string, params ...interface{}) ([]map[string]string, error) {
+	rows, err := db.Query(sqlStr, params...)
 	if err != nil {
 		return nil, err
 	}
@@ -579,7 +597,6 @@ func indexName(tableName, idxName string) string {
 }
 
 func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) {
-
 	if len(m) == 0 {
 		return false, false
 	}

+ 0 - 65
vendor/github.com/go-xorm/xorm/mymysql_driver.go

@@ -1,65 +0,0 @@
-// 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 (
-	"errors"
-	"strings"
-	"time"
-
-	"github.com/go-xorm/core"
-)
-
-type mymysqlDriver struct {
-}
-
-func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
-	db := &core.Uri{DbType: core.MYSQL}
-
-	pd := strings.SplitN(dataSourceName, "*", 2)
-	if len(pd) == 2 {
-		// Parse protocol part of URI
-		p := strings.SplitN(pd[0], ":", 2)
-		if len(p) != 2 {
-			return nil, errors.New("Wrong protocol part of URI")
-		}
-		db.Proto = p[0]
-		options := strings.Split(p[1], ",")
-		db.Raddr = options[0]
-		for _, o := range options[1:] {
-			kv := strings.SplitN(o, "=", 2)
-			var k, v string
-			if len(kv) == 2 {
-				k, v = kv[0], kv[1]
-			} else {
-				k, v = o, "true"
-			}
-			switch k {
-			case "laddr":
-				db.Laddr = v
-			case "timeout":
-				to, err := time.ParseDuration(v)
-				if err != nil {
-					return nil, err
-				}
-				db.Timeout = to
-			default:
-				return nil, errors.New("Unknown option: " + k)
-			}
-		}
-		// Remove protocol part
-		pd = pd[1:]
-	}
-	// Parse database part of URI
-	dup := strings.SplitN(pd[0], "/", 3)
-	if len(dup) != 3 {
-		return nil, errors.New("Wrong database part of URI")
-	}
-	db.DbName = dup[0]
-	db.User = dup[1]
-	db.Passwd = dup[2]
-
-	return db, nil
-}

+ 0 - 50
vendor/github.com/go-xorm/xorm/mysql_driver.go

@@ -1,50 +0,0 @@
-// 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 (
-	"regexp"
-	"strings"
-
-	"github.com/go-xorm/core"
-)
-
-type mysqlDriver struct {
-}
-
-func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
-	dsnPattern := regexp.MustCompile(
-		`^(?:(?P<user>.*?)(?::(?P<passwd>.*))?@)?` + // [user[:password]@]
-			`(?:(?P<net>[^\(]*)(?:\((?P<addr>[^\)]*)\))?)?` + // [net[(addr)]]
-			`\/(?P<dbname>.*?)` + // /dbname
-			`(?:\?(?P<params>[^\?]*))?$`) // [?param1=value1&paramN=valueN]
-	matches := dsnPattern.FindStringSubmatch(dataSourceName)
-	//tlsConfigRegister := make(map[string]*tls.Config)
-	names := dsnPattern.SubexpNames()
-
-	uri := &core.Uri{DbType: core.MYSQL}
-
-	for i, match := range matches {
-		switch names[i] {
-		case "dbname":
-			uri.DbName = match
-		case "params":
-			if len(match) > 0 {
-				kvs := strings.Split(match, "&")
-				for _, kv := range kvs {
-					splits := strings.Split(kv, "=")
-					if len(splits) == 2 {
-						switch splits[0] {
-						case "charset":
-							uri.Charset = splits[1]
-						}
-					}
-				}
-			}
-
-		}
-	}
-	return uri, nil
-}

+ 0 - 37
vendor/github.com/go-xorm/xorm/oci8_driver.go

@@ -1,37 +0,0 @@
-// 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 (
-	"errors"
-	"regexp"
-
-	"github.com/go-xorm/core"
-)
-
-type oci8Driver struct {
-}
-
-//dataSourceName=user/password@ipv4:port/dbname
-//dataSourceName=user/password@[ipv6]:port/dbname
-func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
-	db := &core.Uri{DbType: core.ORACLE}
-	dsnPattern := regexp.MustCompile(
-		`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
-			`(?P<net>.*)` + // ip:port
-			`\/(?P<dbname>.*)`) // dbname
-	matches := dsnPattern.FindStringSubmatch(dataSourceName)
-	names := dsnPattern.SubexpNames()
-	for i, match := range matches {
-		switch names[i] {
-		case "dbname":
-			db.DbName = match
-		}
-	}
-	if db.DbName == "" {
-		return nil, errors.New("dbname is empty")
-	}
-	return db, nil
-}

+ 0 - 34
vendor/github.com/go-xorm/xorm/odbc_driver.go

@@ -1,34 +0,0 @@
-// 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 (
-	"errors"
-	"strings"
-
-	"github.com/go-xorm/core"
-)
-
-type odbcDriver struct {
-}
-
-func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
-	kv := strings.Split(dataSourceName, ";")
-	var dbName string
-
-	for _, c := range kv {
-		vv := strings.Split(strings.TrimSpace(c), "=")
-		if len(vv) == 2 {
-			switch strings.ToLower(vv[0]) {
-			case "database":
-				dbName = vv[1]
-			}
-		}
-	}
-	if dbName == "" {
-		return nil, errors.New("no db name provided")
-	}
-	return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil
-}

+ 0 - 119
vendor/github.com/go-xorm/xorm/pq_driver.go

@@ -1,119 +0,0 @@
-// 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 (
-	"errors"
-	"fmt"
-	"net/url"
-	"sort"
-	"strings"
-
-	"github.com/go-xorm/core"
-)
-
-type pqDriver struct {
-}
-
-type values map[string]string
-
-func (vs values) Set(k, v string) {
-	vs[k] = v
-}
-
-func (vs values) Get(k string) (v string) {
-	return vs[k]
-}
-
-func errorf(s string, args ...interface{}) {
-	panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
-}
-
-func parseURL(connstr string) (string, error) {
-	u, err := url.Parse(connstr)
-	if err != nil {
-		return "", err
-	}
-
-	if u.Scheme != "postgresql" && u.Scheme != "postgres" {
-		return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
-	}
-
-	var kvs []string
-	escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
-	accrue := func(k, v string) {
-		if v != "" {
-			kvs = append(kvs, k+"="+escaper.Replace(v))
-		}
-	}
-
-	if u.User != nil {
-		v := u.User.Username()
-		accrue("user", v)
-
-		v, _ = u.User.Password()
-		accrue("password", v)
-	}
-
-	i := strings.Index(u.Host, ":")
-	if i < 0 {
-		accrue("host", u.Host)
-	} else {
-		accrue("host", u.Host[:i])
-		accrue("port", u.Host[i+1:])
-	}
-
-	if u.Path != "" {
-		accrue("dbname", u.Path[1:])
-	}
-
-	q := u.Query()
-	for k := range q {
-		accrue(k, q.Get(k))
-	}
-
-	sort.Strings(kvs) // Makes testing easier (not a performance concern)
-	return strings.Join(kvs, " "), nil
-}
-
-func parseOpts(name string, o values) {
-	if len(name) == 0 {
-		return
-	}
-
-	name = strings.TrimSpace(name)
-
-	ps := strings.Split(name, " ")
-	for _, p := range ps {
-		kv := strings.Split(p, "=")
-		if len(kv) < 2 {
-			errorf("invalid option: %q", p)
-		}
-		o.Set(kv[0], kv[1])
-	}
-}
-
-func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
-	db := &core.Uri{DbType: core.POSTGRES}
-	o := make(values)
-	var err error
-	if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") {
-		dataSourceName, err = parseURL(dataSourceName)
-		if err != nil {
-			return nil, err
-		}
-	}
-	parseOpts(dataSourceName, o)
-
-	db.DbName = o.Get("dbname")
-	if db.DbName == "" {
-		return nil, errors.New("dbname is empty")
-	}
-	/*db.Schema = o.Get("schema")
-	if len(db.Schema) == 0 {
-		db.Schema = "public"
-	}*/
-	return db, nil
-}

+ 17 - 10
vendor/github.com/go-xorm/xorm/rows.go

@@ -16,13 +16,12 @@ import (
 type Rows struct {
 	NoTypeCheck bool
 
-	session     *Session
-	stmt        *core.Stmt
-	rows        *core.Rows
-	fields      []string
-	fieldsCount int
-	beanType    reflect.Type
-	lastError   error
+	session   *Session
+	stmt      *core.Stmt
+	rows      *core.Rows
+	fields    []string
+	beanType  reflect.Type
+	lastError error
 }
 
 func newRows(session *Session, bean interface{}) (*Rows, error) {
@@ -35,7 +34,10 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
 	var sqlStr string
 	var args []interface{}
 
-	rows.session.Statement.setRefValue(rValue(bean))
+	if err := rows.session.Statement.setRefValue(rValue(bean)); err != nil {
+		return nil, err
+	}
+
 	if len(session.Statement.TableName()) <= 0 {
 		return nil, ErrTableNotFound
 	}
@@ -82,7 +84,6 @@ func newRows(session *Session, bean interface{}) (*Rows, error) {
 		rows.Close()
 		return nil, err
 	}
-	rows.fieldsCount = len(rows.fields)
 
 	return rows, nil
 }
@@ -114,7 +115,13 @@ func (rows *Rows) Scan(bean interface{}) error {
 		return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType)
 	}
 
-	return rows.session.row2Bean(rows.rows, rows.fields, rows.fieldsCount, bean)
+	dataStruct := rValue(bean)
+	if err := rows.session.Statement.setRefValue(dataStruct); err != nil {
+		return err
+	}
+	_, err := rows.session.row2Bean(rows.rows, rows.fields, len(rows.fields), bean, &dataStruct, rows.session.Statement.RefTable)
+
+	return err
 }
 
 // Close session if session.IsAutoClose is true, and claimed any opened resources

+ 114 - 974
vendor/github.com/go-xorm/xorm/session.go

@@ -6,17 +6,14 @@ package xorm
 
 import (
 	"database/sql"
-	"database/sql/driver"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"hash/crc32"
 	"reflect"
-	"strconv"
 	"strings"
 	"time"
 
-	"github.com/go-xorm/builder"
 	"github.com/go-xorm/core"
 )
 
@@ -29,7 +26,6 @@ type Session struct {
 	Statement              Statement
 	IsAutoCommit           bool
 	IsCommitedOrRollbacked bool
-	TransType              string
 	IsAutoClose            bool
 
 	// Automatically reset the statement after operations that execute a SQL
@@ -47,7 +43,6 @@ type Session struct {
 
 	prepareStmt bool
 	stmtCache   map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
-	cascadeDeep int
 
 	// !evalphobia! stored the last executed query on this session
 	//beforeSQLExec func(string, ...interface{})
@@ -113,52 +108,6 @@ func (session *Session) Prepare() *Session {
 	return session
 }
 
-// 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 (session *Session) Sql(query string, args ...interface{}) *Session {
-	return session.SQL(query, args...)
-}
-
-// 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 (session *Session) SQL(query interface{}, args ...interface{}) *Session {
-	session.Statement.SQL(query, args...)
-	return session
-}
-
-// Where provides custom query condition.
-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(query interface{}, args ...interface{}) *Session {
-	session.Statement.And(query, args...)
-	return session
-}
-
-// Or provides custom query condition.
-func (session *Session) Or(query interface{}, args ...interface{}) *Session {
-	session.Statement.Or(query, args...)
-	return session
-}
-
-// Id provides converting id as a query condition
-//
-// Deprecated: use ID instead
-func (session *Session) Id(id interface{}) *Session {
-	return session.ID(id)
-}
-
-// ID provides converting id as a query condition
-func (session *Session) ID(id interface{}) *Session {
-	session.Statement.ID(id)
-	return session
-}
-
 // Before Apply before Processor, affected bean is passed to closure arg
 func (session *Session) Before(closures func(interface{})) *Session {
 	if closures != nil {
@@ -187,109 +136,18 @@ func (session *Session) Alias(alias string) *Session {
 	return session
 }
 
-// In provides a query string like "id in (1, 2, 3)"
-func (session *Session) In(column string, args ...interface{}) *Session {
-	session.Statement.In(column, args...)
-	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...)
-	return session
-}
-
-// Decr provides a query string like "count = count - 1"
-func (session *Session) Decr(column string, arg ...interface{}) *Session {
-	session.Statement.Decr(column, arg...)
-	return session
-}
-
-// SetExpr provides a query string like "column = {expression}"
-func (session *Session) SetExpr(column string, expression string) *Session {
-	session.Statement.SetExpr(column, expression)
-	return session
-}
-
-// Select provides some columns to special
-func (session *Session) Select(str string) *Session {
-	session.Statement.Select(str)
-	return session
-}
-
-// Cols provides some columns to special
-func (session *Session) Cols(columns ...string) *Session {
-	session.Statement.Cols(columns...)
-	return session
-}
-
-// AllCols ask all columns
-func (session *Session) AllCols() *Session {
-	session.Statement.AllCols()
-	return session
-}
-
-// MustCols specify some columns must use even if they are empty
-func (session *Session) MustCols(columns ...string) *Session {
-	session.Statement.MustCols(columns...)
-	return session
-}
-
 // NoCascade indicate that no cascade load child object
 func (session *Session) NoCascade() *Session {
 	session.Statement.UseCascade = false
 	return session
 }
 
-// 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 (session *Session) UseBool(columns ...string) *Session {
-	session.Statement.UseBool(columns...)
-	return session
-}
-
-// 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 (session *Session) Distinct(columns ...string) *Session {
-	session.Statement.Distinct(columns...)
-	return session
-}
-
 // ForUpdate Set Read/Write locking for UPDATE
 func (session *Session) ForUpdate() *Session {
 	session.Statement.IsForUpdate = true
 	return session
 }
 
-// Omit Only not use the parameters as select or update columns
-func (session *Session) Omit(columns ...string) *Session {
-	session.Statement.Omit(columns...)
-	return session
-}
-
-// Nullable Set null when column is zero-value and nullable for update
-func (session *Session) Nullable(columns ...string) *Session {
-	session.Statement.Nullable(columns...)
-	return session
-}
-
-// NoAutoTime means do not automatically give created field and updated field
-// the current time on the current session temporarily
-func (session *Session) NoAutoTime() *Session {
-	session.Statement.UseAutoTime = false
-	return session
-}
-
 // NoAutoCondition disable generate SQL condition from beans
 func (session *Session) NoAutoCondition(no ...bool) *Session {
 	session.Statement.NoAutoCondition(no...)
@@ -375,63 +233,12 @@ func (session *Session) DB() *core.DB {
 	return session.db
 }
 
-// Conds returns session query conditions
-func (session *Session) Conds() builder.Cond {
-	return session.Statement.cond
-}
-
 func cleanupProcessorsClosures(slices *[]func(interface{})) {
 	if len(*slices) > 0 {
 		*slices = make([]func(interface{}), 0)
 	}
 }
 
-func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error {
-	dataStruct := rValue(obj)
-	if dataStruct.Kind() != reflect.Struct {
-		return errors.New("Expected a pointer to a struct")
-	}
-
-	var col *core.Column
-	session.Statement.setRefValue(dataStruct)
-	table := session.Statement.RefTable
-	tableName := session.Statement.tableName
-
-	for key, data := range objMap {
-		if col = table.GetColumn(key); col == nil {
-			session.Engine.logger.Warnf("struct %v's has not field %v. %v",
-				table.Type.Name(), key, table.ColumnsSeq())
-			continue
-		}
-
-		fieldName := col.FieldName
-		fieldPath := strings.Split(fieldName, ".")
-		var fieldValue reflect.Value
-		if len(fieldPath) > 2 {
-			session.Engine.logger.Error("Unsupported mutliderive", fieldName)
-			continue
-		} else if len(fieldPath) == 2 {
-			parentField := dataStruct.FieldByName(fieldPath[0])
-			if parentField.IsValid() {
-				fieldValue = parentField.FieldByName(fieldPath[1])
-			}
-		} else {
-			fieldValue = dataStruct.FieldByName(fieldName)
-		}
-		if !fieldValue.IsValid() || !fieldValue.CanSet() {
-			session.Engine.logger.Warnf("table %v's column %v is not valid or cannot set", tableName, key)
-			continue
-		}
-
-		err := session.bytes2Value(col, &fieldValue, data)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
 func (session *Session) canCache() bool {
 	if session.Statement.RefTable == nil ||
 		session.Statement.JoinStr != "" ||
@@ -484,40 +291,38 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c
 type Cell *interface{}
 
 func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount int,
-	table *core.Table, newElemFunc func() reflect.Value,
-	sliceValueSetFunc func(*reflect.Value)) error {
+	table *core.Table, newElemFunc func([]string) reflect.Value,
+	sliceValueSetFunc func(*reflect.Value, core.PK) error) error {
 	for rows.Next() {
-		var newValue = newElemFunc()
+		var newValue = newElemFunc(fields)
 		bean := newValue.Interface()
 		dataStruct := rValue(bean)
-		err := session._row2Bean(rows, fields, fieldsCount, bean, &dataStruct, table)
+		pk, err := session.row2Bean(rows, fields, fieldsCount, bean, &dataStruct, table)
+		if err != nil {
+			return err
+		}
+
+		err = sliceValueSetFunc(&newValue, pk)
 		if err != nil {
 			return err
 		}
-		sliceValueSetFunc(&newValue)
 	}
 	return nil
 }
 
-func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error {
-	dataStruct := rValue(bean)
-	if dataStruct.Kind() != reflect.Struct {
-		return errors.New("Expected a pointer to a struct")
+func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) (core.PK, error) {
+	// handle beforeClosures
+	for _, closure := range session.beforeClosures {
+		closure(bean)
 	}
 
-	session.Statement.setRefValue(dataStruct)
-
-	return session._row2Bean(rows, fields, fieldsCount, bean, &dataStruct, session.Statement.RefTable)
-}
-
-func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) error {
 	scanResults := make([]interface{}, fieldsCount)
 	for i := 0; i < len(fields); i++ {
 		var cell interface{}
 		scanResults[i] = &cell
 	}
 	if err := rows.Scan(scanResults...); err != nil {
-		return err
+		return nil, err
 	}
 
 	if b, hasBeforeSet := bean.(BeforeSetProcessor); hasBeforeSet {
@@ -532,9 +337,24 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 				b.AfterSet(key, Cell(scanResults[ii].(*interface{})))
 			}
 		}
+
+		// handle afterClosures
+		for _, closure := range session.afterClosures {
+			closure(bean)
+		}
 	}()
 
+	dbTZ := session.Engine.DatabaseTZ
+	if dbTZ == nil {
+		if session.Engine.dialect.DBType() == core.SQLITE {
+			dbTZ = time.UTC
+		} else {
+			dbTZ = time.Local
+		}
+	}
+
 	var tempMap = make(map[string]int)
+	var pk core.PK
 	for ii, key := range fields {
 		var idx int
 		var ok bool
@@ -557,9 +377,11 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 			if fieldValue.CanAddr() {
 				if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
 					if data, err := value2Bytes(&rawValue); err == nil {
-						structConvert.FromDB(data)
+						if err := structConvert.FromDB(data); err != nil {
+							return nil, err
+						}
 					} else {
-						session.Engine.logger.Error(err)
+						return nil, err
 					}
 					continue
 				}
@@ -572,17 +394,19 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 					}
 					fieldValue.Interface().(core.Conversion).FromDB(data)
 				} else {
-					session.Engine.logger.Error(err)
+					return nil, err
 				}
 				continue
 			}
 
 			rawValueType := reflect.TypeOf(rawValue.Interface())
 			vv := reflect.ValueOf(rawValue.Interface())
-
+			col := table.GetColumnIdx(key, idx)
+			if col.IsPrimaryKey {
+				pk = append(pk, rawValue.Interface())
+			}
 			fieldType := fieldValue.Type()
 			hasAssigned := false
-			col := table.GetColumnIdx(key, idx)
 
 			if col.SQLType.IsJson() {
 				var bs []byte
@@ -591,7 +415,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 				} else if rawValueType.ConvertibleTo(core.BytesType) {
 					bs = vv.Bytes()
 				} else {
-					return fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind())
+					return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind())
 				}
 
 				hasAssigned = true
@@ -600,15 +424,13 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 					if fieldValue.CanAddr() {
 						err := json.Unmarshal(bs, fieldValue.Addr().Interface())
 						if err != nil {
-							session.Engine.logger.Error(key, err)
-							return err
+							return nil, err
 						}
 					} else {
 						x := reflect.New(fieldType)
 						err := json.Unmarshal(bs, x.Interface())
 						if err != nil {
-							session.Engine.logger.Error(key, err)
-							return err
+							return nil, err
 						}
 						fieldValue.Set(x.Elem())
 					}
@@ -632,15 +454,13 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 					if fieldValue.CanAddr() {
 						err := json.Unmarshal(bs, fieldValue.Addr().Interface())
 						if err != nil {
-							session.Engine.logger.Error(err)
-							return err
+							return nil, err
 						}
 					} else {
 						x := reflect.New(fieldType)
 						err := json.Unmarshal(bs, x.Interface())
 						if err != nil {
-							session.Engine.logger.Error(err)
-							return err
+							return nil, err
 						}
 						fieldValue.Set(x.Elem())
 					}
@@ -652,7 +472,26 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 					case reflect.Uint8:
 						if fieldType.Elem().Kind() == reflect.Uint8 {
 							hasAssigned = true
-							fieldValue.Set(vv)
+							if col.SQLType.IsText() {
+								x := reflect.New(fieldType)
+								err := json.Unmarshal(vv.Bytes(), x.Interface())
+								if err != nil {
+									return nil, err
+								}
+								fieldValue.Set(x.Elem())
+							} else {
+								if fieldValue.Len() > 0 {
+									for i := 0; i < fieldValue.Len(); i++ {
+										if i < vv.Len() {
+											fieldValue.Index(i).Set(vv.Index(i))
+										}
+									}
+								} else {
+									for i := 0; i < vv.Len(); i++ {
+										fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i)))
+									}
+								}
+							}
 						}
 					}
 				}
@@ -689,21 +528,19 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 				}
 			case reflect.Struct:
 				if fieldType.ConvertibleTo(core.TimeType) {
+					var tz *time.Location
+					if col.TimeZone == nil {
+						tz = session.Engine.TZLocation
+					} else {
+						tz = col.TimeZone
+					}
+
 					if rawValueType == core.TimeType {
 						hasAssigned = true
 
 						t := vv.Convert(core.TimeType).Interface().(time.Time)
 
 						z, _ := t.Zone()
-						dbTZ := session.Engine.DatabaseTZ
-						if dbTZ == nil {
-							if session.Engine.dialect.DBType() == core.SQLITE {
-								dbTZ = time.UTC
-							} else {
-								dbTZ = time.Local
-							}
-						}
-
 						// set new location if database don't save timezone or give an incorrect timezone
 						if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location
 							session.Engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
@@ -712,27 +549,13 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 						}
 
 						// !nashtsai! convert to engine location
-						if col.TimeZone == nil {
-							t = t.In(session.Engine.TZLocation)
-						} else {
-							t = t.In(col.TimeZone)
-						}
+						t = t.In(tz)
 						fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
-
-						// t = fieldValue.Interface().(time.Time)
-						// z, _ = t.Zone()
-						// session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
 					} else if rawValueType == core.IntType || rawValueType == core.Int64Type ||
 						rawValueType == core.Int32Type {
 						hasAssigned = true
-						var tz *time.Location
-						if col.TimeZone == nil {
-							tz = session.Engine.TZLocation
-						} else {
-							tz = col.TimeZone
-						}
+
 						t := time.Unix(vv.Int(), 0).In(tz)
-						//vv = reflect.ValueOf(t)
 						fieldValue.Set(reflect.ValueOf(t).Convert(fieldType))
 					} else {
 						if d, ok := vv.Interface().([]uint8); ok {
@@ -771,8 +594,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 						if len([]byte(vv.String())) > 0 {
 							err := json.Unmarshal([]byte(vv.String()), x.Interface())
 							if err != nil {
-								session.Engine.logger.Error(err)
-								return err
+								return nil, err
 							}
 							fieldValue.Set(x.Elem())
 						}
@@ -782,76 +604,47 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 						if len(vv.Bytes()) > 0 {
 							err := json.Unmarshal(vv.Bytes(), x.Interface())
 							if err != nil {
-								session.Engine.logger.Error(err)
-								return err
+								return nil, err
 							}
 							fieldValue.Set(x.Elem())
 						}
 					}
 				} else if session.Statement.UseCascade {
-					table := session.Engine.autoMapType(*fieldValue)
-					if table != nil {
-						hasAssigned = true
-						if len(table.PrimaryKeys) != 1 {
-							panic("unsupported non or composited primary key cascade")
-						}
-						var pk = make(core.PK, len(table.PrimaryKeys))
-
-						switch rawValueType.Kind() {
-						case reflect.Int64:
-							pk[0] = vv.Int()
-						case reflect.Int:
-							pk[0] = int(vv.Int())
-						case reflect.Int32:
-							pk[0] = int32(vv.Int())
-						case reflect.Int16:
-							pk[0] = int16(vv.Int())
-						case reflect.Int8:
-							pk[0] = int8(vv.Int())
-						case reflect.Uint64:
-							pk[0] = vv.Uint()
-						case reflect.Uint:
-							pk[0] = uint(vv.Uint())
-						case reflect.Uint32:
-							pk[0] = uint32(vv.Uint())
-						case reflect.Uint16:
-							pk[0] = uint16(vv.Uint())
-						case reflect.Uint8:
-							pk[0] = uint8(vv.Uint())
-						case reflect.String:
-							pk[0] = vv.String()
-						case reflect.Slice:
-							pk[0], _ = strconv.ParseInt(string(rawValue.Interface().([]byte)), 10, 64)
-						default:
-							panic(fmt.Sprintf("unsupported primary key type: %v, %v", rawValueType, fieldValue))
-						}
+					table, err := session.Engine.autoMapType(*fieldValue)
+					if err != nil {
+						return nil, err
+					}
 
-						if !isPKZero(pk) {
-							// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
-							// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
-							// property to be fetched lazily
-							structInter := reflect.New(fieldValue.Type())
-							newsession := session.Engine.NewSession()
-							defer newsession.Close()
-							has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
-							if err != nil {
-								return err
-							}
-							if has {
-								//v := structInter.Elem().Interface()
-								//fieldValue.Set(reflect.ValueOf(v))
-								fieldValue.Set(structInter.Elem())
-							} else {
-								return errors.New("cascade obj is not exist")
-							}
+					hasAssigned = true
+					if len(table.PrimaryKeys) != 1 {
+						panic("unsupported non or composited primary key cascade")
+					}
+					var pk = make(core.PK, len(table.PrimaryKeys))
+					pk[0], err = asKind(vv, rawValueType)
+					if err != nil {
+						return nil, err
+					}
+
+					if !isPKZero(pk) {
+						// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+						// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+						// property to be fetched lazily
+						structInter := reflect.New(fieldValue.Type())
+						newsession := session.Engine.NewSession()
+						defer newsession.Close()
+						has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
+						if err != nil {
+							return nil, err
+						}
+						if has {
+							fieldValue.Set(structInter.Elem())
+						} else {
+							return nil, errors.New("cascade obj is not exist")
 						}
-					} else {
-						session.Engine.logger.Error("unsupported struct type in Scan: ", fieldValue.Type().String())
 					}
 				}
 			case reflect.Ptr:
 				// !nashtsai! TODO merge duplicated codes above
-				//typeStr := fieldType.String()
 				switch fieldType {
 				// following types case matching ptr's native type, therefore assign ptr directly
 				case core.PtrStringType:
@@ -949,10 +742,9 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 					if len([]byte(vv.String())) > 0 {
 						err := json.Unmarshal([]byte(vv.String()), &x)
 						if err != nil {
-							session.Engine.logger.Error(err)
-						} else {
-							fieldValue.Set(reflect.ValueOf(&x))
+							return nil, err
 						}
+						fieldValue.Set(reflect.ValueOf(&x))
 					}
 					hasAssigned = true
 				case core.Complex128Type:
@@ -960,29 +752,28 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
 					if len([]byte(vv.String())) > 0 {
 						err := json.Unmarshal([]byte(vv.String()), &x)
 						if err != nil {
-							session.Engine.logger.Error(err)
-						} else {
-							fieldValue.Set(reflect.ValueOf(&x))
+							return nil, err
 						}
+						fieldValue.Set(reflect.ValueOf(&x))
 					}
 					hasAssigned = true
 				} // switch fieldType
-				// default:
-				// 	session.Engine.LogError("unsupported type in Scan: ", reflect.TypeOf(v).String())
 			} // switch fieldType.Kind()
 
 			// !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value
 			if !hasAssigned {
 				data, err := value2Bytes(&rawValue)
-				if err == nil {
-					session.bytes2Value(col, fieldValue, data)
-				} else {
-					session.Engine.logger.Error(err.Error())
+				if err != nil {
+					return nil, err
+				}
+
+				if err = session.bytes2Value(col, fieldValue, data); err != nil {
+					return nil, err
 				}
 			}
 		}
 	}
-	return nil
+	return pk, nil
 }
 
 func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) {
@@ -993,657 +784,6 @@ func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{})
 	session.saveLastSQL(*sqlStr, paramStr...)
 }
 
-func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) {
-	sdata := strings.TrimSpace(data)
-	var x time.Time
-	var err error
-
-	if sdata == "0000-00-00 00:00:00" ||
-		sdata == "0001-01-01 00:00:00" {
-	} else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
-		// time stamp
-		sd, err := strconv.ParseInt(sdata, 10, 64)
-		if err == nil {
-			x = time.Unix(sd, 0)
-			// !nashtsai! HACK mymysql driver is causing Local location being change to CHAT and cause wrong time conversion
-			if col.TimeZone == nil {
-				x = x.In(session.Engine.TZLocation)
-			} else {
-				x = x.In(col.TimeZone)
-			}
-			session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-		} else {
-			session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-		}
-	} else if len(sdata) > 19 && strings.Contains(sdata, "-") {
-		x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-		if err != nil {
-			x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation)
-			session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-		}
-		if err != nil {
-			x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation)
-			session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-		}
-
-	} else if len(sdata) == 19 && strings.Contains(sdata, "-") {
-		x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-	} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
-		x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-	} else if col.SQLType.Name == core.Time {
-		if strings.Contains(sdata, " ") {
-			ssd := strings.Split(sdata, " ")
-			sdata = ssd[1]
-		}
-
-		sdata = strings.TrimSpace(sdata)
-		if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 {
-			sdata = sdata[len(sdata)-8:]
-		}
-
-		st := fmt.Sprintf("2006-01-02 %v", sdata)
-		x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation)
-		session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
-	} else {
-		outErr = fmt.Errorf("unsupported time format %v", sdata)
-		return
-	}
-	if err != nil {
-		outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
-		return
-	}
-	outTime = x
-	return
-}
-
-func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) {
-	return session.str2Time(col, string(data))
-}
-
-// convert a db data([]byte) to a field value
-func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error {
-	if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
-		return structConvert.FromDB(data)
-	}
-
-	if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
-		return structConvert.FromDB(data)
-	}
-
-	var v interface{}
-	key := col.Name
-	fieldType := fieldValue.Type()
-
-	switch fieldType.Kind() {
-	case reflect.Complex64, reflect.Complex128:
-		x := reflect.New(fieldType)
-		if len(data) > 0 {
-			err := json.Unmarshal(data, x.Interface())
-			if err != nil {
-				session.Engine.logger.Error(err)
-				return err
-			}
-			fieldValue.Set(x.Elem())
-		}
-	case reflect.Slice, reflect.Array, reflect.Map:
-		v = data
-		t := fieldType.Elem()
-		k := t.Kind()
-		if col.SQLType.IsText() {
-			x := reflect.New(fieldType)
-			if len(data) > 0 {
-				err := json.Unmarshal(data, x.Interface())
-				if err != nil {
-					session.Engine.logger.Error(err)
-					return err
-				}
-				fieldValue.Set(x.Elem())
-			}
-		} else if col.SQLType.IsBlob() {
-			if k == reflect.Uint8 {
-				fieldValue.Set(reflect.ValueOf(v))
-			} else {
-				x := reflect.New(fieldType)
-				if len(data) > 0 {
-					err := json.Unmarshal(data, x.Interface())
-					if err != nil {
-						session.Engine.logger.Error(err)
-						return err
-					}
-					fieldValue.Set(x.Elem())
-				}
-			}
-		} else {
-			return ErrUnSupportedType
-		}
-	case reflect.String:
-		fieldValue.SetString(string(data))
-	case reflect.Bool:
-		d := string(data)
-		v, err := strconv.ParseBool(d)
-		if err != nil {
-			return fmt.Errorf("arg %v as bool: %s", key, err.Error())
-		}
-		fieldValue.Set(reflect.ValueOf(v))
-	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		sdata := string(data)
-		var x int64
-		var err error
-		// for mysql, when use bit, it returned \x01
-		if col.SQLType.Name == core.Bit &&
-			session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
-			if len(data) == 1 {
-				x = int64(data[0])
-			} else {
-				x = 0
-			}
-		} else if strings.HasPrefix(sdata, "0x") {
-			x, err = strconv.ParseInt(sdata, 16, 64)
-		} else if strings.HasPrefix(sdata, "0") {
-			x, err = strconv.ParseInt(sdata, 8, 64)
-		} else if strings.EqualFold(sdata, "true") {
-			x = 1
-		} else if strings.EqualFold(sdata, "false") {
-			x = 0
-		} else {
-			x, err = strconv.ParseInt(sdata, 10, 64)
-		}
-		if err != nil {
-			return fmt.Errorf("arg %v as int: %s", key, err.Error())
-		}
-		fieldValue.SetInt(x)
-	case reflect.Float32, reflect.Float64:
-		x, err := strconv.ParseFloat(string(data), 64)
-		if err != nil {
-			return fmt.Errorf("arg %v as float64: %s", key, err.Error())
-		}
-		fieldValue.SetFloat(x)
-	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
-		x, err := strconv.ParseUint(string(data), 10, 64)
-		if err != nil {
-			return fmt.Errorf("arg %v as int: %s", key, err.Error())
-		}
-		fieldValue.SetUint(x)
-	//Currently only support Time type
-	case reflect.Struct:
-		// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
-		if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
-			if err := nulVal.Scan(data); err != nil {
-				return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error())
-			}
-		} else {
-			if fieldType.ConvertibleTo(core.TimeType) {
-				x, err := session.byte2Time(col, data)
-				if err != nil {
-					return err
-				}
-				v = x
-				fieldValue.Set(reflect.ValueOf(v).Convert(fieldType))
-			} else if session.Statement.UseCascade {
-				table := session.Engine.autoMapType(*fieldValue)
-				if table != nil {
-					// TODO: current only support 1 primary key
-					if len(table.PrimaryKeys) > 1 {
-						panic("unsupported composited primary key cascade")
-					}
-					var pk = make(core.PK, len(table.PrimaryKeys))
-					rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
-					var err error
-					pk[0], err = str2PK(string(data), rawValueType)
-					if err != nil {
-						return err
-					}
-
-					if !isPKZero(pk) {
-						// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
-						// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
-						// property to be fetched lazily
-						structInter := reflect.New(fieldValue.Type())
-						newsession := session.Engine.NewSession()
-						defer newsession.Close()
-						has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
-						if err != nil {
-							return err
-						}
-						if has {
-							v = structInter.Elem().Interface()
-							fieldValue.Set(reflect.ValueOf(v))
-						} else {
-							return errors.New("cascade obj is not exist")
-						}
-					}
-				} else {
-					return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
-				}
-			}
-		}
-	case reflect.Ptr:
-		// !nashtsai! TODO merge duplicated codes above
-		//typeStr := fieldType.String()
-		switch fieldType.Elem().Kind() {
-		// case "*string":
-		case core.StringType.Kind():
-			x := string(data)
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*bool":
-		case core.BoolType.Kind():
-			d := string(data)
-			v, err := strconv.ParseBool(d)
-			if err != nil {
-				return fmt.Errorf("arg %v as bool: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType))
-		// case "*complex64":
-		case core.Complex64Type.Kind():
-			var x complex64
-			if len(data) > 0 {
-				err := json.Unmarshal(data, &x)
-				if err != nil {
-					session.Engine.logger.Error(err)
-					return err
-				}
-				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-			}
-		// case "*complex128":
-		case core.Complex128Type.Kind():
-			var x complex128
-			if len(data) > 0 {
-				err := json.Unmarshal(data, &x)
-				if err != nil {
-					session.Engine.logger.Error(err)
-					return err
-				}
-				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-			}
-		// case "*float64":
-		case core.Float64Type.Kind():
-			x, err := strconv.ParseFloat(string(data), 64)
-			if err != nil {
-				return fmt.Errorf("arg %v as float64: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*float32":
-		case core.Float32Type.Kind():
-			var x float32
-			x1, err := strconv.ParseFloat(string(data), 32)
-			if err != nil {
-				return fmt.Errorf("arg %v as float32: %s", key, err.Error())
-			}
-			x = float32(x1)
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*uint64":
-		case core.Uint64Type.Kind():
-			var x uint64
-			x, err := strconv.ParseUint(string(data), 10, 64)
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*uint":
-		case core.UintType.Kind():
-			var x uint
-			x1, err := strconv.ParseUint(string(data), 10, 64)
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			x = uint(x1)
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*uint32":
-		case core.Uint32Type.Kind():
-			var x uint32
-			x1, err := strconv.ParseUint(string(data), 10, 64)
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			x = uint32(x1)
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*uint8":
-		case core.Uint8Type.Kind():
-			var x uint8
-			x1, err := strconv.ParseUint(string(data), 10, 64)
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			x = uint8(x1)
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*uint16":
-		case core.Uint16Type.Kind():
-			var x uint16
-			x1, err := strconv.ParseUint(string(data), 10, 64)
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			x = uint16(x1)
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*int64":
-		case core.Int64Type.Kind():
-			sdata := string(data)
-			var x int64
-			var err error
-			// for mysql, when use bit, it returned \x01
-			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
-				if len(data) == 1 {
-					x = int64(data[0])
-				} else {
-					x = 0
-				}
-			} else if strings.HasPrefix(sdata, "0x") {
-				x, err = strconv.ParseInt(sdata, 16, 64)
-			} else if strings.HasPrefix(sdata, "0") {
-				x, err = strconv.ParseInt(sdata, 8, 64)
-			} else {
-				x, err = strconv.ParseInt(sdata, 10, 64)
-			}
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*int":
-		case core.IntType.Kind():
-			sdata := string(data)
-			var x int
-			var x1 int64
-			var err error
-			// for mysql, when use bit, it returned \x01
-			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
-				if len(data) == 1 {
-					x = int(data[0])
-				} else {
-					x = 0
-				}
-			} else if strings.HasPrefix(sdata, "0x") {
-				x1, err = strconv.ParseInt(sdata, 16, 64)
-				x = int(x1)
-			} else if strings.HasPrefix(sdata, "0") {
-				x1, err = strconv.ParseInt(sdata, 8, 64)
-				x = int(x1)
-			} else {
-				x1, err = strconv.ParseInt(sdata, 10, 64)
-				x = int(x1)
-			}
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*int32":
-		case core.Int32Type.Kind():
-			sdata := string(data)
-			var x int32
-			var x1 int64
-			var err error
-			// for mysql, when use bit, it returned \x01
-			if col.SQLType.Name == core.Bit &&
-				session.Engine.dialect.DBType() == core.MYSQL {
-				if len(data) == 1 {
-					x = int32(data[0])
-				} else {
-					x = 0
-				}
-			} else if strings.HasPrefix(sdata, "0x") {
-				x1, err = strconv.ParseInt(sdata, 16, 64)
-				x = int32(x1)
-			} else if strings.HasPrefix(sdata, "0") {
-				x1, err = strconv.ParseInt(sdata, 8, 64)
-				x = int32(x1)
-			} else {
-				x1, err = strconv.ParseInt(sdata, 10, 64)
-				x = int32(x1)
-			}
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*int8":
-		case core.Int8Type.Kind():
-			sdata := string(data)
-			var x int8
-			var x1 int64
-			var err error
-			// for mysql, when use bit, it returned \x01
-			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
-				if len(data) == 1 {
-					x = int8(data[0])
-				} else {
-					x = 0
-				}
-			} else if strings.HasPrefix(sdata, "0x") {
-				x1, err = strconv.ParseInt(sdata, 16, 64)
-				x = int8(x1)
-			} else if strings.HasPrefix(sdata, "0") {
-				x1, err = strconv.ParseInt(sdata, 8, 64)
-				x = int8(x1)
-			} else {
-				x1, err = strconv.ParseInt(sdata, 10, 64)
-				x = int8(x1)
-			}
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*int16":
-		case core.Int16Type.Kind():
-			sdata := string(data)
-			var x int16
-			var x1 int64
-			var err error
-			// for mysql, when use bit, it returned \x01
-			if col.SQLType.Name == core.Bit &&
-				strings.Contains(session.Engine.DriverName(), "mysql") {
-				if len(data) == 1 {
-					x = int16(data[0])
-				} else {
-					x = 0
-				}
-			} else if strings.HasPrefix(sdata, "0x") {
-				x1, err = strconv.ParseInt(sdata, 16, 64)
-				x = int16(x1)
-			} else if strings.HasPrefix(sdata, "0") {
-				x1, err = strconv.ParseInt(sdata, 8, 64)
-				x = int16(x1)
-			} else {
-				x1, err = strconv.ParseInt(sdata, 10, 64)
-				x = int16(x1)
-			}
-			if err != nil {
-				return fmt.Errorf("arg %v as int: %s", key, err.Error())
-			}
-			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
-		// case "*SomeStruct":
-		case reflect.Struct:
-			switch fieldType {
-			// case "*.time.Time":
-			case core.PtrTimeType:
-				x, err := session.byte2Time(col, data)
-				if err != nil {
-					return err
-				}
-				v = x
-				fieldValue.Set(reflect.ValueOf(&x))
-			default:
-				if session.Statement.UseCascade {
-					structInter := reflect.New(fieldType.Elem())
-					table := session.Engine.autoMapType(structInter.Elem())
-					if table != nil {
-						if len(table.PrimaryKeys) > 1 {
-							panic("unsupported composited primary key cascade")
-						}
-						var pk = make(core.PK, len(table.PrimaryKeys))
-						var err error
-						rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
-						pk[0], err = str2PK(string(data), rawValueType)
-						if err != nil {
-							return err
-						}
-
-						if !isPKZero(pk) {
-							// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
-							// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
-							// property to be fetched lazily
-							newsession := session.Engine.NewSession()
-							defer newsession.Close()
-							has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
-							if err != nil {
-								return err
-							}
-							if has {
-								v = structInter.Interface()
-								fieldValue.Set(reflect.ValueOf(v))
-							} else {
-								return errors.New("cascade obj is not exist")
-							}
-						}
-					}
-				} else {
-					return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
-				}
-			}
-		default:
-			return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
-		}
-	default:
-		return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
-	}
-
-	return nil
-}
-
-// convert a field value of a struct to interface for put into db
-func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) {
-	if fieldValue.CanAddr() {
-		if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
-			data, err := fieldConvert.ToDB()
-			if err != nil {
-				return 0, err
-			}
-			if col.SQLType.IsBlob() {
-				return data, nil
-			}
-			return string(data), nil
-		}
-	}
-
-	if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
-		data, err := fieldConvert.ToDB()
-		if err != nil {
-			return 0, err
-		}
-		if col.SQLType.IsBlob() {
-			return data, nil
-		}
-		return string(data), nil
-	}
-
-	fieldType := fieldValue.Type()
-	k := fieldType.Kind()
-	if k == reflect.Ptr {
-		if fieldValue.IsNil() {
-			return nil, nil
-		} else if !fieldValue.IsValid() {
-			session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid")
-			return nil, nil
-		} else {
-			// !nashtsai! deference pointer type to instance type
-			fieldValue = fieldValue.Elem()
-			fieldType = fieldValue.Type()
-			k = fieldType.Kind()
-		}
-	}
-
-	switch k {
-	case reflect.Bool:
-		return fieldValue.Bool(), nil
-	case reflect.String:
-		return fieldValue.String(), nil
-	case reflect.Struct:
-		if fieldType.ConvertibleTo(core.TimeType) {
-			t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
-			if session.Engine.dialect.DBType() == core.MSSQL {
-				if t.IsZero() {
-					return nil, nil
-				}
-			}
-			tf := session.Engine.FormatTime(col.SQLType.Name, t)
-			return tf, nil
-		}
-
-		if !col.SQLType.IsJson() {
-			// !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString
-			if v, ok := fieldValue.Interface().(driver.Valuer); ok {
-				return v.Value()
-			}
-
-			fieldTable := session.Engine.autoMapType(fieldValue)
-			if len(fieldTable.PrimaryKeys) == 1 {
-				pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
-				return pkField.Interface(), nil
-			}
-			return 0, fmt.Errorf("no primary key for col %v", col.Name)
-		}
-
-		if col.SQLType.IsText() {
-			bytes, err := json.Marshal(fieldValue.Interface())
-			if err != nil {
-				session.Engine.logger.Error(err)
-				return 0, err
-			}
-			return string(bytes), nil
-		} else if col.SQLType.IsBlob() {
-			bytes, err := json.Marshal(fieldValue.Interface())
-			if err != nil {
-				session.Engine.logger.Error(err)
-				return 0, err
-			}
-			return bytes, nil
-		}
-		return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type())
-	case reflect.Complex64, reflect.Complex128:
-		bytes, err := json.Marshal(fieldValue.Interface())
-		if err != nil {
-			session.Engine.logger.Error(err)
-			return 0, err
-		}
-		return string(bytes), nil
-	case reflect.Array, reflect.Slice, reflect.Map:
-		if !fieldValue.IsValid() {
-			return fieldValue.Interface(), nil
-		}
-
-		if col.SQLType.IsText() {
-			bytes, err := json.Marshal(fieldValue.Interface())
-			if err != nil {
-				session.Engine.logger.Error(err)
-				return 0, err
-			}
-			return string(bytes), nil
-		} else if col.SQLType.IsBlob() {
-			var bytes []byte
-			var err error
-			if (k == reflect.Array || k == reflect.Slice) &&
-				(fieldValue.Type().Elem().Kind() == reflect.Uint8) {
-				bytes = fieldValue.Bytes()
-			} else {
-				bytes, err = json.Marshal(fieldValue.Interface())
-				if err != nil {
-					session.Engine.logger.Error(err)
-					return 0, err
-				}
-			}
-			return bytes, nil
-		}
-		return nil, ErrUnSupportedType
-	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
-		return int64(fieldValue.Uint()), nil
-	default:
-		return fieldValue.Interface(), nil
-	}
-}
-
 // saveLastSQL stores executed query information
 func (session *Session) saveLastSQL(sql string, args ...interface{}) {
 	session.lastSQL = sql

+ 84 - 0
vendor/github.com/go-xorm/xorm/session_cols.go

@@ -0,0 +1,84 @@
+// 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
+
+// Incr provides a query string like "count = count + 1"
+func (session *Session) Incr(column string, arg ...interface{}) *Session {
+	session.Statement.Incr(column, arg...)
+	return session
+}
+
+// Decr provides a query string like "count = count - 1"
+func (session *Session) Decr(column string, arg ...interface{}) *Session {
+	session.Statement.Decr(column, arg...)
+	return session
+}
+
+// SetExpr provides a query string like "column = {expression}"
+func (session *Session) SetExpr(column string, expression string) *Session {
+	session.Statement.SetExpr(column, expression)
+	return session
+}
+
+// Select provides some columns to special
+func (session *Session) Select(str string) *Session {
+	session.Statement.Select(str)
+	return session
+}
+
+// Cols provides some columns to special
+func (session *Session) Cols(columns ...string) *Session {
+	session.Statement.Cols(columns...)
+	return session
+}
+
+// AllCols ask all columns
+func (session *Session) AllCols() *Session {
+	session.Statement.AllCols()
+	return session
+}
+
+// MustCols specify some columns must use even if they are empty
+func (session *Session) MustCols(columns ...string) *Session {
+	session.Statement.MustCols(columns...)
+	return session
+}
+
+// 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 (session *Session) UseBool(columns ...string) *Session {
+	session.Statement.UseBool(columns...)
+	return session
+}
+
+// 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 (session *Session) Distinct(columns ...string) *Session {
+	session.Statement.Distinct(columns...)
+	return session
+}
+
+// Omit Only not use the parameters as select or update columns
+func (session *Session) Omit(columns ...string) *Session {
+	session.Statement.Omit(columns...)
+	return session
+}
+
+// Nullable Set null when column is zero-value and nullable for update
+func (session *Session) Nullable(columns ...string) *Session {
+	session.Statement.Nullable(columns...)
+	return session
+}
+
+// NoAutoTime means do not automatically give created field and updated field
+// the current time on the current session temporarily
+func (session *Session) NoAutoTime() *Session {
+	session.Statement.UseAutoTime = false
+	return session
+}

+ 70 - 0
vendor/github.com/go-xorm/xorm/session_cond.go

@@ -0,0 +1,70 @@
+// Copyright 2017 The Xorm Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package xorm
+
+import "github.com/go-xorm/builder"
+
+// 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 (session *Session) Sql(query string, args ...interface{}) *Session {
+	return session.SQL(query, args...)
+}
+
+// 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 (session *Session) SQL(query interface{}, args ...interface{}) *Session {
+	session.Statement.SQL(query, args...)
+	return session
+}
+
+// Where provides custom query condition.
+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(query interface{}, args ...interface{}) *Session {
+	session.Statement.And(query, args...)
+	return session
+}
+
+// Or provides custom query condition.
+func (session *Session) Or(query interface{}, args ...interface{}) *Session {
+	session.Statement.Or(query, args...)
+	return session
+}
+
+// Id provides converting id as a query condition
+//
+// Deprecated: use ID instead
+func (session *Session) Id(id interface{}) *Session {
+	return session.ID(id)
+}
+
+// ID provides converting id as a query condition
+func (session *Session) ID(id interface{}) *Session {
+	session.Statement.ID(id)
+	return session
+}
+
+// In provides a query string like "id in (1, 2, 3)"
+func (session *Session) In(column string, args ...interface{}) *Session {
+	session.Statement.In(column, args...)
+	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
+}
+
+// Conds returns session query conditions
+func (session *Session) Conds() builder.Cond {
+	return session.Statement.cond
+}

+ 673 - 0
vendor/github.com/go-xorm/xorm/session_convert.go

@@ -0,0 +1,673 @@
+// 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"
+	"database/sql/driver"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/go-xorm/core"
+)
+
+func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) {
+	sdata := strings.TrimSpace(data)
+	var x time.Time
+	var err error
+
+	if sdata == "0000-00-00 00:00:00" ||
+		sdata == "0001-01-01 00:00:00" {
+	} else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column
+		// time stamp
+		sd, err := strconv.ParseInt(sdata, 10, 64)
+		if err == nil {
+			x = time.Unix(sd, 0)
+			// !nashtsai! HACK mymysql driver is causing Local location being change to CHAT and cause wrong time conversion
+			if col.TimeZone == nil {
+				x = x.In(session.Engine.TZLocation)
+			} else {
+				x = x.In(col.TimeZone)
+			}
+			session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		} else {
+			session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		}
+	} else if len(sdata) > 19 && strings.Contains(sdata, "-") {
+		x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation)
+		session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		if err != nil {
+			x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation)
+			session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		}
+		if err != nil {
+			x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation)
+			session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+		}
+
+	} else if len(sdata) == 19 && strings.Contains(sdata, "-") {
+		x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation)
+		session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+	} else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' {
+		x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation)
+		session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+	} else if col.SQLType.Name == core.Time {
+		if strings.Contains(sdata, " ") {
+			ssd := strings.Split(sdata, " ")
+			sdata = ssd[1]
+		}
+
+		sdata = strings.TrimSpace(sdata)
+		if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 {
+			sdata = sdata[len(sdata)-8:]
+		}
+
+		st := fmt.Sprintf("2006-01-02 %v", sdata)
+		x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation)
+		session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata)
+	} else {
+		outErr = fmt.Errorf("unsupported time format %v", sdata)
+		return
+	}
+	if err != nil {
+		outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
+		return
+	}
+	outTime = x
+	return
+}
+
+func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) {
+	return session.str2Time(col, string(data))
+}
+
+// convert a db data([]byte) to a field value
+func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error {
+	if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+		return structConvert.FromDB(data)
+	}
+
+	if structConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+		return structConvert.FromDB(data)
+	}
+
+	var v interface{}
+	key := col.Name
+	fieldType := fieldValue.Type()
+
+	switch fieldType.Kind() {
+	case reflect.Complex64, reflect.Complex128:
+		x := reflect.New(fieldType)
+		if len(data) > 0 {
+			err := json.Unmarshal(data, x.Interface())
+			if err != nil {
+				session.Engine.logger.Error(err)
+				return err
+			}
+			fieldValue.Set(x.Elem())
+		}
+	case reflect.Slice, reflect.Array, reflect.Map:
+		v = data
+		t := fieldType.Elem()
+		k := t.Kind()
+		if col.SQLType.IsText() {
+			x := reflect.New(fieldType)
+			if len(data) > 0 {
+				err := json.Unmarshal(data, x.Interface())
+				if err != nil {
+					session.Engine.logger.Error(err)
+					return err
+				}
+				fieldValue.Set(x.Elem())
+			}
+		} else if col.SQLType.IsBlob() {
+			if k == reflect.Uint8 {
+				fieldValue.Set(reflect.ValueOf(v))
+			} else {
+				x := reflect.New(fieldType)
+				if len(data) > 0 {
+					err := json.Unmarshal(data, x.Interface())
+					if err != nil {
+						session.Engine.logger.Error(err)
+						return err
+					}
+					fieldValue.Set(x.Elem())
+				}
+			}
+		} else {
+			return ErrUnSupportedType
+		}
+	case reflect.String:
+		fieldValue.SetString(string(data))
+	case reflect.Bool:
+		d := string(data)
+		v, err := strconv.ParseBool(d)
+		if err != nil {
+			return fmt.Errorf("arg %v as bool: %s", key, err.Error())
+		}
+		fieldValue.Set(reflect.ValueOf(v))
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		sdata := string(data)
+		var x int64
+		var err error
+		// for mysql, when use bit, it returned \x01
+		if col.SQLType.Name == core.Bit &&
+			session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
+			if len(data) == 1 {
+				x = int64(data[0])
+			} else {
+				x = 0
+			}
+		} else if strings.HasPrefix(sdata, "0x") {
+			x, err = strconv.ParseInt(sdata, 16, 64)
+		} else if strings.HasPrefix(sdata, "0") {
+			x, err = strconv.ParseInt(sdata, 8, 64)
+		} else if strings.EqualFold(sdata, "true") {
+			x = 1
+		} else if strings.EqualFold(sdata, "false") {
+			x = 0
+		} else {
+			x, err = strconv.ParseInt(sdata, 10, 64)
+		}
+		if err != nil {
+			return fmt.Errorf("arg %v as int: %s", key, err.Error())
+		}
+		fieldValue.SetInt(x)
+	case reflect.Float32, reflect.Float64:
+		x, err := strconv.ParseFloat(string(data), 64)
+		if err != nil {
+			return fmt.Errorf("arg %v as float64: %s", key, err.Error())
+		}
+		fieldValue.SetFloat(x)
+	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+		x, err := strconv.ParseUint(string(data), 10, 64)
+		if err != nil {
+			return fmt.Errorf("arg %v as int: %s", key, err.Error())
+		}
+		fieldValue.SetUint(x)
+	//Currently only support Time type
+	case reflect.Struct:
+		// !<winxxp>! 增加支持sql.Scanner接口的结构,如sql.NullString
+		if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
+			if err := nulVal.Scan(data); err != nil {
+				return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error())
+			}
+		} else {
+			if fieldType.ConvertibleTo(core.TimeType) {
+				x, err := session.byte2Time(col, data)
+				if err != nil {
+					return err
+				}
+				v = x
+				fieldValue.Set(reflect.ValueOf(v).Convert(fieldType))
+			} else if session.Statement.UseCascade {
+				table, err := session.Engine.autoMapType(*fieldValue)
+				if err != nil {
+					return err
+				}
+
+				// TODO: current only support 1 primary key
+				if len(table.PrimaryKeys) > 1 {
+					panic("unsupported composited primary key cascade")
+				}
+				var pk = make(core.PK, len(table.PrimaryKeys))
+				rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+				pk[0], err = str2PK(string(data), rawValueType)
+				if err != nil {
+					return err
+				}
+
+				if !isPKZero(pk) {
+					// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+					// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+					// property to be fetched lazily
+					structInter := reflect.New(fieldValue.Type())
+					newsession := session.Engine.NewSession()
+					defer newsession.Close()
+					has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
+					if err != nil {
+						return err
+					}
+					if has {
+						v = structInter.Elem().Interface()
+						fieldValue.Set(reflect.ValueOf(v))
+					} else {
+						return errors.New("cascade obj is not exist")
+					}
+				}
+			}
+		}
+	case reflect.Ptr:
+		// !nashtsai! TODO merge duplicated codes above
+		//typeStr := fieldType.String()
+		switch fieldType.Elem().Kind() {
+		// case "*string":
+		case core.StringType.Kind():
+			x := string(data)
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*bool":
+		case core.BoolType.Kind():
+			d := string(data)
+			v, err := strconv.ParseBool(d)
+			if err != nil {
+				return fmt.Errorf("arg %v as bool: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType))
+		// case "*complex64":
+		case core.Complex64Type.Kind():
+			var x complex64
+			if len(data) > 0 {
+				err := json.Unmarshal(data, &x)
+				if err != nil {
+					session.Engine.logger.Error(err)
+					return err
+				}
+				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+			}
+		// case "*complex128":
+		case core.Complex128Type.Kind():
+			var x complex128
+			if len(data) > 0 {
+				err := json.Unmarshal(data, &x)
+				if err != nil {
+					session.Engine.logger.Error(err)
+					return err
+				}
+				fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+			}
+		// case "*float64":
+		case core.Float64Type.Kind():
+			x, err := strconv.ParseFloat(string(data), 64)
+			if err != nil {
+				return fmt.Errorf("arg %v as float64: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*float32":
+		case core.Float32Type.Kind():
+			var x float32
+			x1, err := strconv.ParseFloat(string(data), 32)
+			if err != nil {
+				return fmt.Errorf("arg %v as float32: %s", key, err.Error())
+			}
+			x = float32(x1)
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*uint64":
+		case core.Uint64Type.Kind():
+			var x uint64
+			x, err := strconv.ParseUint(string(data), 10, 64)
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*uint":
+		case core.UintType.Kind():
+			var x uint
+			x1, err := strconv.ParseUint(string(data), 10, 64)
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			x = uint(x1)
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*uint32":
+		case core.Uint32Type.Kind():
+			var x uint32
+			x1, err := strconv.ParseUint(string(data), 10, 64)
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			x = uint32(x1)
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*uint8":
+		case core.Uint8Type.Kind():
+			var x uint8
+			x1, err := strconv.ParseUint(string(data), 10, 64)
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			x = uint8(x1)
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*uint16":
+		case core.Uint16Type.Kind():
+			var x uint16
+			x1, err := strconv.ParseUint(string(data), 10, 64)
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			x = uint16(x1)
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*int64":
+		case core.Int64Type.Kind():
+			sdata := string(data)
+			var x int64
+			var err error
+			// for mysql, when use bit, it returned \x01
+			if col.SQLType.Name == core.Bit &&
+				strings.Contains(session.Engine.DriverName(), "mysql") {
+				if len(data) == 1 {
+					x = int64(data[0])
+				} else {
+					x = 0
+				}
+			} else if strings.HasPrefix(sdata, "0x") {
+				x, err = strconv.ParseInt(sdata, 16, 64)
+			} else if strings.HasPrefix(sdata, "0") {
+				x, err = strconv.ParseInt(sdata, 8, 64)
+			} else {
+				x, err = strconv.ParseInt(sdata, 10, 64)
+			}
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*int":
+		case core.IntType.Kind():
+			sdata := string(data)
+			var x int
+			var x1 int64
+			var err error
+			// for mysql, when use bit, it returned \x01
+			if col.SQLType.Name == core.Bit &&
+				strings.Contains(session.Engine.DriverName(), "mysql") {
+				if len(data) == 1 {
+					x = int(data[0])
+				} else {
+					x = 0
+				}
+			} else if strings.HasPrefix(sdata, "0x") {
+				x1, err = strconv.ParseInt(sdata, 16, 64)
+				x = int(x1)
+			} else if strings.HasPrefix(sdata, "0") {
+				x1, err = strconv.ParseInt(sdata, 8, 64)
+				x = int(x1)
+			} else {
+				x1, err = strconv.ParseInt(sdata, 10, 64)
+				x = int(x1)
+			}
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*int32":
+		case core.Int32Type.Kind():
+			sdata := string(data)
+			var x int32
+			var x1 int64
+			var err error
+			// for mysql, when use bit, it returned \x01
+			if col.SQLType.Name == core.Bit &&
+				session.Engine.dialect.DBType() == core.MYSQL {
+				if len(data) == 1 {
+					x = int32(data[0])
+				} else {
+					x = 0
+				}
+			} else if strings.HasPrefix(sdata, "0x") {
+				x1, err = strconv.ParseInt(sdata, 16, 64)
+				x = int32(x1)
+			} else if strings.HasPrefix(sdata, "0") {
+				x1, err = strconv.ParseInt(sdata, 8, 64)
+				x = int32(x1)
+			} else {
+				x1, err = strconv.ParseInt(sdata, 10, 64)
+				x = int32(x1)
+			}
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*int8":
+		case core.Int8Type.Kind():
+			sdata := string(data)
+			var x int8
+			var x1 int64
+			var err error
+			// for mysql, when use bit, it returned \x01
+			if col.SQLType.Name == core.Bit &&
+				strings.Contains(session.Engine.DriverName(), "mysql") {
+				if len(data) == 1 {
+					x = int8(data[0])
+				} else {
+					x = 0
+				}
+			} else if strings.HasPrefix(sdata, "0x") {
+				x1, err = strconv.ParseInt(sdata, 16, 64)
+				x = int8(x1)
+			} else if strings.HasPrefix(sdata, "0") {
+				x1, err = strconv.ParseInt(sdata, 8, 64)
+				x = int8(x1)
+			} else {
+				x1, err = strconv.ParseInt(sdata, 10, 64)
+				x = int8(x1)
+			}
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*int16":
+		case core.Int16Type.Kind():
+			sdata := string(data)
+			var x int16
+			var x1 int64
+			var err error
+			// for mysql, when use bit, it returned \x01
+			if col.SQLType.Name == core.Bit &&
+				strings.Contains(session.Engine.DriverName(), "mysql") {
+				if len(data) == 1 {
+					x = int16(data[0])
+				} else {
+					x = 0
+				}
+			} else if strings.HasPrefix(sdata, "0x") {
+				x1, err = strconv.ParseInt(sdata, 16, 64)
+				x = int16(x1)
+			} else if strings.HasPrefix(sdata, "0") {
+				x1, err = strconv.ParseInt(sdata, 8, 64)
+				x = int16(x1)
+			} else {
+				x1, err = strconv.ParseInt(sdata, 10, 64)
+				x = int16(x1)
+			}
+			if err != nil {
+				return fmt.Errorf("arg %v as int: %s", key, err.Error())
+			}
+			fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType))
+		// case "*SomeStruct":
+		case reflect.Struct:
+			switch fieldType {
+			// case "*.time.Time":
+			case core.PtrTimeType:
+				x, err := session.byte2Time(col, data)
+				if err != nil {
+					return err
+				}
+				v = x
+				fieldValue.Set(reflect.ValueOf(&x))
+			default:
+				if session.Statement.UseCascade {
+					structInter := reflect.New(fieldType.Elem())
+					table, err := session.Engine.autoMapType(structInter.Elem())
+					if err != nil {
+						return err
+					}
+
+					if len(table.PrimaryKeys) > 1 {
+						panic("unsupported composited primary key cascade")
+					}
+					var pk = make(core.PK, len(table.PrimaryKeys))
+					rawValueType := table.ColumnType(table.PKColumns()[0].FieldName)
+					pk[0], err = str2PK(string(data), rawValueType)
+					if err != nil {
+						return err
+					}
+
+					if !isPKZero(pk) {
+						// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
+						// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
+						// property to be fetched lazily
+						newsession := session.Engine.NewSession()
+						defer newsession.Close()
+						has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface())
+						if err != nil {
+							return err
+						}
+						if has {
+							v = structInter.Interface()
+							fieldValue.Set(reflect.ValueOf(v))
+						} else {
+							return errors.New("cascade obj is not exist")
+						}
+					}
+				} else {
+					return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String())
+				}
+			}
+		default:
+			return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
+		}
+	default:
+		return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String())
+	}
+
+	return nil
+}
+
+// convert a field value of a struct to interface for put into db
+func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) {
+	if fieldValue.CanAddr() {
+		if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
+			data, err := fieldConvert.ToDB()
+			if err != nil {
+				return 0, err
+			}
+			if col.SQLType.IsBlob() {
+				return data, nil
+			}
+			return string(data), nil
+		}
+	}
+
+	if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok {
+		data, err := fieldConvert.ToDB()
+		if err != nil {
+			return 0, err
+		}
+		if col.SQLType.IsBlob() {
+			return data, nil
+		}
+		return string(data), nil
+	}
+
+	fieldType := fieldValue.Type()
+	k := fieldType.Kind()
+	if k == reflect.Ptr {
+		if fieldValue.IsNil() {
+			return nil, nil
+		} else if !fieldValue.IsValid() {
+			session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid")
+			return nil, nil
+		} else {
+			// !nashtsai! deference pointer type to instance type
+			fieldValue = fieldValue.Elem()
+			fieldType = fieldValue.Type()
+			k = fieldType.Kind()
+		}
+	}
+
+	switch k {
+	case reflect.Bool:
+		return fieldValue.Bool(), nil
+	case reflect.String:
+		return fieldValue.String(), nil
+	case reflect.Struct:
+		if fieldType.ConvertibleTo(core.TimeType) {
+			t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
+			if session.Engine.dialect.DBType() == core.MSSQL {
+				if t.IsZero() {
+					return nil, nil
+				}
+			}
+			tf := session.Engine.FormatTime(col.SQLType.Name, t)
+			return tf, nil
+		}
+
+		if !col.SQLType.IsJson() {
+			// !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString
+			if v, ok := fieldValue.Interface().(driver.Valuer); ok {
+				return v.Value()
+			}
+
+			fieldTable, err := session.Engine.autoMapType(fieldValue)
+			if err != nil {
+				return nil, err
+			}
+			if len(fieldTable.PrimaryKeys) == 1 {
+				pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
+				return pkField.Interface(), nil
+			}
+			return 0, fmt.Errorf("no primary key for col %v", col.Name)
+		}
+
+		if col.SQLType.IsText() {
+			bytes, err := json.Marshal(fieldValue.Interface())
+			if err != nil {
+				session.Engine.logger.Error(err)
+				return 0, err
+			}
+			return string(bytes), nil
+		} else if col.SQLType.IsBlob() {
+			bytes, err := json.Marshal(fieldValue.Interface())
+			if err != nil {
+				session.Engine.logger.Error(err)
+				return 0, err
+			}
+			return bytes, nil
+		}
+		return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type())
+	case reflect.Complex64, reflect.Complex128:
+		bytes, err := json.Marshal(fieldValue.Interface())
+		if err != nil {
+			session.Engine.logger.Error(err)
+			return 0, err
+		}
+		return string(bytes), nil
+	case reflect.Array, reflect.Slice, reflect.Map:
+		if !fieldValue.IsValid() {
+			return fieldValue.Interface(), nil
+		}
+
+		if col.SQLType.IsText() {
+			bytes, err := json.Marshal(fieldValue.Interface())
+			if err != nil {
+				session.Engine.logger.Error(err)
+				return 0, err
+			}
+			return string(bytes), nil
+		} else if col.SQLType.IsBlob() {
+			var bytes []byte
+			var err error
+			if (k == reflect.Array || k == reflect.Slice) &&
+				(fieldValue.Type().Elem().Kind() == reflect.Uint8) {
+				bytes = fieldValue.Bytes()
+			} else {
+				bytes, err = json.Marshal(fieldValue.Interface())
+				if err != nil {
+					session.Engine.logger.Error(err)
+					return 0, err
+				}
+			}
+			return bytes, nil
+		}
+		return nil, ErrUnSupportedType
+	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+		return int64(fieldValue.Uint()), nil
+	default:
+		return fieldValue.Interface(), nil
+	}
+}

+ 3 - 1
vendor/github.com/go-xorm/xorm/session_delete.go

@@ -83,7 +83,9 @@ func (session *Session) Delete(bean interface{}) (int64, error) {
 		defer session.Close()
 	}
 
-	session.Statement.setRefValue(rValue(bean))
+	if err := session.Statement.setRefValue(rValue(bean)); err != nil {
+		return 0, err
+	}
 	var table = session.Statement.RefTable
 
 	// handle before delete processors

+ 97 - 83
vendor/github.com/go-xorm/xorm/session_find.go

@@ -41,16 +41,18 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
 		if sliceElementType.Kind() == reflect.Ptr {
 			if sliceElementType.Elem().Kind() == reflect.Struct {
 				pv := reflect.New(sliceElementType.Elem())
-				session.Statement.setRefValue(pv.Elem())
+				if err := session.Statement.setRefValue(pv.Elem()); err != nil {
+					return err
+				}
 			} else {
-				//return errors.New("slice type")
 				tp = tpNonStruct
 			}
 		} else if sliceElementType.Kind() == reflect.Struct {
 			pv := reflect.New(sliceElementType)
-			session.Statement.setRefValue(pv.Elem())
+			if err := session.Statement.setRefValue(pv.Elem()); err != nil {
+				return err
+			}
 		} else {
-			//return errors.New("slice type")
 			tp = tpNonStruct
 		}
 	}
@@ -148,62 +150,10 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
 		}
 	}
 
-	if sliceValue.Kind() != reflect.Map {
-		return session.noCacheFind(sliceValue, sqlStr, args...)
-	}
-
-	resultsSlice, err := session.query(sqlStr, args...)
-	if err != nil {
-		return err
-	}
-
-	keyType := sliceValue.Type().Key()
-
-	for _, results := range resultsSlice {
-		var newValue reflect.Value
-		if sliceElementType.Kind() == reflect.Ptr {
-			newValue = reflect.New(sliceElementType.Elem())
-		} else {
-			newValue = reflect.New(sliceElementType)
-		}
-		err := session.scanMapIntoStruct(newValue.Interface(), results)
-		if err != nil {
-			return err
-		}
-		var key interface{}
-		// if there is only one pk, we can put the id as map key.
-		if len(table.PrimaryKeys) == 1 {
-			key, err = str2PK(string(results[table.PrimaryKeys[0]]), keyType)
-			if err != nil {
-				return err
-			}
-		} else {
-			if keyType.Kind() != reflect.Slice {
-				panic("don't support multiple primary key's map has non-slice key type")
-			} else {
-				var keys core.PK = make([]interface{}, 0, len(table.PrimaryKeys))
-				for _, pk := range table.PrimaryKeys {
-					skey, err := str2PK(string(results[pk]), keyType)
-					if err != nil {
-						return err
-					}
-					keys = append(keys, skey)
-				}
-				key = keys
-			}
-		}
-
-		if sliceElementType.Kind() == reflect.Ptr {
-			sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(newValue.Interface()))
-		} else {
-			sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.Indirect(reflect.ValueOf(newValue.Interface())))
-		}
-	}
-
-	return nil
+	return session.noCacheFind(table, sliceValue, sqlStr, args...)
 }
 
-func (session *Session) noCacheFind(sliceValue reflect.Value, sqlStr string, args ...interface{}) error {
+func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Value, sqlStr string, args ...interface{}) error {
 	var rawRows *core.Rows
 	var err error
 
@@ -223,51 +173,112 @@ func (session *Session) noCacheFind(sliceValue reflect.Value, sqlStr string, arg
 		return err
 	}
 
-	var newElemFunc func() reflect.Value
-	sliceElementType := sliceValue.Type().Elem()
-	if sliceElementType.Kind() == reflect.Ptr {
-		newElemFunc = func() reflect.Value {
-			return reflect.New(sliceElementType.Elem())
-		}
-	} else {
-		newElemFunc = func() reflect.Value {
-			return reflect.New(sliceElementType)
+	var newElemFunc func(fields []string) reflect.Value
+	elemType := containerValue.Type().Elem()
+	var isPointer bool
+	if elemType.Kind() == reflect.Ptr {
+		isPointer = true
+		elemType = elemType.Elem()
+	}
+	if elemType.Kind() == reflect.Ptr {
+		return errors.New("pointer to pointer is not supported")
+	}
+
+	newElemFunc = func(fields []string) reflect.Value {
+		switch elemType.Kind() {
+		case reflect.Slice:
+			slice := reflect.MakeSlice(elemType, len(fields), len(fields))
+			x := reflect.New(slice.Type())
+			x.Elem().Set(slice)
+			return x
+		case reflect.Map:
+			mp := reflect.MakeMap(elemType)
+			x := reflect.New(mp.Type())
+			x.Elem().Set(mp)
+			return x
 		}
+		return reflect.New(elemType)
 	}
 
-	var sliceValueSetFunc func(*reflect.Value)
+	var containerValueSetFunc func(*reflect.Value, core.PK) error
 
-	if sliceValue.Kind() == reflect.Slice {
-		if sliceElementType.Kind() == reflect.Ptr {
-			sliceValueSetFunc = func(newValue *reflect.Value) {
-				sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface())))
+	if containerValue.Kind() == reflect.Slice {
+		containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
+			if isPointer {
+				containerValue.Set(reflect.Append(containerValue, newValue.Elem().Addr()))
+			} else {
+				containerValue.Set(reflect.Append(containerValue, newValue.Elem()))
 			}
-		} else {
-			sliceValueSetFunc = func(newValue *reflect.Value) {
-				sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface()))))
+			return nil
+		}
+	} else {
+		keyType := containerValue.Type().Key()
+		if len(table.PrimaryKeys) == 0 {
+			return errors.New("don't support multiple primary key's map has non-slice key type")
+		}
+		if len(table.PrimaryKeys) > 1 && keyType.Kind() != reflect.Slice {
+			return errors.New("don't support multiple primary key's map has non-slice key type")
+		}
+
+		containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error {
+			keyValue := reflect.New(keyType)
+			err := convertPKToValue(table, keyValue.Interface(), pk)
+			if err != nil {
+				return err
 			}
+			if isPointer {
+				containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr())
+			} else {
+				containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem())
+			}
+			return nil
 		}
 	}
 
-	var newValue = newElemFunc()
-	dataStruct := rValue(newValue.Interface())
-	if dataStruct.Kind() == reflect.Struct {
-		return session.rows2Beans(rawRows, fields, len(fields), session.Engine.autoMapType(dataStruct), newElemFunc, sliceValueSetFunc)
+	if elemType.Kind() == reflect.Struct {
+		var newValue = newElemFunc(fields)
+		dataStruct := rValue(newValue.Interface())
+		tb, err := session.Engine.autoMapType(dataStruct)
+		if err != nil {
+			return err
+		}
+		return session.rows2Beans(rawRows, fields, len(fields), tb, newElemFunc, containerValueSetFunc)
 	}
 
 	for rawRows.Next() {
-		var newValue = newElemFunc()
+		var newValue = newElemFunc(fields)
 		bean := newValue.Interface()
 
-		if err := rawRows.Scan(bean); err != nil {
+		switch elemType.Kind() {
+		case reflect.Slice:
+			err = rawRows.ScanSlice(bean)
+		case reflect.Map:
+			err = rawRows.ScanMap(bean)
+		default:
+			err = rawRows.Scan(bean)
+		}
+
+		if err != nil {
 			return err
 		}
 
-		sliceValueSetFunc(&newValue)
+		if err := containerValueSetFunc(&newValue, nil); err != nil {
+			return err
+		}
 	}
 	return nil
 }
 
+func convertPKToValue(table *core.Table, dst interface{}, pk core.PK) error {
+	cols := table.PKColumns()
+	if len(cols) == 1 {
+		return convertAssign(dst, pk[0])
+	}
+
+	dst = pk
+	return nil
+}
+
 func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) {
 	if !session.canCache() ||
 		indexNoCase(sqlStr, "having") != -1 ||
@@ -404,7 +415,10 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in
 			if rv.Kind() != reflect.Ptr {
 				rv = rv.Addr()
 			}
-			id := session.Engine.IdOfV(rv)
+			id, err := session.Engine.idOfV(rv)
+			if err != nil {
+				return err
+			}
 			sid, err := id.ToString()
 			if err != nil {
 				return err

+ 35 - 21
vendor/github.com/go-xorm/xorm/session_get.go

@@ -20,7 +20,16 @@ func (session *Session) Get(bean interface{}) (bool, error) {
 		defer session.Close()
 	}
 
-	session.Statement.setRefValue(rValue(bean))
+	beanValue := reflect.ValueOf(bean)
+	if beanValue.Kind() != reflect.Ptr {
+		return false, errors.New("needs a pointer")
+	}
+
+	if beanValue.Elem().Kind() == reflect.Struct {
+		if err := session.Statement.setRefValue(beanValue.Elem()); err != nil {
+			return false, err
+		}
+	}
 
 	var sqlStr string
 	var args []interface{}
@@ -36,7 +45,7 @@ func (session *Session) Get(bean interface{}) (bool, error) {
 		args = session.Statement.RawParams
 	}
 
-	if session.canCache() {
+	if session.canCache() && beanValue.Elem().Kind() == reflect.Struct {
 		if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil &&
 			!session.Statement.unscoped {
 			has, err := session.cacheGet(bean, sqlStr, args...)
@@ -46,13 +55,14 @@ func (session *Session) Get(bean interface{}) (bool, error) {
 		}
 	}
 
-	return session.nocacheGet(bean, sqlStr, args...)
+	return session.nocacheGet(beanValue.Elem().Kind(), bean, sqlStr, args...)
 }
 
-func (session *Session) nocacheGet(bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
+func (session *Session) nocacheGet(beanKind reflect.Kind, bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
+	session.queryPreprocess(&sqlStr, args...)
+
 	var rawRows *core.Rows
 	var err error
-	session.queryPreprocess(&sqlStr, args...)
 	if session.IsAutoCommit {
 		_, rawRows, err = session.innerQuery(sqlStr, args...)
 	} else {
@@ -65,10 +75,26 @@ func (session *Session) nocacheGet(bean interface{}, sqlStr string, args ...inte
 	defer rawRows.Close()
 
 	if rawRows.Next() {
-		fields, err := rawRows.Columns()
-		if err == nil {
-			err = session.row2Bean(rawRows, fields, len(fields), bean)
+		switch beanKind {
+		case reflect.Struct:
+			fields, err := rawRows.Columns()
+			if err != nil {
+				// WARN: Alougth rawRows return true, but get fields failed
+				return true, err
+			}
+			dataStruct := rValue(bean)
+			if err := session.Statement.setRefValue(dataStruct); err != nil {
+				return false, err
+			}
+			_, err = session.row2Bean(rawRows, fields, len(fields), bean, &dataStruct, session.Statement.RefTable)
+		case reflect.Slice:
+			err = rawRows.ScanSlice(bean)
+		case reflect.Map:
+			err = rawRows.ScanMap(bean)
+		default:
+			err = rawRows.Scan(bean)
 		}
+
 		return true, err
 	}
 	return false, nil
@@ -145,20 +171,8 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf
 		}
 		cacheBean := cacher.GetBean(tableName, sid)
 		if cacheBean == nil {
-			/*newSession := session.Engine.NewSession()
-			defer newSession.Close()
-			cacheBean = reflect.New(structValue.Type()).Interface()
-			newSession.Id(id).NoCache()
-			if session.Statement.AltTableName != "" {
-				newSession.Table(session.Statement.AltTableName)
-			}
-			if !session.Statement.UseCascade {
-				newSession.NoCascade()
-			}
-			has, err = newSession.Get(cacheBean)
-			*/
 			cacheBean = bean
-			has, err = session.nocacheGet(cacheBean, sqlStr, args...)
+			has, err = session.nocacheGet(reflect.Struct, cacheBean, sqlStr, args...)
 			if err != nil || !has {
 				return has, err
 			}

+ 29 - 9
vendor/github.com/go-xorm/xorm/session_insert.go

@@ -67,7 +67,9 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
 		return 0, errors.New("could not insert a empty slice")
 	}
 
-	session.Statement.setRefValue(sliceValue.Index(0))
+	if err := session.Statement.setRefValue(sliceValue.Index(0)); err != nil {
+		return 0, err
+	}
 
 	if len(session.Statement.TableName()) <= 0 {
 		return 0, ErrTableNotFound
@@ -210,13 +212,29 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error
 	}
 	cleanupProcessorsClosures(&session.beforeClosures)
 
-	statement := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
-		session.Engine.Quote(session.Statement.TableName()),
-		session.Engine.QuoteStr(),
-		strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()),
-		session.Engine.QuoteStr(),
-		strings.Join(colMultiPlaces, "),("))
-
+	var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)"
+	var statement string
+	if session.Engine.dialect.DBType() == core.ORACLE {
+		sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL"
+		temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (",
+			session.Engine.Quote(session.Statement.TableName()),
+			session.Engine.QuoteStr(),
+			strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()),
+			session.Engine.QuoteStr())
+		statement = fmt.Sprintf(sql,
+			session.Engine.Quote(session.Statement.TableName()),
+			session.Engine.QuoteStr(),
+			strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()),
+			session.Engine.QuoteStr(),
+			strings.Join(colMultiPlaces, temp))
+	} else {
+		statement = fmt.Sprintf(sql,
+			session.Engine.Quote(session.Statement.TableName()),
+			session.Engine.QuoteStr(),
+			strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()),
+			session.Engine.QuoteStr(),
+			strings.Join(colMultiPlaces, "),("))
+	}
 	res, err := session.exec(statement, args...)
 	if err != nil {
 		return 0, err
@@ -281,7 +299,9 @@ func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
 }
 
 func (session *Session) innerInsert(bean interface{}) (int64, error) {
-	session.Statement.setRefValue(rValue(bean))
+	if err := session.Statement.setRefValue(rValue(bean)); err != nil {
+		return 0, err
+	}
 	if len(session.Statement.TableName()) <= 0 {
 		return 0, ErrTableNotFound
 	}

+ 14 - 11
vendor/github.com/go-xorm/xorm/session_raw.go

@@ -10,7 +10,7 @@ import (
 	"github.com/go-xorm/core"
 )
 
-func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
+func (session *Session) query(sqlStr string, paramStr ...interface{}) ([]map[string][]byte, error) {
 	session.queryPreprocess(&sqlStr, paramStr...)
 
 	if session.IsAutoCommit {
@@ -19,7 +19,7 @@ func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSl
 	return session.txQuery(session.Tx, sqlStr, paramStr...)
 }
 
-func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) {
+func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{}) ([]map[string][]byte, error) {
 	rows, err := tx.Query(sqlStr, params...)
 	if err != nil {
 		return nil, err
@@ -70,8 +70,8 @@ func (session *Session) innerQuery2(sqlStr string, params ...interface{}) ([]map
 	return rows2maps(rows)
 }
 
-// Query a raw sql and return records as []map[string][]byte
-func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
+// Query runs a raw sql and return records as []map[string][]byte
+func (session *Session) Query(sqlStr string, paramStr ...interface{}) ([]map[string][]byte, error) {
 	defer session.resetStatement()
 	if session.IsAutoClose {
 		defer session.Close()
@@ -80,16 +80,19 @@ func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSl
 	return session.query(sqlStr, paramStr...)
 }
 
-// =============================
-// for string
-// =============================
-func (session *Session) query2(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string]string, err error) {
-	session.queryPreprocess(&sqlStr, paramStr...)
+// QueryString runs a raw sql and return records as []map[string]string
+func (session *Session) QueryString(sqlStr string, args ...interface{}) ([]map[string]string, error) {
+	defer session.resetStatement()
+	if session.IsAutoClose {
+		defer session.Close()
+	}
+
+	session.queryPreprocess(&sqlStr, args...)
 
 	if session.IsAutoCommit {
-		return query2(session.DB(), sqlStr, paramStr...)
+		return query2(session.DB(), sqlStr, args...)
 	}
-	return txQuery2(session.Tx, sqlStr, paramStr...)
+	return txQuery2(session.Tx, sqlStr, args...)
 }
 
 // Execute sql

+ 16 - 5
vendor/github.com/go-xorm/xorm/session_schema.go

@@ -27,7 +27,9 @@ func (session *Session) Ping() error {
 // CreateTable create a table according a bean
 func (session *Session) CreateTable(bean interface{}) error {
 	v := rValue(bean)
-	session.Statement.setRefValue(v)
+	if err := session.Statement.setRefValue(v); err != nil {
+		return err
+	}
 
 	defer session.resetStatement()
 	if session.IsAutoClose {
@@ -40,7 +42,9 @@ func (session *Session) CreateTable(bean interface{}) error {
 // CreateIndexes create indexes
 func (session *Session) CreateIndexes(bean interface{}) error {
 	v := rValue(bean)
-	session.Statement.setRefValue(v)
+	if err := session.Statement.setRefValue(v); err != nil {
+		return err
+	}
 
 	defer session.resetStatement()
 	if session.IsAutoClose {
@@ -60,7 +64,9 @@ func (session *Session) CreateIndexes(bean interface{}) error {
 // CreateUniques create uniques
 func (session *Session) CreateUniques(bean interface{}) error {
 	v := rValue(bean)
-	session.Statement.setRefValue(v)
+	if err := session.Statement.setRefValue(v); err != nil {
+		return err
+	}
 
 	defer session.resetStatement()
 	if session.IsAutoClose {
@@ -104,7 +110,9 @@ func (session *Session) createAll() error {
 // DropIndexes drop indexes
 func (session *Session) DropIndexes(bean interface{}) error {
 	v := rValue(bean)
-	session.Statement.setRefValue(v)
+	if err := session.Statement.setRefValue(v); err != nil {
+		return err
+	}
 
 	defer session.resetStatement()
 	if session.IsAutoClose {
@@ -306,7 +314,10 @@ func (session *Session) Sync2(beans ...interface{}) error {
 
 	for _, bean := range beans {
 		v := rValue(bean)
-		table := engine.mapType(v)
+		table, err := engine.mapType(v)
+		if err != nil {
+			return err
+		}
 		structTables = append(structTables, table)
 		var tbName = session.tbNameNoSchema(table)
 

+ 1 - 1
vendor/github.com/go-xorm/xorm/session_sum.go

@@ -123,7 +123,7 @@ func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int6
 	session.queryPreprocess(&sqlStr, args...)
 
 	var err error
-	var res = make([]int64, 0, len(columnNames))
+	var res = make([]int64, len(columnNames), len(columnNames))
 	if session.IsAutoCommit {
 		err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res)
 	} else {

+ 42 - 29
vendor/github.com/go-xorm/xorm/session_update.go

@@ -169,7 +169,9 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
 	var isMap = t.Kind() == reflect.Map
 	var isStruct = t.Kind() == reflect.Struct
 	if isStruct {
-		session.Statement.setRefValue(v)
+		if err := session.Statement.setRefValue(v); err != nil {
+			return 0, err
+		}
 
 		if len(session.Statement.TableName()) <= 0 {
 			return 0, ErrTableNotFound
@@ -253,48 +255,59 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
 	var condSQL string
 	cond := session.Statement.cond.And(autoCond)
 
-	doIncVer := false
+	var doIncVer = (table != nil && table.Version != "" && session.Statement.checkVersion)
 	var verValue *reflect.Value
-	if table != nil && table.Version != "" && session.Statement.checkVersion {
+	if doIncVer {
 		verValue, err = table.VersionColumn().ValueOf(bean)
 		if err != nil {
 			return 0, err
 		}
 
 		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 {
-			condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
-		}
+		colNames = append(colNames, session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1")
+	}
 
-		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",
-			condSQL)
+	condSQL, condArgs, _ = builder.ToSQL(cond)
+	if len(condSQL) > 0 {
+		condSQL = "WHERE " + condSQL
+	}
 
-		doIncVer = true
-	} else {
-		condSQL, condArgs, _ = builder.ToSQL(cond)
-		if len(condSQL) > 0 {
-			condSQL = "WHERE " + condSQL
-		}
+	if st.OrderStr != "" {
+		condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr)
+	}
 
-		if st.LimitN > 0 {
+	// TODO: Oracle support needed
+	var top string
+	if st.LimitN > 0 {
+		if st.Engine.dialect.DBType() == core.MYSQL {
 			condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+		} else if st.Engine.dialect.DBType() == core.SQLITE {
+			tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+			cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)",
+				session.Engine.Quote(session.Statement.TableName()), tempCondSQL), condArgs...))
+			condSQL, condArgs, _ = builder.ToSQL(cond)
+			if len(condSQL) > 0 {
+				condSQL = "WHERE " + condSQL
+			}
+		} else if st.Engine.dialect.DBType() == core.POSTGRES {
+			tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
+			cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)",
+				session.Engine.Quote(session.Statement.TableName()), tempCondSQL), condArgs...))
+			condSQL, condArgs, _ = builder.ToSQL(cond)
+			if len(condSQL) > 0 {
+				condSQL = "WHERE " + condSQL
+			}
+		} else if st.Engine.dialect.DBType() == core.MSSQL {
+			top = fmt.Sprintf("top (%d) ", st.LimitN)
 		}
-
-		sqlStr = fmt.Sprintf("UPDATE %v SET %v %v",
-			session.Engine.Quote(session.Statement.TableName()),
-			strings.Join(colNames, ", "),
-			condSQL)
 	}
 
+	sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v",
+		top,
+		session.Engine.Quote(session.Statement.TableName()),
+		strings.Join(colNames, ", "),
+		condSQL)
+
 	res, err := session.exec(sqlStr, append(args, condArgs...)...)
 	if err != nil {
 		return 0, err

+ 0 - 20
vendor/github.com/go-xorm/xorm/sqlite3_driver.go

@@ -1,20 +0,0 @@
-// 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 (
-	"github.com/go-xorm/core"
-)
-
-// func init() {
-// 	core.RegisterDriver("sqlite3", &sqlite3Driver{})
-// }
-
-type sqlite3Driver struct {
-}
-
-func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) {
-	return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil
-}

+ 66 - 40
vendor/github.com/go-xorm/xorm/statement.go

@@ -39,7 +39,7 @@ type Statement struct {
 	Engine          *Engine
 	Start           int
 	LimitN          int
-	IdParam         *core.PK
+	idParam         *core.PK
 	OrderStr        string
 	JoinStr         string
 	joinArgs        []interface{}
@@ -91,7 +91,7 @@ func (statement *Statement) Init() {
 	statement.columnMap = make(map[string]bool)
 	statement.AltTableName = ""
 	statement.tableName = ""
-	statement.IdParam = nil
+	statement.idParam = nil
 	statement.RawSQL = ""
 	statement.RawParams = make([]interface{}, 0)
 	statement.UseCache = true
@@ -195,29 +195,26 @@ func (statement *Statement) Or(query interface{}, args ...interface{}) *Statemen
 
 // In generate "Where column IN (?) " statement
 func (statement *Statement) In(column string, args ...interface{}) *Statement {
-	if len(args) == 0 {
-		return statement
-	}
-
-	in := builder.In(column, args...)
+	in := builder.In(statement.Engine.Quote(column), args...)
 	statement.cond = statement.cond.And(in)
 	return statement
 }
 
 // NotIn generate "Where column NOT IN (?) " statement
 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)
+	notIn := builder.NotIn(statement.Engine.Quote(column), args...)
+	statement.cond = statement.cond.And(notIn)
 	return statement
 }
 
-func (statement *Statement) setRefValue(v reflect.Value) {
-	statement.RefTable = statement.Engine.autoMapType(reflect.Indirect(v))
+func (statement *Statement) setRefValue(v reflect.Value) error {
+	var err error
+	statement.RefTable, err = statement.Engine.autoMapType(reflect.Indirect(v))
+	if err != nil {
+		return err
+	}
 	statement.tableName = statement.Engine.tbName(v)
+	return nil
 }
 
 // Table tempororily set table name, the parameter could be a string or a pointer of struct
@@ -227,7 +224,12 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement {
 	if t.Kind() == reflect.String {
 		statement.AltTableName = tableNameOrBean.(string)
 	} else if t.Kind() == reflect.Struct {
-		statement.RefTable = statement.Engine.autoMapType(v)
+		var err error
+		statement.RefTable, err = statement.Engine.autoMapType(v)
+		if err != nil {
+			statement.Engine.logger.Error(err)
+			return statement
+		}
 		statement.AltTableName = statement.Engine.tbName(v)
 	}
 	return statement
@@ -418,7 +420,11 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
 				if fieldValue == reflect.Zero(fieldType) {
 					continue
 				}
-				if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
+				if fieldType.Kind() == reflect.Array {
+					if isArrayValueZero(fieldValue) {
+						continue
+					}
+				} else if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 {
 					continue
 				}
 			}
@@ -433,13 +439,16 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
 			} else if col.SQLType.IsBlob() {
 				var bytes []byte
 				var err error
-				if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) &&
+				if fieldType.Kind() == reflect.Slice &&
 					fieldType.Elem().Kind() == reflect.Uint8 {
 					if fieldValue.Len() > 0 {
 						val = fieldValue.Bytes()
 					} else {
 						continue
 					}
+				} else if fieldType.Kind() == reflect.Array &&
+					fieldType.Elem().Kind() == reflect.Uint8 {
+					val = fieldValue.Slice(0, 0).Interface()
 				} else {
 					bytes, err = json.Marshal(fieldValue.Interface())
 					if err != nil {
@@ -651,7 +660,9 @@ func buildConds(engine *Engine, table *core.Table, bean interface{},
 					}
 				}
 			}
-		case reflect.Array, reflect.Slice, reflect.Map:
+		case reflect.Array:
+			continue
+		case reflect.Slice, reflect.Map:
 			if fieldValue == reflect.Zero(fieldType) {
 				continue
 			}
@@ -706,13 +717,6 @@ func (statement *Statement) TableName() string {
 	return statement.tableName
 }
 
-// Id generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?"
-//
-// Deprecated: use ID instead
-func (statement *Statement) Id(id interface{}) *Statement {
-	return statement.ID(id)
-}
-
 // ID generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?"
 func (statement *Statement) ID(id interface{}) *Statement {
 	idValue := reflect.ValueOf(id)
@@ -721,23 +725,23 @@ func (statement *Statement) ID(id interface{}) *Statement {
 	switch idType {
 	case ptrPkType:
 		if pkPtr, ok := (id).(*core.PK); ok {
-			statement.IdParam = pkPtr
+			statement.idParam = pkPtr
 			return statement
 		}
 	case pkType:
 		if pk, ok := (id).(core.PK); ok {
-			statement.IdParam = &pk
+			statement.idParam = &pk
 			return statement
 		}
 	}
 
 	switch idType.Kind() {
 	case reflect.String:
-		statement.IdParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()}
+		statement.idParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()}
 		return statement
 	}
 
-	statement.IdParam = &core.PK{id}
+	statement.idParam = &core.PK{id}
 	return statement
 }
 
@@ -1120,7 +1124,11 @@ func (statement *Statement) genConds(bean interface{}) (string, []interface{}, e
 }
 
 func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}) {
-	statement.setRefValue(rValue(bean))
+	v := rValue(bean)
+	isStruct := v.Kind() == reflect.Struct
+	if isStruct {
+		statement.setRefValue(v)
+	}
 
 	var columnStr = statement.ColumnStr
 	if len(statement.selectStr) > 0 {
@@ -1139,14 +1147,22 @@ func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{})
 			if len(columnStr) == 0 {
 				if len(statement.GroupByStr) > 0 {
 					columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1))
-				} else {
-					columnStr = "*"
 				}
 			}
 		}
 	}
 
-	condSQL, condArgs, _ := statement.genConds(bean)
+	if len(columnStr) == 0 {
+		columnStr = "*"
+	}
+
+	var condSQL string
+	var condArgs []interface{}
+	if isStruct {
+		condSQL, condArgs, _ = statement.genConds(bean)
+	} else {
+		condSQL, condArgs, _ = builder.ToSQL(statement.cond)
+	}
 
 	return statement.genSelectSQL(columnStr, condSQL), append(statement.joinArgs, condArgs...)
 }
@@ -1172,17 +1188,21 @@ func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (stri
 
 	var sumStrs = make([]string, 0, len(columns))
 	for _, colName := range columns {
+		if !strings.Contains(colName, " ") && !strings.Contains(colName, "(") {
+			colName = statement.Engine.Quote(colName)
+		}
 		sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", colName))
 	}
+	sumSelect := strings.Join(sumStrs, ", ")
 
 	condSQL, condArgs, _ := statement.genConds(bean)
 
-	return statement.genSelectSQL(strings.Join(sumStrs, ", "), condSQL), append(statement.joinArgs, condArgs...)
+	return statement.genSelectSQL(sumSelect, condSQL), append(statement.joinArgs, condArgs...)
 }
 
 func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) {
 	var distinct string
-	if statement.IsDistinct {
+	if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") {
 		distinct = "DISTINCT "
 	}
 
@@ -1198,8 +1218,14 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) {
 		fmt.Fprintf(&buf, " WHERE %v", condSQL)
 	}
 	var whereStr = buf.String()
+	var fromStr = " FROM "
+
+	if dialect.DBType() == core.MSSQL && strings.Contains(statement.TableName(), "..") {
+		fromStr += statement.TableName()
+	} else {
+		fromStr += quote(statement.TableName())
+	}
 
-	var fromStr = " FROM " + quote(statement.TableName())
 	if statement.TableAlias != "" {
 		if dialect.DBType() == core.ORACLE {
 			fromStr += " " + quote(statement.TableAlias)
@@ -1289,14 +1315,14 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) {
 }
 
 func (statement *Statement) processIDParam() {
-	if statement.IdParam == nil {
+	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]})
+		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: ""})
 		}

+ 281 - 0
vendor/github.com/go-xorm/xorm/tag.go

@@ -0,0 +1,281 @@
+// 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 (
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/go-xorm/core"
+)
+
+type tagContext struct {
+	tagName         string
+	params          []string
+	preTag, nextTag string
+	table           *core.Table
+	col             *core.Column
+	fieldValue      reflect.Value
+	isIndex         bool
+	isUnique        bool
+	indexNames      map[string]int
+	engine          *Engine
+	hasCacheTag     bool
+	hasNoCacheTag   bool
+	ignoreNext      bool
+}
+
+// tagHandler describes tag handler for XORM
+type tagHandler func(ctx *tagContext) error
+
+var (
+	// defaultTagHandlers enumerates all the default tag handler
+	defaultTagHandlers = map[string]tagHandler{
+		"<-":       OnlyFromDBTagHandler,
+		"->":       OnlyToDBTagHandler,
+		"PK":       PKTagHandler,
+		"NULL":     NULLTagHandler,
+		"NOT":      IgnoreTagHandler,
+		"AUTOINCR": AutoIncrTagHandler,
+		"DEFAULT":  DefaultTagHandler,
+		"CREATED":  CreatedTagHandler,
+		"UPDATED":  UpdatedTagHandler,
+		"DELETED":  DeletedTagHandler,
+		"VERSION":  VersionTagHandler,
+		"UTC":      UTCTagHandler,
+		"LOCAL":    LocalTagHandler,
+		"NOTNULL":  NotNullTagHandler,
+		"INDEX":    IndexTagHandler,
+		"UNIQUE":   UniqueTagHandler,
+		"CACHE":    CacheTagHandler,
+		"NOCACHE":  NoCacheTagHandler,
+	}
+)
+
+func init() {
+	for k := range core.SqlTypes {
+		defaultTagHandlers[k] = SQLTypeTagHandler
+	}
+}
+
+// IgnoreTagHandler describes ignored tag handler
+func IgnoreTagHandler(ctx *tagContext) error {
+	return nil
+}
+
+// OnlyFromDBTagHandler describes mapping direction tag handler
+func OnlyFromDBTagHandler(ctx *tagContext) error {
+	ctx.col.MapType = core.ONLYFROMDB
+	return nil
+}
+
+// OnlyToDBTagHandler describes mapping direction tag handler
+func OnlyToDBTagHandler(ctx *tagContext) error {
+	ctx.col.MapType = core.ONLYTODB
+	return nil
+}
+
+// PKTagHandler decribes primary key tag handler
+func PKTagHandler(ctx *tagContext) error {
+	ctx.col.IsPrimaryKey = true
+	ctx.col.Nullable = false
+	return nil
+}
+
+// NULLTagHandler describes null tag handler
+func NULLTagHandler(ctx *tagContext) error {
+	ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT")
+	return nil
+}
+
+// NotNullTagHandler describes notnull tag handler
+func NotNullTagHandler(ctx *tagContext) error {
+	ctx.col.Nullable = false
+	return nil
+}
+
+// AutoIncrTagHandler describes autoincr tag handler
+func AutoIncrTagHandler(ctx *tagContext) error {
+	ctx.col.IsAutoIncrement = true
+	/*
+		if len(ctx.params) > 0 {
+			autoStartInt, err := strconv.Atoi(ctx.params[0])
+			if err != nil {
+				return err
+			}
+			ctx.col.AutoIncrStart = autoStartInt
+		} else {
+			ctx.col.AutoIncrStart = 1
+		}
+	*/
+	return nil
+}
+
+// DefaultTagHandler describes default tag handler
+func DefaultTagHandler(ctx *tagContext) error {
+	if len(ctx.params) > 0 {
+		ctx.col.Default = ctx.params[0]
+	} else {
+		ctx.col.Default = ctx.nextTag
+		ctx.ignoreNext = true
+	}
+	return nil
+}
+
+// CreatedTagHandler describes created tag handler
+func CreatedTagHandler(ctx *tagContext) error {
+	ctx.col.IsCreated = true
+	return nil
+}
+
+// VersionTagHandler describes version tag handler
+func VersionTagHandler(ctx *tagContext) error {
+	ctx.col.IsVersion = true
+	ctx.col.Default = "1"
+	return nil
+}
+
+// UTCTagHandler describes utc tag handler
+func UTCTagHandler(ctx *tagContext) error {
+	ctx.col.TimeZone = time.UTC
+	return nil
+}
+
+// LocalTagHandler describes local tag handler
+func LocalTagHandler(ctx *tagContext) error {
+	if len(ctx.params) == 0 {
+		ctx.col.TimeZone = time.Local
+	} else {
+		var err error
+		ctx.col.TimeZone, err = time.LoadLocation(ctx.params[0])
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// UpdatedTagHandler describes updated tag handler
+func UpdatedTagHandler(ctx *tagContext) error {
+	ctx.col.IsUpdated = true
+	return nil
+}
+
+// DeletedTagHandler describes deleted tag handler
+func DeletedTagHandler(ctx *tagContext) error {
+	ctx.col.IsDeleted = true
+	return nil
+}
+
+// IndexTagHandler describes index tag handler
+func IndexTagHandler(ctx *tagContext) error {
+	if len(ctx.params) > 0 {
+		ctx.indexNames[ctx.params[0]] = core.IndexType
+	} else {
+		ctx.isIndex = true
+	}
+	return nil
+}
+
+// UniqueTagHandler describes unique tag handler
+func UniqueTagHandler(ctx *tagContext) error {
+	if len(ctx.params) > 0 {
+		ctx.indexNames[ctx.params[0]] = core.UniqueType
+	} else {
+		ctx.isUnique = true
+	}
+	return nil
+}
+
+// SQLTypeTagHandler describes SQL Type tag handler
+func SQLTypeTagHandler(ctx *tagContext) error {
+	ctx.col.SQLType = core.SQLType{Name: ctx.tagName}
+	if len(ctx.params) > 0 {
+		if ctx.tagName == core.Enum {
+			ctx.col.EnumOptions = make(map[string]int)
+			for k, v := range ctx.params {
+				v = strings.TrimSpace(v)
+				v = strings.Trim(v, "'")
+				ctx.col.EnumOptions[v] = k
+			}
+		} else if ctx.tagName == core.Set {
+			ctx.col.SetOptions = make(map[string]int)
+			for k, v := range ctx.params {
+				v = strings.TrimSpace(v)
+				v = strings.Trim(v, "'")
+				ctx.col.SetOptions[v] = k
+			}
+		} else {
+			var err error
+			if len(ctx.params) == 2 {
+				ctx.col.Length, err = strconv.Atoi(ctx.params[0])
+				if err != nil {
+					return err
+				}
+				ctx.col.Length2, err = strconv.Atoi(ctx.params[1])
+				if err != nil {
+					return err
+				}
+			} else if len(ctx.params) == 1 {
+				ctx.col.Length, err = strconv.Atoi(ctx.params[0])
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+// ExtendsTagHandler describes extends tag handler
+func ExtendsTagHandler(ctx *tagContext) error {
+	var fieldValue = ctx.fieldValue
+	switch fieldValue.Kind() {
+	case reflect.Ptr:
+		f := fieldValue.Type().Elem()
+		if f.Kind() == reflect.Struct {
+			fieldPtr := fieldValue
+			fieldValue = fieldValue.Elem()
+			if !fieldValue.IsValid() || fieldPtr.IsNil() {
+				fieldValue = reflect.New(f).Elem()
+			}
+		}
+		fallthrough
+	case reflect.Struct:
+		parentTable, err := ctx.engine.mapType(fieldValue)
+		if err != nil {
+			return err
+		}
+		for _, col := range parentTable.Columns() {
+			col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName)
+			ctx.table.AddColumn(col)
+			for indexName, indexType := range col.Indexes {
+				addIndex(indexName, ctx.table, col, indexType)
+			}
+		}
+	default:
+		//TODO: warning
+	}
+	return nil
+}
+
+// CacheTagHandler describes cache tag handler
+func CacheTagHandler(ctx *tagContext) error {
+	if !ctx.hasCacheTag {
+		ctx.hasCacheTag = true
+	}
+	return nil
+}
+
+// NoCacheTagHandler describes nocache tag handler
+func NoCacheTagHandler(ctx *tagContext) error {
+	if !ctx.hasNoCacheTag {
+		ctx.hasNoCacheTag = true
+	}
+	return nil
+}

+ 2 - 1
vendor/github.com/go-xorm/xorm/xorm.go

@@ -17,7 +17,7 @@ import (
 
 const (
 	// Version show the xorm's version
-	Version string = "0.6.0.1022"
+	Version string = "0.6.2.0412"
 )
 
 func regDrvsNDialects() bool {
@@ -86,6 +86,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
 		mutex:         &sync.RWMutex{},
 		TagIdentifier: "xorm",
 		TZLocation:    time.Local,
+		tagHandlers:   defaultTagHandlers,
 	}
 
 	logger := NewSimpleLogger(os.Stdout)

+ 3 - 3
vendor/vendor.json

@@ -159,10 +159,10 @@
 			"revisionTime": "2017-02-06T15:24:21Z"
 		},
 		{
-			"checksumSHA1": "BGWfs63vC5cJuxhVRrj+7YJKz7A=",
+			"checksumSHA1": "Ka4hFMvc75Fb57ZNLALyYSM7CCE=",
 			"path": "github.com/go-xorm/xorm",
-			"revision": "19f6dfc2e8c069adc624ca56cf8127444159d5c1",
-			"revisionTime": "2017-02-10T01:55:37Z"
+			"revision": "d52a762fba17a2ed265463c1c7b608c14836eaaf",
+			"revisionTime": "2017-04-20T16:02:48Z"
 		},
 		{
 			"checksumSHA1": "1ft/4j5MFa7C9dPI9whL03HSUzk=",