From 29ba280ee594e128d2b299e3031ed6e328fc5698 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 2 Jun 2017 10:04:44 +0800 Subject: [PATCH] fix tests bug --- helpers.go | 170 +---------------------------------- helpler_time.go | 21 +++++ session.go | 22 ++--- session_raw.go | 173 ++++++++++++++++++++++++++++++++++++ time_test.go | 229 +++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 425 insertions(+), 190 deletions(-) create mode 100644 helpler_time.go diff --git a/helpers.go b/helpers.go index bac1fd9c..5e54466e 100644 --- a/helpers.go +++ b/helpers.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "time" + "github.com/go-xorm/core" ) @@ -319,175 +320,6 @@ func sliceEq(left, right []string) bool { return true } -func reflect2value(rawValue *reflect.Value) (str string, err error) { - aa := reflect.TypeOf((*rawValue).Interface()) - vv := reflect.ValueOf((*rawValue).Interface()) - switch aa.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - str = strconv.FormatInt(vv.Int(), 10) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - str = strconv.FormatUint(vv.Uint(), 10) - case reflect.Float32, reflect.Float64: - str = strconv.FormatFloat(vv.Float(), 'f', -1, 64) - case reflect.String: - str = vv.String() - case reflect.Array, reflect.Slice: - switch aa.Elem().Kind() { - case reflect.Uint8: - data := rawValue.Interface().([]byte) - str = string(data) - default: - err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) - } - // time type - case reflect.Struct: - if aa.ConvertibleTo(core.TimeType) { - str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano) - } else { - err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) - } - case reflect.Bool: - str = strconv.FormatBool(vv.Bool()) - case reflect.Complex128, reflect.Complex64: - str = fmt.Sprintf("%v", vv.Complex()) - /* TODO: unsupported types below - case reflect.Map: - case reflect.Ptr: - case reflect.Uintptr: - case reflect.UnsafePointer: - case reflect.Chan, reflect.Func, reflect.Interface: - */ - default: - err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) - } - return -} - -func value2Bytes(rawValue *reflect.Value) (data []byte, err error) { - var str string - str, err = reflect2value(rawValue) - if err != nil { - return - } - data = []byte(str) - return -} - -func value2String(rawValue *reflect.Value) (data string, err error) { - data, err = reflect2value(rawValue) - if err != nil { - return - } - return -} - -func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { - fields, err := rows.Columns() - if err != nil { - return nil, err - } - for rows.Next() { - result, err := row2mapStr(rows, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - - return resultsSlice, nil -} - -func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) { - fields, err := rows.Columns() - if err != nil { - return nil, err - } - for rows.Next() { - result, err := row2map(rows, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - - return resultsSlice, nil -} - -func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) { - result := make(map[string][]byte) - scanResultContainers := make([]interface{}, len(fields)) - for i := 0; i < len(fields); i++ { - var scanResultContainer interface{} - scanResultContainers[i] = &scanResultContainer - } - if err := rows.Scan(scanResultContainers...); err != nil { - return nil, err - } - - for ii, key := range fields { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) - //if row is null then ignore - if rawValue.Interface() == nil { - //fmt.Println("ignore ...", key, rawValue) - continue - } - - if data, err := value2Bytes(&rawValue); err == nil { - result[key] = data - } else { - return nil, err // !nashtsai! REVIEW, should return err or just error log? - } - } - return result, nil -} - -func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) { - result := make(map[string]string) - scanResultContainers := make([]interface{}, len(fields)) - for i := 0; i < len(fields); i++ { - var scanResultContainer interface{} - scanResultContainers[i] = &scanResultContainer - } - if err := rows.Scan(scanResultContainers...); err != nil { - return nil, err - } - - for ii, key := range fields { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) - //if row is null then ignore - if rawValue.Interface() == nil { - //fmt.Println("ignore ...", key, rawValue) - continue - } - - if data, err := value2String(&rawValue); err == nil { - result[key] = data - } else { - return nil, err // !nashtsai! REVIEW, should return err or just error log? - } - } - return result, nil -} - -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 - } - defer rows.Close() - - return rows2Strings(rows) -} - -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 - } - defer rows.Close() - return rows2Strings(rows) -} - func setColumnInt(bean interface{}, col *core.Column, t int64) { v, err := col.ValueOf(bean) if err != nil { diff --git a/helpler_time.go b/helpler_time.go new file mode 100644 index 00000000..f4013e27 --- /dev/null +++ b/helpler_time.go @@ -0,0 +1,21 @@ +// 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 "time" + +const ( + zeroTime0 = "0000-00-00 00:00:00" + zeroTime1 = "0001-01-01 00:00:00" +) + +func formatTime(t time.Time) string { + return t.Format("2006-01-02 15:04:05") +} + +func isTimeZero(t time.Time) bool { + return t.IsZero() || formatTime(t) == zeroTime0 || + formatTime(t) == zeroTime1 +} diff --git a/session.go b/session.go index 475c769f..bbe56adc 100644 --- a/session.go +++ b/session.go @@ -344,15 +344,6 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i } }() - 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 { @@ -528,11 +519,9 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i } case reflect.Struct: if fieldType.ConvertibleTo(core.TimeType) { - var tz *time.Location - if col.TimeZone == nil { - tz = session.Engine.TZLocation - } else { - tz = col.TimeZone + dbTZ := session.Engine.DatabaseTZ + if col.TimeZone != nil { + dbTZ = col.TimeZone } if rawValueType == core.TimeType { @@ -548,14 +537,13 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i t.Minute(), t.Second(), t.Nanosecond(), dbTZ) } - // !nashtsai! convert to engine location - t = t.In(tz) + t = t.In(session.Engine.TZLocation) fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) } else if rawValueType == core.IntType || rawValueType == core.Int64Type || rawValueType == core.Int32Type { hasAssigned = true - t := time.Unix(vv.Int(), 0).In(tz) + t := time.Unix(vv.Int(), 0).In(session.Engine.TZLocation) fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) } else { if d, ok := vv.Interface().([]uint8); ok { diff --git a/session_raw.go b/session_raw.go index 0f5a0a43..b44b1cd5 100644 --- a/session_raw.go +++ b/session_raw.go @@ -6,6 +6,10 @@ package xorm import ( "database/sql" + "fmt" + "reflect" + "strconv" + "time" "github.com/go-xorm/core" ) @@ -59,6 +63,60 @@ func (session *Session) innerQuery(sqlStr string, params ...interface{}) (*core. return stmt, rows, nil } +func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + for rows.Next() { + result, err := row2map(rows, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, result) + } + + return resultsSlice, nil +} + +func value2Bytes(rawValue *reflect.Value) (data []byte, err error) { + var str string + str, err = reflect2value(rawValue) + if err != nil { + return + } + data = []byte(str) + return +} + +func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) { + result := make(map[string][]byte) + scanResultContainers := make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var scanResultContainer interface{} + scanResultContainers[i] = &scanResultContainer + } + if err := rows.Scan(scanResultContainers...); err != nil { + return nil, err + } + + for ii, key := range fields { + rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) + //if row is null then ignore + if rawValue.Interface() == nil { + //fmt.Println("ignore ...", key, rawValue) + continue + } + + if data, err := value2Bytes(&rawValue); err == nil { + result[key] = data + } else { + return nil, err // !nashtsai! REVIEW, should return err or just error log? + } + } + return result, nil +} + func (session *Session) innerQuery2(sqlStr string, params ...interface{}) ([]map[string][]byte, error) { _, rows, err := session.innerQuery(sqlStr, params...) if rows != nil { @@ -80,6 +138,121 @@ func (session *Session) Query(sqlStr string, paramStr ...interface{}) ([]map[str return session.query(sqlStr, paramStr...) } +func rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + for rows.Next() { + result, err := row2mapStr(rows, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, result) + } + + return resultsSlice, nil +} + +func reflect2value(rawValue *reflect.Value) (str string, err error) { + aa := reflect.TypeOf((*rawValue).Interface()) + vv := reflect.ValueOf((*rawValue).Interface()) + switch aa.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + str = strconv.FormatInt(vv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + str = strconv.FormatUint(vv.Uint(), 10) + case reflect.Float32, reflect.Float64: + str = strconv.FormatFloat(vv.Float(), 'f', -1, 64) + case reflect.String: + str = vv.String() + case reflect.Array, reflect.Slice: + switch aa.Elem().Kind() { + case reflect.Uint8: + data := rawValue.Interface().([]byte) + str = string(data) + default: + err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) + } + // time type + case reflect.Struct: + if aa.ConvertibleTo(core.TimeType) { + str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano) + } else { + err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) + } + case reflect.Bool: + str = strconv.FormatBool(vv.Bool()) + case reflect.Complex128, reflect.Complex64: + str = fmt.Sprintf("%v", vv.Complex()) + /* TODO: unsupported types below + case reflect.Map: + case reflect.Ptr: + case reflect.Uintptr: + case reflect.UnsafePointer: + case reflect.Chan, reflect.Func, reflect.Interface: + */ + default: + err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) + } + return +} + +func value2String(rawValue *reflect.Value) (data string, err error) { + data, err = reflect2value(rawValue) + if err != nil { + return + } + return +} + +func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) { + result := make(map[string]string) + scanResultContainers := make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var scanResultContainer interface{} + scanResultContainers[i] = &scanResultContainer + } + if err := rows.Scan(scanResultContainers...); err != nil { + return nil, err + } + + for ii, key := range fields { + rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) + //if row is null then ignore + if rawValue.Interface() == nil { + //fmt.Println("ignore ...", key, rawValue) + continue + } + + if data, err := value2String(&rawValue); err == nil { + result[key] = data + } else { + return nil, err // !nashtsai! REVIEW, should return err or just error log? + } + } + return result, nil +} + +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 + } + defer rows.Close() + + return rows2Strings(rows) +} + +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 + } + defer rows.Close() + return rows2Strings(rows) +} + // 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() diff --git a/time_test.go b/time_test.go index 51dba462..27c5b0ca 100644 --- a/time_test.go +++ b/time_test.go @@ -6,16 +6,13 @@ package xorm import ( "fmt" + "strings" "testing" "time" "github.com/stretchr/testify/assert" ) -func formatTime(t time.Time) string { - return t.Format("2006-01-02 15:04:05") -} - func TestTimeUserTime(t *testing.T) { assert.NoError(t, prepareEngine()) @@ -253,3 +250,227 @@ func TestTimeUserUpdatedDiffLoc(t *testing.T) { assert.EqualValues(t, formatTime(user3.UpdatedAt), formatTime(user4.UpdatedAt)) fmt.Println("user3", user.CreatedAt, user4.UpdatedAt) } + +func TestTimeUserDeleted(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type UserDeleted struct { + Id string + CreatedAt time.Time `xorm:"created"` + UpdatedAt time.Time `xorm:"updated"` + DeletedAt time.Time `xorm:"deleted"` + } + + assertSync(t, new(UserDeleted)) + + var user = UserDeleted{ + Id: "lunny", + } + + cnt, err := testEngine.Insert(&user) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + fmt.Println("user", user.CreatedAt, user.UpdatedAt, user.DeletedAt) + + var user2 UserDeleted + has, err := testEngine.Get(&user2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user.CreatedAt.Unix(), user2.CreatedAt.Unix()) + assert.EqualValues(t, formatTime(user.CreatedAt), formatTime(user2.CreatedAt)) + assert.EqualValues(t, user.UpdatedAt.Unix(), user2.UpdatedAt.Unix()) + assert.EqualValues(t, formatTime(user.UpdatedAt), formatTime(user2.UpdatedAt)) + assert.True(t, isTimeZero(user2.DeletedAt)) + fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) + + var user3 UserDeleted + cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + assert.True(t, !isTimeZero(user3.DeletedAt)) + + var user4 UserDeleted + has, err = testEngine.Unscoped().Get(&user4) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user3.DeletedAt.Unix(), user4.DeletedAt.Unix()) + assert.EqualValues(t, formatTime(user3.DeletedAt), formatTime(user4.DeletedAt)) + fmt.Println("user3", user3.DeletedAt, user4.DeletedAt) +} + +func TestTimeUserDeletedDiffLoc(t *testing.T) { + assert.NoError(t, prepareEngine()) + loc, err := time.LoadLocation("Asia/Shanghai") + assert.NoError(t, err) + testEngine.TZLocation = loc + dbLoc, err := time.LoadLocation("America/New_York") + assert.NoError(t, err) + testEngine.DatabaseTZ = dbLoc + + type UserDeleted struct { + Id string + CreatedAt time.Time `xorm:"created"` + UpdatedAt time.Time `xorm:"updated"` + DeletedAt time.Time `xorm:"deleted"` + } + + assertSync(t, new(UserDeleted)) + + var user = UserDeleted{ + Id: "lunny", + } + + cnt, err := testEngine.Insert(&user) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + fmt.Println("user", user.CreatedAt, user.UpdatedAt, user.DeletedAt) + + var user2 UserDeleted + has, err := testEngine.Get(&user2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user.CreatedAt.Unix(), user2.CreatedAt.Unix()) + assert.EqualValues(t, formatTime(user.CreatedAt), formatTime(user2.CreatedAt)) + assert.EqualValues(t, user.UpdatedAt.Unix(), user2.UpdatedAt.Unix()) + assert.EqualValues(t, formatTime(user.UpdatedAt), formatTime(user2.UpdatedAt)) + assert.True(t, isTimeZero(user2.DeletedAt)) + fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) + + var user3 UserDeleted + cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + assert.True(t, !isTimeZero(user3.DeletedAt)) + + var user4 UserDeleted + has, err = testEngine.Unscoped().Get(&user4) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user3.DeletedAt.Unix(), user4.DeletedAt.Unix()) + assert.EqualValues(t, formatTime(user3.DeletedAt), formatTime(user4.DeletedAt)) + fmt.Println("user3", user3.DeletedAt, user4.DeletedAt) +} + +type JsonDate time.Time + +func (j JsonDate) MarshalJSON() ([]byte, error) { + if time.Time(j).IsZero() { + return []byte(`""`), nil + } + return []byte(`"` + time.Time(j).Format("2006-01-02 15:04:05") + `"`), nil +} + +func (j *JsonDate) UnmarshalJSON(value []byte) error { + var v = strings.TrimSpace(strings.Trim(string(value), "\"")) + + t, err := time.ParseInLocation("2006-01-02 15:04:05", v, time.Local) + if err != nil { + return err + } + *j = JsonDate(t) + return nil +} + +func (j *JsonDate) Unix() int64 { + return (*time.Time)(j).Unix() +} + +func TestCustomTimeUserDeleted(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type UserDeleted struct { + Id string + CreatedAt JsonDate `xorm:"created"` + UpdatedAt JsonDate `xorm:"updated"` + DeletedAt JsonDate `xorm:"deleted"` + } + + assertSync(t, new(UserDeleted)) + + var user = UserDeleted{ + Id: "lunny", + } + + cnt, err := testEngine.Insert(&user) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + fmt.Println("user", user.CreatedAt, user.UpdatedAt, user.DeletedAt) + + var user2 UserDeleted + has, err := testEngine.Get(&user2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user.CreatedAt.Unix(), user2.CreatedAt.Unix()) + assert.EqualValues(t, formatTime(time.Time(user.CreatedAt)), formatTime(time.Time(user2.CreatedAt))) + assert.EqualValues(t, user.UpdatedAt.Unix(), user2.UpdatedAt.Unix()) + assert.EqualValues(t, formatTime(time.Time(user.UpdatedAt)), formatTime(time.Time(user2.UpdatedAt))) + assert.True(t, isTimeZero(time.Time(user2.DeletedAt))) + fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) + + var user3 UserDeleted + cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + assert.True(t, !isTimeZero(time.Time(user3.DeletedAt))) + + var user4 UserDeleted + has, err = testEngine.Unscoped().Get(&user4) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user3.DeletedAt.Unix(), user4.DeletedAt.Unix()) + assert.EqualValues(t, formatTime(time.Time(user3.DeletedAt)), formatTime(time.Time(user4.DeletedAt))) + fmt.Println("user3", user3.DeletedAt, user4.DeletedAt) +} + +func TestCustomTimeUserDeletedDiffLoc(t *testing.T) { + assert.NoError(t, prepareEngine()) + loc, err := time.LoadLocation("Asia/Shanghai") + assert.NoError(t, err) + testEngine.TZLocation = loc + dbLoc, err := time.LoadLocation("America/New_York") + assert.NoError(t, err) + testEngine.DatabaseTZ = dbLoc + + type UserDeleted struct { + Id string + CreatedAt JsonDate `xorm:"created"` + UpdatedAt JsonDate `xorm:"updated"` + DeletedAt JsonDate `xorm:"deleted"` + } + + assertSync(t, new(UserDeleted)) + + var user = UserDeleted{ + Id: "lunny", + } + + cnt, err := testEngine.Insert(&user) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + fmt.Println("user", user.CreatedAt, user.UpdatedAt, user.DeletedAt) + + var user2 UserDeleted + has, err := testEngine.Get(&user2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user.CreatedAt.Unix(), user2.CreatedAt.Unix()) + assert.EqualValues(t, formatTime(time.Time(user.CreatedAt)), formatTime(time.Time(user2.CreatedAt))) + assert.EqualValues(t, user.UpdatedAt.Unix(), user2.UpdatedAt.Unix()) + assert.EqualValues(t, formatTime(time.Time(user.UpdatedAt)), formatTime(time.Time(user2.UpdatedAt))) + assert.True(t, isTimeZero(time.Time(user2.DeletedAt))) + fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) + + var user3 UserDeleted + cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + assert.True(t, !isTimeZero(time.Time(user3.DeletedAt))) + + var user4 UserDeleted + has, err = testEngine.Unscoped().Get(&user4) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, user3.DeletedAt.Unix(), user4.DeletedAt.Unix()) + assert.EqualValues(t, formatTime(time.Time(user3.DeletedAt)), formatTime(time.Time(user4.DeletedAt))) + fmt.Println("user3", user3.DeletedAt, user4.DeletedAt) +}