THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

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
tags/v0.6.3
Lunny Xiao GitHub 2 years ago
parent
commit
942887dea0
21 changed files with 782 additions and 273 deletions
  1. +1
    -1
      circle.yml
  2. +16
    -7
      dialect_mssql.go
  3. +15
    -45
      engine.go
  4. +1
    -169
      helpers.go
  5. +21
    -0
      helpler_time.go
  6. +5
    -17
      session.go
  7. +6
    -1
      session_cols_test.go
  8. +13
    -20
      session_convert.go
  9. +6
    -0
      session_get_test.go
  10. +3
    -3
      session_insert.go
  11. +173
    -0
      session_raw.go
  12. +13
    -1
      session_update.go
  13. +0
    -1
      session_update_test.go
  14. +2
    -2
      statement.go
  15. +1
    -1
      statement_test.go
  16. +14
    -0
      tag_extends_test.go
  17. +2
    -2
      tag_id_test.go
  18. +1
    -0
      test_mssql.sh
  19. +476
    -0
      time_test.go
  20. +6
    -0
      xorm.go
  21. +7
    -3
      xorm_test.go

+ 1
- 1
circle.yml View File

@@ -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

+ 16
- 7
dialect_mssql.go View File

@@ -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 {

+ 15
- 45
engine.go View File

@@ -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)
}

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)
if col.TimeZone != nil {
return engine.formatTime(col.SQLType.Name, t.In(col.TimeZone))
}
return engine.formatTime(col.SQLType.Name, t.In(engine.DatabaseTZ))
}

// 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)
}

+ 1
- 169
helpers.go View File

@@ -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 {

+ 21
- 0
helpler_time.go View File

@@ -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
}

+ 5
- 17
session.go View File

@@ -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 {

+ 6
- 1
session_cols_test.go View File

@@ -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)
}

+ 13
- 20
session_convert.go View File

@@ -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
}


+ 6
- 0
session_get_test.go View File

@@ -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)

+ 3
- 3
session_insert.go View File

@@ -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
- 0
session_raw.go View File

@@ -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()

+ 13
- 1
session_update.go View File

@@ -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)
}
}
}


+ 0
- 1
session_update_test.go View File

@@ -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)

+ 2
- 2
statement.go View File

@@ -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 {

+ 1
- 1
statement_test.go View File

@@ -26,7 +26,7 @@ var colStrTests = []struct {
}

func TestColumnsStringGeneration(t *testing.T) {
if dbType == "postgres" {
if dbType == "postgres" || dbType == "mssql" {
return
}


+ 14
- 0
tag_extends_test.go View File

@@ -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)

+ 2
- 2
tag_id_test.go View File

@@ -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

+ 1
- 0
test_mssql.sh View File

@@ -0,0 +1 @@
go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test"

+ 476
- 0
time_test.go View File

@@ -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
- 0
xorm.go View File

@@ -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)

+ 7
- 3
xorm_test.go View File

@@ -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…
Cancel
Save