fix time issues and add some tests for time (#604)
* fix time issues and add some tests for time * fix tests bug * fix tests * some fixes with tests and added mssql support * fix tests
This commit is contained in:
parent
9c179e47a1
commit
942887dea0
|
@ -21,7 +21,7 @@ database:
|
|||
test:
|
||||
override:
|
||||
# './...' is a relative pattern which means all subdirectories
|
||||
- go test -v -race -db="sqlite3;mysql;postgres" -conn_str="./test.db;root:@/xorm_test;dbname=xorm_test sslmode=disable" -coverprofile=coverage.txt -covermode=atomic
|
||||
- go test -v -race -db="sqlite3::mysql::postgres" -conn_str="./test.db::root:@/xorm_test::dbname=xorm_test sslmode=disable" -coverprofile=coverage.txt -covermode=atomic
|
||||
- cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./sqlite3.sh
|
||||
- cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./mysql.sh
|
||||
- cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./postgres.sh
|
||||
|
|
|
@ -215,7 +215,7 @@ func (db *mssql) SqlType(c *core.Column) string {
|
|||
var res string
|
||||
switch t := c.SQLType.Name; t {
|
||||
case core.Bool:
|
||||
res = core.TinyInt
|
||||
res = core.Bit
|
||||
if strings.EqualFold(c.Default, "true") {
|
||||
c.Default = "1"
|
||||
} else {
|
||||
|
@ -250,6 +250,9 @@ func (db *mssql) SqlType(c *core.Column) string {
|
|||
case core.Uuid:
|
||||
res = core.Varchar
|
||||
c.Length = 40
|
||||
case core.TinyInt:
|
||||
res = core.TinyInt
|
||||
c.Length = 0
|
||||
default:
|
||||
res = t
|
||||
}
|
||||
|
@ -335,9 +338,15 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) {
|
|||
func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) {
|
||||
args := []interface{}{}
|
||||
s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable,
|
||||
replace(replace(isnull(c.text,''),'(',''),')','') as vdefault
|
||||
from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id
|
||||
left join sys.syscomments c on a.default_object_id=c.id
|
||||
replace(replace(isnull(c.text,''),'(',''),')','') as vdefault,
|
||||
ISNULL(i.is_primary_key, 0)
|
||||
from sys.columns a
|
||||
left join sys.types b on a.user_type_id=b.user_type_id
|
||||
left join sys.syscomments c on a.default_object_id=c.id
|
||||
LEFT OUTER JOIN
|
||||
sys.index_columns ic ON ic.object_id = a.object_id AND ic.column_id = a.column_id
|
||||
LEFT OUTER JOIN
|
||||
sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
|
||||
where a.object_id=object_id('` + tableName + `')`
|
||||
db.LogSQL(s, args)
|
||||
|
||||
|
@ -352,8 +361,8 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column
|
|||
for rows.Next() {
|
||||
var name, ctype, vdefault string
|
||||
var maxLen, precision, scale int
|
||||
var nullable bool
|
||||
err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault)
|
||||
var nullable, isPK bool
|
||||
err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault, &isPK)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -363,6 +372,7 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column
|
|||
col.Name = strings.Trim(name, "` ")
|
||||
col.Nullable = nullable
|
||||
col.Default = vdefault
|
||||
col.IsPrimaryKey = isPK
|
||||
ct := strings.ToUpper(ctype)
|
||||
if ct == "DECIMAL" {
|
||||
col.Length = precision
|
||||
|
@ -536,7 +546,6 @@ 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 {
|
||||
|
|
60
engine.go
60
engine.go
|
@ -40,7 +40,7 @@ type Engine struct {
|
|||
showExecTime bool
|
||||
|
||||
logger core.ILogger
|
||||
TZLocation *time.Location
|
||||
TZLocation *time.Location // The timezone of the application
|
||||
DatabaseTZ *time.Location // The timezone of the database
|
||||
|
||||
disableGlobalCache bool
|
||||
|
@ -1498,7 +1498,6 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
|
|||
results = append(results, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
//lastError = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1506,49 +1505,28 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
|
|||
return results, lastError
|
||||
}
|
||||
|
||||
// TZTime change one time to xorm time location
|
||||
func (engine *Engine) TZTime(t time.Time) time.Time {
|
||||
if !t.IsZero() { // if time is not initialized it's not suitable for Time.In()
|
||||
return t.In(engine.TZLocation)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// NowTime return current time
|
||||
func (engine *Engine) NowTime(sqlTypeName string) interface{} {
|
||||
t := time.Now()
|
||||
return engine.FormatTime(sqlTypeName, t)
|
||||
}
|
||||
|
||||
// NowTime2 return current time
|
||||
func (engine *Engine) NowTime2(sqlTypeName string) (interface{}, time.Time) {
|
||||
t := time.Now()
|
||||
return engine.FormatTime(sqlTypeName, t), t
|
||||
}
|
||||
|
||||
// FormatTime format time
|
||||
func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{}) {
|
||||
return engine.formatTime(engine.TZLocation, sqlTypeName, t)
|
||||
return engine.formatTime(sqlTypeName, t.In(engine.DatabaseTZ)), t.In(engine.TZLocation)
|
||||
}
|
||||
|
||||
func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{}) {
|
||||
if col.DisableTimeZone {
|
||||
return engine.formatTime(nil, col.SQLType.Name, t)
|
||||
} else if col.TimeZone != nil {
|
||||
return engine.formatTime(col.TimeZone, col.SQLType.Name, t)
|
||||
if t.IsZero() {
|
||||
if col.Nullable {
|
||||
return nil
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return engine.formatTime(engine.TZLocation, col.SQLType.Name, t)
|
||||
|
||||
if col.TimeZone != nil {
|
||||
return engine.formatTime(col.SQLType.Name, t.In(col.TimeZone))
|
||||
}
|
||||
return engine.formatTime(col.SQLType.Name, t.In(engine.DatabaseTZ))
|
||||
}
|
||||
|
||||
func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.Time) (v interface{}) {
|
||||
if engine.dialect.DBType() == core.ORACLE {
|
||||
return t
|
||||
}
|
||||
if tz != nil {
|
||||
t = t.In(tz)
|
||||
} else {
|
||||
t = engine.TZTime(t)
|
||||
}
|
||||
// formatTime format time as column type
|
||||
func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{}) {
|
||||
switch sqlTypeName {
|
||||
case core.Time:
|
||||
s := t.Format("2006-01-02 15:04:05") //time.RFC3339
|
||||
|
@ -1556,18 +1534,10 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T
|
|||
case core.Date:
|
||||
v = t.Format("2006-01-02")
|
||||
case core.DateTime, core.TimeStamp:
|
||||
if engine.dialect.DBType() == "ql" {
|
||||
v = t
|
||||
} else if engine.dialect.DBType() == "sqlite3" {
|
||||
v = t.UTC().Format("2006-01-02 15:04:05")
|
||||
} else {
|
||||
v = t.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
v = t.Format("2006-01-02 15:04:05")
|
||||
case core.TimeStampz:
|
||||
if engine.dialect.DBType() == core.MSSQL {
|
||||
v = t.Format("2006-01-02T15:04:05.9999999Z07:00")
|
||||
} else if engine.DriverName() == "mssql" {
|
||||
v = t
|
||||
} else {
|
||||
v = t.Format(time.RFC3339Nano)
|
||||
}
|
||||
|
|
170
helpers.go
170
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 {
|
||||
|
|
|
@ -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
|
||||
}
|
22
session.go
22
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 {
|
||||
|
|
|
@ -7,6 +7,7 @@ package xorm
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -26,7 +27,11 @@ func TestSetExpr(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
cnt, err = testEngine.SetExpr("show", "NOT `show`").Id(1).Update(new(User))
|
||||
var not = "NOT"
|
||||
if testEngine.dialect.DBType() == core.MSSQL {
|
||||
not = "~"
|
||||
}
|
||||
cnt, err = testEngine.SetExpr("show", not+" `show`").Id(1).Update(new(User))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
|
|||
var x time.Time
|
||||
var err error
|
||||
|
||||
var parseLoc = session.Engine.DatabaseTZ
|
||||
if col.TimeZone != nil {
|
||||
parseLoc = col.TimeZone
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -30,33 +35,26 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
|
|||
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)
|
||||
x, err = time.ParseInLocation(time.RFC3339Nano, sdata, parseLoc)
|
||||
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)
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, parseLoc)
|
||||
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)
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, parseLoc)
|
||||
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)
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, parseLoc)
|
||||
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)
|
||||
x, err = time.ParseInLocation("2006-01-02", sdata, parseLoc)
|
||||
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, " ") {
|
||||
|
@ -70,7 +68,7 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
|
|||
}
|
||||
|
||||
st := fmt.Sprintf("2006-01-02 %v", sdata)
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation)
|
||||
x, err = time.ParseInLocation("2006-01-02 15:04:05", st, parseLoc)
|
||||
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)
|
||||
|
@ -80,7 +78,7 @@ func (session *Session) str2Time(col *core.Column, data string) (outTime time.Ti
|
|||
outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err)
|
||||
return
|
||||
}
|
||||
outTime = x
|
||||
outTime = x.In(session.Engine.TZLocation)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -588,12 +586,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
|
|||
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)
|
||||
tf := session.Engine.formatColTime(col, t)
|
||||
return tf, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -119,6 +120,11 @@ func TestGetStruct(t *testing.T) {
|
|||
|
||||
assert.NoError(t, testEngine.Sync(new(UserinfoGet)))
|
||||
|
||||
var err error
|
||||
if testEngine.dialect.DBType() == core.MSSQL {
|
||||
_, err = testEngine.Exec("SET IDENTITY_INSERT userinfo_get ON")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
cnt, err := testEngine.Insert(&UserinfoGet{Uid: 2})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
|
|
@ -357,10 +357,10 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
|
|||
session.Engine.QuoteStr(),
|
||||
colPlaces)
|
||||
} else {
|
||||
if session.Engine.dialect.DBType() == core.SQLITE || session.Engine.dialect.DBType() == core.POSTGRES {
|
||||
sqlStr = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", session.Engine.Quote(session.Statement.TableName()))
|
||||
} else {
|
||||
if session.Engine.dialect.DBType() == core.MYSQL {
|
||||
sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.Engine.Quote(session.Statement.TableName()))
|
||||
} else {
|
||||
sqlStr = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", session.Engine.Quote(session.Statement.TableName()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
173
session_raw.go
173
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()
|
||||
|
|
|
@ -298,7 +298,19 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
|
|||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
} else if st.Engine.dialect.DBType() == core.MSSQL {
|
||||
top = fmt.Sprintf("top (%d) ", st.LimitN)
|
||||
if st.OrderStr != "" && st.Engine.dialect.DBType() == core.MSSQL &&
|
||||
table != nil && len(table.PrimaryKeys) == 1 {
|
||||
cond = builder.Expr(fmt.Sprintf("%s IN (SELECT TOP (%d) %s FROM %v%v)",
|
||||
table.PrimaryKeys[0], st.LimitN, table.PrimaryKeys[0],
|
||||
session.Engine.Quote(session.Statement.TableName()), condSQL), condArgs...)
|
||||
|
||||
condSQL, condArgs, _ = builder.ToSQL(cond)
|
||||
if len(condSQL) > 0 {
|
||||
condSQL = "WHERE " + condSQL
|
||||
}
|
||||
} else {
|
||||
top = fmt.Sprintf("TOP (%d) ", st.LimitN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -356,7 +356,6 @@ func TestUpdate1(t *testing.T) {
|
|||
And("departname = ?", "").
|
||||
And("detail_id = ?", 0).
|
||||
And("is_man = ?", 0).
|
||||
And("created IS NOT NULL").
|
||||
Get(&Userinfo{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
|
@ -376,7 +376,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{},
|
|||
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
|
||||
continue
|
||||
}
|
||||
val = engine.FormatTime(col.SQLType.Name, t)
|
||||
val = engine.formatColTime(col, t)
|
||||
} else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok {
|
||||
val, _ = nulType.Value()
|
||||
} else {
|
||||
|
@ -612,7 +612,7 @@ func buildConds(engine *Engine, table *core.Table, bean interface{},
|
|||
if !requiredField && (t.IsZero() || !fieldValue.IsValid()) {
|
||||
continue
|
||||
}
|
||||
val = engine.FormatTime(col.SQLType.Name, t)
|
||||
val = engine.formatColTime(col, t)
|
||||
} else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok {
|
||||
continue
|
||||
} else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok {
|
||||
|
|
|
@ -26,7 +26,7 @@ var colStrTests = []struct {
|
|||
}
|
||||
|
||||
func TestColumnsStringGeneration(t *testing.T) {
|
||||
if dbType == "postgres" {
|
||||
if dbType == "postgres" || dbType == "mssql" {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -328,6 +329,11 @@ func TestExtends2(t *testing.T) {
|
|||
Uid: sender.Id,
|
||||
ToUid: receiver.Id,
|
||||
}
|
||||
if testEngine.dialect.DBType() == core.MSSQL {
|
||||
_, err = testEngine.Exec("SET IDENTITY_INSERT message ON")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
_, err = testEngine.Insert(&msg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@ -395,6 +401,10 @@ func TestExtends3(t *testing.T) {
|
|||
Uid: sender.Id,
|
||||
ToUid: receiver.Id,
|
||||
}
|
||||
if testEngine.dialect.DBType() == core.MSSQL {
|
||||
_, err = testEngine.Exec("SET IDENTITY_INSERT message ON")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
_, err = testEngine.Insert(&msg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@ -478,6 +488,10 @@ func TestExtends4(t *testing.T) {
|
|||
Content: "test",
|
||||
Uid: sender.Id,
|
||||
}
|
||||
if testEngine.dialect.DBType() == core.MSSQL {
|
||||
_, err = testEngine.Exec("SET IDENTITY_INSERT message ON")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
_, err = testEngine.Insert(&msg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestGonicMapperID(t *testing.T) {
|
|||
|
||||
for _, tb := range tables {
|
||||
if tb.Name == "id_gonic_mapper" {
|
||||
if len(tb.PKColumns()) != 1 && !tb.PKColumns()[0].IsPrimaryKey && !tb.PKColumns()[0].IsPrimaryKey {
|
||||
if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "id" {
|
||||
t.Fatal(tb)
|
||||
}
|
||||
return
|
||||
|
@ -75,7 +75,7 @@ func TestSameMapperID(t *testing.T) {
|
|||
|
||||
for _, tb := range tables {
|
||||
if tb.Name == "IDSameMapper" {
|
||||
if len(tb.PKColumns()) != 1 && !tb.PKColumns()[0].IsPrimaryKey && !tb.PKColumns()[0].IsPrimaryKey {
|
||||
if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "ID" {
|
||||
t.Fatal(tb)
|
||||
}
|
||||
return
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test"
|
|
@ -0,0 +1,476 @@
|
|||
// 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"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTimeUserTime(t *testing.T) {
|
||||
assert.NoError(t, prepareEngine())
|
||||
|
||||
type TimeUser struct {
|
||||
Id string
|
||||
OperTime time.Time
|
||||
}
|
||||
|
||||
assertSync(t, new(TimeUser))
|
||||
|
||||
var user = TimeUser{
|
||||
Id: "lunny",
|
||||
OperTime: time.Now(),
|
||||
}
|
||||
|
||||
fmt.Println("user", user.OperTime)
|
||||
|
||||
cnt, err := testEngine.Insert(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var user2 TimeUser
|
||||
has, err := testEngine.Get(&user2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, user.OperTime.Unix(), user2.OperTime.Unix())
|
||||
assert.EqualValues(t, formatTime(user.OperTime), formatTime(user2.OperTime))
|
||||
fmt.Println("user2", user2.OperTime)
|
||||
}
|
||||
|
||||
func TestTimeUserTimeDiffLoc(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 TimeUser struct {
|
||||
Id string
|
||||
OperTime time.Time
|
||||
}
|
||||
|
||||
assertSync(t, new(TimeUser))
|
||||
|
||||
var user = TimeUser{
|
||||
Id: "lunny",
|
||||
OperTime: time.Now(),
|
||||
}
|
||||
|
||||
fmt.Println("user", user.OperTime)
|
||||
|
||||
cnt, err := testEngine.Insert(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var user2 TimeUser
|
||||
has, err := testEngine.Get(&user2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, user.OperTime.Unix(), user2.OperTime.Unix())
|
||||
assert.EqualValues(t, formatTime(user.OperTime.In(loc)), formatTime(user2.OperTime))
|
||||
fmt.Println("user2", user2.OperTime)
|
||||
}
|
||||
|
||||
func TestTimeUserCreated(t *testing.T) {
|
||||
assert.NoError(t, prepareEngine())
|
||||
|
||||
type UserCreated struct {
|
||||
Id string
|
||||
CreatedAt time.Time `xorm:"created"`
|
||||
}
|
||||
|
||||
assertSync(t, new(UserCreated))
|
||||
|
||||
var user = UserCreated{
|
||||
Id: "lunny",
|
||||
}
|
||||
|
||||
fmt.Println("user", user.CreatedAt)
|
||||
|
||||
cnt, err := testEngine.Insert(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var user2 UserCreated
|
||||
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))
|
||||
fmt.Println("user2", user2.CreatedAt)
|
||||
}
|
||||
|
||||
func TestTimeUserCreatedDiffLoc(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 UserCreated struct {
|
||||
Id string
|
||||
CreatedAt time.Time `xorm:"created"`
|
||||
}
|
||||
|
||||
assertSync(t, new(UserCreated))
|
||||
|
||||
var user = UserCreated{
|
||||
Id: "lunny",
|
||||
}
|
||||
|
||||
fmt.Println("user", user.CreatedAt)
|
||||
|
||||
cnt, err := testEngine.Insert(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var user2 UserCreated
|
||||
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))
|
||||
fmt.Println("user2", user2.CreatedAt)
|
||||
}
|
||||
|
||||
func TestTimeUserUpdated(t *testing.T) {
|
||||
assert.NoError(t, prepareEngine())
|
||||
|
||||
type UserUpdated struct {
|
||||
Id string
|
||||
CreatedAt time.Time `xorm:"created"`
|
||||
UpdatedAt time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
assertSync(t, new(UserUpdated))
|
||||
|
||||
var user = UserUpdated{
|
||||
Id: "lunny",
|
||||
}
|
||||
|
||||
fmt.Println("user", user.CreatedAt, user.UpdatedAt)
|
||||
|
||||
cnt, err := testEngine.Insert(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var user2 UserUpdated
|
||||
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))
|
||||
fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt)
|
||||
|
||||
var user3 = UserUpdated{
|
||||
Id: "lunny2",
|
||||
}
|
||||
|
||||
cnt, err = testEngine.Update(&user3)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
assert.True(t, user.UpdatedAt.Unix() <= user3.UpdatedAt.Unix())
|
||||
|
||||
var user4 UserUpdated
|
||||
has, err = testEngine.Get(&user4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, user.CreatedAt.Unix(), user4.CreatedAt.Unix())
|
||||
assert.EqualValues(t, formatTime(user.CreatedAt), formatTime(user4.CreatedAt))
|
||||
assert.EqualValues(t, user3.UpdatedAt.Unix(), user4.UpdatedAt.Unix())
|
||||
assert.EqualValues(t, formatTime(user3.UpdatedAt), formatTime(user4.UpdatedAt))
|
||||
fmt.Println("user3", user.CreatedAt, user4.UpdatedAt)
|
||||
}
|
||||
|
||||
func TestTimeUserUpdatedDiffLoc(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 UserUpdated struct {
|
||||
Id string
|
||||
CreatedAt time.Time `xorm:"created"`
|
||||
UpdatedAt time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
assertSync(t, new(UserUpdated))
|
||||
|
||||
var user = UserUpdated{
|
||||
Id: "lunny",
|
||||
}
|
||||
|
||||
fmt.Println("user", user.CreatedAt, user.UpdatedAt)
|
||||
|
||||
cnt, err := testEngine.Insert(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var user2 UserUpdated
|
||||
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))
|
||||
fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt)
|
||||
|
||||
var user3 = UserUpdated{
|
||||
Id: "lunny2",
|
||||
}
|
||||
|
||||
cnt, err = testEngine.Update(&user3)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
assert.True(t, user.UpdatedAt.Unix() <= user3.UpdatedAt.Unix())
|
||||
|
||||
var user4 UserUpdated
|
||||
has, err = testEngine.Get(&user4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, user.CreatedAt.Unix(), user4.CreatedAt.Unix())
|
||||
assert.EqualValues(t, formatTime(user.CreatedAt), formatTime(user4.CreatedAt))
|
||||
assert.EqualValues(t, user3.UpdatedAt.Unix(), user4.UpdatedAt.Unix())
|
||||
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)
|
||||
}
|
6
xorm.go
6
xorm.go
|
@ -89,6 +89,12 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) {
|
|||
tagHandlers: defaultTagHandlers,
|
||||
}
|
||||
|
||||
if uri.DbType == core.SQLITE {
|
||||
engine.DatabaseTZ = time.UTC
|
||||
} else {
|
||||
engine.DatabaseTZ = time.Local
|
||||
}
|
||||
|
||||
logger := NewSimpleLogger(os.Stdout)
|
||||
logger.SetLevel(core.LOG_INFO)
|
||||
engine.SetLogger(logger)
|
||||
|
|
10
xorm_test.go
10
xorm_test.go
|
@ -7,6 +7,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "github.com/denisenkom/go-mssqldb"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
_ "github.com/lib/pq"
|
||||
|
@ -45,7 +46,10 @@ func createEngine(dbType, connStr string) error {
|
|||
for _, table := range tables {
|
||||
tableNames = append(tableNames, table.Name)
|
||||
}
|
||||
return testEngine.DropTables(tableNames...)
|
||||
if err = testEngine.DropTables(tableNames...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareEngine() error {
|
||||
|
@ -70,8 +74,8 @@ func TestMain(m *testing.M) {
|
|||
connString = *ptrConnStr
|
||||
}
|
||||
|
||||
dbs := strings.Split(*db, ";")
|
||||
conns := strings.Split(connString, ";")
|
||||
dbs := strings.Split(*db, "::")
|
||||
conns := strings.Split(connString, "::")
|
||||
|
||||
var res int
|
||||
for i := 0; i < len(dbs); i++ {
|
||||
|
|
Loading…
Reference in New Issue