diff --git a/cache_lru.go b/cache_lru.go index 4a745043..c9672ceb 100644 --- a/cache_lru.go +++ b/cache_lru.go @@ -15,13 +15,12 @@ import ( // 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 + 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 MaxElementSize int Expired time.Duration GcInterval time.Duration @@ -54,8 +53,6 @@ func (m *LRUCacher) RunGC() { // 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 @@ -64,12 +61,10 @@ func (m *LRUCacher) GC() { 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 } } @@ -80,12 +75,10 @@ func (m *LRUCacher) GC() { 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 } } @@ -116,7 +109,6 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} { } m.delIds(tableName, sql) - return nil } @@ -134,7 +126,6 @@ func (m *LRUCacher) GetBean(tableName string, id string) interface{} { // 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) @@ -148,7 +139,6 @@ func (m *LRUCacher) GetBean(tableName string, id string) interface{} { // store bean is not exist, then remove memory's index m.delBean(tableName, id) - //m.clearIds(tableName) return nil } @@ -166,8 +156,8 @@ func (m *LRUCacher) clearIds(tableName string) { // 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) + m.mutex.Unlock() } func (m *LRUCacher) clearBeans(tableName string) { @@ -184,14 +174,13 @@ func (m *LRUCacher) clearBeans(tableName string) { // ClearBeans clears all beans in some table func (m *LRUCacher) ClearBeans(tableName string) { m.mutex.Lock() - defer m.mutex.Unlock() m.clearBeans(tableName) + m.mutex.Unlock() } // 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) } @@ -207,12 +196,12 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { node := e.Value.(*sqlNode) m.delIds(node.tbName, node.sql) } + m.mutex.Unlock() } // 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 @@ -229,6 +218,7 @@ func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { node := e.Value.(*idNode) m.delBean(node.tbName, node.id) } + m.mutex.Unlock() } func (m *LRUCacher) delIds(tableName, sql string) { @@ -244,8 +234,8 @@ func (m *LRUCacher) delIds(tableName, sql string) { // DelIds deletes ids func (m *LRUCacher) DelIds(tableName, sql string) { m.mutex.Lock() - defer m.mutex.Unlock() m.delIds(tableName, sql) + m.mutex.Unlock() } func (m *LRUCacher) delBean(tableName string, id string) { @@ -261,8 +251,8 @@ func (m *LRUCacher) delBean(tableName string, id string) { // 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) + m.mutex.Unlock() } type idNode struct { diff --git a/cache_lru_test.go b/cache_lru_test.go new file mode 100644 index 00000000..28854474 --- /dev/null +++ b/cache_lru_test.go @@ -0,0 +1,52 @@ +// 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 ( + "testing" + + "github.com/go-xorm/core" + "github.com/stretchr/testify/assert" +) + +func TestLRUCache(t *testing.T) { + type CacheObject1 struct { + Id int64 + } + + store := NewMemoryStore() + cacher := NewLRUCacher(store, 10000) + + tableName := "cache_object1" + pks := []core.PK{ + {1}, + {2}, + } + + for _, pk := range pks { + sid, err := pk.ToString() + assert.NoError(t, err) + + cacher.PutIds(tableName, "select * from cache_object1", sid) + ids := cacher.GetIds(tableName, "select * from cache_object1") + assert.EqualValues(t, sid, ids) + + cacher.ClearIds(tableName) + ids2 := cacher.GetIds(tableName, "select * from cache_object1") + assert.Nil(t, ids2) + + obj2 := cacher.GetBean(tableName, sid) + assert.Nil(t, obj2) + + var obj = new(CacheObject1) + cacher.PutBean(tableName, sid, obj) + obj3 := cacher.GetBean(tableName, sid) + assert.EqualValues(t, obj, obj3) + + cacher.DelBean(tableName, sid) + obj4 := cacher.GetBean(tableName, sid) + assert.Nil(t, obj4) + } +} diff --git a/cache_memory_store_test.go b/cache_memory_store_test.go new file mode 100644 index 00000000..fc27ae32 --- /dev/null +++ b/cache_memory_store_test.go @@ -0,0 +1,37 @@ +// 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 ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMemoryStore(t *testing.T) { + store := NewMemoryStore() + var kvs = map[string]interface{}{ + "a": "b", + } + for k, v := range kvs { + assert.NoError(t, store.Put(k, v)) + } + + for k, v := range kvs { + val, err := store.Get(k) + assert.NoError(t, err) + assert.EqualValues(t, v, val) + } + + for k, _ := range kvs { + err := store.Del(k) + assert.NoError(t, err) + } + + for k, _ := range kvs { + _, err := store.Get(k) + assert.EqualValues(t, ErrNotExist, err) + } +} diff --git a/cache_test.go b/cache_test.go index 636e000f..f106234b 100644 --- a/cache_test.go +++ b/cache_test.go @@ -20,6 +20,7 @@ func TestCacheFind(t *testing.T) { Password string } + oldCacher := testEngine.Cacher cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) @@ -58,7 +59,7 @@ func TestCacheFind(t *testing.T) { assert.Equal(t, inserts[i].Password, box.Password) } - testEngine.SetDefaultCacher(nil) + testEngine.SetDefaultCacher(oldCacher) } func TestCacheFind2(t *testing.T) { @@ -70,6 +71,7 @@ func TestCacheFind2(t *testing.T) { Password string } + oldCacher := testEngine.Cacher cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) @@ -108,7 +110,7 @@ func TestCacheFind2(t *testing.T) { assert.Equal(t, inserts[i].Password, box.Password) } - testEngine.SetDefaultCacher(nil) + testEngine.SetDefaultCacher(oldCacher) } func TestCacheGet(t *testing.T) { @@ -120,6 +122,7 @@ func TestCacheGet(t *testing.T) { Password string } + oldCacher := testEngine.Cacher cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) @@ -148,5 +151,5 @@ func TestCacheGet(t *testing.T) { assert.EqualValues(t, "user1", box2.Username) assert.EqualValues(t, "pass1", box2.Password) - testEngine.SetDefaultCacher(nil) + testEngine.SetDefaultCacher(oldCacher) } diff --git a/circle.yml b/circle.yml index e81bdb0e..69fc7164 100644 --- a/circle.yml +++ b/circle.yml @@ -21,7 +21,16 @@ database: test: override: # './...' is a relative pattern which means all subdirectories - - go test -v -race -db="sqlite3::mysql::mymysql::postgres" -conn_str="./test.db::root:@/xorm_test::xorm_test/root/::dbname=xorm_test sslmode=disable" -coverprofile=coverage.txt -covermode=atomic + - go get -u github.com/wadey/gocovmerge; + - go test -v -race -db="sqlite3" -conn_str="./test.db" -coverprofile=coverage1-1.txt -covermode=atomic + - go test -v -race -db="sqlite3" -conn_str="./test.db" -cache=true -coverprofile=coverage1-2.txt -covermode=atomic + - go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -coverprofile=coverage2-1.txt -covermode=atomic + - go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic + - go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -coverprofile=coverage3-1.txt -covermode=atomic + - go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic + - go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -coverprofile=coverage4-1.txt -covermode=atomic + - go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic + - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt > coverage.txt - 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 diff --git a/processors_test.go b/processors_test.go index 5fc2170d..4ee59066 100644 --- a/processors_test.go +++ b/processors_test.go @@ -42,24 +42,24 @@ func TestBefore_Get(t *testing.T) { func TestBefore_Find(t *testing.T) { assert.NoError(t, prepareEngine()) - type BeforeTable struct { + type BeforeTable2 struct { Id int64 Name string Val string `xorm:"-"` } - assert.NoError(t, testEngine.Sync2(new(BeforeTable))) + assert.NoError(t, testEngine.Sync2(new(BeforeTable2))) - cnt, err := testEngine.Insert([]BeforeTable{ + cnt, err := testEngine.Insert([]BeforeTable2{ {Name: "test1"}, {Name: "test2"}, }) assert.NoError(t, err) assert.EqualValues(t, 2, cnt) - var be []BeforeTable + var be []BeforeTable2 err = testEngine.Before(func(bean interface{}) { - bean.(*BeforeTable).Val = "val" + bean.(*BeforeTable2).Val = "val" }).Find(&be) assert.NoError(t, err) assert.Equal(t, 2, len(be)) diff --git a/session_cols_test.go b/session_cols_test.go index 2a292bd3..43854723 100644 --- a/session_cols_test.go +++ b/session_cols_test.go @@ -14,14 +14,14 @@ import ( func TestSetExpr(t *testing.T) { assert.NoError(t, prepareEngine()) - type User struct { + type UserExpr struct { Id int64 Show bool } - assert.NoError(t, testEngine.Sync2(new(User))) + assert.NoError(t, testEngine.Sync2(new(UserExpr))) - cnt, err := testEngine.Insert(&User{ + cnt, err := testEngine.Insert(&UserExpr{ Show: true, }) assert.NoError(t, err) @@ -31,7 +31,7 @@ func TestSetExpr(t *testing.T) { if testEngine.dialect.DBType() == core.MSSQL { not = "~" } - cnt, err = testEngine.SetExpr("show", not+" `show`").ID(1).Update(new(User)) + cnt, err = testEngine.SetExpr("show", not+" `show`").ID(1).Update(new(UserExpr)) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) } diff --git a/session_delete.go b/session_delete.go index f8a4c665..1d7d662c 100644 --- a/session_delete.go +++ b/session_delete.go @@ -12,14 +12,14 @@ import ( "github.com/go-xorm/core" ) -func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { - if session.statement.RefTable == nil || +func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error { + if table == nil || session.tx != nil { return ErrCacheFailed } for _, filter := range session.engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr, session.engine.dialect, session.statement.RefTable) + sqlStr = filter.Do(sqlStr, session.engine.dialect, table) } newsql := session.statement.convertIDSQL(sqlStr) @@ -27,8 +27,8 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { return ErrCacheFailed } - cacher := session.engine.getCacher2(session.statement.RefTable) - tableName := session.statement.TableName() + cacher := session.engine.getCacher2(table) + pkColumns := table.PKColumns() ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { resultsSlice, err := session.queryBytes(newsql, args...) @@ -40,7 +40,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { for _, data := range resultsSlice { var id int64 var pk core.PK = make([]interface{}, 0) - for _, col := range session.statement.RefTable.PKColumns() { + for _, col := range pkColumns { if v, ok := data[col.Name]; !ok { return errors.New("no id") } else if col.SQLType.IsText() { @@ -58,20 +58,17 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { ids = append(ids, pk) } } - } /*else { - session.engine.LogDebug("delete cache sql %v", newsql) - cacher.DelIds(tableName, genSqlKey(newsql, args)) - }*/ + } for _, id := range ids { - session.engine.logger.Debug("[cacheDelete] delete cache obj", tableName, id) + session.engine.logger.Debug("[cacheDelete] delete cache obj:", tableName, id) sid, err := id.ToString() if err != nil { return err } cacher.DelBean(tableName, sid) } - session.engine.logger.Debug("[cacheDelete] clear cache sql", tableName) + session.engine.logger.Debug("[cacheDelete] clear cache table:", tableName) cacher.ClearIds(tableName) return nil } @@ -85,7 +82,6 @@ func (session *Session) Delete(bean interface{}) (int64, error) { if err := session.statement.setRefValue(rValue(bean)); err != nil { return 0, err } - var table = session.statement.RefTable // handle before delete processors for _, closure := range session.beforeClosures { @@ -105,7 +101,9 @@ func (session *Session) Delete(bean interface{}) (int64, error) { return 0, ErrNeedDeletedCond } - var tableName = session.engine.Quote(session.statement.TableName()) + var tableNameNoQuote = session.statement.TableName() + var tableName = session.engine.Quote(tableNameNoQuote) + var table = session.statement.RefTable var deleteSQL string if len(condSQL) > 0 { deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL) @@ -201,10 +199,11 @@ func (session *Session) Delete(bean interface{}) (int64, error) { }) } - if cacher := session.engine.getCacher2(session.statement.RefTable); cacher != nil && session.statement.UseCache { - session.cacheDelete(deleteSQL, argsForCache...) + if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { + session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) } + session.statement.RefTable = table res, err := session.exec(realSQL, condArgs...) if err != nil { return 0, err diff --git a/session_delete_test.go b/session_delete_test.go index 7eddd4cd..592ed575 100644 --- a/session_delete_test.go +++ b/session_delete_test.go @@ -137,3 +137,35 @@ func TestDeleted(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, len(records3)) } + +func TestCacheDelete(t *testing.T) { + assert.NoError(t, prepareEngine()) + + oldCacher := testEngine.Cacher + cacher := NewLRUCacher(NewMemoryStore(), 1000) + testEngine.SetDefaultCacher(cacher) + + type CacheDeleteStruct struct { + Id int64 + } + + err := testEngine.CreateTables(&CacheDeleteStruct{}) + assert.NoError(t, err) + + _, err = testEngine.Insert(&CacheDeleteStruct{}) + assert.NoError(t, err) + + aff, err := testEngine.Delete(&CacheDeleteStruct{ + Id: 1, + }) + assert.NoError(t, err) + assert.EqualValues(t, aff, 1) + + aff, err = testEngine.Unscoped().Delete(&CacheDeleteStruct{ + Id: 1, + }) + assert.NoError(t, err) + assert.EqualValues(t, aff, 0) + + testEngine.SetDefaultCacher(oldCacher) +} diff --git a/session_find.go b/session_find.go index b7cc446a..191695a7 100644 --- a/session_find.go +++ b/session_find.go @@ -293,12 +293,11 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } tableName := session.statement.TableName() - table := session.statement.RefTable cacher := session.engine.getCacher2(table) ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { - rows, err := session.NoCache().queryRows(newsql, args...) + rows, err := session.queryRows(newsql, args...) if err != nil { return err } @@ -328,13 +327,13 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in ids = append(ids, pk) } - session.engine.logger.Debug("[cacheFind] cache sql:", ids, tableName, newsql, args) + session.engine.logger.Debug("[cacheFind] cache sql:", ids, tableName, sqlStr, newsql, args) err = core.PutCacheSql(cacher, ids, tableName, newsql, args) if err != nil { return err } } else { - session.engine.logger.Debug("[cacheFind] cache hit sql:", newsql, args) + session.engine.logger.Debug("[cacheFind] cache hit sql:", tableName, sqlStr, newsql, args) } sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) diff --git a/session_get.go b/session_get.go index 4d33a201..6d17171b 100644 --- a/session_get.go +++ b/session_get.go @@ -125,8 +125,8 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf cacher := session.engine.getCacher2(session.statement.RefTable) tableName := session.statement.TableName() session.engine.logger.Debug("[cacheGet] find sql:", newsql, args) - ids, err := core.GetCacheSql(cacher, tableName, newsql, args) table := session.statement.RefTable + ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { var res = make([]string, len(table.PrimaryKeys)) rows, err := session.NoCache().queryRows(newsql, args...) @@ -166,7 +166,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf return false, err } } else { - session.engine.logger.Debug("[cacheGet] cache hit sql:", newsql) + session.engine.logger.Debug("[cacheGet] cache hit sql:", newsql, ids) } if len(ids) > 0 { diff --git a/session_insert.go b/session_insert.go index cafac151..705f6a89 100644 --- a/session_insert.go +++ b/session_insert.go @@ -213,22 +213,23 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)" var statement string + var tableName = session.statement.TableName() if session.engine.dialect.DBType() == core.ORACLE { sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL" temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (", - session.engine.Quote(session.statement.TableName()), + session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr()) statement = fmt.Sprintf(sql, - session.engine.Quote(session.statement.TableName()), + session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr(), strings.Join(colMultiPlaces, temp)) } else { statement = fmt.Sprintf(sql, - session.engine.Quote(session.statement.TableName()), + session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), session.engine.QuoteStr(), @@ -240,7 +241,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error } if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(session.statement.TableName()) + session.cacheInsert(table, tableName) } lenAfterClosures := len(session.afterClosures) @@ -347,18 +348,19 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { } var sqlStr string + var tableName = session.statement.TableName() if len(colPlaces) > 0 { sqlStr = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", - session.engine.Quote(session.statement.TableName()), + session.engine.Quote(tableName), session.engine.QuoteStr(), strings.Join(colNames, session.engine.Quote(", ")), session.engine.QuoteStr(), colPlaces) } else { if session.engine.dialect.DBType() == core.MYSQL { - sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.engine.Quote(session.statement.TableName())) + sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.engine.Quote(tableName)) } else { - sqlStr = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", session.engine.Quote(session.statement.TableName())) + sqlStr = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", session.engine.Quote(tableName)) } } @@ -401,7 +403,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { handleAfterInsertProcessorFunc(bean) if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(session.statement.TableName()) + session.cacheInsert(table, tableName) } if table.Version != "" && session.statement.checkVersion { @@ -446,7 +448,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { handleAfterInsertProcessorFunc(bean) if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(session.statement.TableName()) + session.cacheInsert(table, tableName) } if table.Version != "" && session.statement.checkVersion { @@ -489,7 +491,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - session.cacheInsert(session.statement.TableName()) + session.cacheInsert(table, tableName) } if table.Version != "" && session.statement.checkVersion { @@ -537,14 +539,12 @@ func (session *Session) InsertOne(bean interface{}) (int64, error) { return session.innerInsert(bean) } -func (session *Session) cacheInsert(tables ...string) error { - if session.statement.RefTable == nil { +func (session *Session) cacheInsert(table *core.Table, tables ...string) error { + if table == nil { return ErrCacheFailed } - table := session.statement.RefTable cacher := session.engine.getCacher2(table) - for _, t := range tables { session.engine.logger.Debug("[cache] clear sql:", t) cacher.ClearIds(t) diff --git a/session_insert_test.go b/session_insert_test.go index 6c63ebf3..427ca10f 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -125,15 +125,15 @@ func TestInsertOneIfPkIsPoint(t *testing.T) { func TestInsertOneIfPkIsPointRename(t *testing.T) { assert.NoError(t, prepareEngine()) type ID *int64 - type TestPoint struct { + type TestPoint2 struct { Id ID `xorm:"autoincr pk notnull 'id'"` Msg *string `xorm:"varchar(255)"` Created *time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestPoint))) + assert.NoError(t, testEngine.Sync2(new(TestPoint2))) msg := "hi" - data := TestPoint{Msg: &msg} + data := TestPoint2{Msg: &msg} _, err := testEngine.InsertOne(&data) assert.NoError(t, err) } diff --git a/session_pk_test.go b/session_pk_test.go index c919cd4f..3370b2ad 100644 --- a/session_pk_test.go +++ b/session_pk_test.go @@ -764,21 +764,11 @@ func TestCompositeKey(t *testing.T) { t.Error(errors.New("failed to insert CompositeKey{22, 22}")) } - if testEngine.Cacher != nil { - testEngine.Cacher.ClearBeans(testEngine.TableInfo(compositeKeyVal).Name) - } - cps = make([]CompositeKey, 0) err = testEngine.Find(&cps) - if err != nil { - t.Error(err) - } - if len(cps) != 2 { - t.Error(errors.New("should has two record")) - } - if cps[0] != compositeKeyVal { - t.Error(errors.New("should be equeal")) - } + assert.NoError(t, err) + assert.EqualValues(t, 2, len(cps), "should has two record") + assert.EqualValues(t, compositeKeyVal, cps[0], "should be equeal") compositeKeyVal = CompositeKey{UpdateStr: "test1"} cnt, err = testEngine.ID(core.PK{11, 22}).Update(&compositeKeyVal) @@ -796,16 +786,16 @@ func TestCompositeKey(t *testing.T) { } } -type User struct { - UserId string `xorm:"varchar(19) not null pk"` - NickName string `xorm:"varchar(19) not null"` - GameId uint32 `xorm:"integer pk"` - Score int32 `xorm:"integer"` -} - func TestCompositeKey2(t *testing.T) { assert.NoError(t, prepareEngine()) + type User struct { + UserId string `xorm:"varchar(19) not null pk"` + NickName string `xorm:"varchar(19) not null"` + GameId uint32 `xorm:"integer pk"` + Score int32 `xorm:"integer"` + } + err := testEngine.DropTables(&User{}) if err != nil { diff --git a/session_query_test.go b/session_query_test.go index 61a0a7fc..bdb1e1b2 100644 --- a/session_query_test.go +++ b/session_query_test.go @@ -16,7 +16,7 @@ import ( func TestQueryString(t *testing.T) { assert.NoError(t, prepareEngine()) - type GetVar struct { + type GetVar2 struct { Id int64 `xorm:"autoincr pk"` Msg string `xorm:"varchar(255)"` Age int @@ -24,9 +24,9 @@ func TestQueryString(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(GetVar))) + assert.NoError(t, testEngine.Sync2(new(GetVar2))) - var data = GetVar{ + var data = GetVar2{ Msg: "hi", Age: 28, Money: 1.5, @@ -34,7 +34,7 @@ func TestQueryString(t *testing.T) { _, err := testEngine.InsertOne(data) assert.NoError(t, err) - records, err := testEngine.QueryString("select * from get_var") + records, err := testEngine.QueryString("select * from get_var2") assert.NoError(t, err) assert.Equal(t, 1, len(records)) assert.Equal(t, 5, len(records[0])) diff --git a/session_schema_test.go b/session_schema_test.go index 8a7016c9..be999ce3 100644 --- a/session_schema_test.go +++ b/session_schema_test.go @@ -197,8 +197,9 @@ func TestMetaInfo(t *testing.T) { tables, err := testEngine.DBMetas() assert.NoError(t, err) assert.EqualValues(t, 2, len(tables)) - assert.EqualValues(t, "customtablename", tables[0].Name) - assert.EqualValues(t, "index_or_unique", tables[1].Name) + tableNames := []string{tables[0].Name, tables[1].Name} + assert.Contains(t, tableNames, "customtablename") + assert.Contains(t, tableNames, "index_or_unique") } func TestCharst(t *testing.T) { diff --git a/session_stats_test.go b/session_stats_test.go index 73f30e1d..17eaf6dc 100644 --- a/session_stats_test.go +++ b/session_stats_test.go @@ -76,26 +76,26 @@ func TestSum(t *testing.T) { func TestSumCustomColumn(t *testing.T) { assert.NoError(t, prepareEngine()) - type SumStruct struct { + type SumStruct2 struct { Int int Float float32 } var ( - cases = []SumStruct{ + cases = []SumStruct2{ {1, 6.2}, {2, 5.3}, {92, -0.2}, } ) - assert.NoError(t, testEngine.Sync2(new(SumStruct))) + assert.NoError(t, testEngine.Sync2(new(SumStruct2))) cnt, err := testEngine.Insert(cases) assert.NoError(t, err) assert.EqualValues(t, 3, cnt) - sumInt, err := testEngine.Sum(new(SumStruct), + sumInt, err := testEngine.Sum(new(SumStruct2), "CASE WHEN `int` <= 2 THEN `int` ELSE 0 END") assert.NoError(t, err) assert.EqualValues(t, 3, int(sumInt)) diff --git a/session_update.go b/session_update.go index ee0066ce..6e9d1168 100644 --- a/session_update.go +++ b/session_update.go @@ -15,8 +15,8 @@ import ( "github.com/go-xorm/core" ) -func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { - if session.statement.RefTable == nil || +func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, args ...interface{}) error { + if table == nil || session.tx != nil { return ErrCacheFailed } @@ -26,7 +26,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { return ErrCacheFailed } for _, filter := range session.engine.dialect.Filters() { - newsql = filter.Do(newsql, session.engine.dialect, session.statement.RefTable) + newsql = filter.Do(newsql, session.engine.dialect, table) } session.engine.logger.Debug("[cacheUpdate] new sql", oldhead, newsql) @@ -39,9 +39,8 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { nStart = strings.Count(oldhead, "$") } } - table := session.statement.RefTable + cacher := session.engine.getCacher2(table) - tableName := session.statement.TableName() session.engine.logger.Debug("[cacheUpdate] get cache sql", newsql, args[nStart:]) ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:]) if err != nil { @@ -280,6 +279,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr) } + var tableName = session.statement.TableName() // TODO: Oracle support needed var top string if st.LimitN > 0 { @@ -288,7 +288,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } else if st.Engine.dialect.DBType() == core.SQLITE { tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", - session.engine.Quote(session.statement.TableName()), tempCondSQL), condArgs...)) + session.engine.Quote(tableName), tempCondSQL), condArgs...)) condSQL, condArgs, err = builder.ToSQL(cond) if err != nil { return 0, err @@ -299,7 +299,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } else if st.Engine.dialect.DBType() == core.POSTGRES { tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)", - session.engine.Quote(session.statement.TableName()), tempCondSQL), condArgs...)) + session.engine.Quote(tableName), tempCondSQL), condArgs...)) condSQL, condArgs, err = builder.ToSQL(cond) if err != nil { return 0, err @@ -313,7 +313,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 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...) + session.engine.Quote(tableName), condSQL), condArgs...) condSQL, condArgs, err = builder.ToSQL(cond) if err != nil { @@ -334,7 +334,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v", top, - session.engine.Quote(session.statement.TableName()), + session.engine.Quote(tableName), strings.Join(colNames, ", "), condSQL) @@ -349,8 +349,9 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 if table != nil { if cacher := session.engine.getCacher2(table); cacher != nil && session.statement.UseCache { - cacher.ClearIds(session.statement.TableName()) - cacher.ClearBeans(session.statement.TableName()) + //session.cacheUpdate(table, tableName, sqlStr, args...) + cacher.ClearIds(tableName) + cacher.ClearBeans(tableName) } } @@ -360,7 +361,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 closure(bean) } if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok { - session.engine.logger.Debug("[event]", session.statement.TableName(), " has after update processor") + session.engine.logger.Debug("[event]", tableName, " has after update processor") processor.AfterUpdate() } } else { diff --git a/session_update_test.go b/session_update_test.go index ccd713ae..d59e283e 100644 --- a/session_update_test.go +++ b/session_update_test.go @@ -43,14 +43,14 @@ func TestUpdateMap(t *testing.T) { func TestUpdateLimit(t *testing.T) { assert.NoError(t, prepareEngine()) - type UpdateTable struct { + type UpdateTable2 struct { Id int64 Name string Age int } - assert.NoError(t, testEngine.Sync2(new(UpdateTable))) - var tb = UpdateTable{ + assert.NoError(t, testEngine.Sync2(new(UpdateTable2))) + var tb = UpdateTable2{ Name: "test1", Age: 35, } @@ -64,13 +64,13 @@ func TestUpdateLimit(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - cnt, err = testEngine.OrderBy("name desc").Limit(1).Update(&UpdateTable{ + cnt, err = testEngine.OrderBy("name desc").Limit(1).Update(&UpdateTable2{ Age: 30, }) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var uts []UpdateTable + var uts []UpdateTable2 err = testEngine.Find(&uts) assert.NoError(t, err) assert.EqualValues(t, 2, len(uts)) diff --git a/statement.go b/statement.go index 50008d1d..dc8a0c9b 100644 --- a/statement.go +++ b/statement.go @@ -1205,7 +1205,8 @@ func (statement *Statement) convertIDSQL(sqlStr string) string { top = fmt.Sprintf("TOP %d ", statement.LimitN) } - return fmt.Sprintf("SELECT %s%s FROM %v", top, colstrs, sqls[1]) + newsql := fmt.Sprintf("SELECT %s%s FROM %v", top, colstrs, sqls[1]) + return newsql } return "" } diff --git a/test_mssql_cache.sh b/test_mssql_cache.sh new file mode 100755 index 00000000..76efd6ca --- /dev/null +++ b/test_mssql_cache.sh @@ -0,0 +1 @@ +go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test" -cache=true \ No newline at end of file diff --git a/test_mymysql_cache.sh b/test_mymysql_cache.sh new file mode 100755 index 00000000..0100286d --- /dev/null +++ b/test_mymysql_cache.sh @@ -0,0 +1 @@ +go test -db=mymysql -conn_str="xorm_test/root/" -cache=true \ No newline at end of file diff --git a/test_mysql_cache.sh b/test_mysql_cache.sh new file mode 100755 index 00000000..c542e735 --- /dev/null +++ b/test_mysql_cache.sh @@ -0,0 +1 @@ +go test -db=mysql -conn_str="root:@/xorm_test" -cache=true \ No newline at end of file diff --git a/test_postgres_cache.sh b/test_postgres_cache.sh new file mode 100755 index 00000000..462fc948 --- /dev/null +++ b/test_postgres_cache.sh @@ -0,0 +1 @@ +go test -db=postgres -conn_str="dbname=xorm_test sslmode=disable" -cache=true \ No newline at end of file diff --git a/test_sqlite_cache.sh b/test_sqlite_cache.sh new file mode 100755 index 00000000..75a054c3 --- /dev/null +++ b/test_sqlite_cache.sh @@ -0,0 +1 @@ +go test -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" -cache=true \ No newline at end of file diff --git a/time_test.go b/time_test.go index fe5864d4..15b20c37 100644 --- a/time_test.go +++ b/time_test.go @@ -52,14 +52,14 @@ func TestTimeUserTimeDiffLoc(t *testing.T) { assert.NoError(t, err) testEngine.DatabaseTZ = dbLoc - type TimeUser struct { + type TimeUser2 struct { Id string OperTime time.Time } - assertSync(t, new(TimeUser)) + assertSync(t, new(TimeUser2)) - var user = TimeUser{ + var user = TimeUser2{ Id: "lunny", OperTime: time.Now(), } @@ -70,7 +70,7 @@ func TestTimeUserTimeDiffLoc(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var user2 TimeUser + var user2 TimeUser2 has, err := testEngine.Get(&user2) assert.NoError(t, err) assert.True(t, has) @@ -117,14 +117,14 @@ func TestTimeUserCreatedDiffLoc(t *testing.T) { assert.NoError(t, err) testEngine.DatabaseTZ = dbLoc - type UserCreated struct { + type UserCreated2 struct { Id string CreatedAt time.Time `xorm:"created"` } - assertSync(t, new(UserCreated)) + assertSync(t, new(UserCreated2)) - var user = UserCreated{ + var user = UserCreated2{ Id: "lunny", } @@ -134,7 +134,7 @@ func TestTimeUserCreatedDiffLoc(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var user2 UserCreated + var user2 UserCreated2 has, err := testEngine.Get(&user2) assert.NoError(t, err) assert.True(t, has) @@ -203,15 +203,15 @@ func TestTimeUserUpdatedDiffLoc(t *testing.T) { assert.NoError(t, err) testEngine.DatabaseTZ = dbLoc - type UserUpdated struct { + type UserUpdated2 struct { Id string CreatedAt time.Time `xorm:"created"` UpdatedAt time.Time `xorm:"updated"` } - assertSync(t, new(UserUpdated)) + assertSync(t, new(UserUpdated2)) - var user = UserUpdated{ + var user = UserUpdated2{ Id: "lunny", } @@ -221,7 +221,7 @@ func TestTimeUserUpdatedDiffLoc(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - var user2 UserUpdated + var user2 UserUpdated2 has, err := testEngine.Get(&user2) assert.NoError(t, err) assert.True(t, has) @@ -231,7 +231,7 @@ func TestTimeUserUpdatedDiffLoc(t *testing.T) { assert.EqualValues(t, formatTime(user.UpdatedAt), formatTime(user2.UpdatedAt)) fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt) - var user3 = UserUpdated{ + var user3 = UserUpdated2{ Id: "lunny2", } @@ -240,7 +240,7 @@ func TestTimeUserUpdatedDiffLoc(t *testing.T) { assert.EqualValues(t, 1, cnt) assert.True(t, user.UpdatedAt.Unix() <= user3.UpdatedAt.Unix()) - var user4 UserUpdated + var user4 UserUpdated2 has, err = testEngine.Get(&user4) assert.NoError(t, err) assert.True(t, has) @@ -307,16 +307,16 @@ func TestTimeUserDeletedDiffLoc(t *testing.T) { assert.NoError(t, err) testEngine.DatabaseTZ = dbLoc - type UserDeleted struct { + type UserDeleted2 struct { Id string CreatedAt time.Time `xorm:"created"` UpdatedAt time.Time `xorm:"updated"` DeletedAt time.Time `xorm:"deleted"` } - assertSync(t, new(UserDeleted)) + assertSync(t, new(UserDeleted2)) - var user = UserDeleted{ + var user = UserDeleted2{ Id: "lunny", } @@ -325,7 +325,7 @@ func TestTimeUserDeletedDiffLoc(t *testing.T) { assert.EqualValues(t, 1, cnt) fmt.Println("user", user.CreatedAt, user.UpdatedAt, user.DeletedAt) - var user2 UserDeleted + var user2 UserDeleted2 has, err := testEngine.Get(&user2) assert.NoError(t, err) assert.True(t, has) @@ -336,13 +336,13 @@ func TestTimeUserDeletedDiffLoc(t *testing.T) { assert.True(t, isTimeZero(user2.DeletedAt)) fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) - var user3 UserDeleted + var user3 UserDeleted2 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 + var user4 UserDeleted2 has, err = testEngine.Unscoped().Get(&user4) assert.NoError(t, err) assert.True(t, has) @@ -378,16 +378,16 @@ func (j *JsonDate) Unix() int64 { func TestCustomTimeUserDeleted(t *testing.T) { assert.NoError(t, prepareEngine()) - type UserDeleted struct { + type UserDeleted3 struct { Id string CreatedAt JsonDate `xorm:"created"` UpdatedAt JsonDate `xorm:"updated"` DeletedAt JsonDate `xorm:"deleted"` } - assertSync(t, new(UserDeleted)) + assertSync(t, new(UserDeleted3)) - var user = UserDeleted{ + var user = UserDeleted3{ Id: "lunny", } @@ -396,7 +396,7 @@ func TestCustomTimeUserDeleted(t *testing.T) { assert.EqualValues(t, 1, cnt) fmt.Println("user", user.CreatedAt, user.UpdatedAt, user.DeletedAt) - var user2 UserDeleted + var user2 UserDeleted3 has, err := testEngine.Get(&user2) assert.NoError(t, err) assert.True(t, has) @@ -407,13 +407,13 @@ func TestCustomTimeUserDeleted(t *testing.T) { assert.True(t, isTimeZero(time.Time(user2.DeletedAt))) fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) - var user3 UserDeleted + var user3 UserDeleted3 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 + var user4 UserDeleted3 has, err = testEngine.Unscoped().Get(&user4) assert.NoError(t, err) assert.True(t, has) @@ -431,16 +431,16 @@ func TestCustomTimeUserDeletedDiffLoc(t *testing.T) { assert.NoError(t, err) testEngine.DatabaseTZ = dbLoc - type UserDeleted struct { + type UserDeleted4 struct { Id string CreatedAt JsonDate `xorm:"created"` UpdatedAt JsonDate `xorm:"updated"` DeletedAt JsonDate `xorm:"deleted"` } - assertSync(t, new(UserDeleted)) + assertSync(t, new(UserDeleted4)) - var user = UserDeleted{ + var user = UserDeleted4{ Id: "lunny", } @@ -449,7 +449,7 @@ func TestCustomTimeUserDeletedDiffLoc(t *testing.T) { assert.EqualValues(t, 1, cnt) fmt.Println("user", user.CreatedAt, user.UpdatedAt, user.DeletedAt) - var user2 UserDeleted + var user2 UserDeleted4 has, err := testEngine.Get(&user2) assert.NoError(t, err) assert.True(t, has) @@ -460,13 +460,13 @@ func TestCustomTimeUserDeletedDiffLoc(t *testing.T) { assert.True(t, isTimeZero(time.Time(user2.DeletedAt))) fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) - var user3 UserDeleted + var user3 UserDeleted4 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 + var user4 UserDeleted4 has, err = testEngine.Unscoped().Get(&user4) assert.NoError(t, err) assert.True(t, has) diff --git a/xorm_test.go b/xorm_test.go index e7deec11..c4b029f7 100644 --- a/xorm_test.go +++ b/xorm_test.go @@ -37,6 +37,21 @@ func createEngine(dbType, connStr string) error { testEngine.ShowSQL(*showSQL) testEngine.logger.SetLevel(core.LOG_DEBUG) + if *cache { + cacher := NewLRUCacher(NewMemoryStore(), 100000) + testEngine.SetDefaultCacher(cacher) + } + + if len(*mapType) > 0 { + switch *mapType { + case "snake": + testEngine.SetMapper(core.SnakeMapper{}) + case "same": + testEngine.SetMapper(core.SameMapper{}) + case "gonic": + testEngine.SetMapper(core.LintGonicMapper) + } + } } tables, err := testEngine.DBMetas()