├── .circleci └── config.yml ├── .drone.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── README_CN.md ├── cache_lru.go ├── cache_lru_test.go ├── cache_memory_store.go ├── cache_memory_store_test.go ├── cache_test.go ├── context_cache.go ├── convert.go ├── dialect_mssql.go ├── dialect_mssql_test.go ├── dialect_mysql.go ├── dialect_oracle.go ├── dialect_postgres.go ├── dialect_postgres_test.go ├── dialect_sqlite3.go ├── dialect_sqlite3_test.go ├── doc.go ├── docs └── images │ ├── cache_design.graffle │ └── cache_design.png ├── engine.go ├── engine_cond.go ├── engine_context.go ├── engine_context_test.go ├── engine_group.go ├── engine_group_policy.go ├── engine_table.go ├── engine_table_test.go ├── error.go ├── examples ├── README.md ├── cache.go ├── cachegoroutine.go ├── conversion.go ├── derive.go ├── find.go ├── goroutine.go ├── maxconnect.go ├── singlemapping.go ├── sync.go └── tables.go ├── gen_reserved.sh ├── go.mod ├── go.sum ├── helpers.go ├── helpers_test.go ├── helpler_time.go ├── interface.go ├── json.go ├── logger.go ├── migrate ├── migrate.go └── migrate_test.go ├── pg_reserved.txt ├── processors.go ├── processors_test.go ├── rows.go ├── rows_test.go ├── session.go ├── session_cols.go ├── session_cols_test.go ├── session_cond.go ├── session_cond_test.go ├── session_context.go ├── session_context_test.go ├── session_convert.go ├── session_delete.go ├── session_delete_test.go ├── session_exist.go ├── session_exist_test.go ├── session_find.go ├── session_find_test.go ├── session_get.go ├── session_get_test.go ├── session_insert.go ├── session_insert_test.go ├── session_iterate.go ├── session_iterate_test.go ├── session_pk_test.go ├── session_query.go ├── session_query_test.go ├── session_raw.go ├── session_raw_test.go ├── session_schema.go ├── session_schema_test.go ├── session_stats.go ├── session_stats_test.go ├── session_test.go ├── session_tx.go ├── session_tx_test.go ├── session_update.go ├── session_update_test.go ├── statement.go ├── statement_args.go ├── statement_columnmap.go ├── statement_exprparam.go ├── statement_quote.go ├── statement_test.go ├── syslogger.go ├── tag.go ├── tag_cache_test.go ├── tag_extends_test.go ├── tag_id_test.go ├── tag_test.go ├── tag_version_test.go ├── test_mssql.sh ├── test_mssql_cache.sh ├── test_mymysql.sh ├── test_mymysql_cache.sh ├── test_mysql.sh ├── test_mysql_cache.sh ├── test_postgres.sh ├── test_postgres_cache.sh ├── test_sqlite.sh ├── test_sqlite_cache.sh ├── test_tidb.sh ├── time_test.go ├── transaction.go ├── transancation_test.go ├── types.go ├── types_null_test.go ├── types_test.go ├── xorm.go └── xorm_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-go/ for more details 4 | version: 2 5 | jobs: 6 | build: 7 | docker: 8 | # specify the version 9 | - image: circleci/golang:1.10 10 | 11 | - image: circleci/mysql:5.7 12 | environment: 13 | MYSQL_ALLOW_EMPTY_PASSWORD: true 14 | MYSQL_DATABASE: xorm_test 15 | MYSQL_HOST: 127.0.0.1 16 | MYSQL_ROOT_HOST: '%' 17 | MYSQL_USER: root 18 | 19 | # CircleCI PostgreSQL images available at: https://hub.docker.com/r/circleci/postgres/ 20 | - image: circleci/postgres:9.6.2-alpine 21 | environment: 22 | POSTGRES_USER: circleci 23 | POSTGRES_DB: xorm_test 24 | 25 | - image: microsoft/mssql-server-linux:latest 26 | environment: 27 | ACCEPT_EULA: Y 28 | SA_PASSWORD: yourStrong(!)Password 29 | MSSQL_PID: Developer 30 | 31 | - image: pingcap/tidb:v2.1.2 32 | 33 | working_directory: /go/src/github.com/go-xorm/xorm 34 | steps: 35 | - checkout 36 | 37 | - run: go get -t -d -v ./... 38 | - run: go get -u xorm.io/core 39 | - run: go get -u xorm.io/builder 40 | - run: GO111MODULE=off go build -v 41 | - run: GO111MODULE=on go build -v 42 | 43 | - run: go get -u github.com/wadey/gocovmerge 44 | 45 | - run: go test -v -race -db="sqlite3" -conn_str="./test.db" -coverprofile=coverage1-1.txt -covermode=atomic 46 | - run: go test -v -race -db="sqlite3" -conn_str="./test.db" -cache=true -coverprofile=coverage1-2.txt -covermode=atomic 47 | - run: go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -coverprofile=coverage2-1.txt -covermode=atomic 48 | - run: go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic 49 | - run: go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -coverprofile=coverage3-1.txt -covermode=atomic 50 | - run: go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic 51 | - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -coverprofile=coverage4-1.txt -covermode=atomic 52 | - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic 53 | - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic 54 | - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic 55 | - run: go test -v -race -db="mssql" -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" -coverprofile=coverage6-1.txt -covermode=atomic 56 | - run: go test -v -race -db="mssql" -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic 57 | - run: go test -v -race -db="mysql" -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic 58 | - run: go test -v -race -db="mysql" -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic 59 | - run: 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 coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt 60 | 61 | - run: bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | *.db 6 | 7 | # Folders 8 | _obj 9 | _test 10 | 11 | # Architecture specific extensions/prefixes 12 | *.[568vq] 13 | [568vq].out 14 | 15 | *.cgo1.go 16 | *.cgo2.c 17 | _cgo_defun.c 18 | _cgo_gotypes.go 19 | _cgo_export.* 20 | 21 | _testmain.go 22 | 23 | *.exe 24 | 25 | *.log 26 | .vendor 27 | temp_test.go 28 | .vscode 29 | xorm.test 30 | *.sqlite3 31 | test.db.sql 32 | 33 | .idea/ 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to xorm 2 | 3 | `xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very 4 | much welcome. You can help with patch review, submitting bug reports, 5 | or adding new functionality. There is no formal style guide, but 6 | please conform to the style of existing code and general Go formatting 7 | conventions when submitting patches. 8 | 9 | * [fork a repo](https://help.github.com/articles/fork-a-repo) 10 | * [creating a pull request ](https://help.github.com/articles/creating-a-pull-request) 11 | 12 | ### Language 13 | 14 | Since `xorm` is a world-wide open source project, please describe your issues or code changes in English as soon as possible. 15 | 16 | ### Sign your codes with comments 17 | ``` 18 | // !! your comments 19 | 20 | e.g., 21 | 22 | // !lunny! this is comments made by lunny 23 | ``` 24 | 25 | ### Patch review 26 | 27 | Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or 28 | proposed functionality. 29 | 30 | ### Bug reports 31 | 32 | We appreciate any bug reports, but especially ones with self-contained 33 | (doesn't depend on code outside of xorm), minimal (can't be simplified 34 | further) test cases. It's especially helpful if you can submit a pull 35 | request with just the failing test case(you can find some example test file like [session_get_test.go](https://github.com/go-xorm/xorm/blob/master/session_get_test.go)). 36 | 37 | If you implements a new database interface, you maybe need to add a test_.sh file. 38 | For example, [mysql_test.go](https://github.com/go-xorm/xorm/blob/master/test_mysql.sh) 39 | 40 | ### New functionality 41 | 42 | There are a number of pending patches for new functionality, so 43 | additional feature patches will take a while to merge. Still, patches 44 | are generally reviewed based on usefulness and complexity in addition 45 | to time-in-queue, so if you have a knockout idea, take a shot. Feel 46 | free to open an issue discussion your proposed patch beforehand. 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 - 2015 The Xorm Authors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /cache_lru_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "xorm.io/core" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestLRUCache(t *testing.T) { 15 | type CacheObject1 struct { 16 | Id int64 17 | } 18 | 19 | store := NewMemoryStore() 20 | cacher := NewLRUCacher(store, 10000) 21 | 22 | tableName := "cache_object1" 23 | pks := []core.PK{ 24 | {1}, 25 | {2}, 26 | } 27 | 28 | for _, pk := range pks { 29 | sid, err := pk.ToString() 30 | assert.NoError(t, err) 31 | 32 | cacher.PutIds(tableName, "select * from cache_object1", sid) 33 | ids := cacher.GetIds(tableName, "select * from cache_object1") 34 | assert.EqualValues(t, sid, ids) 35 | 36 | cacher.ClearIds(tableName) 37 | ids2 := cacher.GetIds(tableName, "select * from cache_object1") 38 | assert.Nil(t, ids2) 39 | 40 | obj2 := cacher.GetBean(tableName, sid) 41 | assert.Nil(t, obj2) 42 | 43 | var obj = new(CacheObject1) 44 | cacher.PutBean(tableName, sid, obj) 45 | obj3 := cacher.GetBean(tableName, sid) 46 | assert.EqualValues(t, obj, obj3) 47 | 48 | cacher.DelBean(tableName, sid) 49 | obj4 := cacher.GetBean(tableName, sid) 50 | assert.Nil(t, obj4) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /cache_memory_store.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "sync" 9 | 10 | "xorm.io/core" 11 | ) 12 | 13 | var _ core.CacheStore = NewMemoryStore() 14 | 15 | // MemoryStore represents in-memory store 16 | type MemoryStore struct { 17 | store map[interface{}]interface{} 18 | mutex sync.RWMutex 19 | } 20 | 21 | // NewMemoryStore creates a new store in memory 22 | func NewMemoryStore() *MemoryStore { 23 | return &MemoryStore{store: make(map[interface{}]interface{})} 24 | } 25 | 26 | // Put puts object into store 27 | func (s *MemoryStore) Put(key string, value interface{}) error { 28 | s.mutex.Lock() 29 | defer s.mutex.Unlock() 30 | s.store[key] = value 31 | return nil 32 | } 33 | 34 | // Get gets object from store 35 | func (s *MemoryStore) Get(key string) (interface{}, error) { 36 | s.mutex.RLock() 37 | defer s.mutex.RUnlock() 38 | if v, ok := s.store[key]; ok { 39 | return v, nil 40 | } 41 | 42 | return nil, ErrNotExist 43 | } 44 | 45 | // Del deletes object 46 | func (s *MemoryStore) Del(key string) error { 47 | s.mutex.Lock() 48 | defer s.mutex.Unlock() 49 | delete(s.store, key) 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /cache_memory_store_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestMemoryStore(t *testing.T) { 14 | store := NewMemoryStore() 15 | var kvs = map[string]interface{}{ 16 | "a": "b", 17 | } 18 | for k, v := range kvs { 19 | assert.NoError(t, store.Put(k, v)) 20 | } 21 | 22 | for k, v := range kvs { 23 | val, err := store.Get(k) 24 | assert.NoError(t, err) 25 | assert.EqualValues(t, v, val) 26 | } 27 | 28 | for k, _ := range kvs { 29 | err := store.Del(k) 30 | assert.NoError(t, err) 31 | } 32 | 33 | for k, _ := range kvs { 34 | _, err := store.Get(k) 35 | assert.EqualValues(t, ErrNotExist, err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cache_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCacheFind(t *testing.T) { 15 | assert.NoError(t, prepareEngine()) 16 | 17 | type MailBox struct { 18 | Id int64 `xorm:"pk"` 19 | Username string 20 | Password string 21 | } 22 | 23 | oldCacher := testEngine.GetDefaultCacher() 24 | cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) 25 | testEngine.SetDefaultCacher(cacher) 26 | 27 | assert.NoError(t, testEngine.Sync2(new(MailBox))) 28 | 29 | var inserts = []*MailBox{ 30 | { 31 | Id: 0, 32 | Username: "user1", 33 | Password: "pass1", 34 | }, 35 | { 36 | Id: 1, 37 | Username: "user2", 38 | Password: "pass2", 39 | }, 40 | } 41 | _, err := testEngine.Insert(inserts[0], inserts[1]) 42 | assert.NoError(t, err) 43 | 44 | var boxes []MailBox 45 | assert.NoError(t, testEngine.Find(&boxes)) 46 | assert.EqualValues(t, 2, len(boxes)) 47 | for i, box := range boxes { 48 | assert.Equal(t, inserts[i].Id, box.Id) 49 | assert.Equal(t, inserts[i].Username, box.Username) 50 | assert.Equal(t, inserts[i].Password, box.Password) 51 | } 52 | 53 | boxes = make([]MailBox, 0, 2) 54 | assert.NoError(t, testEngine.Find(&boxes)) 55 | assert.EqualValues(t, 2, len(boxes)) 56 | for i, box := range boxes { 57 | assert.Equal(t, inserts[i].Id, box.Id) 58 | assert.Equal(t, inserts[i].Username, box.Username) 59 | assert.Equal(t, inserts[i].Password, box.Password) 60 | } 61 | 62 | boxes = make([]MailBox, 0, 2) 63 | assert.NoError(t, testEngine.Alias("a").Where("a.id > -1").Asc("a.id").Find(&boxes)) 64 | assert.EqualValues(t, 2, len(boxes)) 65 | for i, box := range boxes { 66 | assert.Equal(t, inserts[i].Id, box.Id) 67 | assert.Equal(t, inserts[i].Username, box.Username) 68 | assert.Equal(t, inserts[i].Password, box.Password) 69 | } 70 | 71 | type MailBox4 struct { 72 | Id int64 73 | Username string 74 | Password string 75 | } 76 | 77 | boxes2 := make([]MailBox4, 0, 2) 78 | assert.NoError(t, testEngine.Table("mail_box").Where("mail_box.id > -1").Asc("mail_box.id").Find(&boxes2)) 79 | assert.EqualValues(t, 2, len(boxes2)) 80 | for i, box := range boxes2 { 81 | assert.Equal(t, inserts[i].Id, box.Id) 82 | assert.Equal(t, inserts[i].Username, box.Username) 83 | assert.Equal(t, inserts[i].Password, box.Password) 84 | } 85 | 86 | testEngine.SetDefaultCacher(oldCacher) 87 | } 88 | 89 | func TestCacheFind2(t *testing.T) { 90 | assert.NoError(t, prepareEngine()) 91 | 92 | type MailBox2 struct { 93 | Id uint64 `xorm:"pk"` 94 | Username string 95 | Password string 96 | } 97 | 98 | oldCacher := testEngine.GetDefaultCacher() 99 | cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) 100 | testEngine.SetDefaultCacher(cacher) 101 | 102 | assert.NoError(t, testEngine.Sync2(new(MailBox2))) 103 | 104 | var inserts = []*MailBox2{ 105 | { 106 | Id: 0, 107 | Username: "user1", 108 | Password: "pass1", 109 | }, 110 | { 111 | Id: 1, 112 | Username: "user2", 113 | Password: "pass2", 114 | }, 115 | } 116 | _, err := testEngine.Insert(inserts[0], inserts[1]) 117 | assert.NoError(t, err) 118 | 119 | var boxes []MailBox2 120 | assert.NoError(t, testEngine.Find(&boxes)) 121 | assert.EqualValues(t, 2, len(boxes)) 122 | for i, box := range boxes { 123 | assert.Equal(t, inserts[i].Id, box.Id) 124 | assert.Equal(t, inserts[i].Username, box.Username) 125 | assert.Equal(t, inserts[i].Password, box.Password) 126 | } 127 | 128 | boxes = make([]MailBox2, 0, 2) 129 | assert.NoError(t, testEngine.Find(&boxes)) 130 | assert.EqualValues(t, 2, len(boxes)) 131 | for i, box := range boxes { 132 | assert.Equal(t, inserts[i].Id, box.Id) 133 | assert.Equal(t, inserts[i].Username, box.Username) 134 | assert.Equal(t, inserts[i].Password, box.Password) 135 | } 136 | 137 | testEngine.SetDefaultCacher(oldCacher) 138 | } 139 | 140 | func TestCacheGet(t *testing.T) { 141 | assert.NoError(t, prepareEngine()) 142 | 143 | type MailBox3 struct { 144 | Id uint64 145 | Username string 146 | Password string 147 | } 148 | 149 | oldCacher := testEngine.GetDefaultCacher() 150 | cacher := NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) 151 | testEngine.SetDefaultCacher(cacher) 152 | 153 | assert.NoError(t, testEngine.Sync2(new(MailBox3))) 154 | 155 | var inserts = []*MailBox3{ 156 | { 157 | Username: "user1", 158 | Password: "pass1", 159 | }, 160 | } 161 | _, err := testEngine.Insert(inserts[0]) 162 | assert.NoError(t, err) 163 | 164 | var box1 MailBox3 165 | has, err := testEngine.Where("id = ?", inserts[0].Id).Get(&box1) 166 | assert.NoError(t, err) 167 | assert.True(t, has) 168 | assert.EqualValues(t, "user1", box1.Username) 169 | assert.EqualValues(t, "pass1", box1.Password) 170 | 171 | var box2 MailBox3 172 | has, err = testEngine.Where("id = ?", inserts[0].Id).Get(&box2) 173 | assert.NoError(t, err) 174 | assert.True(t, has) 175 | assert.EqualValues(t, "user1", box2.Username) 176 | assert.EqualValues(t, "pass1", box2.Password) 177 | 178 | testEngine.SetDefaultCacher(oldCacher) 179 | } 180 | -------------------------------------------------------------------------------- /context_cache.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | // ContextCache is the interface that operates the cache data. 8 | type ContextCache interface { 9 | // Put puts value into cache with key. 10 | Put(key string, val interface{}) 11 | // Get gets cached value by given key. 12 | Get(key string) interface{} 13 | } 14 | 15 | type memoryContextCache map[string]interface{} 16 | 17 | // NewMemoryContextCache return memoryContextCache 18 | func NewMemoryContextCache() memoryContextCache { 19 | return make(map[string]interface{}) 20 | } 21 | 22 | // Put puts value into cache with key. 23 | func (m memoryContextCache) Put(key string, val interface{}) { 24 | m[key] = val 25 | } 26 | 27 | // Get gets cached value by given key. 28 | func (m memoryContextCache) Get(key string) interface{} { 29 | return m[key] 30 | } 31 | -------------------------------------------------------------------------------- /dialect_mssql_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "reflect" 9 | "testing" 10 | 11 | "xorm.io/core" 12 | ) 13 | 14 | func TestParseMSSQL(t *testing.T) { 15 | tests := []struct { 16 | in string 17 | expected string 18 | valid bool 19 | }{ 20 | {"sqlserver://sa:yourStrong(!)Password@localhost:1433?database=db&connection+timeout=30", "db", true}, 21 | {"server=localhost;user id=sa;password=yourStrong(!)Password;database=db", "db", true}, 22 | } 23 | 24 | driver := core.QueryDriver("mssql") 25 | 26 | for _, test := range tests { 27 | uri, err := driver.Parse("mssql", test.in) 28 | 29 | if err != nil && test.valid { 30 | t.Errorf("%q got unexpected error: %s", test.in, err) 31 | } else if err == nil && !reflect.DeepEqual(test.expected, uri.DbName) { 32 | t.Errorf("%q got: %#v want: %#v", test.in, uri.DbName, test.expected) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dialect_postgres_test.go: -------------------------------------------------------------------------------- 1 | package xorm 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "xorm.io/core" 9 | ) 10 | 11 | func TestParsePostgres(t *testing.T) { 12 | tests := []struct { 13 | in string 14 | expected string 15 | valid bool 16 | }{ 17 | {"postgres://auser:password@localhost:5432/db?sslmode=disable", "db", true}, 18 | {"postgresql://auser:password@localhost:5432/db?sslmode=disable", "db", true}, 19 | {"postg://auser:password@localhost:5432/db?sslmode=disable", "db", false}, 20 | //{"postgres://auser:pass with space@localhost:5432/db?sslmode=disable", "db", true}, 21 | //{"postgres:// auser : password@localhost:5432/db?sslmode=disable", "db", true}, 22 | {"postgres://%20auser%20:pass%20with%20space@localhost:5432/db?sslmode=disable", "db", true}, 23 | //{"postgres://auser:パスワード@localhost:5432/データベース?sslmode=disable", "データベース", true}, 24 | {"dbname=db sslmode=disable", "db", true}, 25 | {"user=auser password=password dbname=db sslmode=disable", "db", true}, 26 | {"", "db", false}, 27 | {"dbname=db =disable", "db", false}, 28 | } 29 | 30 | driver := core.QueryDriver("postgres") 31 | 32 | for _, test := range tests { 33 | uri, err := driver.Parse("postgres", test.in) 34 | 35 | if err != nil && test.valid { 36 | t.Errorf("%q got unexpected error: %s", test.in, err) 37 | } else if err == nil && !reflect.DeepEqual(test.expected, uri.DbName) { 38 | t.Errorf("%q got: %#v want: %#v", test.in, uri.DbName, test.expected) 39 | } 40 | } 41 | } 42 | 43 | func TestParsePgx(t *testing.T) { 44 | tests := []struct { 45 | in string 46 | expected string 47 | valid bool 48 | }{ 49 | {"postgres://auser:password@localhost:5432/db?sslmode=disable", "db", true}, 50 | {"postgresql://auser:password@localhost:5432/db?sslmode=disable", "db", true}, 51 | {"postg://auser:password@localhost:5432/db?sslmode=disable", "db", false}, 52 | //{"postgres://auser:pass with space@localhost:5432/db?sslmode=disable", "db", true}, 53 | //{"postgres:// auser : password@localhost:5432/db?sslmode=disable", "db", true}, 54 | {"postgres://%20auser%20:pass%20with%20space@localhost:5432/db?sslmode=disable", "db", true}, 55 | //{"postgres://auser:パスワード@localhost:5432/データベース?sslmode=disable", "データベース", true}, 56 | {"dbname=db sslmode=disable", "db", true}, 57 | {"user=auser password=password dbname=db sslmode=disable", "db", true}, 58 | {"", "db", false}, 59 | {"dbname=db =disable", "db", false}, 60 | } 61 | 62 | driver := core.QueryDriver("pgx") 63 | 64 | for _, test := range tests { 65 | uri, err := driver.Parse("pgx", test.in) 66 | 67 | if err != nil && test.valid { 68 | t.Errorf("%q got unexpected error: %s", test.in, err) 69 | } else if err == nil && !reflect.DeepEqual(test.expected, uri.DbName) { 70 | t.Errorf("%q got: %#v want: %#v", test.in, uri.DbName, test.expected) 71 | } 72 | 73 | // Register DriverConfig 74 | uri, err = driver.Parse("pgx", test.in) 75 | if err != nil && test.valid { 76 | t.Errorf("%q got unexpected error: %s", test.in, err) 77 | } else if err == nil && !reflect.DeepEqual(test.expected, uri.DbName) { 78 | t.Errorf("%q got: %#v want: %#v", test.in, uri.DbName, test.expected) 79 | } 80 | 81 | } 82 | 83 | } 84 | 85 | func TestGetIndexColName(t *testing.T) { 86 | t.Run("Index", func(t *testing.T) { 87 | s := "CREATE INDEX test2_mm_idx ON test2 (major);" 88 | colNames := getIndexColName(s) 89 | assert.Equal(t, []string{"major"}, colNames) 90 | }) 91 | 92 | t.Run("Multicolumn indexes", func(t *testing.T) { 93 | s := "CREATE INDEX test2_mm_idx ON test2 (major, minor);" 94 | colNames := getIndexColName(s) 95 | assert.Equal(t, []string{"major", "minor"}, colNames) 96 | }) 97 | 98 | t.Run("Indexes and ORDER BY", func(t *testing.T) { 99 | s := "CREATE INDEX test2_mm_idx ON test2 (major NULLS FIRST, minor DESC NULLS LAST);" 100 | colNames := getIndexColName(s) 101 | assert.Equal(t, []string{"major", "minor"}, colNames) 102 | }) 103 | 104 | t.Run("Combining Multiple Indexes", func(t *testing.T) { 105 | s := "CREATE INDEX test2_mm_cm_idx ON public.test2 USING btree (major, minor) WHERE ((major <> 5) AND (minor <> 6))" 106 | colNames := getIndexColName(s) 107 | assert.Equal(t, []string{"major", "minor"}, colNames) 108 | }) 109 | 110 | t.Run("unique", func(t *testing.T) { 111 | s := "CREATE UNIQUE INDEX test2_mm_uidx ON test2 (major);" 112 | colNames := getIndexColName(s) 113 | assert.Equal(t, []string{"major"}, colNames) 114 | }) 115 | 116 | t.Run("Indexes on Expressions", func(t *testing.T) {}) 117 | } 118 | -------------------------------------------------------------------------------- /dialect_sqlite3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestSplitColStr(t *testing.T) { 14 | var kases = []struct { 15 | colStr string 16 | fields []string 17 | }{ 18 | { 19 | colStr: "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL", 20 | fields: []string{ 21 | "`id`", "INTEGER", "PRIMARY", "KEY", "AUTOINCREMENT", "NOT", "NULL", 22 | }, 23 | }, 24 | { 25 | colStr: "`created` DATETIME DEFAULT '2006-01-02 15:04:05' NULL", 26 | fields: []string{ 27 | "`created`", "DATETIME", "DEFAULT", "'2006-01-02 15:04:05'", "NULL", 28 | }, 29 | }, 30 | } 31 | 32 | for _, kase := range kases { 33 | assert.EqualValues(t, kase.fields, splitColStr(kase.colStr)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 - 2016 The XORM Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | 7 | Package xorm is a simple and powerful ORM for Go. 8 | 9 | Installation 10 | 11 | Make sure you have installed Go 1.6+ and then: 12 | 13 | go get github.com/go-xorm/xorm 14 | 15 | Create Engine 16 | 17 | Firstly, we should new an engine for a database 18 | 19 | engine, err := xorm.NewEngine(driverName, dataSourceName) 20 | 21 | Method NewEngine's parameters is the same as sql.Open. It depends 22 | drivers' implementation. 23 | Generally, one engine for an application is enough. You can set it as package variable. 24 | 25 | Raw Methods 26 | 27 | XORM also support raw SQL execution: 28 | 29 | 1. query a SQL string, the returned results is []map[string][]byte 30 | 31 | results, err := engine.Query("select * from user") 32 | 33 | 2. execute a SQL string, the returned results 34 | 35 | affected, err := engine.Exec("update user set .... where ...") 36 | 37 | ORM Methods 38 | 39 | There are 8 major ORM methods and many helpful methods to use to operate database. 40 | 41 | 1. Insert one or multiple records to database 42 | 43 | affected, err := engine.Insert(&struct) 44 | // INSERT INTO struct () values () 45 | affected, err := engine.Insert(&struct1, &struct2) 46 | // INSERT INTO struct1 () values () 47 | // INSERT INTO struct2 () values () 48 | affected, err := engine.Insert(&sliceOfStruct) 49 | // INSERT INTO struct () values (),(),() 50 | affected, err := engine.Insert(&struct1, &sliceOfStruct2) 51 | // INSERT INTO struct1 () values () 52 | // INSERT INTO struct2 () values (),(),() 53 | 54 | 2. Query one record or one variable from database 55 | 56 | has, err := engine.Get(&user) 57 | // SELECT * FROM user LIMIT 1 58 | 59 | var id int64 60 | has, err := engine.Table("user").Where("name = ?", name).Get(&id) 61 | // SELECT id FROM user WHERE name = ? LIMIT 1 62 | 63 | 3. Query multiple records from database 64 | 65 | var sliceOfStructs []Struct 66 | err := engine.Find(&sliceOfStructs) 67 | // SELECT * FROM user 68 | 69 | var mapOfStructs = make(map[int64]Struct) 70 | err := engine.Find(&mapOfStructs) 71 | // SELECT * FROM user 72 | 73 | var int64s []int64 74 | err := engine.Table("user").Cols("id").Find(&int64s) 75 | // SELECT id FROM user 76 | 77 | 4. Query multiple records and record by record handle, there two methods, one is Iterate, 78 | another is Rows 79 | 80 | err := engine.Iterate(...) 81 | // SELECT * FROM user 82 | 83 | rows, err := engine.Rows(...) 84 | // SELECT * FROM user 85 | defer rows.Close() 86 | bean := new(Struct) 87 | for rows.Next() { 88 | err = rows.Scan(bean) 89 | } 90 | 91 | 5. Update one or more records 92 | 93 | affected, err := engine.ID(...).Update(&user) 94 | // UPDATE user SET ... 95 | 96 | 6. Delete one or more records, Delete MUST has condition 97 | 98 | affected, err := engine.Where(...).Delete(&user) 99 | // DELETE FROM user Where ... 100 | 101 | 7. Count records 102 | 103 | counts, err := engine.Count(&user) 104 | // SELECT count(*) AS total FROM user 105 | 106 | counts, err := engine.SQL("select count(*) FROM user").Count() 107 | // select count(*) FROM user 108 | 109 | 8. Sum records 110 | 111 | sumFloat64, err := engine.Sum(&user, "id") 112 | // SELECT sum(id) from user 113 | 114 | sumFloat64s, err := engine.Sums(&user, "id1", "id2") 115 | // SELECT sum(id1), sum(id2) from user 116 | 117 | sumInt64s, err := engine.SumsInt(&user, "id1", "id2") 118 | // SELECT sum(id1), sum(id2) from user 119 | 120 | Conditions 121 | 122 | The above 8 methods could use with condition methods chainable. 123 | Attention: the above 8 methods should be the last chainable method. 124 | 125 | 1. ID, In 126 | 127 | engine.ID(1).Get(&user) // for single primary key 128 | // SELECT * FROM user WHERE id = 1 129 | engine.ID(core.PK{1, 2}).Get(&user) // for composite primary keys 130 | // SELECT * FROM user WHERE id1 = 1 AND id2 = 2 131 | engine.In("id", 1, 2, 3).Find(&users) 132 | // SELECT * FROM user WHERE id IN (1, 2, 3) 133 | engine.In("id", []int{1, 2, 3}).Find(&users) 134 | // SELECT * FROM user WHERE id IN (1, 2, 3) 135 | 136 | 2. Where, And, Or 137 | 138 | engine.Where().And().Or().Find() 139 | // SELECT * FROM user WHERE (.. AND ..) OR ... 140 | 141 | 3. OrderBy, Asc, Desc 142 | 143 | engine.Asc().Desc().Find() 144 | // SELECT * FROM user ORDER BY .. ASC, .. DESC 145 | engine.OrderBy().Find() 146 | // SELECT * FROM user ORDER BY .. 147 | 148 | 4. Limit, Top 149 | 150 | engine.Limit().Find() 151 | // SELECT * FROM user LIMIT .. OFFSET .. 152 | engine.Top(5).Find() 153 | // SELECT TOP 5 * FROM user // for mssql 154 | // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases 155 | 156 | 5. SQL, let you custom SQL 157 | 158 | var users []User 159 | engine.SQL("select * from user").Find(&users) 160 | 161 | 6. Cols, Omit, Distinct 162 | 163 | var users []*User 164 | engine.Cols("col1, col2").Find(&users) 165 | // SELECT col1, col2 FROM user 166 | engine.Cols("col1", "col2").Where().Update(user) 167 | // UPDATE user set col1 = ?, col2 = ? Where ... 168 | engine.Omit("col1").Find(&users) 169 | // SELECT col2, col3 FROM user 170 | engine.Omit("col1").Insert(&user) 171 | // INSERT INTO table (non-col1) VALUES () 172 | engine.Distinct("col1").Find(&users) 173 | // SELECT DISTINCT col1 FROM user 174 | 175 | 7. Join, GroupBy, Having 176 | 177 | engine.GroupBy("name").Having("name='xlw'").Find(&users) 178 | //SELECT * FROM user GROUP BY name HAVING name='xlw' 179 | engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users) 180 | //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id 181 | 182 | More usage, please visit http://xorm.io/docs 183 | */ 184 | package xorm 185 | -------------------------------------------------------------------------------- /docs/images/cache_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/go-xorm/xorm/f39e5d9bfdb70c435f8649555f224c0fa4f51e54/docs/images/cache_design.png -------------------------------------------------------------------------------- /engine_cond.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "database/sql/driver" 9 | "fmt" 10 | "reflect" 11 | "strings" 12 | "time" 13 | 14 | "xorm.io/builder" 15 | "xorm.io/core" 16 | ) 17 | 18 | func (engine *Engine) buildConds(table *core.Table, bean interface{}, 19 | includeVersion bool, includeUpdated bool, includeNil bool, 20 | includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool, 21 | mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) { 22 | var conds []builder.Cond 23 | for _, col := range table.Columns() { 24 | if !includeVersion && col.IsVersion { 25 | continue 26 | } 27 | if !includeUpdated && col.IsUpdated { 28 | continue 29 | } 30 | if !includeAutoIncr && col.IsAutoIncrement { 31 | continue 32 | } 33 | 34 | if engine.dialect.DBType() == core.MSSQL && (col.SQLType.Name == core.Text || col.SQLType.IsBlob() || col.SQLType.Name == core.TimeStampz) { 35 | continue 36 | } 37 | if col.SQLType.IsJson() { 38 | continue 39 | } 40 | 41 | var colName string 42 | if addedTableName { 43 | var nm = tableName 44 | if len(aliasName) > 0 { 45 | nm = aliasName 46 | } 47 | colName = engine.Quote(nm) + "." + engine.Quote(col.Name) 48 | } else { 49 | colName = engine.Quote(col.Name) 50 | } 51 | 52 | fieldValuePtr, err := col.ValueOf(bean) 53 | if err != nil { 54 | if !strings.Contains(err.Error(), "is not valid") { 55 | engine.logger.Warn(err) 56 | } 57 | continue 58 | } 59 | 60 | if col.IsDeleted && !unscoped { // tag "deleted" is enabled 61 | conds = append(conds, engine.CondDeleted(colName)) 62 | } 63 | 64 | fieldValue := *fieldValuePtr 65 | if fieldValue.Interface() == nil { 66 | continue 67 | } 68 | 69 | fieldType := reflect.TypeOf(fieldValue.Interface()) 70 | requiredField := useAllCols 71 | 72 | if b, ok := getFlagForColumn(mustColumnMap, col); ok { 73 | if b { 74 | requiredField = true 75 | } else { 76 | continue 77 | } 78 | } 79 | 80 | if fieldType.Kind() == reflect.Ptr { 81 | if fieldValue.IsNil() { 82 | if includeNil { 83 | conds = append(conds, builder.Eq{colName: nil}) 84 | } 85 | continue 86 | } else if !fieldValue.IsValid() { 87 | continue 88 | } else { 89 | // dereference ptr type to instance type 90 | fieldValue = fieldValue.Elem() 91 | fieldType = reflect.TypeOf(fieldValue.Interface()) 92 | requiredField = true 93 | } 94 | } 95 | 96 | var val interface{} 97 | switch fieldType.Kind() { 98 | case reflect.Bool: 99 | if allUseBool || requiredField { 100 | val = fieldValue.Interface() 101 | } else { 102 | // if a bool in a struct, it will not be as a condition because it default is false, 103 | // please use Where() instead 104 | continue 105 | } 106 | case reflect.String: 107 | if !requiredField && fieldValue.String() == "" { 108 | continue 109 | } 110 | // for MyString, should convert to string or panic 111 | if fieldType.String() != reflect.String.String() { 112 | val = fieldValue.String() 113 | } else { 114 | val = fieldValue.Interface() 115 | } 116 | case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64: 117 | if !requiredField && fieldValue.Int() == 0 { 118 | continue 119 | } 120 | val = fieldValue.Interface() 121 | case reflect.Float32, reflect.Float64: 122 | if !requiredField && fieldValue.Float() == 0.0 { 123 | continue 124 | } 125 | val = fieldValue.Interface() 126 | case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64: 127 | if !requiredField && fieldValue.Uint() == 0 { 128 | continue 129 | } 130 | t := int64(fieldValue.Uint()) 131 | val = reflect.ValueOf(&t).Interface() 132 | case reflect.Struct: 133 | if fieldType.ConvertibleTo(core.TimeType) { 134 | t := fieldValue.Convert(core.TimeType).Interface().(time.Time) 135 | if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { 136 | continue 137 | } 138 | val = engine.formatColTime(col, t) 139 | } else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok { 140 | continue 141 | } else if valNul, ok := fieldValue.Interface().(driver.Valuer); ok { 142 | val, _ = valNul.Value() 143 | if val == nil { 144 | continue 145 | } 146 | } else { 147 | if col.SQLType.IsJson() { 148 | if col.SQLType.IsText() { 149 | bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) 150 | if err != nil { 151 | engine.logger.Error(err) 152 | continue 153 | } 154 | val = string(bytes) 155 | } else if col.SQLType.IsBlob() { 156 | var bytes []byte 157 | var err error 158 | bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) 159 | if err != nil { 160 | engine.logger.Error(err) 161 | continue 162 | } 163 | val = bytes 164 | } 165 | } else { 166 | engine.autoMapType(fieldValue) 167 | if table, ok := engine.Tables[fieldValue.Type()]; ok { 168 | if len(table.PrimaryKeys) == 1 { 169 | pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) 170 | // fix non-int pk issues 171 | //if pkField.Int() != 0 { 172 | if pkField.IsValid() && !isZero(pkField.Interface()) { 173 | val = pkField.Interface() 174 | } else { 175 | continue 176 | } 177 | } else { 178 | //TODO: how to handler? 179 | return nil, fmt.Errorf("not supported %v as %v", fieldValue.Interface(), table.PrimaryKeys) 180 | } 181 | } else { 182 | val = fieldValue.Interface() 183 | } 184 | } 185 | } 186 | case reflect.Array: 187 | continue 188 | case reflect.Slice, reflect.Map: 189 | if fieldValue == reflect.Zero(fieldType) { 190 | continue 191 | } 192 | if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { 193 | continue 194 | } 195 | 196 | if col.SQLType.IsText() { 197 | bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) 198 | if err != nil { 199 | engine.logger.Error(err) 200 | continue 201 | } 202 | val = string(bytes) 203 | } else if col.SQLType.IsBlob() { 204 | var bytes []byte 205 | var err error 206 | if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) && 207 | fieldType.Elem().Kind() == reflect.Uint8 { 208 | if fieldValue.Len() > 0 { 209 | val = fieldValue.Bytes() 210 | } else { 211 | continue 212 | } 213 | } else { 214 | bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) 215 | if err != nil { 216 | engine.logger.Error(err) 217 | continue 218 | } 219 | val = bytes 220 | } 221 | } else { 222 | continue 223 | } 224 | default: 225 | val = fieldValue.Interface() 226 | } 227 | 228 | conds = append(conds, builder.Eq{colName: val}) 229 | } 230 | 231 | return builder.And(conds...), nil 232 | } 233 | -------------------------------------------------------------------------------- /engine_context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.8 6 | 7 | package xorm 8 | 9 | import "context" 10 | 11 | // Context creates a session with the context 12 | func (engine *Engine) Context(ctx context.Context) *Session { 13 | session := engine.NewSession() 14 | session.isAutoClose = true 15 | return session.Context(ctx) 16 | } 17 | 18 | // SetDefaultContext set the default context 19 | func (engine *Engine) SetDefaultContext(ctx context.Context) { 20 | engine.defaultContext = ctx 21 | } 22 | 23 | // PingContext tests if database is alive 24 | func (engine *Engine) PingContext(ctx context.Context) error { 25 | session := engine.NewSession() 26 | defer session.Close() 27 | return session.PingContext(ctx) 28 | } 29 | -------------------------------------------------------------------------------- /engine_context_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.8 6 | 7 | package xorm 8 | 9 | import ( 10 | "context" 11 | "testing" 12 | "time" 13 | 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestPingContext(t *testing.T) { 18 | assert.NoError(t, prepareEngine()) 19 | 20 | ctx, canceled := context.WithTimeout(context.Background(), time.Nanosecond) 21 | defer canceled() 22 | 23 | time.Sleep(time.Nanosecond) 24 | 25 | err := testEngine.(*Engine).PingContext(ctx) 26 | assert.Error(t, err) 27 | assert.Contains(t, err.Error(), "context deadline exceeded") 28 | } 29 | -------------------------------------------------------------------------------- /engine_group.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "context" 9 | "time" 10 | 11 | "xorm.io/core" 12 | ) 13 | 14 | // EngineGroup defines an engine group 15 | type EngineGroup struct { 16 | *Engine 17 | slaves []*Engine 18 | policy GroupPolicy 19 | } 20 | 21 | // NewEngineGroup creates a new engine group 22 | func NewEngineGroup(args1 interface{}, args2 interface{}, policies ...GroupPolicy) (*EngineGroup, error) { 23 | var eg EngineGroup 24 | if len(policies) > 0 { 25 | eg.policy = policies[0] 26 | } else { 27 | eg.policy = RoundRobinPolicy() 28 | } 29 | 30 | driverName, ok1 := args1.(string) 31 | conns, ok2 := args2.([]string) 32 | if ok1 && ok2 { 33 | engines := make([]*Engine, len(conns)) 34 | for i, conn := range conns { 35 | engine, err := NewEngine(driverName, conn) 36 | if err != nil { 37 | return nil, err 38 | } 39 | engine.engineGroup = &eg 40 | engines[i] = engine 41 | } 42 | 43 | eg.Engine = engines[0] 44 | eg.slaves = engines[1:] 45 | return &eg, nil 46 | } 47 | 48 | master, ok3 := args1.(*Engine) 49 | slaves, ok4 := args2.([]*Engine) 50 | if ok3 && ok4 { 51 | master.engineGroup = &eg 52 | for i := 0; i < len(slaves); i++ { 53 | slaves[i].engineGroup = &eg 54 | } 55 | eg.Engine = master 56 | eg.slaves = slaves 57 | return &eg, nil 58 | } 59 | return nil, ErrParamsType 60 | } 61 | 62 | // Close the engine 63 | func (eg *EngineGroup) Close() error { 64 | err := eg.Engine.Close() 65 | if err != nil { 66 | return err 67 | } 68 | 69 | for i := 0; i < len(eg.slaves); i++ { 70 | err := eg.slaves[i].Close() 71 | if err != nil { 72 | return err 73 | } 74 | } 75 | return nil 76 | } 77 | 78 | // Context returned a group session 79 | func (eg *EngineGroup) Context(ctx context.Context) *Session { 80 | sess := eg.NewSession() 81 | sess.isAutoClose = true 82 | return sess.Context(ctx) 83 | } 84 | 85 | // NewSession returned a group session 86 | func (eg *EngineGroup) NewSession() *Session { 87 | sess := eg.Engine.NewSession() 88 | sess.sessionType = groupSession 89 | return sess 90 | } 91 | 92 | // Master returns the master engine 93 | func (eg *EngineGroup) Master() *Engine { 94 | return eg.Engine 95 | } 96 | 97 | // Ping tests if database is alive 98 | func (eg *EngineGroup) Ping() error { 99 | if err := eg.Engine.Ping(); err != nil { 100 | return err 101 | } 102 | 103 | for _, slave := range eg.slaves { 104 | if err := slave.Ping(); err != nil { 105 | return err 106 | } 107 | } 108 | return nil 109 | } 110 | 111 | // SetColumnMapper set the column name mapping rule 112 | func (eg *EngineGroup) SetColumnMapper(mapper core.IMapper) { 113 | eg.Engine.ColumnMapper = mapper 114 | for i := 0; i < len(eg.slaves); i++ { 115 | eg.slaves[i].ColumnMapper = mapper 116 | } 117 | } 118 | 119 | // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. 120 | func (eg *EngineGroup) SetConnMaxLifetime(d time.Duration) { 121 | eg.Engine.SetConnMaxLifetime(d) 122 | for i := 0; i < len(eg.slaves); i++ { 123 | eg.slaves[i].SetConnMaxLifetime(d) 124 | } 125 | } 126 | 127 | // SetDefaultCacher set the default cacher 128 | func (eg *EngineGroup) SetDefaultCacher(cacher core.Cacher) { 129 | eg.Engine.SetDefaultCacher(cacher) 130 | for i := 0; i < len(eg.slaves); i++ { 131 | eg.slaves[i].SetDefaultCacher(cacher) 132 | } 133 | } 134 | 135 | // SetLogger set the new logger 136 | func (eg *EngineGroup) SetLogger(logger core.ILogger) { 137 | eg.Engine.SetLogger(logger) 138 | for i := 0; i < len(eg.slaves); i++ { 139 | eg.slaves[i].SetLogger(logger) 140 | } 141 | } 142 | 143 | // SetLogLevel sets the logger level 144 | func (eg *EngineGroup) SetLogLevel(level core.LogLevel) { 145 | eg.Engine.SetLogLevel(level) 146 | for i := 0; i < len(eg.slaves); i++ { 147 | eg.slaves[i].SetLogLevel(level) 148 | } 149 | } 150 | 151 | // SetMapper set the name mapping rules 152 | func (eg *EngineGroup) SetMapper(mapper core.IMapper) { 153 | eg.Engine.SetMapper(mapper) 154 | for i := 0; i < len(eg.slaves); i++ { 155 | eg.slaves[i].SetMapper(mapper) 156 | } 157 | } 158 | 159 | // SetMaxIdleConns set the max idle connections on pool, default is 2 160 | func (eg *EngineGroup) SetMaxIdleConns(conns int) { 161 | eg.Engine.db.SetMaxIdleConns(conns) 162 | for i := 0; i < len(eg.slaves); i++ { 163 | eg.slaves[i].db.SetMaxIdleConns(conns) 164 | } 165 | } 166 | 167 | // SetMaxOpenConns is only available for go 1.2+ 168 | func (eg *EngineGroup) SetMaxOpenConns(conns int) { 169 | eg.Engine.db.SetMaxOpenConns(conns) 170 | for i := 0; i < len(eg.slaves); i++ { 171 | eg.slaves[i].db.SetMaxOpenConns(conns) 172 | } 173 | } 174 | 175 | // SetPolicy set the group policy 176 | func (eg *EngineGroup) SetPolicy(policy GroupPolicy) *EngineGroup { 177 | eg.policy = policy 178 | return eg 179 | } 180 | 181 | // SetTableMapper set the table name mapping rule 182 | func (eg *EngineGroup) SetTableMapper(mapper core.IMapper) { 183 | eg.Engine.TableMapper = mapper 184 | for i := 0; i < len(eg.slaves); i++ { 185 | eg.slaves[i].TableMapper = mapper 186 | } 187 | } 188 | 189 | // ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO 190 | func (eg *EngineGroup) ShowExecTime(show ...bool) { 191 | eg.Engine.ShowExecTime(show...) 192 | for i := 0; i < len(eg.slaves); i++ { 193 | eg.slaves[i].ShowExecTime(show...) 194 | } 195 | } 196 | 197 | // ShowSQL show SQL statement or not on logger if log level is great than INFO 198 | func (eg *EngineGroup) ShowSQL(show ...bool) { 199 | eg.Engine.ShowSQL(show...) 200 | for i := 0; i < len(eg.slaves); i++ { 201 | eg.slaves[i].ShowSQL(show...) 202 | } 203 | } 204 | 205 | // Slave returns one of the physical databases which is a slave according the policy 206 | func (eg *EngineGroup) Slave() *Engine { 207 | switch len(eg.slaves) { 208 | case 0: 209 | return eg.Engine 210 | case 1: 211 | return eg.slaves[0] 212 | } 213 | return eg.policy.Slave(eg) 214 | } 215 | 216 | // Slaves returns all the slaves 217 | func (eg *EngineGroup) Slaves() []*Engine { 218 | return eg.slaves 219 | } 220 | -------------------------------------------------------------------------------- /engine_group_policy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "math/rand" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | // GroupPolicy is be used by chosing the current slave from slaves 14 | type GroupPolicy interface { 15 | Slave(*EngineGroup) *Engine 16 | } 17 | 18 | // GroupPolicyHandler should be used when a function is a GroupPolicy 19 | type GroupPolicyHandler func(*EngineGroup) *Engine 20 | 21 | // Slave implements the chosen of slaves 22 | func (h GroupPolicyHandler) Slave(eg *EngineGroup) *Engine { 23 | return h(eg) 24 | } 25 | 26 | // RandomPolicy implmentes randomly chose the slave of slaves 27 | func RandomPolicy() GroupPolicyHandler { 28 | var r = rand.New(rand.NewSource(time.Now().UnixNano())) 29 | return func(g *EngineGroup) *Engine { 30 | return g.Slaves()[r.Intn(len(g.Slaves()))] 31 | } 32 | } 33 | 34 | // WeightRandomPolicy implmentes randomly chose the slave of slaves 35 | func WeightRandomPolicy(weights []int) GroupPolicyHandler { 36 | var rands = make([]int, 0, len(weights)) 37 | for i := 0; i < len(weights); i++ { 38 | for n := 0; n < weights[i]; n++ { 39 | rands = append(rands, i) 40 | } 41 | } 42 | var r = rand.New(rand.NewSource(time.Now().UnixNano())) 43 | 44 | return func(g *EngineGroup) *Engine { 45 | var slaves = g.Slaves() 46 | idx := rands[r.Intn(len(rands))] 47 | if idx >= len(slaves) { 48 | idx = len(slaves) - 1 49 | } 50 | return slaves[idx] 51 | } 52 | } 53 | 54 | func RoundRobinPolicy() GroupPolicyHandler { 55 | var pos = -1 56 | var lock sync.Mutex 57 | return func(g *EngineGroup) *Engine { 58 | var slaves = g.Slaves() 59 | 60 | lock.Lock() 61 | defer lock.Unlock() 62 | pos++ 63 | if pos >= len(slaves) { 64 | pos = 0 65 | } 66 | 67 | return slaves[pos] 68 | } 69 | } 70 | 71 | func WeightRoundRobinPolicy(weights []int) GroupPolicyHandler { 72 | var rands = make([]int, 0, len(weights)) 73 | for i := 0; i < len(weights); i++ { 74 | for n := 0; n < weights[i]; n++ { 75 | rands = append(rands, i) 76 | } 77 | } 78 | var pos = -1 79 | var lock sync.Mutex 80 | 81 | return func(g *EngineGroup) *Engine { 82 | var slaves = g.Slaves() 83 | lock.Lock() 84 | defer lock.Unlock() 85 | pos++ 86 | if pos >= len(rands) { 87 | pos = 0 88 | } 89 | 90 | idx := rands[pos] 91 | if idx >= len(slaves) { 92 | idx = len(slaves) - 1 93 | } 94 | return slaves[idx] 95 | } 96 | } 97 | 98 | // LeastConnPolicy implements GroupPolicy, every time will get the least connections slave 99 | func LeastConnPolicy() GroupPolicyHandler { 100 | return func(g *EngineGroup) *Engine { 101 | var slaves = g.Slaves() 102 | connections := 0 103 | idx := 0 104 | for i := 0; i < len(slaves); i++ { 105 | openConnections := slaves[i].DB().Stats().OpenConnections 106 | if i == 0 { 107 | connections = openConnections 108 | idx = i 109 | } else if openConnections <= connections { 110 | connections = openConnections 111 | idx = i 112 | } 113 | } 114 | return slaves[idx] 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /engine_table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | "strings" 11 | 12 | "xorm.io/core" 13 | ) 14 | 15 | // tbNameWithSchema will automatically add schema prefix on table name 16 | func (engine *Engine) tbNameWithSchema(v string) string { 17 | // Add schema name as prefix of table name. 18 | // Only for postgres database. 19 | if engine.dialect.DBType() == core.POSTGRES && 20 | engine.dialect.URI().Schema != "" && 21 | engine.dialect.URI().Schema != postgresPublicSchema && 22 | strings.Index(v, ".") == -1 { 23 | return engine.dialect.URI().Schema + "." + v 24 | } 25 | return v 26 | } 27 | 28 | // TableName returns table name with schema prefix if has 29 | func (engine *Engine) TableName(bean interface{}, includeSchema ...bool) string { 30 | tbName := engine.tbNameNoSchema(bean) 31 | if len(includeSchema) > 0 && includeSchema[0] { 32 | tbName = engine.tbNameWithSchema(tbName) 33 | } 34 | 35 | return tbName 36 | } 37 | 38 | // tbName get some table's table name 39 | func (session *Session) tbNameNoSchema(table *core.Table) string { 40 | if len(session.statement.AltTableName) > 0 { 41 | return session.statement.AltTableName 42 | } 43 | 44 | return table.Name 45 | } 46 | 47 | func (engine *Engine) tbNameForMap(v reflect.Value) string { 48 | if v.Type().Implements(tpTableName) { 49 | return v.Interface().(TableName).TableName() 50 | } 51 | if v.Kind() == reflect.Ptr { 52 | v = v.Elem() 53 | if v.Type().Implements(tpTableName) { 54 | return v.Interface().(TableName).TableName() 55 | } 56 | } 57 | 58 | return engine.TableMapper.Obj2Table(v.Type().Name()) 59 | } 60 | 61 | func (engine *Engine) tbNameNoSchema(tablename interface{}) string { 62 | switch tablename.(type) { 63 | case []string: 64 | t := tablename.([]string) 65 | if len(t) > 1 { 66 | return fmt.Sprintf("%v AS %v", engine.Quote(t[0]), engine.Quote(t[1])) 67 | } else if len(t) == 1 { 68 | return engine.Quote(t[0]) 69 | } 70 | case []interface{}: 71 | t := tablename.([]interface{}) 72 | l := len(t) 73 | var table string 74 | if l > 0 { 75 | f := t[0] 76 | switch f.(type) { 77 | case string: 78 | table = f.(string) 79 | case TableName: 80 | table = f.(TableName).TableName() 81 | default: 82 | v := rValue(f) 83 | t := v.Type() 84 | if t.Kind() == reflect.Struct { 85 | table = engine.tbNameForMap(v) 86 | } else { 87 | table = engine.Quote(fmt.Sprintf("%v", f)) 88 | } 89 | } 90 | } 91 | if l > 1 { 92 | return fmt.Sprintf("%v AS %v", engine.Quote(table), 93 | engine.Quote(fmt.Sprintf("%v", t[1]))) 94 | } else if l == 1 { 95 | return engine.Quote(table) 96 | } 97 | case TableName: 98 | return tablename.(TableName).TableName() 99 | case string: 100 | return tablename.(string) 101 | case reflect.Value: 102 | v := tablename.(reflect.Value) 103 | return engine.tbNameForMap(v) 104 | default: 105 | v := rValue(tablename) 106 | t := v.Type() 107 | if t.Kind() == reflect.Struct { 108 | return engine.tbNameForMap(v) 109 | } 110 | return engine.Quote(fmt.Sprintf("%v", tablename)) 111 | } 112 | return "" 113 | } 114 | -------------------------------------------------------------------------------- /engine_table_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | type MCC struct { 14 | ID int64 `xorm:"pk 'id'"` 15 | Code string `xorm:"'code'"` 16 | Description string `xorm:"'description'"` 17 | } 18 | 19 | func (mcc *MCC) TableName() string { 20 | return "mcc" 21 | } 22 | 23 | func TestTableName1(t *testing.T) { 24 | assert.NoError(t, prepareEngine()) 25 | 26 | assert.EqualValues(t, "mcc", testEngine.TableName(new(MCC))) 27 | assert.EqualValues(t, "mcc", testEngine.TableName("mcc")) 28 | } 29 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | var ( 13 | // ErrParamsType params error 14 | ErrParamsType = errors.New("Params type error") 15 | // ErrTableNotFound table not found error 16 | ErrTableNotFound = errors.New("Table not found") 17 | // ErrUnSupportedType unsupported error 18 | ErrUnSupportedType = errors.New("Unsupported type error") 19 | // ErrNotExist record does not exist error 20 | ErrNotExist = errors.New("Record does not exist") 21 | // ErrCacheFailed cache failed error 22 | ErrCacheFailed = errors.New("Cache failed") 23 | // ErrNeedDeletedCond delete needs less one condition error 24 | ErrNeedDeletedCond = errors.New("Delete action needs at least one condition") 25 | // ErrNotImplemented not implemented 26 | ErrNotImplemented = errors.New("Not implemented") 27 | // ErrConditionType condition type unsupported 28 | ErrConditionType = errors.New("Unsupported condition type") 29 | // ErrUnSupportedSQLType parameter of SQL is not supported 30 | ErrUnSupportedSQLType = errors.New("unsupported sql type") 31 | ) 32 | 33 | // ErrFieldIsNotExist columns does not exist 34 | type ErrFieldIsNotExist struct { 35 | FieldName string 36 | TableName string 37 | } 38 | 39 | func (e ErrFieldIsNotExist) Error() string { 40 | return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName) 41 | } 42 | 43 | // ErrFieldIsNotValid is not valid 44 | type ErrFieldIsNotValid struct { 45 | FieldName string 46 | TableName string 47 | } 48 | 49 | func (e ErrFieldIsNotValid) Error() string { 50 | return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName) 51 | } 52 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Xorm Examples 2 | 3 | Notice: all the examples will ask you install extra package `github.com/mattn/go-sqlite3`, since it depends on cgo. You have to compile it after you install a c++ compile. Please see [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3). 4 | 5 | And then, you can run the examples via `go run xxx.go`. Every go file is a standalone example. 6 | -------------------------------------------------------------------------------- /examples/cache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/go-xorm/xorm" 8 | _ "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | // User describes a user 12 | type User struct { 13 | Id int64 14 | Name string 15 | } 16 | 17 | func main() { 18 | f := "cache.db" 19 | os.Remove(f) 20 | 21 | Orm, err := xorm.NewEngine("sqlite3", f) 22 | if err != nil { 23 | fmt.Println(err) 24 | return 25 | } 26 | Orm.ShowSQL(true) 27 | cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) 28 | Orm.SetDefaultCacher(cacher) 29 | 30 | err = Orm.CreateTables(&User{}) 31 | if err != nil { 32 | fmt.Println(err) 33 | return 34 | } 35 | 36 | _, err = Orm.Insert(&User{Name: "xlw"}) 37 | if err != nil { 38 | fmt.Println(err) 39 | return 40 | } 41 | 42 | var users []User 43 | err = Orm.Find(&users) 44 | if err != nil { 45 | fmt.Println(err) 46 | return 47 | } 48 | 49 | fmt.Println("users:", users) 50 | 51 | var users2 []User 52 | err = Orm.Find(&users2) 53 | if err != nil { 54 | fmt.Println(err) 55 | return 56 | } 57 | 58 | fmt.Println("users2:", users2) 59 | 60 | var users3 []User 61 | err = Orm.Find(&users3) 62 | if err != nil { 63 | fmt.Println(err) 64 | return 65 | } 66 | 67 | fmt.Println("users3:", users3) 68 | 69 | user4 := new(User) 70 | has, err := Orm.ID(1).Get(user4) 71 | if err != nil { 72 | fmt.Println(err) 73 | return 74 | } 75 | 76 | fmt.Println("user4:", has, user4) 77 | 78 | user4.Name = "xiaolunwen" 79 | _, err = Orm.ID(1).Update(user4) 80 | if err != nil { 81 | fmt.Println(err) 82 | return 83 | } 84 | fmt.Println("user4:", user4) 85 | 86 | user5 := new(User) 87 | has, err = Orm.ID(1).Get(user5) 88 | if err != nil { 89 | fmt.Println(err) 90 | return 91 | } 92 | fmt.Println("user5:", has, user5) 93 | 94 | _, err = Orm.ID(1).Delete(new(User)) 95 | if err != nil { 96 | fmt.Println(err) 97 | return 98 | } 99 | 100 | for { 101 | user6 := new(User) 102 | has, err = Orm.ID(1).Get(user6) 103 | if err != nil { 104 | fmt.Println(err) 105 | return 106 | } 107 | fmt.Println("user6:", has, user6) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/cachegoroutine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | _ "github.com/go-sql-driver/mysql" 9 | "github.com/go-xorm/xorm" 10 | _ "github.com/mattn/go-sqlite3" 11 | ) 12 | 13 | // User describes a user 14 | type User struct { 15 | Id int64 16 | Name string 17 | } 18 | 19 | func sqliteEngine() (*xorm.Engine, error) { 20 | os.Remove("./test.db") 21 | return xorm.NewEngine("sqlite3", "./goroutine.db") 22 | } 23 | 24 | func mysqlEngine() (*xorm.Engine, error) { 25 | return xorm.NewEngine("mysql", "root:@/test?charset=utf8") 26 | } 27 | 28 | var u = &User{} 29 | 30 | func test(engine *xorm.Engine) { 31 | err := engine.CreateTables(u) 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | size := 500 38 | queue := make(chan int, size) 39 | 40 | for i := 0; i < size; i++ { 41 | go func(x int) { 42 | //x := i 43 | err := engine.Ping() 44 | if err != nil { 45 | fmt.Println(err) 46 | } else { 47 | for j := 0; j < 10; j++ { 48 | if x+j < 2 { 49 | _, err = engine.Get(u) 50 | } else if x+j < 4 { 51 | users := make([]User, 0) 52 | err = engine.Find(&users) 53 | } else if x+j < 8 { 54 | _, err = engine.Count(u) 55 | } else if x+j < 16 { 56 | _, err = engine.Insert(&User{Name: "xlw"}) 57 | } else if x+j < 32 { 58 | //_, err = engine.ID(1).Delete(u) 59 | _, err = engine.Delete(u) 60 | } 61 | if err != nil { 62 | fmt.Println(err) 63 | queue <- x 64 | return 65 | } 66 | } 67 | fmt.Printf("%v success!\n", x) 68 | } 69 | queue <- x 70 | }(i) 71 | } 72 | 73 | for i := 0; i < size; i++ { 74 | <-queue 75 | } 76 | 77 | //conns := atomic.LoadInt32(&xorm.ConnectionNum) 78 | //fmt.Println("connection number:", conns) 79 | fmt.Println("end") 80 | } 81 | 82 | func main() { 83 | fmt.Println("-----start sqlite go routines-----") 84 | engine, err := sqliteEngine() 85 | if err != nil { 86 | fmt.Println(err) 87 | return 88 | } 89 | engine.ShowSQL(true) 90 | cacher := xorm.NewLRUCacher2(xorm.NewMemoryStore(), time.Hour, 1000) 91 | engine.SetDefaultCacher(cacher) 92 | fmt.Println(engine) 93 | test(engine) 94 | fmt.Println("test end") 95 | engine.Close() 96 | 97 | fmt.Println("-----start mysql go routines-----") 98 | engine, err = mysqlEngine() 99 | engine.ShowSQL(true) 100 | cacher = xorm.NewLRUCacher2(xorm.NewMemoryStore(), time.Hour, 1000) 101 | engine.SetDefaultCacher(cacher) 102 | if err != nil { 103 | fmt.Println(err) 104 | return 105 | } 106 | defer engine.Close() 107 | test(engine) 108 | } 109 | -------------------------------------------------------------------------------- /examples/conversion.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/go-xorm/xorm" 9 | _ "github.com/mattn/go-sqlite3" 10 | ) 11 | 12 | // Status describes a status 13 | type Status struct { 14 | Name string 15 | Color string 16 | } 17 | 18 | // defines some statuses 19 | var ( 20 | Registered = Status{"Registered", "white"} 21 | Approved = Status{"Approved", "green"} 22 | Removed = Status{"Removed", "red"} 23 | Statuses = map[string]Status{ 24 | Registered.Name: Registered, 25 | Approved.Name: Approved, 26 | Removed.Name: Removed, 27 | } 28 | ) 29 | 30 | // FromDB implemented xorm.Conversion convent database data to self 31 | func (s *Status) FromDB(bytes []byte) error { 32 | if r, ok := Statuses[string(bytes)]; ok { 33 | *s = r 34 | return nil 35 | } 36 | return errors.New("no this data") 37 | } 38 | 39 | // ToDB implemented xorm.Conversion convent to database data 40 | func (s *Status) ToDB() ([]byte, error) { 41 | return []byte(s.Name), nil 42 | } 43 | 44 | // User describes a user 45 | type User struct { 46 | Id int64 47 | Name string 48 | Status Status `xorm:"varchar(40)"` 49 | } 50 | 51 | func main() { 52 | f := "conversion.db" 53 | os.Remove(f) 54 | 55 | Orm, err := xorm.NewEngine("sqlite3", f) 56 | if err != nil { 57 | fmt.Println(err) 58 | return 59 | } 60 | Orm.ShowSQL(true) 61 | err = Orm.CreateTables(&User{}) 62 | if err != nil { 63 | fmt.Println(err) 64 | return 65 | } 66 | 67 | _, err = Orm.Insert(&User{1, "xlw", Registered}) 68 | if err != nil { 69 | fmt.Println(err) 70 | return 71 | } 72 | 73 | users := make([]User, 0) 74 | err = Orm.Find(&users) 75 | if err != nil { 76 | fmt.Println(err) 77 | return 78 | } 79 | 80 | fmt.Println(users) 81 | } 82 | -------------------------------------------------------------------------------- /examples/derive.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/go-xorm/xorm" 8 | _ "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | // User describes a user 12 | type User struct { 13 | Id int64 14 | Name string 15 | } 16 | 17 | // LoginInfo describes a login information 18 | type LoginInfo struct { 19 | Id int64 20 | IP string 21 | UserId int64 22 | } 23 | 24 | // LoginInfo1 describes a login information 25 | type LoginInfo1 struct { 26 | LoginInfo `xorm:"extends"` 27 | UserName string 28 | } 29 | 30 | func main() { 31 | f := "derive.db" 32 | os.Remove(f) 33 | 34 | orm, err := xorm.NewEngine("sqlite3", f) 35 | if err != nil { 36 | fmt.Println(err) 37 | return 38 | } 39 | defer orm.Close() 40 | orm.ShowSQL(true) 41 | err = orm.CreateTables(&User{}, &LoginInfo{}) 42 | if err != nil { 43 | fmt.Println(err) 44 | return 45 | } 46 | 47 | _, err = orm.Insert(&User{1, "xlw"}, &LoginInfo{1, "127.0.0.1", 1}) 48 | if err != nil { 49 | fmt.Println(err) 50 | return 51 | } 52 | 53 | info := LoginInfo{} 54 | _, err = orm.ID(1).Get(&info) 55 | if err != nil { 56 | fmt.Println(err) 57 | return 58 | } 59 | fmt.Println(info) 60 | 61 | infos := make([]LoginInfo1, 0) 62 | err = orm.Sql(`select *, (select name from user where id = login_info.user_id) as user_name from 63 | login_info limit 10`).Find(&infos) 64 | if err != nil { 65 | fmt.Println(err) 66 | return 67 | } 68 | 69 | fmt.Println(infos) 70 | } 71 | -------------------------------------------------------------------------------- /examples/find.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/go-xorm/xorm" 9 | _ "github.com/mattn/go-sqlite3" 10 | ) 11 | 12 | // User describes a user 13 | type User struct { 14 | Id int64 15 | Name string 16 | Created time.Time `xorm:"created"` 17 | Updated time.Time `xorm:"updated"` 18 | } 19 | 20 | func main() { 21 | f := "conversion.db" 22 | os.Remove(f) 23 | 24 | orm, err := xorm.NewEngine("sqlite3", f) 25 | if err != nil { 26 | fmt.Println(err) 27 | return 28 | } 29 | orm.ShowSQL(true) 30 | 31 | err = orm.CreateTables(&User{}) 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | _, err = orm.Insert(&User{Id: 1, Name: "xlw"}) 38 | if err != nil { 39 | fmt.Println(err) 40 | return 41 | } 42 | 43 | users := make([]User, 0) 44 | err = orm.Find(&users) 45 | if err != nil { 46 | fmt.Println(err) 47 | return 48 | } 49 | 50 | fmt.Println(users) 51 | } 52 | -------------------------------------------------------------------------------- /examples/goroutine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | 8 | _ "github.com/go-sql-driver/mysql" 9 | "github.com/go-xorm/xorm" 10 | _ "github.com/mattn/go-sqlite3" 11 | ) 12 | 13 | // User describes a user 14 | type User struct { 15 | Id int64 16 | Name string 17 | } 18 | 19 | func sqliteEngine() (*xorm.Engine, error) { 20 | os.Remove("./test.db") 21 | return xorm.NewEngine("sqlite3", "./goroutine.db") 22 | } 23 | 24 | func mysqlEngine() (*xorm.Engine, error) { 25 | return xorm.NewEngine("mysql", "root:@/test?charset=utf8") 26 | } 27 | 28 | var u = &User{} 29 | 30 | func test(engine *xorm.Engine) { 31 | err := engine.CreateTables(u) 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | size := 100 38 | queue := make(chan int, size) 39 | 40 | for i := 0; i < size; i++ { 41 | go func(x int) { 42 | //x := i 43 | err := engine.Ping() 44 | if err != nil { 45 | fmt.Println(err) 46 | } else { 47 | /*err = engine.(u) 48 | if err != nil { 49 | fmt.Println("Map user failed") 50 | } else {*/ 51 | for j := 0; j < 10; j++ { 52 | if x+j < 2 { 53 | _, err = engine.Get(u) 54 | } else if x+j < 4 { 55 | users := make([]User, 0) 56 | err = engine.Find(&users) 57 | } else if x+j < 8 { 58 | _, err = engine.Count(u) 59 | } else if x+j < 16 { 60 | _, err = engine.Insert(&User{Name: "xlw"}) 61 | } else if x+j < 32 { 62 | _, err = engine.ID(1).Delete(u) 63 | } 64 | if err != nil { 65 | fmt.Println(err) 66 | queue <- x 67 | return 68 | } 69 | } 70 | fmt.Printf("%v success!\n", x) 71 | //} 72 | } 73 | queue <- x 74 | }(i) 75 | } 76 | 77 | for i := 0; i < size; i++ { 78 | <-queue 79 | } 80 | 81 | //conns := atomic.LoadInt32(&xorm.ConnectionNum) 82 | //fmt.Println("connection number:", conns) 83 | fmt.Println("end") 84 | } 85 | 86 | func main() { 87 | runtime.GOMAXPROCS(2) 88 | fmt.Println("-----start sqlite go routines-----") 89 | engine, err := sqliteEngine() 90 | if err != nil { 91 | fmt.Println(err) 92 | return 93 | } 94 | engine.ShowSQL(true) 95 | fmt.Println(engine) 96 | test(engine) 97 | fmt.Println("test end") 98 | engine.Close() 99 | 100 | fmt.Println("-----start mysql go routines-----") 101 | engine, err = mysqlEngine() 102 | if err != nil { 103 | fmt.Println(err) 104 | return 105 | } 106 | defer engine.Close() 107 | test(engine) 108 | } 109 | -------------------------------------------------------------------------------- /examples/maxconnect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | 8 | _ "github.com/go-sql-driver/mysql" 9 | "github.com/go-xorm/xorm" 10 | _ "github.com/mattn/go-sqlite3" 11 | ) 12 | 13 | // User describes a user 14 | type User struct { 15 | Id int64 16 | Name string 17 | } 18 | 19 | func sqliteEngine() (*xorm.Engine, error) { 20 | os.Remove("./test.db") 21 | return xorm.NewEngine("sqlite3", "./goroutine.db") 22 | } 23 | 24 | func mysqlEngine() (*xorm.Engine, error) { 25 | return xorm.NewEngine("mysql", "root:@/test?charset=utf8") 26 | } 27 | 28 | var u = &User{} 29 | 30 | func test(engine *xorm.Engine) { 31 | err := engine.CreateTables(u) 32 | if err != nil { 33 | fmt.Println(err) 34 | return 35 | } 36 | 37 | engine.ShowSQL(true) 38 | engine.SetMaxOpenConns(5) 39 | 40 | size := 1000 41 | queue := make(chan int, size) 42 | 43 | for i := 0; i < size; i++ { 44 | go func(x int) { 45 | //x := i 46 | err := engine.Ping() 47 | if err != nil { 48 | fmt.Println(err) 49 | } else { 50 | /*err = engine.Map(u) 51 | if err != nil { 52 | fmt.Println("Map user failed") 53 | } else {*/ 54 | for j := 0; j < 10; j++ { 55 | if x+j < 2 { 56 | _, err = engine.Get(u) 57 | } else if x+j < 4 { 58 | users := make([]User, 0) 59 | err = engine.Find(&users) 60 | } else if x+j < 8 { 61 | _, err = engine.Count(u) 62 | } else if x+j < 16 { 63 | _, err = engine.Insert(&User{Name: "xlw"}) 64 | } else if x+j < 32 { 65 | _, err = engine.ID(1).Delete(u) 66 | } 67 | if err != nil { 68 | fmt.Println(err) 69 | queue <- x 70 | return 71 | } 72 | } 73 | fmt.Printf("%v success!\n", x) 74 | //} 75 | } 76 | queue <- x 77 | }(i) 78 | } 79 | 80 | for i := 0; i < size; i++ { 81 | <-queue 82 | } 83 | 84 | fmt.Println("end") 85 | } 86 | 87 | func main() { 88 | runtime.GOMAXPROCS(2) 89 | fmt.Println("create engine") 90 | engine, err := sqliteEngine() 91 | if err != nil { 92 | fmt.Println(err) 93 | return 94 | } 95 | engine.ShowSQL(true) 96 | fmt.Println(engine) 97 | test(engine) 98 | fmt.Println("------------------------") 99 | engine.Close() 100 | 101 | engine, err = mysqlEngine() 102 | if err != nil { 103 | fmt.Println(err) 104 | return 105 | } 106 | defer engine.Close() 107 | test(engine) 108 | } 109 | -------------------------------------------------------------------------------- /examples/singlemapping.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/go-xorm/xorm" 8 | _ "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | // User describes a user 12 | type User struct { 13 | Id int64 14 | Name string 15 | } 16 | 17 | // LoginInfo describes a login information 18 | type LoginInfo struct { 19 | Id int64 20 | IP string 21 | UserId int64 22 | // timestamp should be updated by database, so only allow get from db 23 | TimeStamp string `xorm:"<-"` 24 | // assume 25 | Nonuse int `xorm:"->"` 26 | } 27 | 28 | func main() { 29 | f := "singleMapping.db" 30 | os.Remove(f) 31 | 32 | orm, err := xorm.NewEngine("sqlite3", f) 33 | if err != nil { 34 | fmt.Println(err) 35 | return 36 | } 37 | orm.ShowSQL(true) 38 | err = orm.CreateTables(&User{}, &LoginInfo{}) 39 | if err != nil { 40 | fmt.Println(err) 41 | return 42 | } 43 | 44 | _, err = orm.Insert(&User{1, "xlw"}, &LoginInfo{1, "127.0.0.1", 1, "", 23}) 45 | if err != nil { 46 | fmt.Println(err) 47 | return 48 | } 49 | 50 | info := LoginInfo{} 51 | _, err = orm.ID(1).Get(&info) 52 | if err != nil { 53 | fmt.Println(err) 54 | return 55 | } 56 | fmt.Println(info) 57 | } 58 | -------------------------------------------------------------------------------- /examples/sync.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | _ "github.com/go-sql-driver/mysql" 7 | "github.com/go-xorm/xorm" 8 | _ "github.com/lib/pq" 9 | _ "github.com/mattn/go-sqlite3" 10 | ) 11 | 12 | // SyncUser2 describes a user 13 | type SyncUser2 struct { 14 | Id int64 15 | Name string `xorm:"unique"` 16 | Age int `xorm:"index"` 17 | Title string 18 | Address string 19 | Genre string 20 | Area string 21 | Date int 22 | } 23 | 24 | // SyncLoginInfo2 describes a login information 25 | type SyncLoginInfo2 struct { 26 | Id int64 27 | IP string `xorm:"index"` 28 | UserId int64 29 | AddedCol int 30 | // timestamp should be updated by database, so only allow get from db 31 | TimeStamp string 32 | // assume 33 | Nonuse int `xorm:"unique"` 34 | Newa string `xorm:"index"` 35 | } 36 | 37 | func sync(engine *xorm.Engine) error { 38 | return engine.Sync(&SyncLoginInfo2{}, &SyncUser2{}) 39 | } 40 | 41 | func sqliteEngine() (*xorm.Engine, error) { 42 | f := "sync.db" 43 | //os.Remove(f) 44 | 45 | return xorm.NewEngine("sqlite3", f) 46 | } 47 | 48 | func mysqlEngine() (*xorm.Engine, error) { 49 | return xorm.NewEngine("mysql", "root:@/test?charset=utf8") 50 | } 51 | 52 | func postgresEngine() (*xorm.Engine, error) { 53 | return xorm.NewEngine("postgres", "dbname=xorm_test sslmode=disable") 54 | } 55 | 56 | type engineFunc func() (*xorm.Engine, error) 57 | 58 | func main() { 59 | //engines := []engineFunc{sqliteEngine, mysqlEngine, postgresEngine} 60 | //engines := []engineFunc{sqliteEngine} 61 | //engines := []engineFunc{mysqlEngine} 62 | engines := []engineFunc{postgresEngine} 63 | for _, enginefunc := range engines { 64 | Orm, err := enginefunc() 65 | fmt.Println("--------", Orm.DriverName(), "----------") 66 | if err != nil { 67 | fmt.Println(err) 68 | return 69 | } 70 | Orm.ShowSQL(true) 71 | err = sync(Orm) 72 | if err != nil { 73 | fmt.Println(err) 74 | } 75 | 76 | _, err = Orm.Where("id > 0").Delete(&SyncUser2{}) 77 | if err != nil { 78 | fmt.Println(err) 79 | } 80 | 81 | user := &SyncUser2{ 82 | Name: "testsdf", 83 | Age: 15, 84 | Title: "newsfds", 85 | Address: "fasfdsafdsaf", 86 | Genre: "fsafd", 87 | Area: "fafdsafd", 88 | Date: 1000, 89 | } 90 | _, err = Orm.Insert(user) 91 | if err != nil { 92 | fmt.Println(err) 93 | return 94 | } 95 | 96 | isexist, err := Orm.IsTableExist("sync_user2") 97 | if err != nil { 98 | fmt.Println(err) 99 | return 100 | } 101 | if !isexist { 102 | fmt.Println("sync_user2 is not exist") 103 | return 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/tables.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/go-xorm/xorm" 8 | _ "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) < 2 { 13 | fmt.Println("need db path") 14 | return 15 | } 16 | 17 | orm, err := xorm.NewEngine("sqlite3", os.Args[1]) 18 | if err != nil { 19 | fmt.Println(err) 20 | return 21 | } 22 | defer orm.Close() 23 | orm.ShowSQL(true) 24 | 25 | tables, err := orm.DBMetas() 26 | if err != nil { 27 | fmt.Println(err) 28 | return 29 | } 30 | 31 | for _, table := range tables { 32 | fmt.Println(table.Name) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gen_reserved.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -f $1 ];then 3 | cat $1| awk '{printf("\""$1"\":true,\n")}' 4 | else 5 | echo "argument $1 if not a file!" 6 | fi 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-xorm/xorm 2 | 3 | go 1.11 4 | 5 | require ( 6 | github.com/cockroachdb/apd v1.1.0 // indirect 7 | github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 8 | github.com/go-sql-driver/mysql v1.4.1 9 | github.com/gofrs/uuid v3.2.0+incompatible // indirect 10 | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect 11 | github.com/jackc/pgx v3.6.0+incompatible 12 | github.com/lib/pq v1.0.0 13 | github.com/mattn/go-sqlite3 v1.10.0 14 | github.com/pkg/errors v0.8.1 // indirect 15 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect 16 | github.com/stretchr/testify v1.4.0 17 | github.com/ziutek/mymysql v1.5.4 18 | xorm.io/builder v0.3.6 19 | xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb 20 | ) 21 | -------------------------------------------------------------------------------- /helpers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestEraseAny(t *testing.T) { 14 | raw := "SELECT * FROM `table`.[table_name]" 15 | assert.EqualValues(t, raw, eraseAny(raw)) 16 | assert.EqualValues(t, "SELECT * FROM table.[table_name]", eraseAny(raw, "`")) 17 | assert.EqualValues(t, "SELECT * FROM table.table_name", eraseAny(raw, "`", "[", "]")) 18 | } 19 | 20 | func TestQuoteColumns(t *testing.T) { 21 | cols := []string{"f1", "f2", "f3"} 22 | quoteFunc := func(value string) string { 23 | return "[" + value + "]" 24 | } 25 | 26 | assert.EqualValues(t, "[f1], [f2], [f3]", quoteColumns(cols, quoteFunc, ",")) 27 | } 28 | -------------------------------------------------------------------------------- /helpler_time.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import "time" 8 | 9 | const ( 10 | zeroTime0 = "0000-00-00 00:00:00" 11 | zeroTime1 = "0001-01-01 00:00:00" 12 | ) 13 | 14 | func formatTime(t time.Time) string { 15 | return t.Format("2006-01-02 15:04:05") 16 | } 17 | 18 | func isTimeZero(t time.Time) bool { 19 | return t.IsZero() || formatTime(t) == zeroTime0 || 20 | formatTime(t) == zeroTime1 21 | } 22 | -------------------------------------------------------------------------------- /interface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | "reflect" 11 | "time" 12 | 13 | "xorm.io/core" 14 | ) 15 | 16 | // Interface defines the interface which Engine, EngineGroup and Session will implementate. 17 | type Interface interface { 18 | AllCols() *Session 19 | Alias(alias string) *Session 20 | Asc(colNames ...string) *Session 21 | BufferSize(size int) *Session 22 | Cols(columns ...string) *Session 23 | Count(...interface{}) (int64, error) 24 | CreateIndexes(bean interface{}) error 25 | CreateUniques(bean interface{}) error 26 | Decr(column string, arg ...interface{}) *Session 27 | Desc(...string) *Session 28 | Delete(interface{}) (int64, error) 29 | Distinct(columns ...string) *Session 30 | DropIndexes(bean interface{}) error 31 | Exec(sqlOrArgs ...interface{}) (sql.Result, error) 32 | Exist(bean ...interface{}) (bool, error) 33 | Find(interface{}, ...interface{}) error 34 | FindAndCount(interface{}, ...interface{}) (int64, error) 35 | Get(interface{}) (bool, error) 36 | GroupBy(keys string) *Session 37 | ID(interface{}) *Session 38 | In(string, ...interface{}) *Session 39 | Incr(column string, arg ...interface{}) *Session 40 | Insert(...interface{}) (int64, error) 41 | InsertOne(interface{}) (int64, error) 42 | IsTableEmpty(bean interface{}) (bool, error) 43 | IsTableExist(beanOrTableName interface{}) (bool, error) 44 | Iterate(interface{}, IterFunc) error 45 | Limit(int, ...int) *Session 46 | MustCols(columns ...string) *Session 47 | NoAutoCondition(...bool) *Session 48 | NotIn(string, ...interface{}) *Session 49 | Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session 50 | Omit(columns ...string) *Session 51 | OrderBy(order string) *Session 52 | Ping() error 53 | Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) 54 | QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) 55 | QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) 56 | Rows(bean interface{}) (*Rows, error) 57 | SetExpr(string, interface{}) *Session 58 | SQL(interface{}, ...interface{}) *Session 59 | Sum(bean interface{}, colName string) (float64, error) 60 | SumInt(bean interface{}, colName string) (int64, error) 61 | Sums(bean interface{}, colNames ...string) ([]float64, error) 62 | SumsInt(bean interface{}, colNames ...string) ([]int64, error) 63 | Table(tableNameOrBean interface{}) *Session 64 | Unscoped() *Session 65 | Update(bean interface{}, condiBeans ...interface{}) (int64, error) 66 | UseBool(...string) *Session 67 | Where(interface{}, ...interface{}) *Session 68 | } 69 | 70 | // EngineInterface defines the interface which Engine, EngineGroup will implementate. 71 | type EngineInterface interface { 72 | Interface 73 | 74 | Before(func(interface{})) *Session 75 | Charset(charset string) *Session 76 | ClearCache(...interface{}) error 77 | Context(context.Context) *Session 78 | CreateTables(...interface{}) error 79 | DBMetas() ([]*core.Table, error) 80 | Dialect() core.Dialect 81 | DropTables(...interface{}) error 82 | DumpAllToFile(fp string, tp ...core.DbType) error 83 | GetCacher(string) core.Cacher 84 | GetColumnMapper() core.IMapper 85 | GetDefaultCacher() core.Cacher 86 | GetTableMapper() core.IMapper 87 | GetTZDatabase() *time.Location 88 | GetTZLocation() *time.Location 89 | MapCacher(interface{}, core.Cacher) error 90 | NewSession() *Session 91 | NoAutoTime() *Session 92 | Quote(string) string 93 | SetCacher(string, core.Cacher) 94 | SetConnMaxLifetime(time.Duration) 95 | SetDefaultCacher(core.Cacher) 96 | SetLogger(logger core.ILogger) 97 | SetLogLevel(core.LogLevel) 98 | SetMapper(core.IMapper) 99 | SetMaxOpenConns(int) 100 | SetMaxIdleConns(int) 101 | SetSchema(string) 102 | SetTZDatabase(tz *time.Location) 103 | SetTZLocation(tz *time.Location) 104 | ShowExecTime(...bool) 105 | ShowSQL(show ...bool) 106 | Sync(...interface{}) error 107 | Sync2(...interface{}) error 108 | StoreEngine(storeEngine string) *Session 109 | TableInfo(bean interface{}) *Table 110 | TableName(interface{}, ...bool) string 111 | UnMapType(reflect.Type) 112 | } 113 | 114 | var ( 115 | _ Interface = &Session{} 116 | _ EngineInterface = &Engine{} 117 | _ EngineInterface = &EngineGroup{} 118 | ) 119 | -------------------------------------------------------------------------------- /json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import "encoding/json" 8 | 9 | // JSONInterface represents an interface to handle json data 10 | type JSONInterface interface { 11 | Marshal(v interface{}) ([]byte, error) 12 | Unmarshal(data []byte, v interface{}) error 13 | } 14 | 15 | var ( 16 | // DefaultJSONHandler default json handler 17 | DefaultJSONHandler JSONInterface = StdJSON{} 18 | ) 19 | 20 | // StdJSON implements JSONInterface via encoding/json 21 | type StdJSON struct{} 22 | 23 | // Marshal implements JSONInterface 24 | func (StdJSON) Marshal(v interface{}) ([]byte, error) { 25 | return json.Marshal(v) 26 | } 27 | 28 | // Unmarshal implements JSONInterface 29 | func (StdJSON) Unmarshal(data []byte, v interface{}) error { 30 | return json.Unmarshal(data, v) 31 | } 32 | -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "log" 11 | 12 | "xorm.io/core" 13 | ) 14 | 15 | // default log options 16 | const ( 17 | DEFAULT_LOG_PREFIX = "[xorm]" 18 | DEFAULT_LOG_FLAG = log.Ldate | log.Lmicroseconds 19 | DEFAULT_LOG_LEVEL = core.LOG_DEBUG 20 | ) 21 | 22 | var _ core.ILogger = DiscardLogger{} 23 | 24 | // DiscardLogger don't log implementation for core.ILogger 25 | type DiscardLogger struct{} 26 | 27 | // Debug empty implementation 28 | func (DiscardLogger) Debug(v ...interface{}) {} 29 | 30 | // Debugf empty implementation 31 | func (DiscardLogger) Debugf(format string, v ...interface{}) {} 32 | 33 | // Error empty implementation 34 | func (DiscardLogger) Error(v ...interface{}) {} 35 | 36 | // Errorf empty implementation 37 | func (DiscardLogger) Errorf(format string, v ...interface{}) {} 38 | 39 | // Info empty implementation 40 | func (DiscardLogger) Info(v ...interface{}) {} 41 | 42 | // Infof empty implementation 43 | func (DiscardLogger) Infof(format string, v ...interface{}) {} 44 | 45 | // Warn empty implementation 46 | func (DiscardLogger) Warn(v ...interface{}) {} 47 | 48 | // Warnf empty implementation 49 | func (DiscardLogger) Warnf(format string, v ...interface{}) {} 50 | 51 | // Level empty implementation 52 | func (DiscardLogger) Level() core.LogLevel { 53 | return core.LOG_UNKNOWN 54 | } 55 | 56 | // SetLevel empty implementation 57 | func (DiscardLogger) SetLevel(l core.LogLevel) {} 58 | 59 | // ShowSQL empty implementation 60 | func (DiscardLogger) ShowSQL(show ...bool) {} 61 | 62 | // IsShowSQL empty implementation 63 | func (DiscardLogger) IsShowSQL() bool { 64 | return false 65 | } 66 | 67 | // SimpleLogger is the default implment of core.ILogger 68 | type SimpleLogger struct { 69 | DEBUG *log.Logger 70 | ERR *log.Logger 71 | INFO *log.Logger 72 | WARN *log.Logger 73 | level core.LogLevel 74 | showSQL bool 75 | } 76 | 77 | var _ core.ILogger = &SimpleLogger{} 78 | 79 | // NewSimpleLogger use a special io.Writer as logger output 80 | func NewSimpleLogger(out io.Writer) *SimpleLogger { 81 | return NewSimpleLogger2(out, DEFAULT_LOG_PREFIX, DEFAULT_LOG_FLAG) 82 | } 83 | 84 | // NewSimpleLogger2 let you customrize your logger prefix and flag 85 | func NewSimpleLogger2(out io.Writer, prefix string, flag int) *SimpleLogger { 86 | return NewSimpleLogger3(out, prefix, flag, DEFAULT_LOG_LEVEL) 87 | } 88 | 89 | // NewSimpleLogger3 let you customrize your logger prefix and flag and logLevel 90 | func NewSimpleLogger3(out io.Writer, prefix string, flag int, l core.LogLevel) *SimpleLogger { 91 | return &SimpleLogger{ 92 | DEBUG: log.New(out, fmt.Sprintf("%s [debug] ", prefix), flag), 93 | ERR: log.New(out, fmt.Sprintf("%s [error] ", prefix), flag), 94 | INFO: log.New(out, fmt.Sprintf("%s [info] ", prefix), flag), 95 | WARN: log.New(out, fmt.Sprintf("%s [warn] ", prefix), flag), 96 | level: l, 97 | } 98 | } 99 | 100 | // Error implement core.ILogger 101 | func (s *SimpleLogger) Error(v ...interface{}) { 102 | if s.level <= core.LOG_ERR { 103 | s.ERR.Output(2, fmt.Sprint(v...)) 104 | } 105 | return 106 | } 107 | 108 | // Errorf implement core.ILogger 109 | func (s *SimpleLogger) Errorf(format string, v ...interface{}) { 110 | if s.level <= core.LOG_ERR { 111 | s.ERR.Output(2, fmt.Sprintf(format, v...)) 112 | } 113 | return 114 | } 115 | 116 | // Debug implement core.ILogger 117 | func (s *SimpleLogger) Debug(v ...interface{}) { 118 | if s.level <= core.LOG_DEBUG { 119 | s.DEBUG.Output(2, fmt.Sprint(v...)) 120 | } 121 | return 122 | } 123 | 124 | // Debugf implement core.ILogger 125 | func (s *SimpleLogger) Debugf(format string, v ...interface{}) { 126 | if s.level <= core.LOG_DEBUG { 127 | s.DEBUG.Output(2, fmt.Sprintf(format, v...)) 128 | } 129 | return 130 | } 131 | 132 | // Info implement core.ILogger 133 | func (s *SimpleLogger) Info(v ...interface{}) { 134 | if s.level <= core.LOG_INFO { 135 | s.INFO.Output(2, fmt.Sprint(v...)) 136 | } 137 | return 138 | } 139 | 140 | // Infof implement core.ILogger 141 | func (s *SimpleLogger) Infof(format string, v ...interface{}) { 142 | if s.level <= core.LOG_INFO { 143 | s.INFO.Output(2, fmt.Sprintf(format, v...)) 144 | } 145 | return 146 | } 147 | 148 | // Warn implement core.ILogger 149 | func (s *SimpleLogger) Warn(v ...interface{}) { 150 | if s.level <= core.LOG_WARNING { 151 | s.WARN.Output(2, fmt.Sprint(v...)) 152 | } 153 | return 154 | } 155 | 156 | // Warnf implement core.ILogger 157 | func (s *SimpleLogger) Warnf(format string, v ...interface{}) { 158 | if s.level <= core.LOG_WARNING { 159 | s.WARN.Output(2, fmt.Sprintf(format, v...)) 160 | } 161 | return 162 | } 163 | 164 | // Level implement core.ILogger 165 | func (s *SimpleLogger) Level() core.LogLevel { 166 | return s.level 167 | } 168 | 169 | // SetLevel implement core.ILogger 170 | func (s *SimpleLogger) SetLevel(l core.LogLevel) { 171 | s.level = l 172 | return 173 | } 174 | 175 | // ShowSQL implement core.ILogger 176 | func (s *SimpleLogger) ShowSQL(show ...bool) { 177 | if len(show) == 0 { 178 | s.showSQL = true 179 | return 180 | } 181 | s.showSQL = show[0] 182 | } 183 | 184 | // IsShowSQL implement core.ILogger 185 | func (s *SimpleLogger) IsShowSQL() bool { 186 | return s.showSQL 187 | } 188 | -------------------------------------------------------------------------------- /migrate/migrate.go: -------------------------------------------------------------------------------- 1 | package migrate 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/go-xorm/xorm" 8 | ) 9 | 10 | // MigrateFunc is the func signature for migrating. 11 | type MigrateFunc func(*xorm.Engine) error 12 | 13 | // RollbackFunc is the func signature for rollbacking. 14 | type RollbackFunc func(*xorm.Engine) error 15 | 16 | // InitSchemaFunc is the func signature for initializing the schema. 17 | type InitSchemaFunc func(*xorm.Engine) error 18 | 19 | // Options define options for all migrations. 20 | type Options struct { 21 | // TableName is the migration table. 22 | TableName string 23 | // IDColumnName is the name of column where the migration id will be stored. 24 | IDColumnName string 25 | } 26 | 27 | // Migration represents a database migration (a modification to be made on the database). 28 | type Migration struct { 29 | // ID is the migration identifier. Usually a timestamp like "201601021504". 30 | ID string 31 | // Migrate is a function that will br executed while running this migration. 32 | Migrate MigrateFunc 33 | // Rollback will be executed on rollback. Can be nil. 34 | Rollback RollbackFunc 35 | } 36 | 37 | // Migrate represents a collection of all migrations of a database schema. 38 | type Migrate struct { 39 | db *xorm.Engine 40 | options *Options 41 | migrations []*Migration 42 | initSchema InitSchemaFunc 43 | } 44 | 45 | var ( 46 | // DefaultOptions can be used if you don't want to think about options. 47 | DefaultOptions = &Options{ 48 | TableName: "migrations", 49 | IDColumnName: "id", 50 | } 51 | 52 | // ErrRollbackImpossible is returned when trying to rollback a migration 53 | // that has no rollback function. 54 | ErrRollbackImpossible = errors.New("It's impossible to rollback this migration") 55 | 56 | // ErrNoMigrationDefined is returned when no migration is defined. 57 | ErrNoMigrationDefined = errors.New("No migration defined") 58 | 59 | // ErrMissingID is returned when the ID od migration is equal to "" 60 | ErrMissingID = errors.New("Missing ID in migration") 61 | 62 | // ErrNoRunnedMigration is returned when any runned migration was found while 63 | // running RollbackLast 64 | ErrNoRunnedMigration = errors.New("Could not find last runned migration") 65 | ) 66 | 67 | // New returns a new Gormigrate. 68 | func New(db *xorm.Engine, options *Options, migrations []*Migration) *Migrate { 69 | return &Migrate{ 70 | db: db, 71 | options: options, 72 | migrations: migrations, 73 | } 74 | } 75 | 76 | // InitSchema sets a function that is run if no migration is found. 77 | // The idea is preventing to run all migrations when a new clean database 78 | // is being migrating. In this function you should create all tables and 79 | // foreign key necessary to your application. 80 | func (m *Migrate) InitSchema(initSchema InitSchemaFunc) { 81 | m.initSchema = initSchema 82 | } 83 | 84 | // Migrate executes all migrations that did not run yet. 85 | func (m *Migrate) Migrate() error { 86 | if err := m.createMigrationTableIfNotExists(); err != nil { 87 | return err 88 | } 89 | 90 | if m.initSchema != nil && m.isFirstRun() { 91 | return m.runInitSchema() 92 | } 93 | 94 | for _, migration := range m.migrations { 95 | if err := m.runMigration(migration); err != nil { 96 | return err 97 | } 98 | } 99 | return nil 100 | } 101 | 102 | // RollbackLast undo the last migration 103 | func (m *Migrate) RollbackLast() error { 104 | if len(m.migrations) == 0 { 105 | return ErrNoMigrationDefined 106 | } 107 | 108 | lastRunnedMigration, err := m.getLastRunnedMigration() 109 | if err != nil { 110 | return err 111 | } 112 | 113 | if err := m.RollbackMigration(lastRunnedMigration); err != nil { 114 | return err 115 | } 116 | return nil 117 | } 118 | 119 | func (m *Migrate) getLastRunnedMigration() (*Migration, error) { 120 | for i := len(m.migrations) - 1; i >= 0; i-- { 121 | migration := m.migrations[i] 122 | run, err := m.migrationDidRun(migration) 123 | if err != nil { 124 | return nil, err 125 | } else if run { 126 | return migration, nil 127 | } 128 | } 129 | return nil, ErrNoRunnedMigration 130 | } 131 | 132 | // RollbackMigration undo a migration. 133 | func (m *Migrate) RollbackMigration(mig *Migration) error { 134 | if mig.Rollback == nil { 135 | return ErrRollbackImpossible 136 | } 137 | 138 | if err := mig.Rollback(m.db); err != nil { 139 | return err 140 | } 141 | 142 | sql := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", m.options.TableName, m.options.IDColumnName) 143 | if _, err := m.db.Exec(sql, mig.ID); err != nil { 144 | return err 145 | } 146 | return nil 147 | } 148 | 149 | func (m *Migrate) runInitSchema() error { 150 | if err := m.initSchema(m.db); err != nil { 151 | return err 152 | } 153 | 154 | for _, migration := range m.migrations { 155 | if err := m.insertMigration(migration.ID); err != nil { 156 | return err 157 | } 158 | } 159 | 160 | return nil 161 | } 162 | 163 | func (m *Migrate) runMigration(migration *Migration) error { 164 | if len(migration.ID) == 0 { 165 | return ErrMissingID 166 | } 167 | 168 | run, err := m.migrationDidRun(migration) 169 | if err != nil { 170 | return err 171 | } 172 | 173 | if !run { 174 | if err := migration.Migrate(m.db); err != nil { 175 | return err 176 | } 177 | 178 | if err := m.insertMigration(migration.ID); err != nil { 179 | return err 180 | } 181 | } 182 | return nil 183 | } 184 | 185 | func (m *Migrate) createMigrationTableIfNotExists() error { 186 | exists, err := m.db.IsTableExist(m.options.TableName) 187 | if err != nil { 188 | return err 189 | } 190 | if exists { 191 | return nil 192 | } 193 | 194 | sql := fmt.Sprintf("CREATE TABLE %s (%s VARCHAR(255) PRIMARY KEY)", m.options.TableName, m.options.IDColumnName) 195 | if _, err := m.db.Exec(sql); err != nil { 196 | return err 197 | } 198 | return nil 199 | } 200 | 201 | func (m *Migrate) migrationDidRun(mig *Migration) (bool, error) { 202 | count, err := m.db.SQL(fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s = ?", m.options.TableName, m.options.IDColumnName), mig.ID).Count() 203 | return count > 0, err 204 | } 205 | 206 | func (m *Migrate) isFirstRun() bool { 207 | row := m.db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", m.options.TableName)) 208 | var count int 209 | row.Scan(&count) 210 | return count == 0 211 | } 212 | 213 | func (m *Migrate) insertMigration(id string) error { 214 | sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?)", m.options.TableName, m.options.IDColumnName) 215 | _, err := m.db.Exec(sql, id) 216 | return err 217 | } 218 | -------------------------------------------------------------------------------- /migrate/migrate_test.go: -------------------------------------------------------------------------------- 1 | package migrate 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "testing" 8 | 9 | "github.com/go-xorm/xorm" 10 | _ "github.com/mattn/go-sqlite3" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | type Person struct { 15 | ID int64 16 | Name string 17 | } 18 | 19 | type Pet struct { 20 | ID int64 21 | Name string 22 | PersonID int 23 | } 24 | 25 | const ( 26 | dbName = "testdb.sqlite3" 27 | ) 28 | 29 | var ( 30 | migrations = []*Migration{ 31 | { 32 | ID: "201608301400", 33 | Migrate: func(tx *xorm.Engine) error { 34 | return tx.Sync2(&Person{}) 35 | }, 36 | Rollback: func(tx *xorm.Engine) error { 37 | return tx.DropTables(&Person{}) 38 | }, 39 | }, 40 | { 41 | ID: "201608301430", 42 | Migrate: func(tx *xorm.Engine) error { 43 | return tx.Sync2(&Pet{}) 44 | }, 45 | Rollback: func(tx *xorm.Engine) error { 46 | return tx.DropTables(&Pet{}) 47 | }, 48 | }, 49 | } 50 | ) 51 | 52 | func TestMigration(t *testing.T) { 53 | _ = os.Remove(dbName) 54 | 55 | db, err := xorm.NewEngine("sqlite3", dbName) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | defer db.Close() 60 | 61 | if err = db.DB().Ping(); err != nil { 62 | log.Fatal(err) 63 | } 64 | 65 | m := New(db, DefaultOptions, migrations) 66 | 67 | err = m.Migrate() 68 | assert.NoError(t, err) 69 | exists, _ := db.IsTableExist(&Person{}) 70 | assert.True(t, exists) 71 | exists, _ = db.IsTableExist(&Pet{}) 72 | assert.True(t, exists) 73 | assert.Equal(t, 2, tableCount(db, "migrations")) 74 | 75 | err = m.RollbackLast() 76 | assert.NoError(t, err) 77 | exists, _ = db.IsTableExist(&Person{}) 78 | assert.True(t, exists) 79 | exists, _ = db.IsTableExist(&Pet{}) 80 | assert.False(t, exists) 81 | assert.Equal(t, 1, tableCount(db, "migrations")) 82 | 83 | err = m.RollbackLast() 84 | assert.NoError(t, err) 85 | exists, _ = db.IsTableExist(&Person{}) 86 | assert.False(t, exists) 87 | exists, _ = db.IsTableExist(&Pet{}) 88 | assert.False(t, exists) 89 | assert.Equal(t, 0, tableCount(db, "migrations")) 90 | } 91 | 92 | func TestInitSchema(t *testing.T) { 93 | os.Remove(dbName) 94 | 95 | db, err := xorm.NewEngine("sqlite3", dbName) 96 | if err != nil { 97 | log.Fatal(err) 98 | } 99 | defer db.Close() 100 | if err = db.DB().Ping(); err != nil { 101 | log.Fatal(err) 102 | } 103 | 104 | m := New(db, DefaultOptions, migrations) 105 | m.InitSchema(func(tx *xorm.Engine) error { 106 | if err := tx.Sync2(&Person{}); err != nil { 107 | return err 108 | } 109 | if err := tx.Sync2(&Pet{}); err != nil { 110 | return err 111 | } 112 | return nil 113 | }) 114 | 115 | err = m.Migrate() 116 | assert.NoError(t, err) 117 | exists, _ := db.IsTableExist(&Person{}) 118 | assert.True(t, exists) 119 | exists, _ = db.IsTableExist(&Pet{}) 120 | assert.True(t, exists) 121 | assert.Equal(t, 2, tableCount(db, "migrations")) 122 | } 123 | 124 | func TestMissingID(t *testing.T) { 125 | os.Remove(dbName) 126 | 127 | db, err := xorm.NewEngine("sqlite3", dbName) 128 | assert.NoError(t, err) 129 | if db != nil { 130 | defer db.Close() 131 | } 132 | assert.NoError(t, db.DB().Ping()) 133 | 134 | migrationsMissingID := []*Migration{ 135 | { 136 | Migrate: func(tx *xorm.Engine) error { 137 | return nil 138 | }, 139 | }, 140 | } 141 | 142 | m := New(db, DefaultOptions, migrationsMissingID) 143 | assert.Equal(t, ErrMissingID, m.Migrate()) 144 | } 145 | 146 | func tableCount(db *xorm.Engine, tableName string) (count int) { 147 | row := db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName)) 148 | row.Scan(&count) 149 | return 150 | } 151 | -------------------------------------------------------------------------------- /processors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | // BeforeInsertProcessor executed before an object is initially persisted to the database 8 | type BeforeInsertProcessor interface { 9 | BeforeInsert() 10 | } 11 | 12 | // BeforeUpdateProcessor executed before an object is updated 13 | type BeforeUpdateProcessor interface { 14 | BeforeUpdate() 15 | } 16 | 17 | // BeforeDeleteProcessor executed before an object is deleted 18 | type BeforeDeleteProcessor interface { 19 | BeforeDelete() 20 | } 21 | 22 | // BeforeSetProcessor executed before data set to the struct fields 23 | type BeforeSetProcessor interface { 24 | BeforeSet(string, Cell) 25 | } 26 | 27 | // AfterSetProcessor executed after data set to the struct fields 28 | type AfterSetProcessor interface { 29 | AfterSet(string, Cell) 30 | } 31 | 32 | // AfterInsertProcessor executed after an object is persisted to the database 33 | type AfterInsertProcessor interface { 34 | AfterInsert() 35 | } 36 | 37 | // AfterUpdateProcessor executed after an object has been updated 38 | type AfterUpdateProcessor interface { 39 | AfterUpdate() 40 | } 41 | 42 | // AfterDeleteProcessor executed after an object has been deleted 43 | type AfterDeleteProcessor interface { 44 | AfterDelete() 45 | } 46 | 47 | // AfterLoadProcessor executed after an ojbect has been loaded from database 48 | type AfterLoadProcessor interface { 49 | AfterLoad() 50 | } 51 | 52 | // AfterLoadSessionProcessor executed after an ojbect has been loaded from database with session parameter 53 | type AfterLoadSessionProcessor interface { 54 | AfterLoad(*Session) 55 | } 56 | 57 | type executedProcessorFunc func(*Session, interface{}) error 58 | 59 | type executedProcessor struct { 60 | fun executedProcessorFunc 61 | session *Session 62 | bean interface{} 63 | } 64 | 65 | func (executor *executedProcessor) execute() error { 66 | return executor.fun(executor.session, executor.bean) 67 | } 68 | 69 | func (session *Session) executeProcessors() error { 70 | processors := session.afterProcessors 71 | session.afterProcessors = make([]executedProcessor, 0) 72 | for _, processor := range processors { 73 | if err := processor.execute(); err != nil { 74 | return err 75 | } 76 | } 77 | return nil 78 | } 79 | -------------------------------------------------------------------------------- /rows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "database/sql" 9 | "fmt" 10 | "reflect" 11 | 12 | "xorm.io/core" 13 | ) 14 | 15 | // Rows rows wrapper a rows to 16 | type Rows struct { 17 | session *Session 18 | rows *core.Rows 19 | beanType reflect.Type 20 | lastError error 21 | } 22 | 23 | func newRows(session *Session, bean interface{}) (*Rows, error) { 24 | rows := new(Rows) 25 | rows.session = session 26 | rows.beanType = reflect.Indirect(reflect.ValueOf(bean)).Type() 27 | 28 | var sqlStr string 29 | var args []interface{} 30 | var err error 31 | 32 | if err = rows.session.statement.setRefBean(bean); err != nil { 33 | return nil, err 34 | } 35 | 36 | if len(session.statement.TableName()) <= 0 { 37 | return nil, ErrTableNotFound 38 | } 39 | 40 | if rows.session.statement.RawSQL == "" { 41 | sqlStr, args, err = rows.session.statement.genGetSQL(bean) 42 | if err != nil { 43 | return nil, err 44 | } 45 | } else { 46 | sqlStr = rows.session.statement.RawSQL 47 | args = rows.session.statement.RawParams 48 | } 49 | 50 | rows.rows, err = rows.session.queryRows(sqlStr, args...) 51 | if err != nil { 52 | rows.lastError = err 53 | rows.Close() 54 | return nil, err 55 | } 56 | 57 | return rows, nil 58 | } 59 | 60 | // Next move cursor to next record, return false if end has reached 61 | func (rows *Rows) Next() bool { 62 | if rows.lastError == nil && rows.rows != nil { 63 | hasNext := rows.rows.Next() 64 | if !hasNext { 65 | rows.lastError = sql.ErrNoRows 66 | } 67 | return hasNext 68 | } 69 | return false 70 | } 71 | 72 | // Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close. 73 | func (rows *Rows) Err() error { 74 | return rows.lastError 75 | } 76 | 77 | // Scan row record to bean properties 78 | func (rows *Rows) Scan(bean interface{}) error { 79 | if rows.lastError != nil { 80 | return rows.lastError 81 | } 82 | 83 | if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { 84 | return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) 85 | } 86 | 87 | if err := rows.session.statement.setRefBean(bean); err != nil { 88 | return err 89 | } 90 | 91 | fields, err := rows.rows.Columns() 92 | if err != nil { 93 | return err 94 | } 95 | 96 | scanResults, err := rows.session.row2Slice(rows.rows, fields, bean) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | dataStruct := rValue(bean) 102 | _, err = rows.session.slice2Bean(scanResults, fields, bean, &dataStruct, rows.session.statement.RefTable) 103 | if err != nil { 104 | return err 105 | } 106 | 107 | return rows.session.executeProcessors() 108 | } 109 | 110 | // Close session if session.IsAutoClose is true, and claimed any opened resources 111 | func (rows *Rows) Close() error { 112 | if rows.session.isAutoClose { 113 | defer rows.session.Close() 114 | } 115 | 116 | if rows.rows != nil { 117 | return rows.rows.Close() 118 | } 119 | 120 | return rows.lastError 121 | } 122 | -------------------------------------------------------------------------------- /rows_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestRows(t *testing.T) { 14 | assert.NoError(t, prepareEngine()) 15 | 16 | type UserRows struct { 17 | Id int64 18 | IsMan bool 19 | } 20 | 21 | assert.NoError(t, testEngine.Sync2(new(UserRows))) 22 | 23 | cnt, err := testEngine.Insert(&UserRows{ 24 | IsMan: true, 25 | }) 26 | assert.NoError(t, err) 27 | assert.EqualValues(t, 1, cnt) 28 | 29 | rows, err := testEngine.Rows(new(UserRows)) 30 | assert.NoError(t, err) 31 | defer rows.Close() 32 | 33 | cnt = 0 34 | user := new(UserRows) 35 | for rows.Next() { 36 | err = rows.Scan(user) 37 | assert.NoError(t, err) 38 | cnt++ 39 | } 40 | assert.EqualValues(t, 1, cnt) 41 | assert.False(t, rows.Next()) 42 | assert.NoError(t, rows.Close()) 43 | 44 | rows0, err := testEngine.Where("1>1").Rows(new(UserRows)) 45 | assert.NoError(t, err) 46 | defer rows0.Close() 47 | 48 | cnt = 0 49 | user0 := new(UserRows) 50 | for rows0.Next() { 51 | err = rows0.Scan(user0) 52 | assert.NoError(t, err) 53 | cnt++ 54 | } 55 | assert.EqualValues(t, 0, cnt) 56 | assert.NoError(t, rows0.Close()) 57 | 58 | sess := testEngine.NewSession() 59 | defer sess.Close() 60 | 61 | rows1, err := sess.Prepare().Rows(new(UserRows)) 62 | assert.NoError(t, err) 63 | defer rows1.Close() 64 | 65 | cnt = 0 66 | for rows1.Next() { 67 | err = rows1.Scan(user) 68 | assert.NoError(t, err) 69 | cnt++ 70 | } 71 | assert.EqualValues(t, 1, cnt) 72 | 73 | var tbName = testEngine.Quote(testEngine.TableName(user, true)) 74 | rows2, err := testEngine.SQL("SELECT * FROM " + tbName).Rows(new(UserRows)) 75 | assert.NoError(t, err) 76 | defer rows2.Close() 77 | 78 | cnt = 0 79 | for rows2.Next() { 80 | err = rows2.Scan(user) 81 | assert.NoError(t, err) 82 | cnt++ 83 | } 84 | assert.EqualValues(t, 1, cnt) 85 | } 86 | 87 | func TestRowsMyTableName(t *testing.T) { 88 | assert.NoError(t, prepareEngine()) 89 | 90 | type UserRowsMyTable struct { 91 | Id int64 92 | IsMan bool 93 | } 94 | 95 | var tableName = "user_rows_my_table_name" 96 | 97 | assert.NoError(t, testEngine.Table(tableName).Sync2(new(UserRowsMyTable))) 98 | 99 | cnt, err := testEngine.Table(tableName).Insert(&UserRowsMyTable{ 100 | IsMan: true, 101 | }) 102 | assert.NoError(t, err) 103 | assert.EqualValues(t, 1, cnt) 104 | 105 | rows, err := testEngine.Table(tableName).Rows(new(UserRowsMyTable)) 106 | assert.NoError(t, err) 107 | defer rows.Close() 108 | 109 | cnt = 0 110 | user := new(UserRowsMyTable) 111 | for rows.Next() { 112 | err = rows.Scan(user) 113 | assert.NoError(t, err) 114 | cnt++ 115 | } 116 | assert.EqualValues(t, 1, cnt) 117 | } 118 | 119 | type UserRowsSpecTable struct { 120 | Id int64 121 | IsMan bool 122 | } 123 | 124 | func (UserRowsSpecTable) TableName() string { 125 | return "user_rows_my_table_name" 126 | } 127 | 128 | func TestRowsSpecTableName(t *testing.T) { 129 | assert.NoError(t, prepareEngine()) 130 | assert.NoError(t, testEngine.Sync2(new(UserRowsSpecTable))) 131 | 132 | cnt, err := testEngine.Insert(&UserRowsSpecTable{ 133 | IsMan: true, 134 | }) 135 | assert.NoError(t, err) 136 | assert.EqualValues(t, 1, cnt) 137 | 138 | rows, err := testEngine.Rows(new(UserRowsSpecTable)) 139 | assert.NoError(t, err) 140 | defer rows.Close() 141 | 142 | cnt = 0 143 | user := new(UserRowsSpecTable) 144 | for rows.Next() { 145 | err = rows.Scan(user) 146 | assert.NoError(t, err) 147 | cnt++ 148 | } 149 | assert.EqualValues(t, 1, cnt) 150 | } 151 | -------------------------------------------------------------------------------- /session_cols.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "reflect" 9 | "strings" 10 | "time" 11 | 12 | "xorm.io/core" 13 | ) 14 | 15 | func setColumnInt(bean interface{}, col *core.Column, t int64) { 16 | v, err := col.ValueOf(bean) 17 | if err != nil { 18 | return 19 | } 20 | if v.CanSet() { 21 | switch v.Type().Kind() { 22 | case reflect.Int, reflect.Int64, reflect.Int32: 23 | v.SetInt(t) 24 | case reflect.Uint, reflect.Uint64, reflect.Uint32: 25 | v.SetUint(uint64(t)) 26 | } 27 | } 28 | } 29 | 30 | func setColumnTime(bean interface{}, col *core.Column, t time.Time) { 31 | v, err := col.ValueOf(bean) 32 | if err != nil { 33 | return 34 | } 35 | if v.CanSet() { 36 | switch v.Type().Kind() { 37 | case reflect.Struct: 38 | v.Set(reflect.ValueOf(t).Convert(v.Type())) 39 | case reflect.Int, reflect.Int64, reflect.Int32: 40 | v.SetInt(t.Unix()) 41 | case reflect.Uint, reflect.Uint64, reflect.Uint32: 42 | v.SetUint(uint64(t.Unix())) 43 | } 44 | } 45 | } 46 | 47 | func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) { 48 | if len(m) == 0 { 49 | return false, false 50 | } 51 | 52 | n := len(col.Name) 53 | 54 | for mk := range m { 55 | if len(mk) != n { 56 | continue 57 | } 58 | if strings.EqualFold(mk, col.Name) { 59 | return m[mk], true 60 | } 61 | } 62 | 63 | return false, false 64 | } 65 | 66 | func col2NewCols(columns ...string) []string { 67 | newColumns := make([]string, 0, len(columns)) 68 | for _, col := range columns { 69 | col = strings.Replace(col, "`", "", -1) 70 | col = strings.Replace(col, `"`, "", -1) 71 | ccols := strings.Split(col, ",") 72 | for _, c := range ccols { 73 | newColumns = append(newColumns, strings.TrimSpace(c)) 74 | } 75 | } 76 | return newColumns 77 | } 78 | 79 | // Incr provides a query string like "count = count + 1" 80 | func (session *Session) Incr(column string, arg ...interface{}) *Session { 81 | session.statement.Incr(column, arg...) 82 | return session 83 | } 84 | 85 | // Decr provides a query string like "count = count - 1" 86 | func (session *Session) Decr(column string, arg ...interface{}) *Session { 87 | session.statement.Decr(column, arg...) 88 | return session 89 | } 90 | 91 | // SetExpr provides a query string like "column = {expression}" 92 | func (session *Session) SetExpr(column string, expression interface{}) *Session { 93 | session.statement.SetExpr(column, expression) 94 | return session 95 | } 96 | 97 | // Select provides some columns to special 98 | func (session *Session) Select(str string) *Session { 99 | session.statement.Select(str) 100 | return session 101 | } 102 | 103 | // Cols provides some columns to special 104 | func (session *Session) Cols(columns ...string) *Session { 105 | session.statement.Cols(columns...) 106 | return session 107 | } 108 | 109 | // AllCols ask all columns 110 | func (session *Session) AllCols() *Session { 111 | session.statement.AllCols() 112 | return session 113 | } 114 | 115 | // MustCols specify some columns must use even if they are empty 116 | func (session *Session) MustCols(columns ...string) *Session { 117 | session.statement.MustCols(columns...) 118 | return session 119 | } 120 | 121 | // UseBool automatically retrieve condition according struct, but 122 | // if struct has bool field, it will ignore them. So use UseBool 123 | // to tell system to do not ignore them. 124 | // If no parameters, it will use all the bool field of struct, or 125 | // it will use parameters's columns 126 | func (session *Session) UseBool(columns ...string) *Session { 127 | session.statement.UseBool(columns...) 128 | return session 129 | } 130 | 131 | // Distinct use for distinct columns. Caution: when you are using cache, 132 | // distinct will not be cached because cache system need id, 133 | // but distinct will not provide id 134 | func (session *Session) Distinct(columns ...string) *Session { 135 | session.statement.Distinct(columns...) 136 | return session 137 | } 138 | 139 | // Omit Only not use the parameters as select or update columns 140 | func (session *Session) Omit(columns ...string) *Session { 141 | session.statement.Omit(columns...) 142 | return session 143 | } 144 | 145 | // Nullable Set null when column is zero-value and nullable for update 146 | func (session *Session) Nullable(columns ...string) *Session { 147 | session.statement.Nullable(columns...) 148 | return session 149 | } 150 | 151 | // NoAutoTime means do not automatically give created field and updated field 152 | // the current time on the current session temporarily 153 | func (session *Session) NoAutoTime() *Session { 154 | session.statement.UseAutoTime = false 155 | return session 156 | } 157 | -------------------------------------------------------------------------------- /session_cols_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "xorm.io/builder" 12 | "xorm.io/core" 13 | ) 14 | 15 | func TestSetExpr(t *testing.T) { 16 | assert.NoError(t, prepareEngine()) 17 | 18 | type UserExprIssue struct { 19 | Id int64 20 | Title string 21 | } 22 | 23 | assert.NoError(t, testEngine.Sync2(new(UserExprIssue))) 24 | 25 | var issue = UserExprIssue{ 26 | Title: "my issue", 27 | } 28 | cnt, err := testEngine.Insert(&issue) 29 | assert.NoError(t, err) 30 | assert.EqualValues(t, 1, cnt) 31 | assert.EqualValues(t, 1, issue.Id) 32 | 33 | type UserExpr struct { 34 | Id int64 35 | IssueId int64 `xorm:"index"` 36 | Show bool 37 | } 38 | 39 | assert.NoError(t, testEngine.Sync2(new(UserExpr))) 40 | 41 | cnt, err = testEngine.Insert(&UserExpr{ 42 | Show: true, 43 | }) 44 | assert.NoError(t, err) 45 | assert.EqualValues(t, 1, cnt) 46 | 47 | var not = "NOT" 48 | if testEngine.Dialect().DBType() == core.MSSQL { 49 | not = "~" 50 | } 51 | cnt, err = testEngine.SetExpr("show", not+" `show`").ID(1).Update(new(UserExpr)) 52 | assert.NoError(t, err) 53 | assert.EqualValues(t, 1, cnt) 54 | 55 | tableName := testEngine.TableName(new(UserExprIssue), true) 56 | cnt, err = testEngine.SetExpr("issue_id", 57 | builder.Select("id"). 58 | From(tableName). 59 | Where(builder.Eq{"id": issue.Id})). 60 | ID(1). 61 | Update(new(UserExpr)) 62 | assert.NoError(t, err) 63 | assert.EqualValues(t, 1, cnt) 64 | } 65 | 66 | func TestCols(t *testing.T) { 67 | assert.NoError(t, prepareEngine()) 68 | 69 | type ColsTable struct { 70 | Id int64 71 | Col1 string 72 | Col2 string 73 | } 74 | 75 | assertSync(t, new(ColsTable)) 76 | 77 | _, err := testEngine.Insert(&ColsTable{ 78 | Col1: "1", 79 | Col2: "2", 80 | }) 81 | assert.NoError(t, err) 82 | 83 | sess := testEngine.ID(1) 84 | _, err = sess.Cols("col1").Cols("col2").Update(&ColsTable{ 85 | Col1: "", 86 | Col2: "", 87 | }) 88 | assert.NoError(t, err) 89 | 90 | var tb ColsTable 91 | has, err := testEngine.ID(1).Get(&tb) 92 | assert.NoError(t, err) 93 | assert.True(t, has) 94 | assert.EqualValues(t, "", tb.Col1) 95 | assert.EqualValues(t, "", tb.Col2) 96 | } 97 | 98 | func TestMustCol(t *testing.T) { 99 | assert.NoError(t, prepareEngine()) 100 | 101 | type CustomerUpdate struct { 102 | Id int64 `form:"id" json:"id"` 103 | Username string `form:"username" json:"username" binding:"required"` 104 | Email string `form:"email" json:"email"` 105 | Sex int `form:"sex" json:"sex"` 106 | Name string `form:"name" json:"name" binding:"required"` 107 | Telephone string `form:"telephone" json:"telephone"` 108 | Type int `form:"type" json:"type" binding:"required"` 109 | ParentId int64 `form:"parent_id" json:"parent_id" xorm:"int null"` 110 | Remark string `form:"remark" json:"remark"` 111 | Status int `form:"status" json:"status" binding:"required"` 112 | Age int `form:"age" json:"age"` 113 | CreatedAt int64 `xorm:"created" form:"created_at" json:"created_at"` 114 | UpdatedAt int64 `xorm:"updated" form:"updated_at" json:"updated_at"` 115 | BirthDate int64 `form:"birth_date" json:"birth_date"` 116 | Password string `xorm:"varchar(200)" form:"password" json:"password"` 117 | } 118 | 119 | assertSync(t, new(CustomerUpdate)) 120 | 121 | var customer = CustomerUpdate{ 122 | ParentId: 1, 123 | } 124 | cnt, err := testEngine.Insert(&customer) 125 | assert.NoError(t, err) 126 | assert.EqualValues(t, 1, cnt) 127 | 128 | type CustomerOnlyId struct { 129 | Id int64 130 | } 131 | 132 | customer.ParentId = 0 133 | affected, err := testEngine.MustCols("parent_id").Update(&customer, &CustomerOnlyId{Id: customer.Id}) 134 | assert.NoError(t, err) 135 | assert.EqualValues(t, 1, affected) 136 | } 137 | -------------------------------------------------------------------------------- /session_cond.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import "xorm.io/builder" 8 | 9 | // Sql provides raw sql input parameter. When you have a complex SQL statement 10 | // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. 11 | // 12 | // Deprecated: use SQL instead. 13 | func (session *Session) Sql(query string, args ...interface{}) *Session { 14 | return session.SQL(query, args...) 15 | } 16 | 17 | // SQL provides raw sql input parameter. When you have a complex SQL statement 18 | // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. 19 | func (session *Session) SQL(query interface{}, args ...interface{}) *Session { 20 | session.statement.SQL(query, args...) 21 | return session 22 | } 23 | 24 | // Where provides custom query condition. 25 | func (session *Session) Where(query interface{}, args ...interface{}) *Session { 26 | session.statement.Where(query, args...) 27 | return session 28 | } 29 | 30 | // And provides custom query condition. 31 | func (session *Session) And(query interface{}, args ...interface{}) *Session { 32 | session.statement.And(query, args...) 33 | return session 34 | } 35 | 36 | // Or provides custom query condition. 37 | func (session *Session) Or(query interface{}, args ...interface{}) *Session { 38 | session.statement.Or(query, args...) 39 | return session 40 | } 41 | 42 | // Id provides converting id as a query condition 43 | // 44 | // Deprecated: use ID instead 45 | func (session *Session) Id(id interface{}) *Session { 46 | return session.ID(id) 47 | } 48 | 49 | // ID provides converting id as a query condition 50 | func (session *Session) ID(id interface{}) *Session { 51 | session.statement.ID(id) 52 | return session 53 | } 54 | 55 | // In provides a query string like "id in (1, 2, 3)" 56 | func (session *Session) In(column string, args ...interface{}) *Session { 57 | session.statement.In(column, args...) 58 | return session 59 | } 60 | 61 | // NotIn provides a query string like "id in (1, 2, 3)" 62 | func (session *Session) NotIn(column string, args ...interface{}) *Session { 63 | session.statement.NotIn(column, args...) 64 | return session 65 | } 66 | 67 | // Conds returns session query conditions except auto bean conditions 68 | func (session *Session) Conds() builder.Cond { 69 | return session.statement.cond 70 | } 71 | -------------------------------------------------------------------------------- /session_cond_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "testing" 11 | 12 | "xorm.io/builder" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestBuilder(t *testing.T) { 17 | assert.NoError(t, prepareEngine()) 18 | 19 | const ( 20 | OpEqual int = iota 21 | OpGreatThan 22 | OpLessThan 23 | ) 24 | 25 | type Condition struct { 26 | Id int64 27 | TableName string 28 | ColName string 29 | Op int 30 | Value string 31 | } 32 | 33 | err := testEngine.CreateTables(&Condition{}) 34 | assert.NoError(t, err) 35 | 36 | _, err = testEngine.Insert(&Condition{TableName: "table1", ColName: "col1", Op: OpEqual, Value: "1"}) 37 | assert.NoError(t, err) 38 | 39 | var cond Condition 40 | has, err := testEngine.Where(builder.Eq{"col_name": "col1"}).Get(&cond) 41 | assert.NoError(t, err) 42 | assert.Equal(t, true, has, "records should exist") 43 | 44 | has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. 45 | And(builder.Eq{"op": OpEqual})). 46 | NoAutoCondition(). 47 | Get(&cond) 48 | assert.NoError(t, err) 49 | assert.Equal(t, true, has, "records should exist") 50 | 51 | has, err = testEngine.Where(builder.Eq{"col_name": "col1", "op": OpEqual, "value": "1"}). 52 | NoAutoCondition(). 53 | Get(&cond) 54 | assert.NoError(t, err) 55 | assert.Equal(t, true, has, "records should exist") 56 | 57 | has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. 58 | And(builder.Neq{"op": OpEqual})). 59 | NoAutoCondition(). 60 | Get(&cond) 61 | assert.NoError(t, err) 62 | assert.Equal(t, false, has, "records should not exist") 63 | 64 | var conds []Condition 65 | err = testEngine.Where(builder.Eq{"col_name": "col1"}. 66 | And(builder.Eq{"op": OpEqual})). 67 | Find(&conds) 68 | assert.NoError(t, err) 69 | assert.EqualValues(t, 1, len(conds), "records should exist") 70 | 71 | conds = make([]Condition, 0) 72 | err = testEngine.Where(builder.Like{"col_name", "col"}).Find(&conds) 73 | assert.NoError(t, err) 74 | assert.EqualValues(t, 1, len(conds), "records should exist") 75 | 76 | conds = make([]Condition, 0) 77 | err = testEngine.Where(builder.Expr("col_name = ?", "col1")).Find(&conds) 78 | assert.NoError(t, err) 79 | assert.EqualValues(t, 1, len(conds), "records should exist") 80 | 81 | conds = make([]Condition, 0) 82 | err = testEngine.Where(builder.In("col_name", "col1", "col2")).Find(&conds) 83 | assert.NoError(t, err) 84 | assert.EqualValues(t, 1, len(conds), "records should exist") 85 | 86 | conds = make([]Condition, 0) 87 | err = testEngine.NotIn("col_name", "col1", "col2").Find(&conds) 88 | assert.NoError(t, err) 89 | assert.EqualValues(t, 0, len(conds), "records should not exist") 90 | 91 | // complex condtions 92 | var where = builder.NewCond() 93 | if true { 94 | where = where.And(builder.Eq{"col_name": "col1"}) 95 | where = where.Or(builder.And(builder.In("col_name", "col1", "col2"), builder.Expr("col_name = ?", "col1"))) 96 | } 97 | 98 | conds = make([]Condition, 0) 99 | err = testEngine.Where(where).Find(&conds) 100 | assert.NoError(t, err) 101 | assert.EqualValues(t, 1, len(conds), "records should exist") 102 | } 103 | 104 | func TestIn(t *testing.T) { 105 | assert.NoError(t, prepareEngine()) 106 | assert.NoError(t, testEngine.Sync2(new(Userinfo))) 107 | 108 | cnt, err := testEngine.Insert([]Userinfo{ 109 | { 110 | Username: "user1", 111 | Departname: "dev", 112 | }, 113 | { 114 | Username: "user2", 115 | Departname: "dev", 116 | }, 117 | { 118 | Username: "user3", 119 | Departname: "dev", 120 | }, 121 | }) 122 | assert.NoError(t, err) 123 | assert.EqualValues(t, 3, cnt) 124 | 125 | department := "`" + testEngine.GetColumnMapper().Obj2Table("Departname") + "`" 126 | var usrs []Userinfo 127 | err = testEngine.Where(department+" = ?", "dev").Limit(3).Find(&usrs) 128 | assert.NoError(t, err) 129 | assert.EqualValues(t, 3, len(usrs)) 130 | 131 | var ids []int64 132 | var idsStr string 133 | for _, u := range usrs { 134 | ids = append(ids, u.Uid) 135 | idsStr = fmt.Sprintf("%d,", u.Uid) 136 | } 137 | idsStr = idsStr[:len(idsStr)-1] 138 | 139 | users := make([]Userinfo, 0) 140 | err = testEngine.In("(id)", ids[0], ids[1], ids[2]).Find(&users) 141 | assert.NoError(t, err) 142 | fmt.Println(users) 143 | assert.EqualValues(t, 3, len(users)) 144 | 145 | users = make([]Userinfo, 0) 146 | err = testEngine.In("(id)", ids).Find(&users) 147 | assert.NoError(t, err) 148 | fmt.Println(users) 149 | assert.EqualValues(t, 3, len(users)) 150 | 151 | for _, user := range users { 152 | if user.Uid != ids[0] && user.Uid != ids[1] && user.Uid != ids[2] { 153 | err = errors.New("in uses should be " + idsStr + " total 3") 154 | assert.NoError(t, err) 155 | } 156 | } 157 | 158 | users = make([]Userinfo, 0) 159 | var idsInterface []interface{} 160 | for _, id := range ids { 161 | idsInterface = append(idsInterface, id) 162 | } 163 | 164 | err = testEngine.Where(department+" = ?", "dev").In("(id)", idsInterface...).Find(&users) 165 | assert.NoError(t, err) 166 | fmt.Println(users) 167 | assert.EqualValues(t, 3, len(users)) 168 | 169 | for _, user := range users { 170 | if user.Uid != ids[0] && user.Uid != ids[1] && user.Uid != ids[2] { 171 | err = errors.New("in uses should be " + idsStr + " total 3") 172 | assert.NoError(t, err) 173 | } 174 | } 175 | 176 | dev := testEngine.GetColumnMapper().Obj2Table("Dev") 177 | 178 | err = testEngine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) 179 | assert.NoError(t, err) 180 | fmt.Println(users) 181 | 182 | cnt, err = testEngine.In("(id)", ids[0]).Update(&Userinfo{Departname: "dev-"}) 183 | assert.NoError(t, err) 184 | assert.EqualValues(t, 1, cnt) 185 | 186 | user := new(Userinfo) 187 | has, err := testEngine.ID(ids[0]).Get(user) 188 | assert.NoError(t, err) 189 | assert.True(t, has) 190 | assert.EqualValues(t, "dev-", user.Departname) 191 | 192 | cnt, err = testEngine.In("(id)", ids[0]).Update(&Userinfo{Departname: "dev"}) 193 | assert.NoError(t, err) 194 | assert.EqualValues(t, 1, cnt) 195 | 196 | cnt, err = testEngine.In("(id)", ids[1]).Delete(&Userinfo{}) 197 | assert.NoError(t, err) 198 | assert.EqualValues(t, 1, cnt) 199 | } 200 | 201 | func TestFindAndCount(t *testing.T) { 202 | assert.NoError(t, prepareEngine()) 203 | 204 | type FindAndCount struct { 205 | Id int64 206 | Name string 207 | } 208 | 209 | assert.NoError(t, testEngine.Sync2(new(FindAndCount))) 210 | 211 | _, err := testEngine.Insert([]FindAndCount{ 212 | { 213 | Name: "test1", 214 | }, 215 | { 216 | Name: "test2", 217 | }, 218 | }) 219 | assert.NoError(t, err) 220 | 221 | var results []FindAndCount 222 | sess := testEngine.Where("name = ?", "test1") 223 | conds := sess.Conds() 224 | err = sess.Find(&results) 225 | assert.NoError(t, err) 226 | assert.EqualValues(t, 1, len(results)) 227 | 228 | total, err := testEngine.Where(conds).Count(new(FindAndCount)) 229 | assert.NoError(t, err) 230 | assert.EqualValues(t, 1, total) 231 | } 232 | -------------------------------------------------------------------------------- /session_context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import "context" 8 | 9 | // Context sets the context on this session 10 | func (session *Session) Context(ctx context.Context) *Session { 11 | session.ctx = ctx 12 | return session 13 | } 14 | 15 | // PingContext test if database is ok 16 | func (session *Session) PingContext(ctx context.Context) error { 17 | if session.isAutoClose { 18 | defer session.Close() 19 | } 20 | 21 | session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName()) 22 | return session.DB().PingContext(ctx) 23 | } 24 | -------------------------------------------------------------------------------- /session_context_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "context" 9 | "testing" 10 | "time" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestQueryContext(t *testing.T) { 16 | type ContextQueryStruct struct { 17 | Id int64 18 | Name string 19 | } 20 | 21 | assert.NoError(t, prepareEngine()) 22 | assertSync(t, new(ContextQueryStruct)) 23 | 24 | _, err := testEngine.Insert(&ContextQueryStruct{Name: "1"}) 25 | assert.NoError(t, err) 26 | 27 | ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond) 28 | defer cancel() 29 | 30 | time.Sleep(time.Nanosecond) 31 | 32 | has, err := testEngine.Context(ctx).Exist(&ContextQueryStruct{Name: "1"}) 33 | assert.Error(t, err) 34 | assert.Contains(t, err.Error(), "context deadline exceeded") 35 | assert.False(t, has) 36 | } 37 | -------------------------------------------------------------------------------- /session_delete.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "strconv" 11 | 12 | "xorm.io/core" 13 | ) 14 | 15 | func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error { 16 | if table == nil || 17 | session.tx != nil { 18 | return ErrCacheFailed 19 | } 20 | 21 | for _, filter := range session.engine.dialect.Filters() { 22 | sqlStr = filter.Do(sqlStr, session.engine.dialect, table) 23 | } 24 | 25 | newsql := session.statement.convertIDSQL(sqlStr) 26 | if newsql == "" { 27 | return ErrCacheFailed 28 | } 29 | 30 | cacher := session.engine.getCacher(tableName) 31 | pkColumns := table.PKColumns() 32 | ids, err := core.GetCacheSql(cacher, tableName, newsql, args) 33 | if err != nil { 34 | resultsSlice, err := session.queryBytes(newsql, args...) 35 | if err != nil { 36 | return err 37 | } 38 | ids = make([]core.PK, 0) 39 | if len(resultsSlice) > 0 { 40 | for _, data := range resultsSlice { 41 | var id int64 42 | var pk core.PK = make([]interface{}, 0) 43 | for _, col := range pkColumns { 44 | if v, ok := data[col.Name]; !ok { 45 | return errors.New("no id") 46 | } else if col.SQLType.IsText() { 47 | pk = append(pk, string(v)) 48 | } else if col.SQLType.IsNumeric() { 49 | id, err = strconv.ParseInt(string(v), 10, 64) 50 | if err != nil { 51 | return err 52 | } 53 | pk = append(pk, id) 54 | } else { 55 | return errors.New("not supported primary key type") 56 | } 57 | } 58 | ids = append(ids, pk) 59 | } 60 | } 61 | } 62 | 63 | for _, id := range ids { 64 | session.engine.logger.Debug("[cacheDelete] delete cache obj:", tableName, id) 65 | sid, err := id.ToString() 66 | if err != nil { 67 | return err 68 | } 69 | cacher.DelBean(tableName, sid) 70 | } 71 | session.engine.logger.Debug("[cacheDelete] clear cache table:", tableName) 72 | cacher.ClearIds(tableName) 73 | return nil 74 | } 75 | 76 | // Delete records, bean's non-empty fields are conditions 77 | func (session *Session) Delete(bean interface{}) (int64, error) { 78 | if session.isAutoClose { 79 | defer session.Close() 80 | } 81 | 82 | if session.statement.lastError != nil { 83 | return 0, session.statement.lastError 84 | } 85 | 86 | if err := session.statement.setRefBean(bean); err != nil { 87 | return 0, err 88 | } 89 | 90 | // handle before delete processors 91 | for _, closure := range session.beforeClosures { 92 | closure(bean) 93 | } 94 | cleanupProcessorsClosures(&session.beforeClosures) 95 | 96 | if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok { 97 | processor.BeforeDelete() 98 | } 99 | 100 | condSQL, condArgs, err := session.statement.genConds(bean) 101 | if err != nil { 102 | return 0, err 103 | } 104 | if len(condSQL) == 0 && session.statement.LimitN == 0 { 105 | return 0, ErrNeedDeletedCond 106 | } 107 | 108 | var tableNameNoQuote = session.statement.TableName() 109 | var tableName = session.engine.Quote(tableNameNoQuote) 110 | var table = session.statement.RefTable 111 | var deleteSQL string 112 | if len(condSQL) > 0 { 113 | deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL) 114 | } else { 115 | deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName) 116 | } 117 | 118 | var orderSQL string 119 | if len(session.statement.OrderStr) > 0 { 120 | orderSQL += fmt.Sprintf(" ORDER BY %s", session.statement.OrderStr) 121 | } 122 | if session.statement.LimitN > 0 { 123 | orderSQL += fmt.Sprintf(" LIMIT %d", session.statement.LimitN) 124 | } 125 | 126 | if len(orderSQL) > 0 { 127 | switch session.engine.dialect.DBType() { 128 | case core.POSTGRES: 129 | inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) 130 | if len(condSQL) > 0 { 131 | deleteSQL += " AND " + inSQL 132 | } else { 133 | deleteSQL += " WHERE " + inSQL 134 | } 135 | case core.SQLITE: 136 | inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) 137 | if len(condSQL) > 0 { 138 | deleteSQL += " AND " + inSQL 139 | } else { 140 | deleteSQL += " WHERE " + inSQL 141 | } 142 | // TODO: how to handle delete limit on mssql? 143 | case core.MSSQL: 144 | return 0, ErrNotImplemented 145 | default: 146 | deleteSQL += orderSQL 147 | } 148 | } 149 | 150 | var realSQL string 151 | argsForCache := make([]interface{}, 0, len(condArgs)*2) 152 | if session.statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled 153 | realSQL = deleteSQL 154 | copy(argsForCache, condArgs) 155 | argsForCache = append(condArgs, argsForCache...) 156 | } else { 157 | // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache. 158 | copy(argsForCache, condArgs) 159 | argsForCache = append(condArgs, argsForCache...) 160 | 161 | deletedColumn := table.DeletedColumn() 162 | realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v", 163 | session.engine.Quote(session.statement.TableName()), 164 | session.engine.Quote(deletedColumn.Name), 165 | condSQL) 166 | 167 | if len(orderSQL) > 0 { 168 | switch session.engine.dialect.DBType() { 169 | case core.POSTGRES: 170 | inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) 171 | if len(condSQL) > 0 { 172 | realSQL += " AND " + inSQL 173 | } else { 174 | realSQL += " WHERE " + inSQL 175 | } 176 | case core.SQLITE: 177 | inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) 178 | if len(condSQL) > 0 { 179 | realSQL += " AND " + inSQL 180 | } else { 181 | realSQL += " WHERE " + inSQL 182 | } 183 | // TODO: how to handle delete limit on mssql? 184 | case core.MSSQL: 185 | return 0, ErrNotImplemented 186 | default: 187 | realSQL += orderSQL 188 | } 189 | } 190 | 191 | // !oinume! Insert nowTime to the head of session.statement.Params 192 | condArgs = append(condArgs, "") 193 | paramsLen := len(condArgs) 194 | copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1]) 195 | 196 | val, t := session.engine.nowTime(deletedColumn) 197 | condArgs[0] = val 198 | 199 | var colName = deletedColumn.Name 200 | session.afterClosures = append(session.afterClosures, func(bean interface{}) { 201 | col := table.GetColumn(colName) 202 | setColumnTime(bean, col, t) 203 | }) 204 | } 205 | 206 | if cacher := session.engine.getCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache { 207 | session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) 208 | } 209 | 210 | session.statement.RefTable = table 211 | res, err := session.exec(realSQL, condArgs...) 212 | if err != nil { 213 | return 0, err 214 | } 215 | 216 | // handle after delete processors 217 | if session.isAutoCommit { 218 | for _, closure := range session.afterClosures { 219 | closure(bean) 220 | } 221 | if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok { 222 | processor.AfterDelete() 223 | } 224 | } else { 225 | lenAfterClosures := len(session.afterClosures) 226 | if lenAfterClosures > 0 { 227 | if value, has := session.afterDeleteBeans[bean]; has && value != nil { 228 | *value = append(*value, session.afterClosures...) 229 | } else { 230 | afterClosures := make([]func(interface{}), lenAfterClosures) 231 | copy(afterClosures, session.afterClosures) 232 | session.afterDeleteBeans[bean] = &afterClosures 233 | } 234 | } else { 235 | if _, ok := interface{}(bean).(AfterDeleteProcessor); ok { 236 | session.afterDeleteBeans[bean] = nil 237 | } 238 | } 239 | } 240 | cleanupProcessorsClosures(&session.afterClosures) 241 | // -- 242 | 243 | return res.RowsAffected() 244 | } 245 | -------------------------------------------------------------------------------- /session_delete_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | 11 | "xorm.io/core" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestDelete(t *testing.T) { 16 | assert.NoError(t, prepareEngine()) 17 | 18 | type UserinfoDelete struct { 19 | Uid int64 `xorm:"id pk not null autoincr"` 20 | IsMan bool 21 | } 22 | 23 | assert.NoError(t, testEngine.Sync2(new(UserinfoDelete))) 24 | 25 | session := testEngine.NewSession() 26 | defer session.Close() 27 | 28 | var err error 29 | if testEngine.Dialect().DBType() == core.MSSQL { 30 | err = session.Begin() 31 | assert.NoError(t, err) 32 | _, err = session.Exec("SET IDENTITY_INSERT userinfo_delete ON") 33 | assert.NoError(t, err) 34 | } 35 | 36 | user := UserinfoDelete{Uid: 1} 37 | cnt, err := session.Insert(&user) 38 | assert.NoError(t, err) 39 | assert.EqualValues(t, 1, cnt) 40 | 41 | if testEngine.Dialect().DBType() == core.MSSQL { 42 | err = session.Commit() 43 | assert.NoError(t, err) 44 | } 45 | 46 | cnt, err = testEngine.Delete(&UserinfoDelete{Uid: user.Uid}) 47 | assert.NoError(t, err) 48 | assert.EqualValues(t, 1, cnt) 49 | 50 | user.Uid = 0 51 | user.IsMan = true 52 | has, err := testEngine.ID(1).Get(&user) 53 | assert.NoError(t, err) 54 | assert.False(t, has) 55 | 56 | cnt, err = testEngine.Insert(&user) 57 | assert.NoError(t, err) 58 | assert.EqualValues(t, 1, cnt) 59 | 60 | cnt, err = testEngine.Where("`id`=?", user.Uid).Delete(&UserinfoDelete{}) 61 | assert.NoError(t, err) 62 | assert.EqualValues(t, 1, cnt) 63 | 64 | user.Uid = 0 65 | user.IsMan = true 66 | has, err = testEngine.ID(2).Get(&user) 67 | assert.NoError(t, err) 68 | assert.False(t, has) 69 | } 70 | 71 | func TestDeleted(t *testing.T) { 72 | assert.NoError(t, prepareEngine()) 73 | 74 | type Deleted struct { 75 | Id int64 `xorm:"pk"` 76 | Name string 77 | DeletedAt time.Time `xorm:"deleted"` 78 | } 79 | 80 | err := testEngine.DropTables(&Deleted{}) 81 | assert.NoError(t, err) 82 | 83 | err = testEngine.CreateTables(&Deleted{}) 84 | assert.NoError(t, err) 85 | 86 | _, err = testEngine.InsertOne(&Deleted{Id: 1, Name: "11111"}) 87 | assert.NoError(t, err) 88 | 89 | _, err = testEngine.InsertOne(&Deleted{Id: 2, Name: "22222"}) 90 | assert.NoError(t, err) 91 | 92 | _, err = testEngine.InsertOne(&Deleted{Id: 3, Name: "33333"}) 93 | assert.NoError(t, err) 94 | 95 | // Test normal Find() 96 | var records1 []Deleted 97 | err = testEngine.Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").Find(&records1, &Deleted{}) 98 | assert.EqualValues(t, 3, len(records1)) 99 | 100 | // Test normal Get() 101 | record1 := &Deleted{} 102 | has, err := testEngine.ID(1).Get(record1) 103 | assert.NoError(t, err) 104 | assert.True(t, has) 105 | 106 | // Test Delete() with deleted 107 | affected, err := testEngine.ID(1).Delete(&Deleted{}) 108 | assert.NoError(t, err) 109 | assert.EqualValues(t, 1, affected) 110 | 111 | has, err = testEngine.ID(1).Get(&Deleted{}) 112 | assert.NoError(t, err) 113 | assert.False(t, has) 114 | 115 | var records2 []Deleted 116 | err = testEngine.Where("`" + testEngine.GetColumnMapper().Obj2Table("Id") + "` > 0").Find(&records2) 117 | assert.NoError(t, err) 118 | assert.EqualValues(t, 2, len(records2)) 119 | 120 | // Test no rows affected after Delete() again. 121 | affected, err = testEngine.ID(1).Delete(&Deleted{}) 122 | assert.NoError(t, err) 123 | assert.EqualValues(t, 0, affected) 124 | 125 | // Deleted.DeletedAt must not be updated. 126 | affected, err = testEngine.ID(2).Update(&Deleted{Name: "2", DeletedAt: time.Now()}) 127 | assert.NoError(t, err) 128 | assert.EqualValues(t, 1, affected) 129 | 130 | record2 := &Deleted{} 131 | has, err = testEngine.ID(2).Get(record2) 132 | assert.NoError(t, err) 133 | assert.True(t, record2.DeletedAt.IsZero()) 134 | 135 | // Test find all records whatever `deleted`. 136 | var unscopedRecords1 []Deleted 137 | err = testEngine.Unscoped().Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").Find(&unscopedRecords1, &Deleted{}) 138 | assert.NoError(t, err) 139 | assert.EqualValues(t, 3, len(unscopedRecords1)) 140 | 141 | // Delete() must really delete a record with Unscoped() 142 | affected, err = testEngine.Unscoped().ID(1).Delete(&Deleted{}) 143 | assert.NoError(t, err) 144 | assert.EqualValues(t, 1, affected) 145 | 146 | var unscopedRecords2 []Deleted 147 | err = testEngine.Unscoped().Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").Find(&unscopedRecords2, &Deleted{}) 148 | assert.NoError(t, err) 149 | assert.EqualValues(t, 2, len(unscopedRecords2)) 150 | 151 | var records3 []Deleted 152 | err = testEngine.Where("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` > 0").And("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"`> 1"). 153 | Or("`"+testEngine.GetColumnMapper().Obj2Table("Id")+"` = ?", 3).Find(&records3) 154 | assert.NoError(t, err) 155 | assert.EqualValues(t, 2, len(records3)) 156 | } 157 | 158 | func TestCacheDelete(t *testing.T) { 159 | assert.NoError(t, prepareEngine()) 160 | 161 | oldCacher := testEngine.GetDefaultCacher() 162 | cacher := NewLRUCacher(NewMemoryStore(), 1000) 163 | testEngine.SetDefaultCacher(cacher) 164 | 165 | type CacheDeleteStruct struct { 166 | Id int64 167 | } 168 | 169 | err := testEngine.CreateTables(&CacheDeleteStruct{}) 170 | assert.NoError(t, err) 171 | 172 | _, err = testEngine.Insert(&CacheDeleteStruct{}) 173 | assert.NoError(t, err) 174 | 175 | aff, err := testEngine.Delete(&CacheDeleteStruct{ 176 | Id: 1, 177 | }) 178 | assert.NoError(t, err) 179 | assert.EqualValues(t, aff, 1) 180 | 181 | aff, err = testEngine.Unscoped().Delete(&CacheDeleteStruct{ 182 | Id: 1, 183 | }) 184 | assert.NoError(t, err) 185 | assert.EqualValues(t, aff, 0) 186 | 187 | testEngine.SetDefaultCacher(oldCacher) 188 | } 189 | 190 | func TestUnscopeDelete(t *testing.T) { 191 | assert.NoError(t, prepareEngine()) 192 | 193 | type UnscopeDeleteStruct struct { 194 | Id int64 195 | Name string 196 | DeletedAt time.Time `xorm:"deleted"` 197 | } 198 | 199 | assertSync(t, new(UnscopeDeleteStruct)) 200 | 201 | cnt, err := testEngine.Insert(&UnscopeDeleteStruct{ 202 | Name: "test", 203 | }) 204 | assert.NoError(t, err) 205 | assert.EqualValues(t, 1, cnt) 206 | 207 | var nowUnix = time.Now().Unix() 208 | var s UnscopeDeleteStruct 209 | cnt, err = testEngine.ID(1).Delete(&s) 210 | assert.NoError(t, err) 211 | assert.EqualValues(t, 1, cnt) 212 | assert.EqualValues(t, nowUnix, s.DeletedAt.Unix()) 213 | 214 | var s1 UnscopeDeleteStruct 215 | has, err := testEngine.ID(1).Get(&s1) 216 | assert.NoError(t, err) 217 | assert.False(t, has) 218 | 219 | var s2 UnscopeDeleteStruct 220 | has, err = testEngine.ID(1).Unscoped().Get(&s2) 221 | assert.NoError(t, err) 222 | assert.True(t, has) 223 | assert.EqualValues(t, "test", s2.Name) 224 | assert.EqualValues(t, nowUnix, s2.DeletedAt.Unix()) 225 | 226 | cnt, err = testEngine.ID(1).Unscoped().Delete(new(UnscopeDeleteStruct)) 227 | assert.NoError(t, err) 228 | assert.EqualValues(t, 1, cnt) 229 | 230 | var s3 UnscopeDeleteStruct 231 | has, err = testEngine.ID(1).Get(&s3) 232 | assert.NoError(t, err) 233 | assert.False(t, has) 234 | 235 | var s4 UnscopeDeleteStruct 236 | has, err = testEngine.ID(1).Unscoped().Get(&s4) 237 | assert.NoError(t, err) 238 | assert.False(t, has) 239 | } 240 | -------------------------------------------------------------------------------- /session_exist.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "reflect" 11 | 12 | "xorm.io/builder" 13 | "xorm.io/core" 14 | ) 15 | 16 | // Exist returns true if the record exist otherwise return false 17 | func (session *Session) Exist(bean ...interface{}) (bool, error) { 18 | if session.isAutoClose { 19 | defer session.Close() 20 | } 21 | 22 | if session.statement.lastError != nil { 23 | return false, session.statement.lastError 24 | } 25 | 26 | var sqlStr string 27 | var args []interface{} 28 | var err error 29 | 30 | if session.statement.RawSQL == "" { 31 | if len(bean) == 0 { 32 | tableName := session.statement.TableName() 33 | if len(tableName) <= 0 { 34 | return false, ErrTableNotFound 35 | } 36 | 37 | tableName = session.statement.Engine.Quote(tableName) 38 | 39 | if session.statement.cond.IsValid() { 40 | condSQL, condArgs, err := builder.ToSQL(session.statement.cond) 41 | if err != nil { 42 | return false, err 43 | } 44 | 45 | if session.engine.dialect.DBType() == core.MSSQL { 46 | sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s WHERE %s", tableName, condSQL) 47 | } else if session.engine.dialect.DBType() == core.ORACLE { 48 | sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) AND ROWNUM=1", tableName, condSQL) 49 | } else { 50 | sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) 51 | } 52 | args = condArgs 53 | } else { 54 | if session.engine.dialect.DBType() == core.MSSQL { 55 | sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s", tableName) 56 | } else if session.engine.dialect.DBType() == core.ORACLE { 57 | sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE ROWNUM=1", tableName) 58 | } else { 59 | sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) 60 | } 61 | args = []interface{}{} 62 | } 63 | } else { 64 | beanValue := reflect.ValueOf(bean[0]) 65 | if beanValue.Kind() != reflect.Ptr { 66 | return false, errors.New("needs a pointer") 67 | } 68 | 69 | if beanValue.Elem().Kind() == reflect.Struct { 70 | if err := session.statement.setRefBean(bean[0]); err != nil { 71 | return false, err 72 | } 73 | } 74 | 75 | if len(session.statement.TableName()) <= 0 { 76 | return false, ErrTableNotFound 77 | } 78 | session.statement.Limit(1) 79 | sqlStr, args, err = session.statement.genGetSQL(bean[0]) 80 | if err != nil { 81 | return false, err 82 | } 83 | } 84 | } else { 85 | sqlStr = session.statement.RawSQL 86 | args = session.statement.RawParams 87 | } 88 | 89 | rows, err := session.queryRows(sqlStr, args...) 90 | if err != nil { 91 | return false, err 92 | } 93 | defer rows.Close() 94 | 95 | return rows.Next(), nil 96 | } 97 | -------------------------------------------------------------------------------- /session_exist_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestExistStruct(t *testing.T) { 14 | assert.NoError(t, prepareEngine()) 15 | 16 | type RecordExist struct { 17 | Id int64 18 | Name string 19 | } 20 | 21 | assertSync(t, new(RecordExist)) 22 | 23 | has, err := testEngine.Exist(new(RecordExist)) 24 | assert.NoError(t, err) 25 | assert.False(t, has) 26 | 27 | cnt, err := testEngine.Insert(&RecordExist{ 28 | Name: "test1", 29 | }) 30 | assert.NoError(t, err) 31 | assert.EqualValues(t, 1, cnt) 32 | 33 | has, err = testEngine.Exist(new(RecordExist)) 34 | assert.NoError(t, err) 35 | assert.True(t, has) 36 | 37 | has, err = testEngine.Exist(&RecordExist{ 38 | Name: "test1", 39 | }) 40 | assert.NoError(t, err) 41 | assert.True(t, has) 42 | 43 | has, err = testEngine.Exist(&RecordExist{ 44 | Name: "test2", 45 | }) 46 | assert.NoError(t, err) 47 | assert.False(t, has) 48 | 49 | has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) 50 | assert.NoError(t, err) 51 | assert.True(t, has) 52 | 53 | has, err = testEngine.Where("name = ?", "test2").Exist(&RecordExist{}) 54 | assert.NoError(t, err) 55 | assert.False(t, has) 56 | 57 | has, err = testEngine.SQL("select * from "+testEngine.TableName("record_exist", true)+" where name = ?", "test1").Exist() 58 | assert.NoError(t, err) 59 | assert.True(t, has) 60 | 61 | has, err = testEngine.SQL("select * from "+testEngine.TableName("record_exist", true)+" where name = ?", "test2").Exist() 62 | assert.NoError(t, err) 63 | assert.False(t, has) 64 | 65 | has, err = testEngine.Table("record_exist").Exist() 66 | assert.NoError(t, err) 67 | assert.True(t, has) 68 | 69 | has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() 70 | assert.NoError(t, err) 71 | assert.True(t, has) 72 | 73 | has, err = testEngine.Table("record_exist").Where("name = ?", "test2").Exist() 74 | assert.NoError(t, err) 75 | assert.False(t, has) 76 | } 77 | -------------------------------------------------------------------------------- /session_iterate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import "reflect" 8 | 9 | // IterFunc only use by Iterate 10 | type IterFunc func(idx int, bean interface{}) error 11 | 12 | // Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields 13 | // are conditions. 14 | func (session *Session) Rows(bean interface{}) (*Rows, error) { 15 | return newRows(session, bean) 16 | } 17 | 18 | // Iterate record by record handle records from table, condiBeans's non-empty fields 19 | // are conditions. beans could be []Struct, []*Struct, map[int64]Struct 20 | // map[int64]*Struct 21 | func (session *Session) Iterate(bean interface{}, fun IterFunc) error { 22 | if session.isAutoClose { 23 | defer session.Close() 24 | } 25 | 26 | if session.statement.lastError != nil { 27 | return session.statement.lastError 28 | } 29 | 30 | if session.statement.bufferSize > 0 { 31 | return session.bufferIterate(bean, fun) 32 | } 33 | 34 | rows, err := session.Rows(bean) 35 | if err != nil { 36 | return err 37 | } 38 | defer rows.Close() 39 | 40 | i := 0 41 | for rows.Next() { 42 | b := reflect.New(rows.beanType).Interface() 43 | err = rows.Scan(b) 44 | if err != nil { 45 | return err 46 | } 47 | err = fun(i, b) 48 | if err != nil { 49 | return err 50 | } 51 | i++ 52 | } 53 | return err 54 | } 55 | 56 | // BufferSize sets the buffersize for iterate 57 | func (session *Session) BufferSize(size int) *Session { 58 | session.statement.bufferSize = size 59 | return session 60 | } 61 | 62 | func (session *Session) bufferIterate(bean interface{}, fun IterFunc) error { 63 | if session.isAutoClose { 64 | defer session.Close() 65 | } 66 | 67 | var bufferSize = session.statement.bufferSize 68 | var limit = session.statement.LimitN 69 | if limit > 0 && bufferSize > limit { 70 | bufferSize = limit 71 | } 72 | var start = session.statement.Start 73 | v := rValue(bean) 74 | sliceType := reflect.SliceOf(v.Type()) 75 | var idx = 0 76 | for { 77 | slice := reflect.New(sliceType) 78 | if err := session.Limit(bufferSize, start).find(slice.Interface(), bean); err != nil { 79 | return err 80 | } 81 | 82 | for i := 0; i < slice.Elem().Len(); i++ { 83 | if err := fun(idx, slice.Elem().Index(i).Addr().Interface()); err != nil { 84 | return err 85 | } 86 | idx++ 87 | } 88 | 89 | start = start + slice.Elem().Len() 90 | if limit > 0 && idx+bufferSize > limit { 91 | bufferSize = limit - idx 92 | } 93 | 94 | if bufferSize <= 0 || slice.Elem().Len() < bufferSize || idx == limit { 95 | break 96 | } 97 | } 98 | 99 | return nil 100 | } 101 | -------------------------------------------------------------------------------- /session_iterate_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestIterate(t *testing.T) { 14 | assert.NoError(t, prepareEngine()) 15 | 16 | type UserIterate struct { 17 | Id int64 18 | IsMan bool 19 | } 20 | 21 | assert.NoError(t, testEngine.Sync2(new(UserIterate))) 22 | 23 | cnt, err := testEngine.Insert(&UserIterate{ 24 | IsMan: true, 25 | }) 26 | assert.NoError(t, err) 27 | assert.EqualValues(t, 1, cnt) 28 | 29 | cnt = 0 30 | err = testEngine.Iterate(new(UserIterate), func(i int, bean interface{}) error { 31 | user := bean.(*UserIterate) 32 | assert.EqualValues(t, 1, user.Id) 33 | assert.EqualValues(t, true, user.IsMan) 34 | cnt++ 35 | return nil 36 | }) 37 | assert.NoError(t, err) 38 | assert.EqualValues(t, 1, cnt) 39 | } 40 | 41 | func TestBufferIterate(t *testing.T) { 42 | assert.NoError(t, prepareEngine()) 43 | 44 | type UserBufferIterate struct { 45 | Id int64 46 | IsMan bool 47 | } 48 | 49 | assert.NoError(t, testEngine.Sync2(new(UserBufferIterate))) 50 | 51 | var size = 20 52 | for i := 0; i < size; i++ { 53 | cnt, err := testEngine.Insert(&UserBufferIterate{ 54 | IsMan: true, 55 | }) 56 | assert.NoError(t, err) 57 | assert.EqualValues(t, 1, cnt) 58 | } 59 | 60 | var cnt = 0 61 | err := testEngine.BufferSize(9).Iterate(new(UserBufferIterate), func(i int, bean interface{}) error { 62 | user := bean.(*UserBufferIterate) 63 | assert.EqualValues(t, cnt+1, user.Id) 64 | assert.EqualValues(t, true, user.IsMan) 65 | cnt++ 66 | return nil 67 | }) 68 | assert.NoError(t, err) 69 | assert.EqualValues(t, size, cnt) 70 | 71 | cnt = 0 72 | err = testEngine.Limit(20).BufferSize(9).Iterate(new(UserBufferIterate), func(i int, bean interface{}) error { 73 | user := bean.(*UserBufferIterate) 74 | assert.EqualValues(t, cnt+1, user.Id) 75 | assert.EqualValues(t, true, user.IsMan) 76 | cnt++ 77 | return nil 78 | }) 79 | assert.NoError(t, err) 80 | assert.EqualValues(t, size, cnt) 81 | 82 | cnt = 0 83 | err = testEngine.Limit(7).BufferSize(9).Iterate(new(UserBufferIterate), func(i int, bean interface{}) error { 84 | user := bean.(*UserBufferIterate) 85 | assert.EqualValues(t, cnt+1, user.Id) 86 | assert.EqualValues(t, true, user.IsMan) 87 | cnt++ 88 | return nil 89 | }) 90 | assert.NoError(t, err) 91 | assert.EqualValues(t, 7, cnt) 92 | } 93 | -------------------------------------------------------------------------------- /session_raw.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "database/sql" 9 | "reflect" 10 | "time" 11 | 12 | "xorm.io/builder" 13 | "xorm.io/core" 14 | ) 15 | 16 | func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { 17 | for _, filter := range session.engine.dialect.Filters() { 18 | *sqlStr = filter.Do(*sqlStr, session.engine.dialect, session.statement.RefTable) 19 | } 20 | 21 | session.lastSQL = *sqlStr 22 | session.lastSQLArgs = paramStr 23 | } 24 | 25 | func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Rows, error) { 26 | defer session.resetStatement() 27 | 28 | session.queryPreprocess(&sqlStr, args...) 29 | 30 | if session.engine.showSQL { 31 | if session.engine.showExecTime { 32 | b4ExecTime := time.Now() 33 | defer func() { 34 | execDuration := time.Since(b4ExecTime) 35 | if len(args) > 0 { 36 | session.engine.logger.Infof("[SQL] %s %#v - took: %v", sqlStr, args, execDuration) 37 | } else { 38 | session.engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration) 39 | } 40 | }() 41 | } else { 42 | if len(args) > 0 { 43 | session.engine.logger.Infof("[SQL] %v %#v", sqlStr, args) 44 | } else { 45 | session.engine.logger.Infof("[SQL] %v", sqlStr) 46 | } 47 | } 48 | } 49 | 50 | if session.isAutoCommit { 51 | var db *core.DB 52 | if session.sessionType == groupSession { 53 | db = session.engine.engineGroup.Slave().DB() 54 | } else { 55 | db = session.DB() 56 | } 57 | 58 | if session.prepareStmt { 59 | // don't clear stmt since session will cache them 60 | stmt, err := session.doPrepare(db, sqlStr) 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | rows, err := stmt.QueryContext(session.ctx, args...) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return rows, nil 70 | } 71 | 72 | rows, err := db.QueryContext(session.ctx, sqlStr, args...) 73 | if err != nil { 74 | return nil, err 75 | } 76 | return rows, nil 77 | } 78 | 79 | rows, err := session.tx.QueryContext(session.ctx, sqlStr, args...) 80 | if err != nil { 81 | return nil, err 82 | } 83 | return rows, nil 84 | } 85 | 86 | func (session *Session) queryRow(sqlStr string, args ...interface{}) *core.Row { 87 | return core.NewRow(session.queryRows(sqlStr, args...)) 88 | } 89 | 90 | func value2Bytes(rawValue *reflect.Value) ([]byte, error) { 91 | str, err := value2String(rawValue) 92 | if err != nil { 93 | return nil, err 94 | } 95 | return []byte(str), nil 96 | } 97 | 98 | func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) { 99 | result := make(map[string][]byte) 100 | scanResultContainers := make([]interface{}, len(fields)) 101 | for i := 0; i < len(fields); i++ { 102 | var scanResultContainer interface{} 103 | scanResultContainers[i] = &scanResultContainer 104 | } 105 | if err := rows.Scan(scanResultContainers...); err != nil { 106 | return nil, err 107 | } 108 | 109 | for ii, key := range fields { 110 | rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) 111 | //if row is null then ignore 112 | if rawValue.Interface() == nil { 113 | result[key] = []byte{} 114 | continue 115 | } 116 | 117 | if data, err := value2Bytes(&rawValue); err == nil { 118 | result[key] = data 119 | } else { 120 | return nil, err // !nashtsai! REVIEW, should return err or just error log? 121 | } 122 | } 123 | return result, nil 124 | } 125 | 126 | func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) { 127 | fields, err := rows.Columns() 128 | if err != nil { 129 | return nil, err 130 | } 131 | for rows.Next() { 132 | result, err := row2map(rows, fields) 133 | if err != nil { 134 | return nil, err 135 | } 136 | resultsSlice = append(resultsSlice, result) 137 | } 138 | 139 | return resultsSlice, nil 140 | } 141 | 142 | func (session *Session) queryBytes(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { 143 | rows, err := session.queryRows(sqlStr, args...) 144 | if err != nil { 145 | return nil, err 146 | } 147 | defer rows.Close() 148 | 149 | return rows2maps(rows) 150 | } 151 | 152 | func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { 153 | defer session.resetStatement() 154 | 155 | session.queryPreprocess(&sqlStr, args...) 156 | 157 | if session.engine.showSQL { 158 | if session.engine.showExecTime { 159 | b4ExecTime := time.Now() 160 | defer func() { 161 | execDuration := time.Since(b4ExecTime) 162 | if len(args) > 0 { 163 | session.engine.logger.Infof("[SQL] %s %#v - took: %v", sqlStr, args, execDuration) 164 | } else { 165 | session.engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration) 166 | } 167 | }() 168 | } else { 169 | if len(args) > 0 { 170 | session.engine.logger.Infof("[SQL] %v %#v", sqlStr, args) 171 | } else { 172 | session.engine.logger.Infof("[SQL] %v", sqlStr) 173 | } 174 | } 175 | } 176 | 177 | if !session.isAutoCommit { 178 | return session.tx.ExecContext(session.ctx, sqlStr, args...) 179 | } 180 | 181 | if session.prepareStmt { 182 | stmt, err := session.doPrepare(session.DB(), sqlStr) 183 | if err != nil { 184 | return nil, err 185 | } 186 | 187 | res, err := stmt.ExecContext(session.ctx, args...) 188 | if err != nil { 189 | return nil, err 190 | } 191 | return res, nil 192 | } 193 | 194 | return session.DB().ExecContext(session.ctx, sqlStr, args...) 195 | } 196 | 197 | func convertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) { 198 | switch sqlOrArgs[0].(type) { 199 | case string: 200 | return sqlOrArgs[0].(string), sqlOrArgs[1:], nil 201 | case *builder.Builder: 202 | return sqlOrArgs[0].(*builder.Builder).ToSQL() 203 | case builder.Builder: 204 | bd := sqlOrArgs[0].(builder.Builder) 205 | return bd.ToSQL() 206 | } 207 | 208 | return "", nil, ErrUnSupportedType 209 | } 210 | 211 | // Exec raw sql 212 | func (session *Session) Exec(sqlOrArgs ...interface{}) (sql.Result, error) { 213 | if session.isAutoClose { 214 | defer session.Close() 215 | } 216 | 217 | if len(sqlOrArgs) == 0 { 218 | return nil, ErrUnSupportedType 219 | } 220 | 221 | sqlStr, args, err := convertSQLOrArgs(sqlOrArgs...) 222 | if err != nil { 223 | return nil, err 224 | } 225 | 226 | return session.exec(sqlStr, args...) 227 | } 228 | -------------------------------------------------------------------------------- /session_raw_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "strconv" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestExecAndQuery(t *testing.T) { 15 | assert.NoError(t, prepareEngine()) 16 | 17 | type UserinfoQuery struct { 18 | Uid int 19 | Name string 20 | } 21 | 22 | assert.NoError(t, testEngine.Sync2(new(UserinfoQuery))) 23 | 24 | res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_query`", true)+" (uid, name) VALUES (?, ?)", 1, "user") 25 | assert.NoError(t, err) 26 | cnt, err := res.RowsAffected() 27 | assert.NoError(t, err) 28 | assert.EqualValues(t, 1, cnt) 29 | 30 | results, err := testEngine.Query("select * from " + testEngine.TableName("userinfo_query", true)) 31 | assert.NoError(t, err) 32 | assert.EqualValues(t, 1, len(results)) 33 | id, err := strconv.Atoi(string(results[0]["uid"])) 34 | assert.NoError(t, err) 35 | assert.EqualValues(t, 1, id) 36 | assert.Equal(t, "user", string(results[0]["name"])) 37 | } 38 | -------------------------------------------------------------------------------- /session_stats.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "database/sql" 9 | "errors" 10 | "reflect" 11 | ) 12 | 13 | // Count counts the records. bean's non-empty fields 14 | // are conditions. 15 | func (session *Session) Count(bean ...interface{}) (int64, error) { 16 | if session.isAutoClose { 17 | defer session.Close() 18 | } 19 | 20 | var sqlStr string 21 | var args []interface{} 22 | var err error 23 | if session.statement.RawSQL == "" { 24 | sqlStr, args, err = session.statement.genCountSQL(bean...) 25 | if err != nil { 26 | return 0, err 27 | } 28 | } else { 29 | sqlStr = session.statement.RawSQL 30 | args = session.statement.RawParams 31 | } 32 | 33 | var total int64 34 | err = session.queryRow(sqlStr, args...).Scan(&total) 35 | if err == sql.ErrNoRows || err == nil { 36 | return total, nil 37 | } 38 | 39 | return 0, err 40 | } 41 | 42 | // sum call sum some column. bean's non-empty fields are conditions. 43 | func (session *Session) sum(res interface{}, bean interface{}, columnNames ...string) error { 44 | if session.isAutoClose { 45 | defer session.Close() 46 | } 47 | 48 | v := reflect.ValueOf(res) 49 | if v.Kind() != reflect.Ptr { 50 | return errors.New("need a pointer to a variable") 51 | } 52 | 53 | var isSlice = v.Elem().Kind() == reflect.Slice 54 | var sqlStr string 55 | var args []interface{} 56 | var err error 57 | if len(session.statement.RawSQL) == 0 { 58 | sqlStr, args, err = session.statement.genSumSQL(bean, columnNames...) 59 | if err != nil { 60 | return err 61 | } 62 | } else { 63 | sqlStr = session.statement.RawSQL 64 | args = session.statement.RawParams 65 | } 66 | 67 | if isSlice { 68 | err = session.queryRow(sqlStr, args...).ScanSlice(res) 69 | } else { 70 | err = session.queryRow(sqlStr, args...).Scan(res) 71 | } 72 | if err == sql.ErrNoRows || err == nil { 73 | return nil 74 | } 75 | return err 76 | } 77 | 78 | // Sum call sum some column. bean's non-empty fields are conditions. 79 | func (session *Session) Sum(bean interface{}, columnName string) (res float64, err error) { 80 | return res, session.sum(&res, bean, columnName) 81 | } 82 | 83 | // SumInt call sum some column. bean's non-empty fields are conditions. 84 | func (session *Session) SumInt(bean interface{}, columnName string) (res int64, err error) { 85 | return res, session.sum(&res, bean, columnName) 86 | } 87 | 88 | // Sums call sum some columns. bean's non-empty fields are conditions. 89 | func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) { 90 | var res = make([]float64, len(columnNames), len(columnNames)) 91 | return res, session.sum(&res, bean, columnNames...) 92 | } 93 | 94 | // SumsInt sum specify columns and return as []int64 instead of []float64 95 | func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { 96 | var res = make([]int64, len(columnNames), len(columnNames)) 97 | return res, session.sum(&res, bean, columnNames...) 98 | } 99 | -------------------------------------------------------------------------------- /session_stats_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "testing" 11 | 12 | "xorm.io/builder" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func isFloatEq(i, j float64, precision int) bool { 17 | return fmt.Sprintf("%."+strconv.Itoa(precision)+"f", i) == fmt.Sprintf("%."+strconv.Itoa(precision)+"f", j) 18 | } 19 | 20 | func TestSum(t *testing.T) { 21 | type SumStruct struct { 22 | Int int 23 | Float float32 24 | } 25 | 26 | assert.NoError(t, prepareEngine()) 27 | assert.NoError(t, testEngine.Sync2(new(SumStruct))) 28 | 29 | var ( 30 | cases = []SumStruct{ 31 | {1, 6.2}, 32 | {2, 5.3}, 33 | {92, -0.2}, 34 | } 35 | ) 36 | 37 | var i int 38 | var f float32 39 | for _, v := range cases { 40 | i += v.Int 41 | f += v.Float 42 | } 43 | 44 | cnt, err := testEngine.Insert(cases) 45 | assert.NoError(t, err) 46 | assert.EqualValues(t, 3, cnt) 47 | 48 | colInt := testEngine.GetColumnMapper().Obj2Table("Int") 49 | colFloat := testEngine.GetColumnMapper().Obj2Table("Float") 50 | 51 | sumInt, err := testEngine.Sum(new(SumStruct), colInt) 52 | assert.NoError(t, err) 53 | assert.EqualValues(t, int(sumInt), i) 54 | 55 | sumFloat, err := testEngine.Sum(new(SumStruct), colFloat) 56 | assert.NoError(t, err) 57 | assert.Condition(t, func() bool { 58 | return isFloatEq(sumFloat, float64(f), 2) 59 | }) 60 | 61 | sums, err := testEngine.Sums(new(SumStruct), colInt, colFloat) 62 | assert.NoError(t, err) 63 | assert.EqualValues(t, 2, len(sums)) 64 | assert.EqualValues(t, i, int(sums[0])) 65 | assert.Condition(t, func() bool { 66 | return isFloatEq(sums[1], float64(f), 2) 67 | }) 68 | 69 | sumsInt, err := testEngine.SumsInt(new(SumStruct), colInt) 70 | assert.NoError(t, err) 71 | assert.EqualValues(t, 1, len(sumsInt)) 72 | assert.EqualValues(t, i, int(sumsInt[0])) 73 | } 74 | 75 | type SumStructWithTableName struct { 76 | Int int 77 | Float float32 78 | } 79 | 80 | func (s SumStructWithTableName) TableName() string { 81 | return "sum_struct_with_table_name_1" 82 | } 83 | 84 | func TestSumWithTableName(t *testing.T) { 85 | assert.NoError(t, prepareEngine()) 86 | assert.NoError(t, testEngine.Sync2(new(SumStructWithTableName))) 87 | 88 | var ( 89 | cases = []SumStructWithTableName{ 90 | {1, 6.2}, 91 | {2, 5.3}, 92 | {92, -0.2}, 93 | } 94 | ) 95 | 96 | var i int 97 | var f float32 98 | for _, v := range cases { 99 | i += v.Int 100 | f += v.Float 101 | } 102 | 103 | cnt, err := testEngine.Insert(cases) 104 | assert.NoError(t, err) 105 | assert.EqualValues(t, 3, cnt) 106 | 107 | colInt := testEngine.GetColumnMapper().Obj2Table("Int") 108 | colFloat := testEngine.GetColumnMapper().Obj2Table("Float") 109 | 110 | sumInt, err := testEngine.Sum(new(SumStructWithTableName), colInt) 111 | assert.NoError(t, err) 112 | assert.EqualValues(t, int(sumInt), i) 113 | 114 | sumFloat, err := testEngine.Sum(new(SumStructWithTableName), colFloat) 115 | assert.NoError(t, err) 116 | assert.Condition(t, func() bool { 117 | return isFloatEq(sumFloat, float64(f), 2) 118 | }) 119 | 120 | sums, err := testEngine.Sums(new(SumStructWithTableName), colInt, colFloat) 121 | assert.NoError(t, err) 122 | assert.EqualValues(t, 2, len(sums)) 123 | assert.EqualValues(t, i, int(sums[0])) 124 | assert.Condition(t, func() bool { 125 | return isFloatEq(sums[1], float64(f), 2) 126 | }) 127 | 128 | sumsInt, err := testEngine.SumsInt(new(SumStructWithTableName), colInt) 129 | assert.NoError(t, err) 130 | assert.EqualValues(t, 1, len(sumsInt)) 131 | assert.EqualValues(t, i, int(sumsInt[0])) 132 | } 133 | 134 | func TestSumCustomColumn(t *testing.T) { 135 | assert.NoError(t, prepareEngine()) 136 | 137 | type SumStruct2 struct { 138 | Int int 139 | Float float32 140 | } 141 | 142 | var ( 143 | cases = []SumStruct2{ 144 | {1, 6.2}, 145 | {2, 5.3}, 146 | {92, -0.2}, 147 | } 148 | ) 149 | 150 | assert.NoError(t, testEngine.Sync2(new(SumStruct2))) 151 | 152 | cnt, err := testEngine.Insert(cases) 153 | assert.NoError(t, err) 154 | assert.EqualValues(t, 3, cnt) 155 | 156 | sumInt, err := testEngine.Sum(new(SumStruct2), 157 | "CASE WHEN `int` <= 2 THEN `int` ELSE 0 END") 158 | assert.NoError(t, err) 159 | assert.EqualValues(t, 3, int(sumInt)) 160 | } 161 | 162 | func TestCount(t *testing.T) { 163 | assert.NoError(t, prepareEngine()) 164 | 165 | type UserinfoCount struct { 166 | Departname string 167 | } 168 | assert.NoError(t, testEngine.Sync2(new(UserinfoCount))) 169 | 170 | colName := testEngine.GetColumnMapper().Obj2Table("Departname") 171 | var cond builder.Cond = builder.Eq{ 172 | "`" + colName + "`": "dev", 173 | } 174 | 175 | total, err := testEngine.Where(cond).Count(new(UserinfoCount)) 176 | assert.NoError(t, err) 177 | assert.EqualValues(t, 0, total) 178 | 179 | cnt, err := testEngine.Insert(&UserinfoCount{ 180 | Departname: "dev", 181 | }) 182 | assert.NoError(t, err) 183 | assert.EqualValues(t, 1, cnt) 184 | 185 | total, err = testEngine.Where(cond).Count(new(UserinfoCount)) 186 | assert.NoError(t, err) 187 | assert.EqualValues(t, 1, total) 188 | 189 | total, err = testEngine.Where(cond).Table("userinfo_count").Count() 190 | assert.NoError(t, err) 191 | assert.EqualValues(t, 1, total) 192 | 193 | total, err = testEngine.Table("userinfo_count").Count() 194 | assert.NoError(t, err) 195 | assert.EqualValues(t, 1, total) 196 | } 197 | 198 | func TestSQLCount(t *testing.T) { 199 | assert.NoError(t, prepareEngine()) 200 | 201 | type UserinfoCount2 struct { 202 | Id int64 203 | Departname string 204 | } 205 | 206 | type UserinfoBooks struct { 207 | Id int64 208 | Pid int64 209 | IsOpen bool 210 | } 211 | 212 | assertSync(t, new(UserinfoCount2), new(UserinfoBooks)) 213 | 214 | total, err := testEngine.SQL("SELECT count(id) FROM " + testEngine.TableName("userinfo_count2", true)). 215 | Count() 216 | assert.NoError(t, err) 217 | assert.EqualValues(t, 0, total) 218 | } 219 | 220 | func TestCountWithOthers(t *testing.T) { 221 | assert.NoError(t, prepareEngine()) 222 | 223 | type CountWithOthers struct { 224 | Id int64 225 | Name string 226 | } 227 | 228 | assertSync(t, new(CountWithOthers)) 229 | 230 | _, err := testEngine.Insert(&CountWithOthers{ 231 | Name: "orderby", 232 | }) 233 | assert.NoError(t, err) 234 | 235 | _, err = testEngine.Insert(&CountWithOthers{ 236 | Name: "limit", 237 | }) 238 | assert.NoError(t, err) 239 | 240 | total, err := testEngine.OrderBy("id desc").Limit(1).Count(new(CountWithOthers)) 241 | assert.NoError(t, err) 242 | assert.EqualValues(t, 2, total) 243 | } 244 | 245 | type CountWithTableName struct { 246 | Id int64 247 | Name string 248 | } 249 | 250 | func (CountWithTableName) TableName() string { 251 | return "count_with_table_name1" 252 | } 253 | 254 | func TestWithTableName(t *testing.T) { 255 | assert.NoError(t, prepareEngine()) 256 | 257 | assertSync(t, new(CountWithTableName)) 258 | 259 | _, err := testEngine.Insert(&CountWithTableName{ 260 | Name: "orderby", 261 | }) 262 | assert.NoError(t, err) 263 | 264 | _, err = testEngine.Insert(CountWithTableName{ 265 | Name: "limit", 266 | }) 267 | assert.NoError(t, err) 268 | 269 | total, err := testEngine.OrderBy("id desc").Count(new(CountWithTableName)) 270 | assert.NoError(t, err) 271 | assert.EqualValues(t, 2, total) 272 | 273 | total, err = testEngine.OrderBy("id desc").Count(CountWithTableName{}) 274 | assert.NoError(t, err) 275 | assert.EqualValues(t, 2, total) 276 | } 277 | -------------------------------------------------------------------------------- /session_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "database/sql" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestClose(t *testing.T) { 15 | assert.NoError(t, prepareEngine()) 16 | 17 | sess1 := testEngine.NewSession() 18 | sess1.Close() 19 | assert.True(t, sess1.IsClosed()) 20 | 21 | sess2 := testEngine.Where("a = ?", 1) 22 | sess2.Close() 23 | assert.True(t, sess2.IsClosed()) 24 | } 25 | 26 | func TestNullFloatStruct(t *testing.T) { 27 | type MyNullFloat64 sql.NullFloat64 28 | 29 | type MyNullFloatStruct struct { 30 | Uuid string 31 | Amount MyNullFloat64 32 | } 33 | 34 | assert.NoError(t, prepareEngine()) 35 | assert.NoError(t, testEngine.Sync2(new(MyNullFloatStruct))) 36 | 37 | _, err := testEngine.Insert(&MyNullFloatStruct{ 38 | Uuid: "111111", 39 | Amount: MyNullFloat64(sql.NullFloat64{ 40 | Float64: 0.1111, 41 | Valid: true, 42 | }), 43 | }) 44 | assert.NoError(t, err) 45 | } 46 | -------------------------------------------------------------------------------- /session_tx.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | // Begin a transaction 8 | func (session *Session) Begin() error { 9 | if session.isAutoCommit { 10 | tx, err := session.DB().BeginTx(session.ctx, nil) 11 | if err != nil { 12 | return err 13 | } 14 | session.isAutoCommit = false 15 | session.isCommitedOrRollbacked = false 16 | session.tx = tx 17 | session.saveLastSQL("BEGIN TRANSACTION") 18 | } 19 | return nil 20 | } 21 | 22 | // Rollback When using transaction, you can rollback if any error 23 | func (session *Session) Rollback() error { 24 | if !session.isAutoCommit && !session.isCommitedOrRollbacked { 25 | session.saveLastSQL(session.engine.dialect.RollBackStr()) 26 | session.isCommitedOrRollbacked = true 27 | session.isAutoCommit = true 28 | return session.tx.Rollback() 29 | } 30 | return nil 31 | } 32 | 33 | // Commit When using transaction, Commit will commit all operations. 34 | func (session *Session) Commit() error { 35 | if !session.isAutoCommit && !session.isCommitedOrRollbacked { 36 | session.saveLastSQL("COMMIT") 37 | session.isCommitedOrRollbacked = true 38 | session.isAutoCommit = true 39 | var err error 40 | if err = session.tx.Commit(); err == nil { 41 | // handle processors after tx committed 42 | closureCallFunc := func(closuresPtr *[]func(interface{}), bean interface{}) { 43 | if closuresPtr != nil { 44 | for _, closure := range *closuresPtr { 45 | closure(bean) 46 | } 47 | } 48 | } 49 | 50 | for bean, closuresPtr := range session.afterInsertBeans { 51 | closureCallFunc(closuresPtr, bean) 52 | 53 | if processor, ok := interface{}(bean).(AfterInsertProcessor); ok { 54 | processor.AfterInsert() 55 | } 56 | } 57 | for bean, closuresPtr := range session.afterUpdateBeans { 58 | closureCallFunc(closuresPtr, bean) 59 | 60 | if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok { 61 | processor.AfterUpdate() 62 | } 63 | } 64 | for bean, closuresPtr := range session.afterDeleteBeans { 65 | closureCallFunc(closuresPtr, bean) 66 | 67 | if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok { 68 | processor.AfterDelete() 69 | } 70 | } 71 | cleanUpFunc := func(slices *map[interface{}]*[]func(interface{})) { 72 | if len(*slices) > 0 { 73 | *slices = make(map[interface{}]*[]func(interface{}), 0) 74 | } 75 | } 76 | cleanUpFunc(&session.afterInsertBeans) 77 | cleanUpFunc(&session.afterUpdateBeans) 78 | cleanUpFunc(&session.afterDeleteBeans) 79 | } 80 | return err 81 | } 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /session_tx_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | "time" 11 | 12 | "xorm.io/core" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestTransaction(t *testing.T) { 17 | assert.NoError(t, prepareEngine()) 18 | assertSync(t, new(Userinfo)) 19 | 20 | counter := func() { 21 | total, err := testEngine.Count(&Userinfo{}) 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | fmt.Printf("----now total %v records\n", total) 26 | } 27 | 28 | counter() 29 | //defer counter() 30 | 31 | session := testEngine.NewSession() 32 | defer session.Close() 33 | 34 | err := session.Begin() 35 | assert.NoError(t, err) 36 | 37 | user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} 38 | _, err = session.Insert(&user1) 39 | assert.NoError(t, err) 40 | 41 | user2 := Userinfo{Username: "yyy"} 42 | _, err = session.Where("(id) = ?", 0).Update(&user2) 43 | assert.NoError(t, err) 44 | 45 | _, err = session.Delete(&user2) 46 | assert.NoError(t, err) 47 | 48 | err = session.Commit() 49 | assert.NoError(t, err) 50 | } 51 | 52 | func TestCombineTransaction(t *testing.T) { 53 | assert.NoError(t, prepareEngine()) 54 | assertSync(t, new(Userinfo)) 55 | 56 | counter := func() { 57 | total, err := testEngine.Count(&Userinfo{}) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | fmt.Printf("----now total %v records\n", total) 62 | } 63 | 64 | counter() 65 | //defer counter() 66 | session := testEngine.NewSession() 67 | defer session.Close() 68 | 69 | err := session.Begin() 70 | assert.NoError(t, err) 71 | 72 | user1 := Userinfo{Username: "xiaoxiao2", Departname: "dev", Alias: "lunny", Created: time.Now()} 73 | _, err = session.Insert(&user1) 74 | assert.NoError(t, err) 75 | 76 | user2 := Userinfo{Username: "zzz"} 77 | _, err = session.Where("id = ?", 0).Update(&user2) 78 | assert.NoError(t, err) 79 | 80 | _, err = session.Exec("delete from "+testEngine.TableName("userinfo", true)+" where username = ?", user2.Username) 81 | assert.NoError(t, err) 82 | 83 | err = session.Commit() 84 | assert.NoError(t, err) 85 | } 86 | 87 | func TestCombineTransactionSameMapper(t *testing.T) { 88 | assert.NoError(t, prepareEngine()) 89 | 90 | oldMapper := testEngine.GetColumnMapper() 91 | testEngine.UnMapType(rValue(new(Userinfo)).Type()) 92 | testEngine.SetMapper(core.SameMapper{}) 93 | defer func() { 94 | testEngine.UnMapType(rValue(new(Userinfo)).Type()) 95 | testEngine.SetMapper(oldMapper) 96 | }() 97 | 98 | assertSync(t, new(Userinfo)) 99 | 100 | counter := func() { 101 | total, err := testEngine.Count(&Userinfo{}) 102 | if err != nil { 103 | t.Error(err) 104 | } 105 | fmt.Printf("----now total %v records\n", total) 106 | } 107 | 108 | counter() 109 | defer counter() 110 | 111 | session := testEngine.NewSession() 112 | defer session.Close() 113 | 114 | err := session.Begin() 115 | assert.NoError(t, err) 116 | 117 | user1 := Userinfo{Username: "xiaoxiao2", Departname: "dev", Alias: "lunny", Created: time.Now()} 118 | _, err = session.Insert(&user1) 119 | assert.NoError(t, err) 120 | 121 | user2 := Userinfo{Username: "zzz"} 122 | _, err = session.Where("(id) = ?", 0).Update(&user2) 123 | assert.NoError(t, err) 124 | 125 | _, err = session.Exec("delete from "+testEngine.TableName("`Userinfo`", true)+" where `Username` = ?", user2.Username) 126 | assert.NoError(t, err) 127 | 128 | err = session.Commit() 129 | assert.NoError(t, err) 130 | } 131 | 132 | func TestMultipleTransaction(t *testing.T) { 133 | assert.NoError(t, prepareEngine()) 134 | 135 | type MultipleTransaction struct { 136 | Id int64 137 | Name string 138 | } 139 | 140 | assertSync(t, new(MultipleTransaction)) 141 | 142 | session := testEngine.NewSession() 143 | defer session.Close() 144 | 145 | err := session.Begin() 146 | assert.NoError(t, err) 147 | 148 | m1 := MultipleTransaction{Name: "xiaoxiao2"} 149 | _, err = session.Insert(&m1) 150 | assert.NoError(t, err) 151 | 152 | user2 := MultipleTransaction{Name: "zzz"} 153 | _, err = session.Where("id = ?", 0).Update(&user2) 154 | assert.NoError(t, err) 155 | 156 | err = session.Commit() 157 | assert.NoError(t, err) 158 | 159 | var ms []MultipleTransaction 160 | err = session.Find(&ms) 161 | assert.NoError(t, err) 162 | assert.EqualValues(t, 1, len(ms)) 163 | 164 | err = session.Begin() 165 | assert.NoError(t, err) 166 | 167 | _, err = session.Where("id=?", m1.Id).Delete(new(MultipleTransaction)) 168 | assert.NoError(t, err) 169 | 170 | err = session.Commit() 171 | assert.NoError(t, err) 172 | 173 | ms = make([]MultipleTransaction, 0) 174 | err = session.Find(&ms) 175 | assert.NoError(t, err) 176 | assert.EqualValues(t, 0, len(ms)) 177 | 178 | err = session.Begin() 179 | assert.NoError(t, err) 180 | 181 | _, err = session.Insert(&MultipleTransaction{ 182 | Name: "ssss", 183 | }) 184 | assert.NoError(t, err) 185 | 186 | err = session.Rollback() 187 | assert.NoError(t, err) 188 | 189 | ms = make([]MultipleTransaction, 0) 190 | err = session.Find(&ms) 191 | assert.NoError(t, err) 192 | assert.EqualValues(t, 0, len(ms)) 193 | } 194 | -------------------------------------------------------------------------------- /statement_args.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | "strings" 11 | "time" 12 | 13 | "xorm.io/builder" 14 | "xorm.io/core" 15 | ) 16 | 17 | func quoteNeeded(a interface{}) bool { 18 | switch a.(type) { 19 | case int, int8, int16, int32, int64: 20 | return false 21 | case uint, uint8, uint16, uint32, uint64: 22 | return false 23 | case float32, float64: 24 | return false 25 | case bool: 26 | return false 27 | case string: 28 | return true 29 | case time.Time, *time.Time: 30 | return true 31 | case builder.Builder, *builder.Builder: 32 | return false 33 | } 34 | 35 | t := reflect.TypeOf(a) 36 | switch t.Kind() { 37 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 38 | return false 39 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 40 | return false 41 | case reflect.Float32, reflect.Float64: 42 | return false 43 | case reflect.Bool: 44 | return false 45 | case reflect.String: 46 | return true 47 | } 48 | 49 | return true 50 | } 51 | 52 | func convertStringSingleQuote(arg string) string { 53 | return "'" + strings.Replace(arg, "'", "''", -1) + "'" 54 | } 55 | 56 | func convertString(arg string) string { 57 | var buf strings.Builder 58 | buf.WriteRune('\'') 59 | for _, c := range arg { 60 | if c == '\\' || c == '\'' { 61 | buf.WriteRune('\\') 62 | } 63 | buf.WriteRune(c) 64 | } 65 | buf.WriteRune('\'') 66 | return buf.String() 67 | } 68 | 69 | func convertArg(arg interface{}, convertFunc func(string) string) string { 70 | if quoteNeeded(arg) { 71 | argv := fmt.Sprintf("%v", arg) 72 | return convertFunc(argv) 73 | } 74 | 75 | return fmt.Sprintf("%v", arg) 76 | } 77 | 78 | const insertSelectPlaceHolder = true 79 | 80 | func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) error { 81 | switch argv := arg.(type) { 82 | case bool: 83 | if statement.Engine.dialect.DBType() == core.MSSQL { 84 | if argv { 85 | if _, err := w.WriteString("1"); err != nil { 86 | return err 87 | } 88 | } else { 89 | if _, err := w.WriteString("0"); err != nil { 90 | return err 91 | } 92 | } 93 | } else { 94 | if argv { 95 | if _, err := w.WriteString("true"); err != nil { 96 | return err 97 | } 98 | } else { 99 | if _, err := w.WriteString("false"); err != nil { 100 | return err 101 | } 102 | } 103 | } 104 | case *builder.Builder: 105 | if _, err := w.WriteString("("); err != nil { 106 | return err 107 | } 108 | if err := argv.WriteTo(w); err != nil { 109 | return err 110 | } 111 | if _, err := w.WriteString(")"); err != nil { 112 | return err 113 | } 114 | default: 115 | if insertSelectPlaceHolder { 116 | if err := w.WriteByte('?'); err != nil { 117 | return err 118 | } 119 | w.Append(arg) 120 | } else { 121 | var convertFunc = convertStringSingleQuote 122 | if statement.Engine.dialect.DBType() == core.MYSQL { 123 | convertFunc = convertString 124 | } 125 | if _, err := w.WriteString(convertArg(arg, convertFunc)); err != nil { 126 | return err 127 | } 128 | } 129 | } 130 | return nil 131 | } 132 | 133 | func (statement *Statement) writeArgs(w *builder.BytesWriter, args []interface{}) error { 134 | for i, arg := range args { 135 | if err := statement.writeArg(w, arg); err != nil { 136 | return err 137 | } 138 | 139 | if i+1 != len(args) { 140 | if _, err := w.WriteString(","); err != nil { 141 | return err 142 | } 143 | } 144 | } 145 | return nil 146 | } 147 | 148 | func writeStrings(w *builder.BytesWriter, cols []string, leftQuote, rightQuote string) error { 149 | for i, colName := range cols { 150 | if len(leftQuote) > 0 && colName[0] != '`' { 151 | if _, err := w.WriteString(leftQuote); err != nil { 152 | return err 153 | } 154 | } 155 | if _, err := w.WriteString(colName); err != nil { 156 | return err 157 | } 158 | if len(rightQuote) > 0 && colName[len(colName)-1] != '`' { 159 | if _, err := w.WriteString(rightQuote); err != nil { 160 | return err 161 | } 162 | } 163 | if i+1 != len(cols) { 164 | if _, err := w.WriteString(","); err != nil { 165 | return err 166 | } 167 | } 168 | } 169 | return nil 170 | } 171 | -------------------------------------------------------------------------------- /statement_columnmap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import "strings" 8 | 9 | type columnMap []string 10 | 11 | func (m columnMap) contain(colName string) bool { 12 | if len(m) == 0 { 13 | return false 14 | } 15 | 16 | n := len(colName) 17 | for _, mk := range m { 18 | if len(mk) != n { 19 | continue 20 | } 21 | if strings.EqualFold(mk, colName) { 22 | return true 23 | } 24 | } 25 | 26 | return false 27 | } 28 | 29 | func (m *columnMap) add(colName string) bool { 30 | if m.contain(colName) { 31 | return false 32 | } 33 | *m = append(*m, colName) 34 | return true 35 | } 36 | -------------------------------------------------------------------------------- /statement_exprparam.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | 11 | "xorm.io/builder" 12 | ) 13 | 14 | type ErrUnsupportedExprType struct { 15 | tp string 16 | } 17 | 18 | func (err ErrUnsupportedExprType) Error() string { 19 | return fmt.Sprintf("Unsupported expression type: %v", err.tp) 20 | } 21 | 22 | type exprParam struct { 23 | colName string 24 | arg interface{} 25 | } 26 | 27 | type exprParams struct { 28 | colNames []string 29 | args []interface{} 30 | } 31 | 32 | func (exprs *exprParams) Len() int { 33 | return len(exprs.colNames) 34 | } 35 | 36 | func (exprs *exprParams) addParam(colName string, arg interface{}) { 37 | exprs.colNames = append(exprs.colNames, colName) 38 | exprs.args = append(exprs.args, arg) 39 | } 40 | 41 | func (exprs *exprParams) isColExist(colName string) bool { 42 | for _, name := range exprs.colNames { 43 | if strings.EqualFold(trimQuote(name), trimQuote(colName)) { 44 | return true 45 | } 46 | } 47 | return false 48 | } 49 | 50 | func (exprs *exprParams) getByName(colName string) (exprParam, bool) { 51 | for i, name := range exprs.colNames { 52 | if strings.EqualFold(name, colName) { 53 | return exprParam{name, exprs.args[i]}, true 54 | } 55 | } 56 | return exprParam{}, false 57 | } 58 | 59 | func (exprs *exprParams) writeArgs(w *builder.BytesWriter) error { 60 | for i, expr := range exprs.args { 61 | switch arg := expr.(type) { 62 | case *builder.Builder: 63 | if _, err := w.WriteString("("); err != nil { 64 | return err 65 | } 66 | if err := arg.WriteTo(w); err != nil { 67 | return err 68 | } 69 | if _, err := w.WriteString(")"); err != nil { 70 | return err 71 | } 72 | default: 73 | if _, err := w.WriteString(fmt.Sprintf("%v", arg)); err != nil { 74 | return err 75 | } 76 | } 77 | if i != len(exprs.args)-1 { 78 | if _, err := w.WriteString(","); err != nil { 79 | return err 80 | } 81 | } 82 | } 83 | return nil 84 | } 85 | 86 | func (exprs *exprParams) writeNameArgs(w *builder.BytesWriter) error { 87 | for i, colName := range exprs.colNames { 88 | if _, err := w.WriteString(colName); err != nil { 89 | return err 90 | } 91 | if _, err := w.WriteString("="); err != nil { 92 | return err 93 | } 94 | 95 | switch arg := exprs.args[i].(type) { 96 | case *builder.Builder: 97 | if _, err := w.WriteString("("); err != nil { 98 | return err 99 | } 100 | if err := arg.WriteTo(w); err != nil { 101 | return err 102 | } 103 | if _, err := w.WriteString("("); err != nil { 104 | return err 105 | } 106 | default: 107 | w.Append(exprs.args[i]) 108 | } 109 | 110 | if i+1 != len(exprs.colNames) { 111 | if _, err := w.WriteString(","); err != nil { 112 | return err 113 | } 114 | } 115 | } 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /statement_quote.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | func trimQuote(s string) string { 8 | if len(s) == 0 { 9 | return s 10 | } 11 | 12 | if s[0] == '`' { 13 | s = s[1:] 14 | } 15 | if len(s) > 0 && s[len(s)-1] == '`' { 16 | return s[:len(s)-1] 17 | } 18 | return s 19 | } 20 | -------------------------------------------------------------------------------- /statement_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "reflect" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "xorm.io/core" 14 | ) 15 | 16 | var colStrTests = []struct { 17 | omitColumn string 18 | onlyToDBColumnNdx int 19 | expected string 20 | }{ 21 | {"", -1, "`ID`, `IsDeleted`, `Caption`, `Code1`, `Code2`, `Code3`, `ParentID`, `Latitude`, `Longitude`"}, 22 | {"Code2", -1, "`ID`, `IsDeleted`, `Caption`, `Code1`, `Code3`, `ParentID`, `Latitude`, `Longitude`"}, 23 | {"", 1, "`ID`, `Caption`, `Code1`, `Code2`, `Code3`, `ParentID`, `Latitude`, `Longitude`"}, 24 | {"Code3", 1, "`ID`, `Caption`, `Code1`, `Code2`, `ParentID`, `Latitude`, `Longitude`"}, 25 | {"Longitude", 1, "`ID`, `Caption`, `Code1`, `Code2`, `Code3`, `ParentID`, `Latitude`"}, 26 | {"", 8, "`ID`, `IsDeleted`, `Caption`, `Code1`, `Code2`, `Code3`, `ParentID`, `Latitude`"}, 27 | } 28 | 29 | func TestColumnsStringGeneration(t *testing.T) { 30 | if dbType == "postgres" || dbType == "mssql" { 31 | return 32 | } 33 | 34 | var statement *Statement 35 | 36 | for ndx, testCase := range colStrTests { 37 | statement = createTestStatement() 38 | 39 | if testCase.omitColumn != "" { 40 | statement.Omit(testCase.omitColumn) 41 | } 42 | 43 | columns := statement.RefTable.Columns() 44 | if testCase.onlyToDBColumnNdx >= 0 { 45 | columns[testCase.onlyToDBColumnNdx].MapType = core.ONLYTODB 46 | } 47 | 48 | actual := statement.genColumnStr() 49 | 50 | if actual != testCase.expected { 51 | t.Errorf("[test #%d] Unexpected columns string:\nwant:\t%s\nhave:\t%s", ndx, testCase.expected, actual) 52 | } 53 | if testCase.onlyToDBColumnNdx >= 0 { 54 | columns[testCase.onlyToDBColumnNdx].MapType = core.TWOSIDES 55 | } 56 | } 57 | } 58 | 59 | func BenchmarkColumnsStringGeneration(b *testing.B) { 60 | b.StopTimer() 61 | 62 | statement := createTestStatement() 63 | 64 | testCase := colStrTests[0] 65 | 66 | if testCase.omitColumn != "" { 67 | statement.Omit(testCase.omitColumn) // !nemec784! Column must be skipped 68 | } 69 | 70 | if testCase.onlyToDBColumnNdx >= 0 { 71 | columns := statement.RefTable.Columns() 72 | columns[testCase.onlyToDBColumnNdx].MapType = core.ONLYTODB // !nemec784! Column must be skipped 73 | } 74 | 75 | b.StartTimer() 76 | 77 | for i := 0; i < b.N; i++ { 78 | actual := statement.genColumnStr() 79 | 80 | if actual != testCase.expected { 81 | b.Errorf("Unexpected columns string:\nwant:\t%s\nhave:\t%s", testCase.expected, actual) 82 | } 83 | } 84 | } 85 | 86 | func BenchmarkGetFlagForColumnWithICKey_ContainsKey(b *testing.B) { 87 | 88 | b.StopTimer() 89 | 90 | mapCols := make(map[string]bool) 91 | cols := []*core.Column{ 92 | {Name: `ID`}, 93 | {Name: `IsDeleted`}, 94 | {Name: `Caption`}, 95 | {Name: `Code1`}, 96 | {Name: `Code2`}, 97 | {Name: `Code3`}, 98 | {Name: `ParentID`}, 99 | {Name: `Latitude`}, 100 | {Name: `Longitude`}, 101 | } 102 | 103 | for _, col := range cols { 104 | mapCols[strings.ToLower(col.Name)] = true 105 | } 106 | 107 | b.StartTimer() 108 | 109 | for i := 0; i < b.N; i++ { 110 | 111 | for _, col := range cols { 112 | 113 | if _, ok := getFlagForColumn(mapCols, col); !ok { 114 | b.Fatal("Unexpected result") 115 | } 116 | } 117 | } 118 | } 119 | 120 | func BenchmarkGetFlagForColumnWithICKey_EmptyMap(b *testing.B) { 121 | 122 | b.StopTimer() 123 | 124 | mapCols := make(map[string]bool) 125 | cols := []*core.Column{ 126 | {Name: `ID`}, 127 | {Name: `IsDeleted`}, 128 | {Name: `Caption`}, 129 | {Name: `Code1`}, 130 | {Name: `Code2`}, 131 | {Name: `Code3`}, 132 | {Name: `ParentID`}, 133 | {Name: `Latitude`}, 134 | {Name: `Longitude`}, 135 | } 136 | 137 | b.StartTimer() 138 | 139 | for i := 0; i < b.N; i++ { 140 | 141 | for _, col := range cols { 142 | 143 | if _, ok := getFlagForColumn(mapCols, col); ok { 144 | b.Fatal("Unexpected result") 145 | } 146 | } 147 | } 148 | } 149 | 150 | type TestType struct { 151 | ID int64 `xorm:"ID PK"` 152 | IsDeleted bool `xorm:"IsDeleted"` 153 | Caption string `xorm:"Caption"` 154 | Code1 string `xorm:"Code1"` 155 | Code2 string `xorm:"Code2"` 156 | Code3 string `xorm:"Code3"` 157 | ParentID int64 `xorm:"ParentID"` 158 | Latitude float64 `xorm:"Latitude"` 159 | Longitude float64 `xorm:"Longitude"` 160 | } 161 | 162 | func (TestType) TableName() string { 163 | return "TestTable" 164 | } 165 | 166 | func createTestStatement() *Statement { 167 | if engine, ok := testEngine.(*Engine); ok { 168 | statement := &Statement{} 169 | statement.Init() 170 | statement.Engine = engine 171 | statement.setRefValue(reflect.ValueOf(TestType{})) 172 | 173 | return statement 174 | } else if eg, ok := testEngine.(*EngineGroup); ok { 175 | statement := &Statement{} 176 | statement.Init() 177 | statement.Engine = eg.Engine 178 | statement.setRefValue(reflect.ValueOf(TestType{})) 179 | 180 | return statement 181 | } 182 | return nil 183 | } 184 | 185 | func TestDistinctAndCols(t *testing.T) { 186 | type DistinctAndCols struct { 187 | Id int64 188 | Name string 189 | } 190 | 191 | assert.NoError(t, prepareEngine()) 192 | assertSync(t, new(DistinctAndCols)) 193 | 194 | cnt, err := testEngine.Insert(&DistinctAndCols{ 195 | Name: "test", 196 | }) 197 | assert.NoError(t, err) 198 | assert.EqualValues(t, 1, cnt) 199 | 200 | var names []string 201 | err = testEngine.Table("distinct_and_cols").Cols("name").Distinct("name").Find(&names) 202 | assert.NoError(t, err) 203 | assert.EqualValues(t, 1, len(names)) 204 | assert.EqualValues(t, "test", names[0]) 205 | } 206 | 207 | func TestUpdateIgnoreOnlyFromDBFields(t *testing.T) { 208 | type TestOnlyFromDBField struct { 209 | Id int64 `xorm:"PK"` 210 | OnlyFromDBField string `xorm:"<-"` 211 | OnlyToDBField string `xorm:"->"` 212 | IngoreField string `xorm:"-"` 213 | } 214 | 215 | assertGetRecord := func() *TestOnlyFromDBField { 216 | var record TestOnlyFromDBField 217 | has, err := testEngine.Where("id = ?", 1).Get(&record) 218 | assert.NoError(t, err) 219 | assert.EqualValues(t, true, has) 220 | assert.EqualValues(t, "", record.OnlyFromDBField) 221 | return &record 222 | 223 | } 224 | assert.NoError(t, prepareEngine()) 225 | assertSync(t, new(TestOnlyFromDBField)) 226 | 227 | _, err := testEngine.Insert(&TestOnlyFromDBField{ 228 | Id: 1, 229 | OnlyFromDBField: "a", 230 | OnlyToDBField: "b", 231 | IngoreField: "c", 232 | }) 233 | assert.NoError(t, err) 234 | 235 | record := assertGetRecord() 236 | record.OnlyFromDBField = "test" 237 | testEngine.Update(record) 238 | assertGetRecord() 239 | } 240 | 241 | func TestCol2NewColsWithQuote(t *testing.T) { 242 | cols := []string{"f1", "f2", "t3.f3"} 243 | 244 | statement := createTestStatement() 245 | 246 | quotedCols := statement.col2NewColsWithQuote(cols...) 247 | assert.EqualValues(t, []string{statement.Engine.Quote("f1"), statement.Engine.Quote("f2"), statement.Engine.Quote("t3.f3")}, quotedCols) 248 | } 249 | -------------------------------------------------------------------------------- /syslogger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !windows,!nacl,!plan9 6 | 7 | package xorm 8 | 9 | import ( 10 | "fmt" 11 | "log/syslog" 12 | 13 | "xorm.io/core" 14 | ) 15 | 16 | var _ core.ILogger = &SyslogLogger{} 17 | 18 | // SyslogLogger will be depricated 19 | type SyslogLogger struct { 20 | w *syslog.Writer 21 | showSQL bool 22 | } 23 | 24 | // NewSyslogLogger implements core.ILogger 25 | func NewSyslogLogger(w *syslog.Writer) *SyslogLogger { 26 | return &SyslogLogger{w: w} 27 | } 28 | 29 | // Debug log content as Debug 30 | func (s *SyslogLogger) Debug(v ...interface{}) { 31 | s.w.Debug(fmt.Sprint(v...)) 32 | } 33 | 34 | // Debugf log content as Debug and format 35 | func (s *SyslogLogger) Debugf(format string, v ...interface{}) { 36 | s.w.Debug(fmt.Sprintf(format, v...)) 37 | } 38 | 39 | // Error log content as Error 40 | func (s *SyslogLogger) Error(v ...interface{}) { 41 | s.w.Err(fmt.Sprint(v...)) 42 | } 43 | 44 | // Errorf log content as Errorf and format 45 | func (s *SyslogLogger) Errorf(format string, v ...interface{}) { 46 | s.w.Err(fmt.Sprintf(format, v...)) 47 | } 48 | 49 | // Info log content as Info 50 | func (s *SyslogLogger) Info(v ...interface{}) { 51 | s.w.Info(fmt.Sprint(v...)) 52 | } 53 | 54 | // Infof log content as Infof and format 55 | func (s *SyslogLogger) Infof(format string, v ...interface{}) { 56 | s.w.Info(fmt.Sprintf(format, v...)) 57 | } 58 | 59 | // Warn log content as Warn 60 | func (s *SyslogLogger) Warn(v ...interface{}) { 61 | s.w.Warning(fmt.Sprint(v...)) 62 | } 63 | 64 | // Warnf log content as Warnf and format 65 | func (s *SyslogLogger) Warnf(format string, v ...interface{}) { 66 | s.w.Warning(fmt.Sprintf(format, v...)) 67 | } 68 | 69 | // Level shows log level 70 | func (s *SyslogLogger) Level() core.LogLevel { 71 | return core.LOG_UNKNOWN 72 | } 73 | 74 | // SetLevel always return error, as current log/syslog package doesn't allow to set priority level after syslog.Writer created 75 | func (s *SyslogLogger) SetLevel(l core.LogLevel) {} 76 | 77 | // ShowSQL set if logging SQL 78 | func (s *SyslogLogger) ShowSQL(show ...bool) { 79 | if len(show) == 0 { 80 | s.showSQL = true 81 | return 82 | } 83 | s.showSQL = show[0] 84 | } 85 | 86 | // IsShowSQL if logging SQL 87 | func (s *SyslogLogger) IsShowSQL() bool { 88 | return s.showSQL 89 | } 90 | -------------------------------------------------------------------------------- /tag_cache_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestCacheTag(t *testing.T) { 14 | assert.NoError(t, prepareEngine()) 15 | 16 | type CacheDomain struct { 17 | Id int64 `xorm:"pk cache"` 18 | Name string 19 | } 20 | 21 | assert.NoError(t, testEngine.CreateTables(&CacheDomain{})) 22 | assert.True(t, testEngine.GetCacher(testEngine.TableName(&CacheDomain{})) != nil) 23 | } 24 | 25 | func TestNoCacheTag(t *testing.T) { 26 | assert.NoError(t, prepareEngine()) 27 | 28 | type NoCacheDomain struct { 29 | Id int64 `xorm:"pk nocache"` 30 | Name string 31 | } 32 | 33 | assert.NoError(t, testEngine.CreateTables(&NoCacheDomain{})) 34 | assert.True(t, testEngine.GetCacher(testEngine.TableName(&NoCacheDomain{})) == nil) 35 | } 36 | -------------------------------------------------------------------------------- /tag_id_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "testing" 9 | 10 | "xorm.io/core" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | type IDGonicMapper struct { 15 | ID int64 16 | } 17 | 18 | func TestGonicMapperID(t *testing.T) { 19 | assert.NoError(t, prepareEngine()) 20 | 21 | oldMapper := testEngine.GetColumnMapper() 22 | testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) 23 | testEngine.SetMapper(core.LintGonicMapper) 24 | defer func() { 25 | testEngine.UnMapType(rValue(new(IDGonicMapper)).Type()) 26 | testEngine.SetMapper(oldMapper) 27 | }() 28 | 29 | err := testEngine.CreateTables(new(IDGonicMapper)) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | 34 | tables, err := testEngine.DBMetas() 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | for _, tb := range tables { 40 | if tb.Name == "id_gonic_mapper" { 41 | if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "id" { 42 | t.Fatal(tb) 43 | } 44 | return 45 | } 46 | } 47 | 48 | t.Fatal("not table id_gonic_mapper") 49 | } 50 | 51 | type IDSameMapper struct { 52 | ID int64 53 | } 54 | 55 | func TestSameMapperID(t *testing.T) { 56 | assert.NoError(t, prepareEngine()) 57 | 58 | oldMapper := testEngine.GetColumnMapper() 59 | testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) 60 | testEngine.SetMapper(core.SameMapper{}) 61 | defer func() { 62 | testEngine.UnMapType(rValue(new(IDSameMapper)).Type()) 63 | testEngine.SetMapper(oldMapper) 64 | }() 65 | 66 | err := testEngine.CreateTables(new(IDSameMapper)) 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | 71 | tables, err := testEngine.DBMetas() 72 | if err != nil { 73 | t.Fatal(err) 74 | } 75 | 76 | for _, tb := range tables { 77 | if tb.Name == "IDSameMapper" { 78 | if len(tb.PKColumns()) != 1 || tb.PKColumns()[0].Name != "ID" { 79 | t.Fatal(tb) 80 | } 81 | return 82 | } 83 | } 84 | t.Fatal("not table IDSameMapper") 85 | } 86 | -------------------------------------------------------------------------------- /tag_version_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "testing" 11 | "time" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | type VersionS struct { 17 | Id int64 18 | Name string 19 | Ver int `xorm:"version"` 20 | Created time.Time `xorm:"created"` 21 | } 22 | 23 | func TestVersion1(t *testing.T) { 24 | assert.NoError(t, prepareEngine()) 25 | 26 | err := testEngine.DropTables(new(VersionS)) 27 | if err != nil { 28 | t.Error(err) 29 | panic(err) 30 | } 31 | 32 | err = testEngine.CreateTables(new(VersionS)) 33 | if err != nil { 34 | t.Error(err) 35 | panic(err) 36 | } 37 | 38 | ver := &VersionS{Name: "sfsfdsfds"} 39 | _, err = testEngine.Insert(ver) 40 | if err != nil { 41 | t.Error(err) 42 | panic(err) 43 | } 44 | fmt.Println(ver) 45 | if ver.Ver != 1 { 46 | err = errors.New("insert error") 47 | t.Error(err) 48 | panic(err) 49 | } 50 | 51 | newVer := new(VersionS) 52 | has, err := testEngine.ID(ver.Id).Get(newVer) 53 | if err != nil { 54 | t.Error(err) 55 | panic(err) 56 | } 57 | 58 | if !has { 59 | t.Error(errors.New(fmt.Sprintf("no version id is %v", ver.Id))) 60 | panic(err) 61 | } 62 | fmt.Println(newVer) 63 | if newVer.Ver != 1 { 64 | err = errors.New("insert error") 65 | t.Error(err) 66 | panic(err) 67 | } 68 | 69 | newVer.Name = "-------" 70 | _, err = testEngine.ID(ver.Id).Update(newVer) 71 | if err != nil { 72 | t.Error(err) 73 | panic(err) 74 | } 75 | if newVer.Ver != 2 { 76 | err = errors.New("update should set version back to struct") 77 | t.Error(err) 78 | } 79 | 80 | newVer = new(VersionS) 81 | has, err = testEngine.ID(ver.Id).Get(newVer) 82 | if err != nil { 83 | t.Error(err) 84 | panic(err) 85 | } 86 | fmt.Println(newVer) 87 | if newVer.Ver != 2 { 88 | err = errors.New("update error") 89 | t.Error(err) 90 | panic(err) 91 | } 92 | } 93 | 94 | func TestVersion2(t *testing.T) { 95 | assert.NoError(t, prepareEngine()) 96 | 97 | err := testEngine.DropTables(new(VersionS)) 98 | if err != nil { 99 | t.Error(err) 100 | panic(err) 101 | } 102 | 103 | err = testEngine.CreateTables(new(VersionS)) 104 | if err != nil { 105 | t.Error(err) 106 | panic(err) 107 | } 108 | 109 | var vers = []VersionS{ 110 | {Name: "sfsfdsfds"}, 111 | {Name: "xxxxx"}, 112 | } 113 | _, err = testEngine.Insert(vers) 114 | if err != nil { 115 | t.Error(err) 116 | panic(err) 117 | } 118 | 119 | fmt.Println(vers) 120 | 121 | for _, v := range vers { 122 | if v.Ver != 1 { 123 | err := errors.New("version should be 1") 124 | t.Error(err) 125 | panic(err) 126 | } 127 | } 128 | } 129 | 130 | type VersionUintS struct { 131 | Id int64 132 | Name string 133 | Ver uint `xorm:"version"` 134 | Created time.Time `xorm:"created"` 135 | } 136 | 137 | func TestVersion3(t *testing.T) { 138 | assert.NoError(t, prepareEngine()) 139 | 140 | err := testEngine.DropTables(new(VersionUintS)) 141 | if err != nil { 142 | t.Error(err) 143 | panic(err) 144 | } 145 | 146 | err = testEngine.CreateTables(new(VersionUintS)) 147 | if err != nil { 148 | t.Error(err) 149 | panic(err) 150 | } 151 | 152 | ver := &VersionUintS{Name: "sfsfdsfds"} 153 | _, err = testEngine.Insert(ver) 154 | if err != nil { 155 | t.Error(err) 156 | panic(err) 157 | } 158 | fmt.Println(ver) 159 | if ver.Ver != 1 { 160 | err = errors.New("insert error") 161 | t.Error(err) 162 | panic(err) 163 | } 164 | 165 | newVer := new(VersionUintS) 166 | has, err := testEngine.ID(ver.Id).Get(newVer) 167 | if err != nil { 168 | t.Error(err) 169 | panic(err) 170 | } 171 | 172 | if !has { 173 | t.Error(errors.New(fmt.Sprintf("no version id is %v", ver.Id))) 174 | panic(err) 175 | } 176 | fmt.Println(newVer) 177 | if newVer.Ver != 1 { 178 | err = errors.New("insert error") 179 | t.Error(err) 180 | panic(err) 181 | } 182 | 183 | newVer.Name = "-------" 184 | _, err = testEngine.ID(ver.Id).Update(newVer) 185 | if err != nil { 186 | t.Error(err) 187 | panic(err) 188 | } 189 | if newVer.Ver != 2 { 190 | err = errors.New("update should set version back to struct") 191 | t.Error(err) 192 | } 193 | 194 | newVer = new(VersionUintS) 195 | has, err = testEngine.ID(ver.Id).Get(newVer) 196 | if err != nil { 197 | t.Error(err) 198 | panic(err) 199 | } 200 | fmt.Println(newVer) 201 | if newVer.Ver != 2 { 202 | err = errors.New("update error") 203 | t.Error(err) 204 | panic(err) 205 | } 206 | } 207 | 208 | func TestVersion4(t *testing.T) { 209 | assert.NoError(t, prepareEngine()) 210 | 211 | err := testEngine.DropTables(new(VersionUintS)) 212 | if err != nil { 213 | t.Error(err) 214 | panic(err) 215 | } 216 | 217 | err = testEngine.CreateTables(new(VersionUintS)) 218 | if err != nil { 219 | t.Error(err) 220 | panic(err) 221 | } 222 | 223 | var vers = []VersionUintS{ 224 | {Name: "sfsfdsfds"}, 225 | {Name: "xxxxx"}, 226 | } 227 | _, err = testEngine.Insert(vers) 228 | if err != nil { 229 | t.Error(err) 230 | panic(err) 231 | } 232 | 233 | fmt.Println(vers) 234 | 235 | for _, v := range vers { 236 | if v.Ver != 1 { 237 | err := errors.New("version should be 1") 238 | t.Error(err) 239 | panic(err) 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /test_mssql.sh: -------------------------------------------------------------------------------- 1 | go test -db=mssql -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" -------------------------------------------------------------------------------- /test_mssql_cache.sh: -------------------------------------------------------------------------------- 1 | go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test" -cache=true -------------------------------------------------------------------------------- /test_mymysql.sh: -------------------------------------------------------------------------------- 1 | go test -db=mymysql -conn_str="xorm_test/root/" -------------------------------------------------------------------------------- /test_mymysql_cache.sh: -------------------------------------------------------------------------------- 1 | go test -db=mymysql -conn_str="xorm_test/root/" -cache=true -------------------------------------------------------------------------------- /test_mysql.sh: -------------------------------------------------------------------------------- 1 | go test -db=mysql -conn_str="root:@/xorm_test" -------------------------------------------------------------------------------- /test_mysql_cache.sh: -------------------------------------------------------------------------------- 1 | go test -db=mysql -conn_str="root:@/xorm_test" -cache=true -------------------------------------------------------------------------------- /test_postgres.sh: -------------------------------------------------------------------------------- 1 | go test -db=postgres -conn_str="dbname=xorm_test sslmode=disable" -------------------------------------------------------------------------------- /test_postgres_cache.sh: -------------------------------------------------------------------------------- 1 | go test -db=postgres -conn_str="dbname=xorm_test sslmode=disable" -cache=true -------------------------------------------------------------------------------- /test_sqlite.sh: -------------------------------------------------------------------------------- 1 | go test -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" -------------------------------------------------------------------------------- /test_sqlite_cache.sh: -------------------------------------------------------------------------------- 1 | go test -db=sqlite3 -conn_str="./test.db?cache=shared&mode=rwc" -cache=true -------------------------------------------------------------------------------- /test_tidb.sh: -------------------------------------------------------------------------------- 1 | go test -db=mysql -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true -------------------------------------------------------------------------------- /transaction.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | // Transaction Execute sql wrapped in a transaction(abbr as tx), tx will automatic commit if no errors occurred 8 | func (engine *Engine) Transaction(f func(*Session) (interface{}, error)) (interface{}, error) { 9 | session := engine.NewSession() 10 | defer session.Close() 11 | 12 | if err := session.Begin(); err != nil { 13 | return nil, err 14 | } 15 | 16 | result, err := f(session) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | if err := session.Commit(); err != nil { 22 | return nil, err 23 | } 24 | 25 | return result, nil 26 | } 27 | -------------------------------------------------------------------------------- /transancation_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | "time" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestAutoTransaction(t *testing.T) { 16 | assert.NoError(t, prepareEngine()) 17 | 18 | type TestTx struct { 19 | Id int64 `xorm:"autoincr pk"` 20 | Msg string `xorm:"varchar(255)"` 21 | Created time.Time `xorm:"created"` 22 | } 23 | 24 | assert.NoError(t, testEngine.Sync2(new(TestTx))) 25 | 26 | engine := testEngine.(*Engine) 27 | 28 | // will success 29 | engine.Transaction(func(session *Session) (interface{}, error) { 30 | _, err := session.Insert(TestTx{Msg: "hi"}) 31 | assert.NoError(t, err) 32 | 33 | return nil, nil 34 | }) 35 | 36 | has, err := engine.Exist(&TestTx{Msg: "hi"}) 37 | assert.NoError(t, err) 38 | assert.EqualValues(t, true, has) 39 | 40 | // will rollback 41 | _, err = engine.Transaction(func(session *Session) (interface{}, error) { 42 | _, err := session.Insert(TestTx{Msg: "hello"}) 43 | assert.NoError(t, err) 44 | 45 | return nil, fmt.Errorf("rollback") 46 | }) 47 | assert.Error(t, err) 48 | 49 | has, err = engine.Exist(&TestTx{Msg: "hello"}) 50 | assert.NoError(t, err) 51 | assert.EqualValues(t, false, has) 52 | } 53 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "reflect" 9 | 10 | "xorm.io/core" 11 | ) 12 | 13 | var ( 14 | ptrPkType = reflect.TypeOf(&core.PK{}) 15 | pkType = reflect.TypeOf(core.PK{}) 16 | ) 17 | -------------------------------------------------------------------------------- /xorm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build go1.8 6 | 7 | package xorm 8 | 9 | import ( 10 | "context" 11 | "fmt" 12 | "os" 13 | "reflect" 14 | "runtime" 15 | "sync" 16 | "time" 17 | 18 | "xorm.io/core" 19 | ) 20 | 21 | const ( 22 | // Version show the xorm's version 23 | Version string = "0.7.0.0504" 24 | ) 25 | 26 | func regDrvsNDialects() bool { 27 | providedDrvsNDialects := map[string]struct { 28 | dbType core.DbType 29 | getDriver func() core.Driver 30 | getDialect func() core.Dialect 31 | }{ 32 | "mssql": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }}, 33 | "odbc": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }}, // !nashtsai! TODO change this when supporting MS Access 34 | "mysql": {"mysql", func() core.Driver { return &mysqlDriver{} }, func() core.Dialect { return &mysql{} }}, 35 | "mymysql": {"mysql", func() core.Driver { return &mymysqlDriver{} }, func() core.Dialect { return &mysql{} }}, 36 | "postgres": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }}, 37 | "pgx": {"postgres", func() core.Driver { return &pqDriverPgx{} }, func() core.Dialect { return &postgres{} }}, 38 | "sqlite3": {"sqlite3", func() core.Driver { return &sqlite3Driver{} }, func() core.Dialect { return &sqlite3{} }}, 39 | "oci8": {"oracle", func() core.Driver { return &oci8Driver{} }, func() core.Dialect { return &oracle{} }}, 40 | "goracle": {"oracle", func() core.Driver { return &goracleDriver{} }, func() core.Dialect { return &oracle{} }}, 41 | } 42 | 43 | for driverName, v := range providedDrvsNDialects { 44 | if driver := core.QueryDriver(driverName); driver == nil { 45 | core.RegisterDriver(driverName, v.getDriver()) 46 | core.RegisterDialect(v.dbType, v.getDialect) 47 | } 48 | } 49 | return true 50 | } 51 | 52 | func close(engine *Engine) { 53 | engine.Close() 54 | } 55 | 56 | func init() { 57 | regDrvsNDialects() 58 | } 59 | 60 | // NewEngine new a db manager according to the parameter. Currently support four 61 | // drivers 62 | func NewEngine(driverName string, dataSourceName string) (*Engine, error) { 63 | driver := core.QueryDriver(driverName) 64 | if driver == nil { 65 | return nil, fmt.Errorf("Unsupported driver name: %v", driverName) 66 | } 67 | 68 | uri, err := driver.Parse(driverName, dataSourceName) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | dialect := core.QueryDialect(uri.DbType) 74 | if dialect == nil { 75 | return nil, fmt.Errorf("Unsupported dialect type: %v", uri.DbType) 76 | } 77 | 78 | db, err := core.Open(driverName, dataSourceName) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | err = dialect.Init(db, uri, driverName, dataSourceName) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | engine := &Engine{ 89 | db: db, 90 | dialect: dialect, 91 | Tables: make(map[reflect.Type]*core.Table), 92 | mutex: &sync.RWMutex{}, 93 | TagIdentifier: "xorm", 94 | TZLocation: time.Local, 95 | tagHandlers: defaultTagHandlers, 96 | cachers: make(map[string]core.Cacher), 97 | defaultContext: context.Background(), 98 | } 99 | 100 | if uri.DbType == core.SQLITE { 101 | engine.DatabaseTZ = time.UTC 102 | } else { 103 | engine.DatabaseTZ = time.Local 104 | } 105 | 106 | logger := NewSimpleLogger(os.Stdout) 107 | logger.SetLevel(core.LOG_INFO) 108 | engine.SetLogger(logger) 109 | engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) 110 | 111 | runtime.SetFinalizer(engine, close) 112 | 113 | return engine, nil 114 | } 115 | 116 | // NewEngineWithParams new a db manager with params. The params will be passed to dialect. 117 | func NewEngineWithParams(driverName string, dataSourceName string, params map[string]string) (*Engine, error) { 118 | engine, err := NewEngine(driverName, dataSourceName) 119 | engine.dialect.SetParams(params) 120 | return engine, err 121 | } 122 | 123 | // Clone clone an engine 124 | func (engine *Engine) Clone() (*Engine, error) { 125 | return NewEngine(engine.DriverName(), engine.DataSourceName()) 126 | } 127 | -------------------------------------------------------------------------------- /xorm_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Xorm Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xorm 6 | 7 | import ( 8 | "database/sql" 9 | "flag" 10 | "fmt" 11 | "log" 12 | "os" 13 | "strings" 14 | "testing" 15 | 16 | _ "github.com/denisenkom/go-mssqldb" 17 | _ "github.com/go-sql-driver/mysql" 18 | _ "github.com/lib/pq" 19 | _ "github.com/mattn/go-sqlite3" 20 | _ "github.com/ziutek/mymysql/godrv" 21 | "xorm.io/core" 22 | ) 23 | 24 | var ( 25 | testEngine EngineInterface 26 | dbType string 27 | connString string 28 | 29 | db = flag.String("db", "sqlite3", "the tested database") 30 | showSQL = flag.Bool("show_sql", true, "show generated SQLs") 31 | ptrConnStr = flag.String("conn_str", "./test.db?cache=shared&mode=rwc", "test database connection string") 32 | mapType = flag.String("map_type", "snake", "indicate the name mapping") 33 | cache = flag.Bool("cache", false, "if enable cache") 34 | cluster = flag.Bool("cluster", false, "if this is a cluster") 35 | splitter = flag.String("splitter", ";", "the splitter on connstr for cluster") 36 | schema = flag.String("schema", "", "specify the schema") 37 | ignoreSelectUpdate = flag.Bool("ignore_select_update", false, "ignore select update if implementation difference, only for tidb") 38 | 39 | tableMapper core.IMapper 40 | colMapper core.IMapper 41 | ) 42 | 43 | func createEngine(dbType, connStr string) error { 44 | if testEngine == nil { 45 | var err error 46 | 47 | if !*cluster { 48 | switch strings.ToLower(dbType) { 49 | case core.MSSQL: 50 | db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "master", -1)) 51 | if err != nil { 52 | return err 53 | } 54 | if _, err = db.Exec("If(db_id(N'xorm_test') IS NULL) BEGIN CREATE DATABASE xorm_test; END;"); err != nil { 55 | return fmt.Errorf("db.Exec: %v", err) 56 | } 57 | db.Close() 58 | *ignoreSelectUpdate = true 59 | case core.POSTGRES: 60 | db, err := sql.Open(dbType, connStr) 61 | if err != nil { 62 | return err 63 | } 64 | rows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = 'xorm_test'")) 65 | if err != nil { 66 | return fmt.Errorf("db.Query: %v", err) 67 | } 68 | defer rows.Close() 69 | 70 | if !rows.Next() { 71 | if _, err = db.Exec("CREATE DATABASE xorm_test"); err != nil { 72 | return fmt.Errorf("CREATE DATABASE: %v", err) 73 | } 74 | } 75 | if *schema != "" { 76 | if _, err = db.Exec("CREATE SCHEMA IF NOT EXISTS " + *schema); err != nil { 77 | return fmt.Errorf("CREATE SCHEMA: %v", err) 78 | } 79 | } 80 | db.Close() 81 | *ignoreSelectUpdate = true 82 | case core.MYSQL: 83 | db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "mysql", -1)) 84 | if err != nil { 85 | return err 86 | } 87 | if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS xorm_test"); err != nil { 88 | return fmt.Errorf("db.Exec: %v", err) 89 | } 90 | db.Close() 91 | default: 92 | *ignoreSelectUpdate = true 93 | } 94 | 95 | testEngine, err = NewEngine(dbType, connStr) 96 | } else { 97 | testEngine, err = NewEngineGroup(dbType, strings.Split(connStr, *splitter)) 98 | if dbType != "mysql" && dbType != "mymysql" { 99 | *ignoreSelectUpdate = true 100 | } 101 | } 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if *schema != "" { 107 | testEngine.SetSchema(*schema) 108 | } 109 | testEngine.ShowSQL(*showSQL) 110 | testEngine.SetLogLevel(core.LOG_DEBUG) 111 | if *cache { 112 | cacher := NewLRUCacher(NewMemoryStore(), 100000) 113 | testEngine.SetDefaultCacher(cacher) 114 | } 115 | 116 | if len(*mapType) > 0 { 117 | switch *mapType { 118 | case "snake": 119 | testEngine.SetMapper(core.SnakeMapper{}) 120 | case "same": 121 | testEngine.SetMapper(core.SameMapper{}) 122 | case "gonic": 123 | testEngine.SetMapper(core.LintGonicMapper) 124 | } 125 | } 126 | } 127 | 128 | tableMapper = testEngine.GetTableMapper() 129 | colMapper = testEngine.GetColumnMapper() 130 | 131 | tables, err := testEngine.DBMetas() 132 | if err != nil { 133 | return err 134 | } 135 | var tableNames = make([]interface{}, 0, len(tables)) 136 | for _, table := range tables { 137 | tableNames = append(tableNames, table.Name) 138 | } 139 | if err = testEngine.DropTables(tableNames...); err != nil { 140 | return err 141 | } 142 | return nil 143 | } 144 | 145 | func prepareEngine() error { 146 | return createEngine(dbType, connString) 147 | } 148 | 149 | func TestMain(m *testing.M) { 150 | flag.Parse() 151 | 152 | dbType = *db 153 | if *db == "sqlite3" { 154 | if ptrConnStr == nil { 155 | connString = "./test.db?cache=shared&mode=rwc" 156 | } else { 157 | connString = *ptrConnStr 158 | } 159 | } else { 160 | if ptrConnStr == nil { 161 | log.Fatal("you should indicate conn string") 162 | return 163 | } 164 | connString = *ptrConnStr 165 | } 166 | 167 | dbs := strings.Split(*db, "::") 168 | conns := strings.Split(connString, "::") 169 | 170 | var res int 171 | for i := 0; i < len(dbs); i++ { 172 | dbType = dbs[i] 173 | connString = conns[i] 174 | testEngine = nil 175 | fmt.Println("testing", dbType, connString) 176 | 177 | if err := prepareEngine(); err != nil { 178 | log.Fatal(err) 179 | return 180 | } 181 | 182 | code := m.Run() 183 | if code > 0 { 184 | res = code 185 | } 186 | } 187 | 188 | os.Exit(res) 189 | } 190 | 191 | func TestPing(t *testing.T) { 192 | if err := testEngine.Ping(); err != nil { 193 | t.Fatal(err) 194 | } 195 | } 196 | --------------------------------------------------------------------------------