diff options
author | peter zhang <admin@ddatsh.com> | 2017-04-27 07:47:16 +0800 |
---|---|---|
committer | 无闻 <u@gogs.io> | 2017-04-26 19:47:16 -0400 |
commit | 10ee2e0dad6471e8912cc833faf33db7eaa55fa8 (patch) | |
tree | b1d2f03768781a31d7a68d2c5a9a7140e170f261 /vendor/github.com/go-xorm/xorm/session.go | |
parent | 6500aafcb867e37379b1f238198e95134b09ac4e (diff) |
vendor: update xorm version for fix git clone error build with golang 1.8.1 (#4460)
Diffstat (limited to 'vendor/github.com/go-xorm/xorm/session.go')
-rw-r--r-- | vendor/github.com/go-xorm/xorm/session.go | 1088 |
1 files changed, 114 insertions, 974 deletions
diff --git a/vendor/github.com/go-xorm/xorm/session.go b/vendor/github.com/go-xorm/xorm/session.go index 6e1b02af..475c769f 100644 --- a/vendor/github.com/go-xorm/xorm/session.go +++ b/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 |