Browse Source

fix time issues

xormplus 8 years ago
parent
commit
82630a6f58
11 changed files with 252 additions and 614 deletions
  1. 16 7
      dialect_mssql.go
  2. 24 49
      engine.go
  3. 0 169
      helpers.go
  4. 0 294
      lru_cacher.go
  5. 0 47
      memroy_store.go
  6. 5 25
      session.go
  7. 13 20
      session_convert.go
  8. 173 0
      session_raw.go
  9. 13 1
      session_update.go
  10. 2 2
      statement.go
  11. 6 0
      xorm.go

+ 16 - 7
dialect_mssql.go

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

+ 24 - 49
engine.go

@@ -43,7 +43,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
@@ -146,7 +146,6 @@ func (engine *Engine) Quote(value string) string {
 
 // QuoteTo quotes string and writes into the buffer
 func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) {
-
 	if buf == nil {
 		return
 	}
@@ -790,6 +789,12 @@ func (engine *Engine) Having(conditions string) *Session {
 	return session.Having(conditions)
 }
 
+func (engine *Engine) unMapType(t reflect.Type) {
+	engine.mutex.Lock()
+	defer engine.mutex.Unlock()
+	delete(engine.Tables, t)
+}
+
 func (engine *Engine) autoMapType(v reflect.Value) (*core.Table, error) {
 	t := v.Type()
 	engine.mutex.Lock()
@@ -1251,7 +1256,6 @@ func (engine *Engine) Sync(beans ...interface{}) error {
 					return err
 				}
 				if index.Type == core.UniqueType {
-					//isExist, err := session.isIndexExist(table.Name, name, true)
 					isExist, err := session.isIndexExist2(tableName, index.Cols, true)
 					if err != nil {
 						return err
@@ -1358,10 +1362,11 @@ func (engine *Engine) DropTables(beans ...interface{}) error {
 	return session.Commit()
 }
 
-func (engine *Engine) createAll() error {
+// DropIndexes drop indexes of a table
+func (engine *Engine) DropIndexes(bean interface{}) error {
 	session := engine.NewSession()
 	defer session.Close()
-	return session.createAll()
+	return session.DropIndexes(bean)
 }
 
 // Exec raw sql
@@ -1519,7 +1524,6 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) {
 			results = append(results, result)
 			if err != nil {
 				return nil, err
-				//lastError = err
 			}
 		}
 	}
@@ -1527,49 +1531,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
@@ -1577,18 +1560,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.999")
-		}
+		v = t.Format("2006-01-02 15:04:05.999")
 	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)
 		}

+ 0 - 169
helpers.go

@@ -320,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 - 294
lru_cacher.go

@@ -1,294 +0,0 @@
-// Copyright 2015 The Xorm Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xorm
-
-import (
-	"container/list"
-	"fmt"
-	"sync"
-	"time"
-
-	"github.com/xormplus/core"
-)
-
-// LRUCacher implments cache object facilities
-type LRUCacher struct {
-	idList   *list.List
-	sqlList  *list.List
-	idIndex  map[string]map[string]*list.Element
-	sqlIndex map[string]map[string]*list.Element
-	store    core.CacheStore
-	mutex    sync.Mutex
-	// maxSize    int
-	MaxElementSize int
-	Expired        time.Duration
-	GcInterval     time.Duration
-}
-
-// NewLRUCacher creates a cacher
-func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher {
-	return NewLRUCacher2(store, 3600*time.Second, maxElementSize)
-}
-
-// NewLRUCacher2 creates a cache include different params
-func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher {
-	cacher := &LRUCacher{store: store, idList: list.New(),
-		sqlList: list.New(), Expired: expired,
-		GcInterval: core.CacheGcInterval, MaxElementSize: maxElementSize,
-		sqlIndex: make(map[string]map[string]*list.Element),
-		idIndex:  make(map[string]map[string]*list.Element),
-	}
-	cacher.RunGC()
-	return cacher
-}
-
-// RunGC run once every m.GcInterval
-func (m *LRUCacher) RunGC() {
-	time.AfterFunc(m.GcInterval, func() {
-		m.RunGC()
-		m.GC()
-	})
-}
-
-// GC check ids lit and sql list to remove all element expired
-func (m *LRUCacher) GC() {
-	//fmt.Println("begin gc ...")
-	//defer fmt.Println("end gc ...")
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	var removedNum int
-	for e := m.idList.Front(); e != nil; {
-		if removedNum <= core.CacheGcMaxRemoved &&
-			time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
-			removedNum++
-			next := e.Next()
-			//fmt.Println("removing ...", e.Value)
-			node := e.Value.(*idNode)
-			m.delBean(node.tbName, node.id)
-			e = next
-		} else {
-			//fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.idList.Len())
-			break
-		}
-	}
-
-	removedNum = 0
-	for e := m.sqlList.Front(); e != nil; {
-		if removedNum <= core.CacheGcMaxRemoved &&
-			time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
-			removedNum++
-			next := e.Next()
-			//fmt.Println("removing ...", e.Value)
-			node := e.Value.(*sqlNode)
-			m.delIds(node.tbName, node.sql)
-			e = next
-		} else {
-			//fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.sqlList.Len())
-			break
-		}
-	}
-}
-
-// GetIds returns all bean's ids according to sql and parameter from cache
-func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	if _, ok := m.sqlIndex[tableName]; !ok {
-		m.sqlIndex[tableName] = make(map[string]*list.Element)
-	}
-	if v, err := m.store.Get(sql); err == nil {
-		if el, ok := m.sqlIndex[tableName][sql]; !ok {
-			el = m.sqlList.PushBack(newSQLNode(tableName, sql))
-			m.sqlIndex[tableName][sql] = el
-		} else {
-			lastTime := el.Value.(*sqlNode).lastVisit
-			// if expired, remove the node and return nil
-			if time.Now().Sub(lastTime) > m.Expired {
-				m.delIds(tableName, sql)
-				return nil
-			}
-			m.sqlList.MoveToBack(el)
-			el.Value.(*sqlNode).lastVisit = time.Now()
-		}
-		return v
-	}
-
-	m.delIds(tableName, sql)
-
-	return nil
-}
-
-// GetBean returns bean according tableName and id from cache
-func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	if _, ok := m.idIndex[tableName]; !ok {
-		m.idIndex[tableName] = make(map[string]*list.Element)
-	}
-	tid := genID(tableName, id)
-	if v, err := m.store.Get(tid); err == nil {
-		if el, ok := m.idIndex[tableName][id]; ok {
-			lastTime := el.Value.(*idNode).lastVisit
-			// if expired, remove the node and return nil
-			if time.Now().Sub(lastTime) > m.Expired {
-				m.delBean(tableName, id)
-				//m.clearIds(tableName)
-				return nil
-			}
-			m.idList.MoveToBack(el)
-			el.Value.(*idNode).lastVisit = time.Now()
-		} else {
-			el = m.idList.PushBack(newIDNode(tableName, id))
-			m.idIndex[tableName][id] = el
-		}
-		return v
-	}
-
-	// store bean is not exist, then remove memory's index
-	m.delBean(tableName, id)
-	//m.clearIds(tableName)
-	return nil
-}
-
-// clearIds clears all sql-ids mapping on table tableName from cache
-func (m *LRUCacher) clearIds(tableName string) {
-	if tis, ok := m.sqlIndex[tableName]; ok {
-		for sql, v := range tis {
-			m.sqlList.Remove(v)
-			m.store.Del(sql)
-		}
-	}
-	m.sqlIndex[tableName] = make(map[string]*list.Element)
-}
-
-// ClearIds clears all sql-ids mapping on table tableName from cache
-func (m *LRUCacher) ClearIds(tableName string) {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	m.clearIds(tableName)
-}
-
-func (m *LRUCacher) clearBeans(tableName string) {
-	if tis, ok := m.idIndex[tableName]; ok {
-		for id, v := range tis {
-			m.idList.Remove(v)
-			tid := genID(tableName, id)
-			m.store.Del(tid)
-		}
-	}
-	m.idIndex[tableName] = make(map[string]*list.Element)
-}
-
-// ClearBeans clears all beans in some table
-func (m *LRUCacher) ClearBeans(tableName string) {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	m.clearBeans(tableName)
-}
-
-// PutIds pus ids into table
-func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	if _, ok := m.sqlIndex[tableName]; !ok {
-		m.sqlIndex[tableName] = make(map[string]*list.Element)
-	}
-	if el, ok := m.sqlIndex[tableName][sql]; !ok {
-		el = m.sqlList.PushBack(newSQLNode(tableName, sql))
-		m.sqlIndex[tableName][sql] = el
-	} else {
-		el.Value.(*sqlNode).lastVisit = time.Now()
-	}
-	m.store.Put(sql, ids)
-	if m.sqlList.Len() > m.MaxElementSize {
-		e := m.sqlList.Front()
-		node := e.Value.(*sqlNode)
-		m.delIds(node.tbName, node.sql)
-	}
-}
-
-// PutBean puts beans into table
-func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	var el *list.Element
-	var ok bool
-
-	if el, ok = m.idIndex[tableName][id]; !ok {
-		el = m.idList.PushBack(newIDNode(tableName, id))
-		m.idIndex[tableName][id] = el
-	} else {
-		el.Value.(*idNode).lastVisit = time.Now()
-	}
-
-	m.store.Put(genID(tableName, id), obj)
-	if m.idList.Len() > m.MaxElementSize {
-		e := m.idList.Front()
-		node := e.Value.(*idNode)
-		m.delBean(node.tbName, node.id)
-	}
-}
-
-func (m *LRUCacher) delIds(tableName, sql string) {
-	if _, ok := m.sqlIndex[tableName]; ok {
-		if el, ok := m.sqlIndex[tableName][sql]; ok {
-			delete(m.sqlIndex[tableName], sql)
-			m.sqlList.Remove(el)
-		}
-	}
-	m.store.Del(sql)
-}
-
-// DelIds deletes ids
-func (m *LRUCacher) DelIds(tableName, sql string) {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	m.delIds(tableName, sql)
-}
-
-func (m *LRUCacher) delBean(tableName string, id string) {
-	tid := genID(tableName, id)
-	if el, ok := m.idIndex[tableName][id]; ok {
-		delete(m.idIndex[tableName], id)
-		m.idList.Remove(el)
-		m.clearIds(tableName)
-	}
-	m.store.Del(tid)
-}
-
-// DelBean deletes beans in some table
-func (m *LRUCacher) DelBean(tableName string, id string) {
-	m.mutex.Lock()
-	defer m.mutex.Unlock()
-	m.delBean(tableName, id)
-}
-
-type idNode struct {
-	tbName    string
-	id        string
-	lastVisit time.Time
-}
-
-type sqlNode struct {
-	tbName    string
-	sql       string
-	lastVisit time.Time
-}
-
-func genSQLKey(sql string, args interface{}) string {
-	return fmt.Sprintf("%v-%v", sql, args)
-}
-
-func genID(prefix string, id string) string {
-	return fmt.Sprintf("%v-%v", prefix, id)
-}
-
-func newIDNode(tbName string, id string) *idNode {
-	return &idNode{tbName, id, time.Now()}
-}
-
-func newSQLNode(tbName, sql string) *sqlNode {
-	return &sqlNode{tbName, sql, time.Now()}
-}

+ 0 - 47
memroy_store.go

@@ -1,47 +0,0 @@
-// Copyright 2015 The Xorm Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package xorm
-
-import (
-	"sync"
-
-	"github.com/xormplus/core"
-)
-
-var _ core.CacheStore = NewMemoryStore()
-
-// MemoryStore represents in-memory store
-type MemoryStore struct {
-	store map[interface{}]interface{}
-	mutex sync.RWMutex
-}
-
-func NewMemoryStore() *MemoryStore {
-	return &MemoryStore{store: make(map[interface{}]interface{})}
-}
-
-func (s *MemoryStore) Put(key string, value interface{}) error {
-	s.mutex.Lock()
-	defer s.mutex.Unlock()
-	s.store[key] = value
-	return nil
-}
-
-func (s *MemoryStore) Get(key string) (interface{}, error) {
-	s.mutex.RLock()
-	defer s.mutex.RUnlock()
-	if v, ok := s.store[key]; ok {
-		return v, nil
-	}
-
-	return nil, ErrNotExist
-}
-
-func (s *MemoryStore) Del(key string) error {
-	s.mutex.Lock()
-	defer s.mutex.Unlock()
-	delete(s.store, key)
-	return nil
-}

+ 5 - 25
session.go

@@ -350,15 +350,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 {
@@ -534,11 +525,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 {
@@ -554,19 +543,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))
-
-						// t = fieldValue.Interface().(time.Time)
-						// z, _ = t.Zone()
-						// session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
 					} else if rawValueType == core.IntType || rawValueType == core.Int64Type ||
 						rawValueType == core.Int32Type {
 						hasAssigned = true
 
-						t := time.Unix(vv.Int(), 0).In(tz)
-						//vv = reflect.ValueOf(t)
+						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 {
@@ -648,8 +631,6 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 							return nil, err
 						}
 						if has {
-							//v := structInter.Elem().Interface()
-							//fieldValue.Set(reflect.ValueOf(v))
 							fieldValue.Set(structInter.Elem())
 						} else {
 							return nil, errors.New("cascade obj is not exist")
@@ -658,7 +639,6 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i
 				}
 			case reflect.Ptr:
 				// !nashtsai! TODO merge duplicated codes above
-				//typeStr := fieldType.String()
 				switch fieldType {
 				// following types case matching ptr's native type, therefore assign ptr directly
 				case core.PtrStringType:

+ 13 - 20
session_convert.go

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

+ 173 - 0
session_raw.go

@@ -6,6 +6,10 @@ package xorm
 
 import (
 	"database/sql"
+	"fmt"
+	"reflect"
+	"strconv"
+	"time"
 
 	"github.com/xormplus/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) query1(sqlStr string, paramStr ...interface{}) ([]map[st
 	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

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

+ 2 - 2
statement.go

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

+ 6 - 0
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)