diff --git a/.drone.yml b/.drone.yml index 34b9a514..210572b0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -73,7 +73,7 @@ steps: TEST_MYSQL_PASSWORD: commands: - make test-mysql - - TEST_QUOTE_POLICY=reserved make test-mysql + - TEST_QUOTE_POLICY=reserved make test-mysql-tls volumes: - name: cache @@ -363,6 +363,41 @@ services: commands: - /cockroach/cockroach start --insecure +# --- +# kind: pipeline +# name: test-dameng +# depends_on: +# - test-cockroach +# trigger: +# ref: +# - refs/heads/master +# - refs/pull/*/head +# steps: +# - name: test-dameng +# pull: never +# image: golang:1.15 +# volumes: +# - name: cache +# path: /go/pkg/mod +# environment: +# TEST_DAMENG_HOST: "dameng:5236" +# TEST_DAMENG_USERNAME: SYSDBA +# TEST_DAMENG_PASSWORD: SYSDBA +# commands: +# - sleep 30 +# - make test-dameng + +# volumes: +# - name: cache +# host: +# path: /tmp/cache + +# services: +# - name: dameng +# image: lunny/dm:v1.0 +# commands: +# - /bin/bash /startDm.sh + --- kind: pipeline name: merge_coverage @@ -374,6 +409,7 @@ depends_on: - test-mssql - test-tidb - test-cockroach + #- test-dameng trigger: ref: - refs/heads/master diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..7b91f22d --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,24 @@ +linters: + enable: + - gosimple + - deadcode + - typecheck + - govet + - errcheck + - staticcheck + - unused + - structcheck + - varcheck + - dupl + #- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time. + - gofmt + - misspell + - gocritic + - bidichk + - ineffassign + enable-all: false + disable-all: true + fast: false + +run: + timeout: 3m \ No newline at end of file diff --git a/.revive.toml b/.revive.toml deleted file mode 100644 index 9e3b629d..00000000 --- a/.revive.toml +++ /dev/null @@ -1,29 +0,0 @@ -ignoreGeneratedHeader = false -severity = "warning" -confidence = 0.8 -errorCode = 1 -warningCode = 1 - -[rule.blank-imports] -[rule.context-as-argument] -[rule.context-keys-type] -[rule.dot-imports] -[rule.empty-lines] -[rule.errorf] -[rule.error-return] -[rule.error-strings] -[rule.error-naming] -[rule.exported] -[rule.if-return] -[rule.increment-decrement] -[rule.indent-error-flow] -[rule.package-comments] -[rule.range] -[rule.receiver-naming] -[rule.struct-tag] -[rule.time-naming] -[rule.unexported-return] -[rule.unnecessary-stmt] -[rule.var-declaration] -[rule.var-naming] - arguments = [["ID", "UID", "UUID", "URL", "JSON"], []] \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6925a5c..27e6929b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,13 @@ ## Contributing to xorm -`xorm` has a backlog of [pull requests](https://help.github.com/articles/using-pull-requests), but contributions are still very -much welcome. You can help with patch review, submitting bug reports, +`xorm` has a backlog of [pull requests](https://gitea.com/xorm/xorm/pulls), but contributions are still very +much welcome. You can help with patch review, submitting [bug reports](https://gitea.com/xorm/xorm/issues), or adding new functionality. There is no formal style guide, but please conform to the style of existing code and general Go formatting conventions when submitting patches. -* [fork a repo](https://help.github.com/articles/fork-a-repo) -* [creating a pull request ](https://help.github.com/articles/creating-a-pull-request) +* [fork the repo](https://gitea.com/repo/fork/2038) +* [creating a pull request ](https://docs.gitea.io/en-us/pull-request/) ### Language @@ -15,7 +15,7 @@ Since `xorm` is a world-wide open source project, please describe your issues or ### Sign your codes with comments ``` -// !! your comments +// !! your comments e.g., @@ -65,7 +65,7 @@ And if your branch is related with cache, you could also enable it via `TEST_CAC ### Patch review -Help review existing open [pull requests](https://help.github.com/articles/using-pull-requests) by commenting on the code or +Help review existing open [pull requests](https://gitea.com/xorm/xorm/pulls) by commenting on the code or proposed functionality. ### Bug reports diff --git a/Makefile b/Makefile index e986082e..b43c4a4c 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,10 @@ TEST_TIDB_DBNAME ?= xorm_test TEST_TIDB_USERNAME ?= root TEST_TIDB_PASSWORD ?= +TEST_DAMENG_HOST ?= dameng:5236 +TEST_DAMENG_USERNAME ?= SYSDBA +TEST_DAMENG_PASSWORD ?= SYSDBA + TEST_CACHE_ENABLE ?= false TEST_QUOTE_POLICY ?= always @@ -94,8 +98,7 @@ help: @echo " - build creates the entire project" @echo " - clean delete integration files and build files but not css and js files" @echo " - fmt format the code" - @echo " - lint run code linter revive" - @echo " - misspell check if a word is written wrong" + @echo " - lint run code linter" @echo " - test run default unit test" @echo " - test-cockroach run integration tests for cockroach" @echo " - test-mysql run integration tests for mysql" @@ -107,28 +110,25 @@ help: @echo " - vet examines Go source code and reports suspicious constructs" .PHONY: lint -lint: revive +lint: golangci-lint -.PHONY: revive -revive: - @hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/mgechev/revive; \ - fi - revive -config .revive.toml -exclude=./vendor/... ./... || exit 1 +.PHONY: golangci-lint +golangci-lint: golangci-lint-check + golangci-lint run --timeout 10m -.PHONY: misspell -misspell: - @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/client9/misspell/cmd/misspell; \ +.PHONY: golangci-lint-check +golangci-lint-check: + $(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');)) + $(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...))) + @hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ + echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \ + export BINARY="golangci-lint"; \ + curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \ + elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \ + echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \ + export BINARY="golangci-lint"; \ + curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \ fi - misspell -w -i unknwon $(GOFILES) - -.PHONY: misspell-check -misspell-check: - @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ - $(GO) get -u github.com/client9/misspell/cmd/misspell; \ - fi - misspell -error -i unknwon,destory $(GOFILES) .PHONY: test test: go-check @@ -186,6 +186,18 @@ test-mysql\#%: go-check -conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)" \ -coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic +.PNONY: test-mysql-tls +test-mysql-tls: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=mysql -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)&tls=skip-verify" \ + -coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + +.PHONY: test-mysql-tls\#% +test-mysql-tls\#%: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -db=mysql -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="$(TEST_MYSQL_USERNAME):$(TEST_MYSQL_PASSWORD)@tcp($(TEST_MYSQL_HOST))/$(TEST_MYSQL_DBNAME)?charset=$(TEST_MYSQL_CHARSET)&tls=skip-verify" \ + -coverprofile=mysql.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic + .PNONY: test-postgres test-postgres: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=postgres -schema='$(TEST_PGSQL_SCHEMA)' -cache=$(TEST_CACHE_ENABLE) \ @@ -240,7 +252,6 @@ test-sqlite\#%: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -cache=$(TEST_CACHE_ENABLE) -db=sqlite -conn_str="./test.db?cache=shared&mode=rwc" \ -quote=$(TEST_QUOTE_POLICY) -coverprofile=sqlite.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic - .PNONY: test-tidb test-tidb: go-check $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=mysql -cache=$(TEST_CACHE_ENABLE) -ignore_select_update=true \ @@ -253,6 +264,18 @@ test-tidb\#%: go-check -conn_str="$(TEST_TIDB_USERNAME):$(TEST_TIDB_PASSWORD)@tcp($(TEST_TIDB_HOST))/$(TEST_TIDB_DBNAME)" \ -quote=$(TEST_QUOTE_POLICY) -coverprofile=tidb.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic +.PNONY: test-dameng +test-dameng: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -db=dm -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="dm://$(TEST_DAMENG_USERNAME):$(TEST_DAMENG_PASSWORD)@$(TEST_DAMENG_HOST)" \ + -coverprofile=dameng.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + +.PHONY: test-dameng\#% +test-dameng\#%: go-check + $(GO) test $(INTEGRATION_PACKAGES) -v -race -run $* -db=dm -cache=$(TEST_CACHE_ENABLE) -quote=$(TEST_QUOTE_POLICY) \ + -conn_str="dm://$(TEST_DAMENG_USERNAME):$(TEST_DAMENG_PASSWORD)@$(TEST_DAMENG_HOST)" \ + -coverprofile=dameng.$(TEST_QUOTE_POLICY).$(TEST_CACHE_ENABLE).coverage.out -covermode=atomic -timeout=20m + .PHONY: vet vet: $(GO) vet $(shell $(GO) list ./...) diff --git a/README.md b/README.md index f4bee6b6..ccf49348 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Firstly, we should new an engine for a database. engine, err := xorm.NewEngine(driverName, dataSourceName) ``` -* Define a struct and Sync2 table struct to database +* Define a struct and Sync table struct to database ```Go type User struct { @@ -87,7 +87,7 @@ type User struct { Updated time.Time `xorm:"updated"` } -err := engine.Sync2(new(User)) +err := engine.Sync(new(User)) ``` * Create Engine Group @@ -141,6 +141,24 @@ affected, err := engine.Insert(&users) affected, err := engine.Insert(&user1, &users) // INSERT INTO struct1 () values () // INSERT INTO struct2 () values (),(),() + +affected, err := engine.Table("user").Insert(map[string]interface{}{ + "name": "lunny", + "age": 18, +}) +// INSERT INTO user (name, age) values (?,?) + +affected, err := engine.Table("user").Insert([]map[string]interface{}{ + { + "name": "lunny", + "age": 18, + }, + { + "name": "lunny2", + "age": 19, + }, +}) +// INSERT INTO user (name, age) values (?,?),(?,?) ``` * `Get` query one record from database @@ -161,6 +179,11 @@ has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id) has, err := engine.SQL("select id from user").Get(&id) // SELECT id FROM user WHERE name = ? +var id int64 +var name string +has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) +// SELECT id, name FROM user LIMIT 1 + var valuesMap = make(map[string]string) has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) // SELECT * FROM user WHERE id = ? @@ -234,7 +257,11 @@ err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean inter }) // SELECT * FROM user Limit 0, 100 // SELECT * FROM user Limit 101, 100 +``` +You can use rows which is similiar with `sql.Rows` + +```Go rows, err := engine.Rows(&User{Name:name}) // SELECT * FROM user defer rows.Close() @@ -244,6 +271,19 @@ for rows.Next() { } ``` +or + +```Go +rows, err := engine.Cols("name", "age").Rows(&User{Name:name}) +// SELECT * FROM user +defer rows.Close() +for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) +} +``` + * `Update` update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on. ```Go diff --git a/README_CN.md b/README_CN.md index 500bb1fb..a5aaae66 100644 --- a/README_CN.md +++ b/README_CN.md @@ -84,7 +84,7 @@ type User struct { Updated time.Time `xorm:"updated"` } -err := engine.Sync2(new(User)) +err := engine.Sync(new(User)) ``` * 创建Engine组 @@ -138,6 +138,24 @@ affected, err := engine.Insert(&users) affected, err := engine.Insert(&user1, &users) // INSERT INTO struct1 () values () // INSERT INTO struct2 () values (),(),() + +affected, err := engine.Table("user").Insert(map[string]interface{}{ + "name": "lunny", + "age": 18, +}) +// INSERT INTO user (name, age) values (?,?) + +affected, err := engine.Table("user").Insert([]map[string]interface{}{ + { + "name": "lunny", + "age": 18, + }, + { + "name": "lunny2", + "age": 19, + }, +}) +// INSERT INTO user (name, age) values (?,?),(?,?) ``` * `Get` 查询单条记录 @@ -158,6 +176,11 @@ has, err := engine.Table(&user).Where("name = ?", name).Cols("id").Get(&id) has, err := engine.SQL("select id from user").Get(&id) // SELECT id FROM user WHERE name = ? +var id int64 +var name string +has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) +// SELECT id, name FROM user LIMIT 1 + var valuesMap = make(map[string]string) has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) // SELECT * FROM user WHERE id = ? @@ -209,7 +232,7 @@ type UserDetail struct { } var users []UserDetail -err := engine.Table("user").Select("user.*, detail.*") +err := engine.Table("user").Select("user.*, detail.*"). Join("INNER", "detail", "detail.user_id = user.id"). Where("user.name = ?", name).Limit(10, 0). Find(&users) @@ -231,7 +254,11 @@ err := engine.BufferSize(100).Iterate(&User{Name:name}, func(idx int, bean inter }) // SELECT * FROM user Limit 0, 100 // SELECT * FROM user Limit 101, 100 +``` +Rows 的用法类似 `sql.Rows`。 + +```Go rows, err := engine.Rows(&User{Name:name}) // SELECT * FROM user defer rows.Close() @@ -241,6 +268,19 @@ for rows.Next() { } ``` +或者 + +```Go +rows, err := engine.Cols("name", "age").Rows(&User{Name:name}) +// SELECT * FROM user +defer rows.Close() +for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) +} +``` + * `Update` 更新数据,除非使用Cols,AllCols函数指明,默认只更新非空和非0的字段 ```Go diff --git a/caches/encode.go b/caches/encode.go index 95536d7e..8659668c 100644 --- a/caches/encode.go +++ b/caches/encode.go @@ -16,19 +16,19 @@ import ( // Md5 return md5 hash string func Md5(str string) string { m := md5.New() - io.WriteString(m, str) + _, _ = io.WriteString(m, str) return fmt.Sprintf("%x", m.Sum(nil)) } // Encode Encode data func Encode(data interface{}) ([]byte, error) { - //return JsonEncode(data) + // return JsonEncode(data) return GobEncode(data) } // Decode decode data func Decode(data []byte, to interface{}) error { - //return JsonDecode(data, to) + // return JsonDecode(data, to) return GobDecode(data, to) } diff --git a/caches/lru.go b/caches/lru.go index 6b45ac94..885f02d6 100644 --- a/caches/lru.go +++ b/caches/lru.go @@ -56,7 +56,7 @@ func (m *LRUCacher) GC() { var removedNum int for e := m.idList.Front(); e != nil; { if removedNum <= CacheGcMaxRemoved && - time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired { + time.Since(e.Value.(*idNode).lastVisit) > m.Expired { removedNum++ next := e.Next() node := e.Value.(*idNode) @@ -70,7 +70,7 @@ func (m *LRUCacher) GC() { removedNum = 0 for e := m.sqlList.Front(); e != nil; { if removedNum <= CacheGcMaxRemoved && - time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired { + time.Since(e.Value.(*sqlNode).lastVisit) > m.Expired { removedNum++ next := e.Next() node := e.Value.(*sqlNode) @@ -96,7 +96,7 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} { } else { lastTime := el.Value.(*sqlNode).lastVisit // if expired, remove the node and return nil - if time.Now().Sub(lastTime) > m.Expired { + if time.Since(lastTime) > m.Expired { m.delIds(tableName, sql) return nil } @@ -122,7 +122,7 @@ func (m *LRUCacher) GetBean(tableName string, id string) interface{} { if el, ok := m.idIndex[tableName][id]; ok { lastTime := el.Value.(*idNode).lastVisit // if expired, remove the node and return nil - if time.Now().Sub(lastTime) > m.Expired { + if time.Since(lastTime) > m.Expired { m.delBean(tableName, id) return nil } @@ -145,7 +145,7 @@ func (m *LRUCacher) clearIds(tableName string) { if tis, ok := m.sqlIndex[tableName]; ok { for sql, v := range tis { m.sqlList.Remove(v) - m.store.Del(sql) + _ = m.store.Del(sql) } } m.sqlIndex[tableName] = make(map[string]*list.Element) @@ -163,7 +163,7 @@ func (m *LRUCacher) clearBeans(tableName string) { for id, v := range tis { m.idList.Remove(v) tid := genID(tableName, id) - m.store.Del(tid) + _ = m.store.Del(tid) } } m.idIndex[tableName] = make(map[string]*list.Element) @@ -188,7 +188,7 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { } else { el.Value.(*sqlNode).lastVisit = time.Now() } - m.store.Put(sql, ids) + _ = m.store.Put(sql, ids) if m.sqlList.Len() > m.MaxElementSize { e := m.sqlList.Front() node := e.Value.(*sqlNode) @@ -210,7 +210,7 @@ func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { el.Value.(*idNode).lastVisit = time.Now() } - m.store.Put(genID(tableName, id), obj) + _ = m.store.Put(genID(tableName, id), obj) if m.idList.Len() > m.MaxElementSize { e := m.idList.Front() node := e.Value.(*idNode) @@ -226,7 +226,7 @@ func (m *LRUCacher) delIds(tableName, sql string) { m.sqlList.Remove(el) } } - m.store.Del(sql) + _ = m.store.Del(sql) } // DelIds deletes ids @@ -243,7 +243,7 @@ func (m *LRUCacher) delBean(tableName string, id string) { m.idList.Remove(el) m.clearIds(tableName) } - m.store.Del(tid) + _ = m.store.Del(tid) } // DelBean deletes beans in some table @@ -265,10 +265,6 @@ type sqlNode struct { lastVisit time.Time } -func genSQLKey(sql string, args interface{}) string { - return fmt.Sprintf("%s-%v", sql, args) -} - func genID(prefix string, id string) string { return fmt.Sprintf("%s-%s", prefix, id) } diff --git a/contexts/hook.go b/contexts/hook.go index 70f114dd..f6d86cfc 100644 --- a/contexts/hook.go +++ b/contexts/hook.go @@ -36,7 +36,7 @@ func (c *ContextHook) End(ctx context.Context, result sql.Result, err error) { c.Ctx = ctx c.Result = result c.Err = err - c.ExecuteTime = time.Now().Sub(c.start) + c.ExecuteTime = time.Since(c.start) } // Hook represents a hook behaviour diff --git a/convert/conversion.go b/convert/conversion.go index 78a9fd78..b69e345c 100644 --- a/convert/conversion.go +++ b/convert/conversion.go @@ -283,11 +283,9 @@ func Assign(dest, src interface{}, originalLocation *time.Location, convertedLoc } } - var sv reflect.Value - switch d := dest.(type) { case *string: - sv = reflect.ValueOf(src) + var sv = reflect.ValueOf(src) switch sv.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, diff --git a/convert/time.go b/convert/time.go index e53a19cd..cc2e0a10 100644 --- a/convert/time.go +++ b/convert/time.go @@ -48,6 +48,16 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t } dt = dt.In(convertedLocation) return &dt, nil + } else if len(s) == 10 && s[4] == '-' { + if s == "0000-00-00" || s == "0001-01-01" { + return &time.Time{}, nil + } + dt, err := time.ParseInLocation("2006-01-02", s, originalLocation) + if err != nil { + return nil, err + } + dt = dt.In(convertedLocation) + return &dt, nil } else { i, err := strconv.ParseInt(s, 10, 64) if err == nil { diff --git a/convert/time_test.go b/convert/time_test.go index ef01b362..5ddceb64 100644 --- a/convert/time_test.go +++ b/convert/time_test.go @@ -16,6 +16,7 @@ func TestString2Time(t *testing.T) { assert.NoError(t, err) var kases = map[string]time.Time{ + "2021-08-10": time.Date(2021, 8, 10, 8, 0, 0, 0, expectedLoc), "2021-06-06T22:58:20+08:00": time.Date(2021, 6, 6, 22, 58, 20, 0, expectedLoc), "2021-07-11 10:44:00": time.Date(2021, 7, 11, 18, 44, 0, 0, expectedLoc), "2021-08-10T10:33:04Z": time.Date(2021, 8, 10, 18, 33, 04, 0, expectedLoc), diff --git a/core/db.go b/core/db.go index ef5ab227..b476ef9a 100644 --- a/core/db.go +++ b/core/db.go @@ -136,7 +136,7 @@ func (db *DB) reflectNew(typ reflect.Type) reflect.Value { cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), DefaultCacheSize, DefaultCacheSize), 0} db.reflectCache[typ] = cs } else { - cs.idx = cs.idx + 1 + cs.idx++ } return cs.value.Index(cs.idx).Addr() } diff --git a/core/db_test.go b/core/db_test.go index e9c2d82d..a9c19392 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -96,7 +96,7 @@ func BenchmarkOriQuery(b *testing.B) { if err != nil { b.Error(err) } - //fmt.Println(Id, Name, Title, Age, Alias, NickName) + // fmt.Println(Id, Name, Title, Age, Alias, NickName) } rows.Close() } @@ -245,13 +245,13 @@ func BenchmarkSliceInterfaceQuery(b *testing.B) { b.Error(err) } b.Log(slice) - switch slice[1].(type) { + switch st := slice[1].(type) { case *string: - if *slice[1].(*string) != "xlw" { + if *st != "xlw" { b.Error(errors.New("name should be xlw")) } case []byte: - if string(slice[1].([]byte)) != "xlw" { + if string(st) != "xlw" { b.Error(errors.New("name should be xlw")) } } @@ -399,14 +399,14 @@ func BenchmarkMapInterfaceQuery(b *testing.B) { if err != nil { b.Error(err) } - switch m["name"].(type) { + switch t := m["name"].(type) { case string: - if m["name"].(string) != "xlw" { + if t != "xlw" { b.Log(m) b.Error(errors.New("name should be xlw")) } case []byte: - if string(m["name"].([]byte)) != "xlw" { + if string(t) != "xlw" { b.Log(m) b.Error(errors.New("name should be xlw")) } diff --git a/core/rows.go b/core/rows.go index c15a59a3..75d6ebf0 100644 --- a/core/rows.go +++ b/core/rows.go @@ -62,7 +62,7 @@ func (rs *Rows) ScanStructByIndex(dest ...interface{}) error { for _, vvv := range vvvs { for j := 0; j < vvv.NumField(); j++ { newDest[i] = vvv.Field(j).Addr().Interface() - i = i + 1 + i++ } } diff --git a/core/stmt.go b/core/stmt.go index 260843d5..3247efed 100644 --- a/core/stmt.go +++ b/core/stmt.go @@ -93,7 +93,7 @@ func (s *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result if err != nil { return nil, err } - res, err := s.Stmt.ExecContext(ctx, args) + res, err := s.Stmt.ExecContext(ctx, args...) hookCtx.End(ctx, res, err) if err := s.db.afterProcess(hookCtx); err != nil { return nil, err diff --git a/dialects/dameng.go b/dialects/dameng.go new file mode 100644 index 00000000..f4a075d5 --- /dev/null +++ b/dialects/dameng.go @@ -0,0 +1,1201 @@ +// Copyright 2021 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dialects + +import ( + "context" + "database/sql" + "errors" + "fmt" + "net/url" + "strconv" + "strings" + + "xorm.io/xorm/convert" + "xorm.io/xorm/core" + "xorm.io/xorm/internal/utils" + "xorm.io/xorm/schemas" +) + +func init() { + RegisterDriver("dm", &damengDriver{}) + RegisterDialect(schemas.DAMENG, func() Dialect { + return &dameng{} + }) +} + +var ( + damengReservedWords = map[string]bool{ + "ACCESS": true, + "ACCOUNT": true, + "ACTIVATE": true, + "ADD": true, + "ADMIN": true, + "ADVISE": true, + "AFTER": true, + "ALL": true, + "ALL_ROWS": true, + "ALLOCATE": true, + "ALTER": true, + "ANALYZE": true, + "AND": true, + "ANY": true, + "ARCHIVE": true, + "ARCHIVELOG": true, + "ARRAY": true, + "AS": true, + "ASC": true, + "AT": true, + "AUDIT": true, + "AUTHENTICATED": true, + "AUTHORIZATION": true, + "AUTOEXTEND": true, + "AUTOMATIC": true, + "BACKUP": true, + "BECOME": true, + "BEFORE": true, + "BEGIN": true, + "BETWEEN": true, + "BFILE": true, + "BITMAP": true, + "BLOB": true, + "BLOCK": true, + "BODY": true, + "BY": true, + "CACHE": true, + "CACHE_INSTANCES": true, + "CANCEL": true, + "CASCADE": true, + "CAST": true, + "CFILE": true, + "CHAINED": true, + "CHANGE": true, + "CHAR": true, + "CHAR_CS": true, + "CHARACTER": true, + "CHECK": true, + "CHECKPOINT": true, + "CHOOSE": true, + "CHUNK": true, + "CLEAR": true, + "CLOB": true, + "CLONE": true, + "CLOSE": true, + "CLOSE_CACHED_OPEN_CURSORS": true, + "CLUSTER": true, + "COALESCE": true, + "COLUMN": true, + "COLUMNS": true, + "COMMENT": true, + "COMMIT": true, + "COMMITTED": true, + "COMPATIBILITY": true, + "COMPILE": true, + "COMPLETE": true, + "COMPOSITE_LIMIT": true, + "COMPRESS": true, + "COMPUTE": true, + "CONNECT": true, + "CONNECT_TIME": true, + "CONSTRAINT": true, + "CONSTRAINTS": true, + "CONTENTS": true, + "CONTINUE": true, + "CONTROLFILE": true, + "CONVERT": true, + "COST": true, + "CPU_PER_CALL": true, + "CPU_PER_SESSION": true, + "CREATE": true, + "CURRENT": true, + "CURRENT_SCHEMA": true, + "CURREN_USER": true, + "CURSOR": true, + "CYCLE": true, + "DANGLING": true, + "DATABASE": true, + "DATAFILE": true, + "DATAFILES": true, + "DATAOBJNO": true, + "DATE": true, + "DBA": true, + "DBHIGH": true, + "DBLOW": true, + "DBMAC": true, + "DEALLOCATE": true, + "DEBUG": true, + "DEC": true, + "DECIMAL": true, + "DECLARE": true, + "DEFAULT": true, + "DEFERRABLE": true, + "DEFERRED": true, + "DEGREE": true, + "DELETE": true, + "DEREF": true, + "DESC": true, + "DIRECTORY": true, + "DISABLE": true, + "DISCONNECT": true, + "DISMOUNT": true, + "DISTINCT": true, + "DISTRIBUTED": true, + "DML": true, + "DOUBLE": true, + "DROP": true, + "DUMP": true, + "EACH": true, + "ELSE": true, + "ENABLE": true, + "END": true, + "ENFORCE": true, + "ENTRY": true, + "ESCAPE": true, + "EXCEPT": true, + "EXCEPTIONS": true, + "EXCHANGE": true, + "EXCLUDING": true, + "EXCLUSIVE": true, + "EXECUTE": true, + "EXISTS": true, + "EXPIRE": true, + "EXPLAIN": true, + "EXTENT": true, + "EXTENTS": true, + "EXTERNALLY": true, + "FAILED_LOGIN_ATTEMPTS": true, + "FALSE": true, + "FAST": true, + "FILE": true, + "FIRST_ROWS": true, + "FLAGGER": true, + "FLOAT": true, + "FLOB": true, + "FLUSH": true, + "FOR": true, + "FORCE": true, + "FOREIGN": true, + "FREELIST": true, + "FREELISTS": true, + "FROM": true, + "FULL": true, + "FUNCTION": true, + "GLOBAL": true, + "GLOBALLY": true, + "GLOBAL_NAME": true, + "GRANT": true, + "GROUP": true, + "GROUPS": true, + "HASH": true, + "HASHKEYS": true, + "HAVING": true, + "HEADER": true, + "HEAP": true, + "IDENTIFIED": true, + "IDGENERATORS": true, + "IDLE_TIME": true, + "IF": true, + "IMMEDIATE": true, + "IN": true, + "INCLUDING": true, + "INCREMENT": true, + "INDEX": true, + "INDEXED": true, + "INDEXES": true, + "INDICATOR": true, + "IND_PARTITION": true, + "INITIAL": true, + "INITIALLY": true, + "INITRANS": true, + "INSERT": true, + "INSTANCE": true, + "INSTANCES": true, + "INSTEAD": true, + "INT": true, + "INTEGER": true, + "INTERMEDIATE": true, + "INTERSECT": true, + "INTO": true, + "IS": true, + "ISOLATION": true, + "ISOLATION_LEVEL": true, + "KEEP": true, + "KEY": true, + "KILL": true, + "LABEL": true, + "LAYER": true, + "LESS": true, + "LEVEL": true, + "LIBRARY": true, + "LIKE": true, + "LIMIT": true, + "LINK": true, + "LIST": true, + "LOB": true, + "LOCAL": true, + "LOCK": true, + "LOCKED": true, + "LOG": true, + "LOGFILE": true, + "LOGGING": true, + "LOGICAL_READS_PER_CALL": true, + "LOGICAL_READS_PER_SESSION": true, + "LONG": true, + "MANAGE": true, + "MASTER": true, + "MAX": true, + "MAXARCHLOGS": true, + "MAXDATAFILES": true, + "MAXEXTENTS": true, + "MAXINSTANCES": true, + "MAXLOGFILES": true, + "MAXLOGHISTORY": true, + "MAXLOGMEMBERS": true, + "MAXSIZE": true, + "MAXTRANS": true, + "MAXVALUE": true, + "MIN": true, + "MEMBER": true, + "MINIMUM": true, + "MINEXTENTS": true, + "MINUS": true, + "MINVALUE": true, + "MLSLABEL": true, + "MLS_LABEL_FORMAT": true, + "MODE": true, + "MODIFY": true, + "MOUNT": true, + "MOVE": true, + "MTS_DISPATCHERS": true, + "MULTISET": true, + "NATIONAL": true, + "NCHAR": true, + "NCHAR_CS": true, + "NCLOB": true, + "NEEDED": true, + "NESTED": true, + "NETWORK": true, + "NEW": true, + "NEXT": true, + "NOARCHIVELOG": true, + "NOAUDIT": true, + "NOCACHE": true, + "NOCOMPRESS": true, + "NOCYCLE": true, + "NOFORCE": true, + "NOLOGGING": true, + "NOMAXVALUE": true, + "NOMINVALUE": true, + "NONE": true, + "NOORDER": true, + "NOOVERRIDE": true, + "NOPARALLEL": true, + "NOREVERSE": true, + "NORMAL": true, + "NOSORT": true, + "NOT": true, + "NOTHING": true, + "NOWAIT": true, + "NULL": true, + "NUMBER": true, + "NUMERIC": true, + "NVARCHAR2": true, + "OBJECT": true, + "OBJNO": true, + "OBJNO_REUSE": true, + "OF": true, + "OFF": true, + "OFFLINE": true, + "OID": true, + "OIDINDEX": true, + "OLD": true, + "ON": true, + "ONLINE": true, + "ONLY": true, + "OPCODE": true, + "OPEN": true, + "OPTIMAL": true, + "OPTIMIZER_GOAL": true, + "OPTION": true, + "OR": true, + "ORDER": true, + "ORGANIZATION": true, + "OSLABEL": true, + "OVERFLOW": true, + "OWN": true, + "PACKAGE": true, + "PARALLEL": true, + "PARTITION": true, + "PASSWORD": true, + "PASSWORD_GRACE_TIME": true, + "PASSWORD_LIFE_TIME": true, + "PASSWORD_LOCK_TIME": true, + "PASSWORD_REUSE_MAX": true, + "PASSWORD_REUSE_TIME": true, + "PASSWORD_VERIFY_FUNCTION": true, + "PCTFREE": true, + "PCTINCREASE": true, + "PCTTHRESHOLD": true, + "PCTUSED": true, + "PCTVERSION": true, + "PERCENT": true, + "PERMANENT": true, + "PLAN": true, + "PLSQL_DEBUG": true, + "POST_TRANSACTION": true, + "PRECISION": true, + "PRESERVE": true, + "PRIMARY": true, + "PRIOR": true, + "PRIVATE": true, + "PRIVATE_SGA": true, + "PRIVILEGE": true, + "PRIVILEGES": true, + "PROCEDURE": true, + "PROFILE": true, + "PUBLIC": true, + "PURGE": true, + "QUEUE": true, + "QUOTA": true, + "RANGE": true, + "RAW": true, + "RBA": true, + "READ": true, + "READUP": true, + "REAL": true, + "REBUILD": true, + "RECOVER": true, + "RECOVERABLE": true, + "RECOVERY": true, + "REF": true, + "REFERENCES": true, + "REFERENCING": true, + "REFRESH": true, + "RENAME": true, + "REPLACE": true, + "RESET": true, + "RESETLOGS": true, + "RESIZE": true, + "RESOURCE": true, + "RESTRICTED": true, + "RETURN": true, + "RETURNING": true, + "REUSE": true, + "REVERSE": true, + "REVOKE": true, + "ROLE": true, + "ROLES": true, + "ROLLBACK": true, + "ROW": true, + "ROWID": true, + "ROWNUM": true, + "ROWS": true, + "RULE": true, + "SAMPLE": true, + "SAVEPOINT": true, + "SB4": true, + "SCAN_INSTANCES": true, + "SCHEMA": true, + "SCN": true, + "SCOPE": true, + "SD_ALL": true, + "SD_INHIBIT": true, + "SD_SHOW": true, + "SEGMENT": true, + "SEG_BLOCK": true, + "SEG_FILE": true, + "SELECT": true, + "SEQUENCE": true, + "SERIALIZABLE": true, + "SESSION": true, + "SESSION_CACHED_CURSORS": true, + "SESSIONS_PER_USER": true, + "SET": true, + "SHARE": true, + "SHARED": true, + "SHARED_POOL": true, + "SHRINK": true, + "SIZE": true, + "SKIP": true, + "SKIP_UNUSABLE_INDEXES": true, + "SMALLINT": true, + "SNAPSHOT": true, + "SOME": true, + "SORT": true, + "SPECIFICATION": true, + "SPLIT": true, + "SQL_TRACE": true, + "STANDBY": true, + "START": true, + "STATEMENT_ID": true, + "STATISTICS": true, + "STOP": true, + "STORAGE": true, + "STORE": true, + "STRUCTURE": true, + "SUCCESSFUL": true, + "SWITCH": true, + "SYS_OP_ENFORCE_NOT_NULL$": true, + "SYS_OP_NTCIMG$": true, + "SYNONYM": true, + "SYSDATE": true, + "SYSDBA": true, + "SYSOPER": true, + "SYSTEM": true, + "TABLE": true, + "TABLES": true, + "TABLESPACE": true, + "TABLESPACE_NO": true, + "TABNO": true, + "TEMPORARY": true, + "THAN": true, + "THE": true, + "THEN": true, + "THREAD": true, + "TIMESTAMP": true, + "TIME": true, + "TO": true, + "TOPLEVEL": true, + "TRACE": true, + "TRACING": true, + "TRANSACTION": true, + "TRANSITIONAL": true, + "TRIGGER": true, + "TRIGGERS": true, + "TRUE": true, + "TRUNCATE": true, + "TX": true, + "TYPE": true, + "UB2": true, + "UBA": true, + "UID": true, + "UNARCHIVED": true, + "UNDO": true, + "UNION": true, + "UNIQUE": true, + "UNLIMITED": true, + "UNLOCK": true, + "UNRECOVERABLE": true, + "UNTIL": true, + "UNUSABLE": true, + "UNUSED": true, + "UPDATABLE": true, + "UPDATE": true, + "USAGE": true, + "USE": true, + "USER": true, + "USING": true, + "VALIDATE": true, + "VALIDATION": true, + "VALUE": true, + "VALUES": true, + "VARCHAR": true, + "VARCHAR2": true, + "VARYING": true, + "VIEW": true, + "WHEN": true, + "WHENEVER": true, + "WHERE": true, + "WITH": true, + "WITHOUT": true, + "WORK": true, + "WRITE": true, + "WRITEDOWN": true, + "WRITEUP": true, + "XID": true, + "YEAR": true, + "ZONE": true, + } + + damengQuoter = schemas.Quoter{ + Prefix: '"', + Suffix: '"', + IsReserved: schemas.AlwaysReserve, + } +) + +type dameng struct { + Base +} + +func (db *dameng) Init(uri *URI) error { + db.quoter = damengQuoter + return db.Base.Init(db, uri) +} + +func (db *dameng) Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error) { + rows, err := queryer.QueryContext(ctx, "SELECT * FROM V$VERSION") // select id_code + if err != nil { + return nil, err + } + defer rows.Close() + + var version string + if !rows.Next() { + if rows.Err() != nil { + return nil, rows.Err() + } + return nil, errors.New("unknow version") + } + + if err := rows.Scan(&version); err != nil { + return nil, err + } + return &schemas.Version{ + Number: version, + }, nil +} + +func (db *dameng) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: SequenceAutoincrMode, + } +} + +// DropIndexSQL returns a SQL to drop index +func (db *dameng) DropIndexSQL(tableName string, index *schemas.Index) string { + quote := db.dialect.Quoter().Quote + var name string + if index.IsRegular { + name = index.XName(tableName) + } else { + name = index.Name + } + return fmt.Sprintf("DROP INDEX %v", quote(name)) +} + +func (db *dameng) SQLType(c *schemas.Column) string { + var res string + switch t := c.SQLType.Name; t { + case schemas.TinyInt, "BYTE": + return "TINYINT" + case schemas.SmallInt, schemas.MediumInt, schemas.Int, schemas.Integer, schemas.UnsignedTinyInt: + return "INTEGER" + case schemas.BigInt, + schemas.UnsignedBigInt, schemas.UnsignedBit, schemas.UnsignedInt, + schemas.Serial, schemas.BigSerial: + return "BIGINT" + case schemas.Bit, schemas.Bool, schemas.Boolean: + return schemas.Bit + case schemas.Uuid: + res = schemas.Varchar + c.Length = 40 + case schemas.Binary: + if c.Length == 0 { + return schemas.Binary + "(MAX)" + } + case schemas.VarBinary, schemas.Blob, schemas.TinyBlob, schemas.MediumBlob, schemas.LongBlob, schemas.Bytea: + return schemas.VarBinary + case schemas.Date: + return schemas.Date + case schemas.Time: + if c.Length > 0 { + return fmt.Sprintf("%s(%d)", schemas.Time, c.Length) + } + return schemas.Time + case schemas.DateTime, schemas.TimeStamp: + res = schemas.TimeStamp + case schemas.TimeStampz: + if c.Length > 0 { + return fmt.Sprintf("TIMESTAMP(%d) WITH TIME ZONE", c.Length) + } + return "TIMESTAMP WITH TIME ZONE" + case schemas.Float: + res = "FLOAT" + case schemas.Real, schemas.Double: + res = "REAL" + case schemas.Numeric, schemas.Decimal, "NUMBER": + res = "NUMERIC" + case schemas.Text, schemas.Json: + return "TEXT" + case schemas.MediumText, schemas.LongText: + res = "CLOB" + case schemas.Char, schemas.Varchar, schemas.TinyText: + res = "VARCHAR2" + default: + res = t + } + + hasLen1 := (c.Length > 0) + hasLen2 := (c.Length2 > 0) + + if hasLen2 { + res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + } else if hasLen1 { + res += "(" + strconv.Itoa(c.Length) + ")" + } + return res +} + +func (db *dameng) ColumnTypeKind(t string) int { + switch strings.ToUpper(t) { + case "DATE": + return schemas.TIME_TYPE + case "CHAR", "NCHAR", "VARCHAR", "VARCHAR2", "NVARCHAR2", "LONG", "CLOB", "NCLOB": + return schemas.TEXT_TYPE + case "NUMBER": + return schemas.NUMERIC_TYPE + case "BLOB": + return schemas.BLOB_TYPE + default: + return schemas.UNKNOW_TYPE + } +} + +func (db *dameng) AutoIncrStr() string { + return "IDENTITY" +} + +func (db *dameng) IsReserved(name string) bool { + _, ok := damengReservedWords[strings.ToUpper(name)] + return ok +} + +func (db *dameng) DropTableSQL(tableName string) (string, bool) { + return fmt.Sprintf("DROP TABLE %s", db.quoter.Quote(tableName)), false +} + +// ModifyColumnSQL returns a SQL to modify SQL +func (db *dameng) ModifyColumnSQL(tableName string, col *schemas.Column) string { + s, _ := ColumnString(db.dialect, col, false) + return fmt.Sprintf("ALTER TABLE %s MODIFY %s", db.quoter.Quote(tableName), s) +} + +func (db *dameng) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { + if tableName == "" { + tableName = table.Name + } + + quoter := db.Quoter() + var b strings.Builder + if _, err := b.WriteString("CREATE TABLE "); err != nil { + return "", false, err + } + if err := quoter.QuoteTo(&b, tableName); err != nil { + return "", false, err + } + if _, err := b.WriteString(" ("); err != nil { + return "", false, err + } + + pkList := table.PrimaryKeys + + for i, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + if col.SQLType.IsBool() && !col.DefaultIsEmpty { + if col.Default == "true" { + col.Default = "1" + } else if col.Default == "false" { + col.Default = "0" + } + } + + s, _ := ColumnString(db, col, false) + if _, err := b.WriteString(s); err != nil { + return "", false, err + } + if i != len(table.ColumnsSeq())-1 { + if _, err := b.WriteString(", "); err != nil { + return "", false, err + } + } + } + + if len(pkList) > 0 { + if len(table.ColumnsSeq()) > 0 { + if _, err := b.WriteString(", "); err != nil { + return "", false, err + } + } + if _, err := b.WriteString(fmt.Sprintf("CONSTRAINT PK_%s PRIMARY KEY (", tableName)); err != nil { + return "", false, err + } + if err := quoter.JoinWrite(&b, pkList, ","); err != nil { + return "", false, err + } + if _, err := b.WriteString(")"); err != nil { + return "", false, err + } + } + if _, err := b.WriteString(")"); err != nil { + return "", false, err + } + + return b.String(), false, nil +} + +func (db *dameng) SetQuotePolicy(quotePolicy QuotePolicy) { + switch quotePolicy { + case QuotePolicyNone: + var q = damengQuoter + q.IsReserved = schemas.AlwaysNoReserve + db.quoter = q + case QuotePolicyReserved: + var q = damengQuoter + q.IsReserved = db.IsReserved + db.quoter = q + case QuotePolicyAlways: + fallthrough + default: + db.quoter = damengQuoter + } +} + +func (db *dameng) IndexCheckSQL(tableName, idxName string) (string, []interface{}) { + args := []interface{}{tableName, idxName} + return `SELECT INDEX_NAME FROM USER_INDEXES ` + + `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args +} + +func (db *dameng) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) { + return db.HasRecords(queryer, ctx, `SELECT table_name FROM user_tables WHERE table_name = ?`, tableName) +} + +func (db *dameng) IsSequenceExist(ctx context.Context, queryer core.Queryer, seqName string) (bool, error) { + var cnt int + rows, err := queryer.QueryContext(ctx, "SELECT COUNT(*) FROM user_sequences WHERE sequence_name = ?", seqName) + if err != nil { + return false, err + } + defer rows.Close() + if !rows.Next() { + if rows.Err() != nil { + return false, rows.Err() + } + return false, errors.New("query sequence failed") + } + + if err := rows.Scan(&cnt); err != nil { + return false, err + } + return cnt > 0, nil +} + +func (db *dameng) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName, colName string) (bool, error) { + args := []interface{}{tableName, colName} + query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + + " AND column_name = ?" + return db.HasRecords(queryer, ctx, query, args...) +} + +var _ sql.Scanner = &dmClobScanner{} + +type dmClobScanner struct { + valid bool + data string +} + +type dmClobObject interface { + GetLength() (int64, error) + ReadString(int, int) (string, error) +} + +//var _ dmClobObject = &dm.DmClob{} + +func (d *dmClobScanner) Scan(data interface{}) error { + if data == nil { + return nil + } + + switch t := data.(type) { + case dmClobObject: // *dm.DmClob + if t == nil { + return nil + } + l, err := t.GetLength() + if err != nil { + return err + } + if l == 0 { + d.valid = true + return nil + } + d.data, err = t.ReadString(1, int(l)) + if err != nil { + return err + } + d.valid = true + return nil + case []byte: + if t == nil { + return nil + } + d.data = string(t) + d.valid = true + return nil + default: + return fmt.Errorf("cannot convert %T as dmClobScanner", data) + } +} + +func addSingleQuote(name string) string { + if len(name) < 2 { + return name + } + if name[0] == '\'' && name[len(name)-1] == '\'' { + return name + } + return fmt.Sprintf("'%s'", name) +} + +func (db *dameng) GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) { + s := `select column_name from user_cons_columns + where constraint_name = (select constraint_name from user_constraints + where table_name = ? and constraint_type ='P')` + rows, err := queryer.QueryContext(ctx, s, tableName) + if err != nil { + return nil, nil, err + } + defer rows.Close() + + var pkNames []string + for rows.Next() { + var pkName string + err = rows.Scan(&pkName) + if err != nil { + return nil, nil, err + } + pkNames = append(pkNames, pkName) + } + if rows.Err() != nil { + return nil, nil, rows.Err() + } + rows.Close() + + s = `SELECT USER_TAB_COLS.COLUMN_NAME, USER_TAB_COLS.DATA_DEFAULT, USER_TAB_COLS.DATA_TYPE, USER_TAB_COLS.DATA_LENGTH, + USER_TAB_COLS.data_precision, USER_TAB_COLS.data_scale, USER_TAB_COLS.NULLABLE, + user_col_comments.comments + FROM USER_TAB_COLS + LEFT JOIN user_col_comments on user_col_comments.TABLE_NAME=USER_TAB_COLS.TABLE_NAME + AND user_col_comments.COLUMN_NAME=USER_TAB_COLS.COLUMN_NAME + WHERE USER_TAB_COLS.table_name = ?` + rows, err = queryer.QueryContext(ctx, s, tableName) + if err != nil { + return nil, nil, err + } + defer rows.Close() + + cols := make(map[string]*schemas.Column) + colSeq := make([]string, 0) + for rows.Next() { + col := new(schemas.Column) + col.Indexes = make(map[string]int) + + var colDefault dmClobScanner + var colName, nullable, dataType, dataPrecision, comment sql.NullString + var dataScale, dataLen sql.NullInt64 + + err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision, + &dataScale, &nullable, &comment) + if err != nil { + return nil, nil, err + } + + if !colName.Valid { + return nil, nil, errors.New("column name is nil") + } + + col.Name = strings.Trim(colName.String, `" `) + if colDefault.valid { + col.Default = colDefault.data + } else { + col.DefaultIsEmpty = true + } + + if nullable.String == "Y" { + col.Nullable = true + } else { + col.Nullable = false + } + + if !comment.Valid { + col.Comment = comment.String + } + if utils.IndexSlice(pkNames, col.Name) > -1 { + col.IsPrimaryKey = true + has, err := db.HasRecords(queryer, ctx, "SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME = ?", utils.SeqName(tableName)) + if err != nil { + return nil, nil, err + } + if has { + col.IsAutoIncrement = true + } + } + + var ( + ignore bool + dt string + len1, len2 int + ) + + dts := strings.Split(dataType.String, "(") + dt = dts[0] + if len(dts) > 1 { + lens := strings.Split(dts[1][:len(dts[1])-1], ",") + if len(lens) > 1 { + len1, _ = strconv.Atoi(lens[0]) + len2, _ = strconv.Atoi(lens[1]) + } else { + len1, _ = strconv.Atoi(lens[0]) + } + } + + switch dt { + case "VARCHAR2": + col.SQLType = schemas.SQLType{Name: "VARCHAR2", DefaultLength: len1, DefaultLength2: len2} + case "VARCHAR": + col.SQLType = schemas.SQLType{Name: schemas.Varchar, DefaultLength: len1, DefaultLength2: len2} + case "TIMESTAMP WITH TIME ZONE": + col.SQLType = schemas.SQLType{Name: schemas.TimeStampz, DefaultLength: 0, DefaultLength2: 0} + case "NUMBER": + col.SQLType = schemas.SQLType{Name: "NUMBER", DefaultLength: len1, DefaultLength2: len2} + case "LONG", "LONG RAW", "NCLOB", "CLOB", "TEXT": + col.SQLType = schemas.SQLType{Name: schemas.Text, DefaultLength: 0, DefaultLength2: 0} + case "RAW": + col.SQLType = schemas.SQLType{Name: schemas.Binary, DefaultLength: 0, DefaultLength2: 0} + case "ROWID": + col.SQLType = schemas.SQLType{Name: schemas.Varchar, DefaultLength: 18, DefaultLength2: 0} + case "AQ$_SUBSCRIBERS": + ignore = true + default: + col.SQLType = schemas.SQLType{Name: strings.ToUpper(dt), DefaultLength: len1, DefaultLength2: len2} + } + + if ignore { + continue + } + + if _, ok := schemas.SqlTypes[col.SQLType.Name]; !ok { + return nil, nil, fmt.Errorf("unknown colType %v %v", dataType.String, col.SQLType) + } + + if col.SQLType.Name == "TIMESTAMP" { + col.Length = int(dataScale.Int64) + } else { + col.Length = int(dataLen.Int64) + } + + if col.SQLType.IsTime() { + if !col.DefaultIsEmpty && !strings.EqualFold(col.Default, "CURRENT_TIMESTAMP") { + col.Default = addSingleQuote(col.Default) + } + } + cols[col.Name] = col + colSeq = append(colSeq, col.Name) + } + if rows.Err() != nil { + return nil, nil, rows.Err() + } + + return colSeq, cols, nil +} + +func (db *dameng) GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) { + s := "SELECT table_name FROM user_tables WHERE temporary = 'N' AND table_name NOT LIKE ?" + args := []interface{}{strings.ToUpper(db.uri.User), "%$%"} + + rows, err := queryer.QueryContext(ctx, s, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + tables := make([]*schemas.Table, 0) + for rows.Next() { + table := schemas.NewEmptyTable() + err = rows.Scan(&table.Name) + if err != nil { + return nil, err + } + + tables = append(tables, table) + } + if rows.Err() != nil { + return nil, rows.Err() + } + return tables, nil +} + +func (db *dameng) GetIndexes(queryer core.Queryer, ctx context.Context, tableName string) (map[string]*schemas.Index, error) { + args := []interface{}{tableName, tableName} + s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + + "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =?" + + " AND t.index_name not in (SELECT index_name FROM ALL_CONSTRAINTS WHERE CONSTRAINT_TYPE='P' AND table_name = ?)" + + rows, err := queryer.QueryContext(ctx, s, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + indexes := make(map[string]*schemas.Index) + for rows.Next() { + var indexType int + var indexName, colName, uniqueness string + + err = rows.Scan(&colName, &uniqueness, &indexName) + if err != nil { + return nil, err + } + + indexName = strings.Trim(indexName, `" `) + + var isRegular bool + if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { + indexName = indexName[5+len(tableName):] + isRegular = true + } + + if uniqueness == "UNIQUE" { + indexType = schemas.UniqueType + } else { + indexType = schemas.IndexType + } + + var index *schemas.Index + var ok bool + if index, ok = indexes[indexName]; !ok { + index = new(schemas.Index) + index.Type = indexType + index.Name = indexName + index.IsRegular = isRegular + indexes[indexName] = index + } + index.AddColumn(colName) + } + if rows.Err() != nil { + return nil, rows.Err() + } + return indexes, nil +} + +func (db *dameng) Filters() []Filter { + return []Filter{} +} + +type damengDriver struct { + baseDriver +} + +// Features return features +func (d *damengDriver) Features() *DriverFeatures { + return &DriverFeatures{ + SupportReturnInsertedID: false, + } +} + +// Parse parse the datasource +// dm://userName:password@ip:port +func (d *damengDriver) Parse(driverName, dataSourceName string) (*URI, error) { + u, err := url.Parse(dataSourceName) + if err != nil { + return nil, err + } + + if u.User == nil { + return nil, errors.New("user/password needed") + } + + passwd, _ := u.User.Password() + return &URI{ + DBType: schemas.DAMENG, + Proto: u.Scheme, + Host: u.Hostname(), + Port: u.Port(), + DBName: u.User.Username(), + User: u.User.Username(), + Passwd: passwd, + }, nil +} + +func (d *damengDriver) GenScanResult(colType string) (interface{}, error) { + switch colType { + case "CHAR", "NCHAR", "VARCHAR", "VARCHAR2", "NVARCHAR2", "LONG", "CLOB", "NCLOB": + var s sql.NullString + return &s, nil + case "NUMBER": + var s sql.NullString + return &s, nil + case "BIGINT": + var s sql.NullInt64 + return &s, nil + case "INTEGER": + var s sql.NullInt32 + return &s, nil + case "DATE", "TIMESTAMP": + var s sql.NullString + return &s, nil + case "BLOB": + var r sql.RawBytes + return &r, nil + case "FLOAT": + var s sql.NullFloat64 + return &s, nil + default: + var r sql.RawBytes + return &r, nil + } +} + +func (d *damengDriver) Scan(ctx *ScanContext, rows *core.Rows, types []*sql.ColumnType, vv ...interface{}) error { + var scanResults = make([]interface{}, 0, len(types)) + var replaces = make([]bool, 0, len(types)) + var err error + for i, v := range vv { + var replaced bool + var scanResult interface{} + switch types[i].DatabaseTypeName() { + case "CLOB", "TEXT": + scanResult = &dmClobScanner{} + replaced = true + case "TIMESTAMP": + scanResult = &sql.NullString{} + replaced = true + default: + scanResult = v + } + + scanResults = append(scanResults, scanResult) + replaces = append(replaces, replaced) + } + + if err = rows.Scan(scanResults...); err != nil { + return err + } + + for i, replaced := range replaces { + if replaced { + switch t := scanResults[i].(type) { + case *dmClobScanner: + var d interface{} + if t.valid { + d = t.data + } else { + d = nil + } + if err := convert.Assign(vv[i], d, ctx.DBLocation, ctx.UserLocation); err != nil { + return err + } + default: + switch types[i].DatabaseTypeName() { + case "TIMESTAMP": + ns := t.(*sql.NullString) + if !ns.Valid { + break + } + s := ns.String + fields := strings.Split(s, "+") + if err := convert.Assign(vv[i], strings.Replace(fields[0], "T", " ", -1), ctx.DBLocation, ctx.UserLocation); err != nil { + return err + } + default: + return fmt.Errorf("don't support convert %T to %T", t, vv[i]) + } + } + } + } + + return nil +} diff --git a/dialects/dialect.go b/dialects/dialect.go index fc11eac1..555d96c6 100644 --- a/dialects/dialect.go +++ b/dialects/dialect.go @@ -38,11 +38,23 @@ func (uri *URI) SetSchema(schema string) { } } +// enumerates all autoincr mode +const ( + IncrAutoincrMode = iota + SequenceAutoincrMode +) + +// DialectFeatures represents a dialect parameters +type DialectFeatures struct { + AutoincrMode int // 0 autoincrement column, 1 sequence +} + // Dialect represents a kind of database type Dialect interface { Init(*URI) error URI() *URI Version(ctx context.Context, queryer core.Queryer) (*schemas.Version, error) + Features() *DialectFeatures SQLType(*schemas.Column) string Alias(string) string // return what a sql type's alias of @@ -61,9 +73,13 @@ type Dialect interface { GetTables(queryer core.Queryer, ctx context.Context) ([]*schemas.Table, error) IsTableExist(queryer core.Queryer, ctx context.Context, tableName string) (bool, error) - CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) + CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) DropTableSQL(tableName string) (string, bool) + CreateSequenceSQL(ctx context.Context, queryer core.Queryer, seqName string) (string, error) + IsSequenceExist(ctx context.Context, queryer core.Queryer, seqName string) (bool, error) + DropSequenceSQL(seqName string) (string, error) + GetColumns(queryer core.Queryer, ctx context.Context, tableName string) ([]string, map[string]*schemas.Column, error) IsColumnExist(queryer core.Queryer, ctx context.Context, tableName string, colName string) (bool, error) AddColumnSQL(tableName string, col *schemas.Column) string @@ -103,6 +119,59 @@ func (db *Base) URI() *URI { return db.uri } +// CreateTableSQL implements Dialect +func (db *Base) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { + if tableName == "" { + tableName = table.Name + } + + quoter := db.dialect.Quoter() + var b strings.Builder + b.WriteString("CREATE TABLE IF NOT EXISTS ") + if err := quoter.QuoteTo(&b, tableName); err != nil { + return "", false, err + } + b.WriteString(" (") + + for i, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + b.WriteString(s) + + if i != len(table.ColumnsSeq())-1 { + b.WriteString(", ") + } + } + + if len(table.PrimaryKeys) > 1 { + b.WriteString(", PRIMARY KEY (") + b.WriteString(quoter.Join(table.PrimaryKeys, ",")) + b.WriteString(")") + } + + b.WriteString(")") + + return b.String(), false, nil +} + +func (db *Base) CreateSequenceSQL(ctx context.Context, queryer core.Queryer, seqName string) (string, error) { + return fmt.Sprintf(`CREATE SEQUENCE %s + minvalue 1 + nomaxvalue + start with 1 + increment by 1 + nocycle + nocache`, seqName), nil +} + +func (db *Base) IsSequenceExist(ctx context.Context, queryer core.Queryer, seqName string) (bool, error) { + return false, fmt.Errorf("unsupported sequence feature") +} + +func (db *Base) DropSequenceSQL(seqName string) (string, error) { + return fmt.Sprintf("DROP SEQUENCE %s", seqName), nil +} + // DropTableSQL returns drop table SQL func (db *Base) DropTableSQL(tableName string) (string, bool) { quote := db.dialect.Quoter().Quote @@ -141,7 +210,7 @@ func (db *Base) IsColumnExist(queryer core.Queryer, ctx context.Context, tableNa // AddColumnSQL returns a SQL to add a column func (db *Base) AddColumnSQL(tableName string, col *schemas.Column) string { s, _ := ColumnString(db.dialect, col, true) - return fmt.Sprintf("ALTER TABLE %v ADD %v", db.dialect.Quoter().Quote(tableName), s) + return fmt.Sprintf("ALTER TABLE %s ADD %s", db.dialect.Quoter().Quote(tableName), s) } // CreateIndexSQL returns a SQL to create index @@ -173,7 +242,7 @@ func (db *Base) DropIndexSQL(tableName string, index *schemas.Index) string { // ModifyColumnSQL returns a SQL to modify SQL func (db *Base) ModifyColumnSQL(tableName string, col *schemas.Column) string { s, _ := ColumnString(db.dialect, col, false) - return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", tableName, s) + return fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN %s", db.quoter.Quote(tableName), s) } // ForUpdateSQL returns for updateSQL @@ -252,43 +321,41 @@ func ColumnString(dialect Dialect, col *schemas.Column, includePrimaryKey bool) return "", err } - if err := bd.WriteByte(' '); err != nil { - return "", err - } - if includePrimaryKey && col.IsPrimaryKey { - if _, err := bd.WriteString("PRIMARY KEY "); err != nil { + if _, err := bd.WriteString(" PRIMARY KEY"); err != nil { return "", err } - if col.IsAutoIncrement { - if _, err := bd.WriteString(dialect.AutoIncrStr()); err != nil { - return "", err - } if err := bd.WriteByte(' '); err != nil { return "", err } + if _, err := bd.WriteString(dialect.AutoIncrStr()); err != nil { + return "", err + } } } - if col.Default != "" { - if _, err := bd.WriteString("DEFAULT "); err != nil { + if !col.DefaultIsEmpty { + if _, err := bd.WriteString(" DEFAULT "); err != nil { return "", err } - if _, err := bd.WriteString(col.Default); err != nil { - return "", err - } - if err := bd.WriteByte(' '); err != nil { - return "", err + if col.Default == "" { + if _, err := bd.WriteString("''"); err != nil { + return "", err + } + } else { + if _, err := bd.WriteString(col.Default); err != nil { + return "", err + } } } if col.Nullable { - if _, err := bd.WriteString("NULL "); err != nil { + if _, err := bd.WriteString(" NULL"); err != nil { return "", err } } else { - if _, err := bd.WriteString("NOT NULL "); err != nil { + if _, err := bd.WriteString(" NOT NULL"); err != nil { return "", err } } diff --git a/dialects/mssql.go b/dialects/mssql.go index 2121e71d..706a754a 100644 --- a/dialects/mssql.go +++ b/dialects/mssql.go @@ -282,6 +282,12 @@ func (db *mssql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Ve }, nil } +func (db *mssql) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *mssql) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { @@ -423,7 +429,7 @@ func (db *mssql) DropTableSQL(tableName string) (string, bool) { func (db *mssql) ModifyColumnSQL(tableName string, col *schemas.Column) string { s, _ := ColumnString(db.dialect, col, false) - return fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", tableName, s) + return fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s", db.quoter.Quote(tableName), s) } func (db *mssql) IndexCheckSQL(tableName, idxName string) (string, []interface{}) { @@ -625,35 +631,38 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? return indexes, nil } -func (db *mssql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql string +func (db *mssql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { if tableName == "" { tableName = table.Name } - sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE " + quoter := db.dialect.Quoter() + var b strings.Builder + b.WriteString("IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '") + quoter.QuoteTo(&b, tableName) + b.WriteString("' ) CREATE TABLE ") + quoter.QuoteTo(&b, tableName) + b.WriteString(" (") - sql += db.Quoter().Quote(tableName) + " (" - - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { + for i, colName := range table.ColumnsSeq() { col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - sql += ", " + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + b.WriteString(s) + + if i != len(table.ColumnsSeq())-1 { + b.WriteString(", ") + } } - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += strings.Join(pkList, ",") - sql += " ), " + if len(table.PrimaryKeys) > 1 { + b.WriteString(", PRIMARY KEY (") + b.WriteString(quoter.Join(table.PrimaryKeys, ",")) + b.WriteString(")") } - sql = sql[:len(sql)-2] + ")" - sql += ";" - return []string{sql}, true + b.WriteString(")") + + return b.String(), true, nil } func (db *mssql) ForUpdateSQL(query string) string { diff --git a/dialects/mysql.go b/dialects/mysql.go index 21128527..1fad3fee 100644 --- a/dialects/mysql.go +++ b/dialects/mysql.go @@ -6,7 +6,6 @@ package dialects import ( "context" - "crypto/tls" "database/sql" "errors" "fmt" @@ -172,16 +171,7 @@ var ( type mysql struct { Base - net string - addr string - params map[string]string - loc *time.Location - timeout time.Duration - tls *tls.Config - allowAllFiles bool - allowOldPasswords bool - clientFoundRows bool - rowFormat string + rowFormat string } func (db *mysql) Init(uri *URI) error { @@ -244,6 +234,12 @@ func (db *mysql) Version(ctx context.Context, queryer core.Queryer) (*schemas.Ve }, nil } +func (db *mysql) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *mysql) SetParams(params map[string]string) { rowFormat, ok := params["rowFormat"] if ok { @@ -491,15 +487,15 @@ func (db *mysql) GetColumns(queryer core.Queryer, ctx context.Context, tableName if _, ok := schemas.SqlTypes[colType]; ok { col.SQLType = schemas.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2} } else { - return nil, nil, fmt.Errorf("Unknown colType %v", colType) + return nil, nil, fmt.Errorf("unknown colType %v", colType) } if colKey == "PRI" { col.IsPrimaryKey = true } - if colKey == "UNI" { - // col.is - } + // if colKey == "UNI" { + // col.is + // } if extra == "auto_increment" { col.IsAutoIncrement = true @@ -625,43 +621,44 @@ func (db *mysql) GetIndexes(queryer core.Queryer, ctx context.Context, tableName return indexes, nil } -func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql = "CREATE TABLE IF NOT EXISTS " +func (db *mysql) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { if tableName == "" { tableName = table.Name } - quoter := db.Quoter() + quoter := db.dialect.Quoter() + var b strings.Builder + b.WriteString("CREATE TABLE IF NOT EXISTS ") + quoter.QuoteTo(&b, tableName) + b.WriteString(" (") - sql += quoter.Quote(tableName) - sql += " (" + for i, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + s, _ := ColumnString(db.dialect, col, col.IsPrimaryKey && len(table.PrimaryKeys) == 1) + b.WriteString(s) - if len(table.ColumnsSeq()) > 0 { - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { - col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - if len(col.Comment) > 0 { - sql += " COMMENT '" + col.Comment + "'" - } - sql += ", " + if len(col.Comment) > 0 { + b.WriteString(" COMMENT '") + b.WriteString(col.Comment) + b.WriteString("'") } - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += quoter.Join(pkList, ",") - sql += " ), " + if i != len(table.ColumnsSeq())-1 { + b.WriteString(", ") } - - sql = sql[:len(sql)-2] } - sql += ")" + + if len(table.PrimaryKeys) > 1 { + b.WriteString(", PRIMARY KEY (") + b.WriteString(quoter.Join(table.PrimaryKeys, ",")) + b.WriteString(")") + } + + b.WriteString(")") if table.StoreEngine != "" { - sql += " ENGINE=" + table.StoreEngine + b.WriteString(" ENGINE=") + b.WriteString(table.StoreEngine) } var charset = table.Charset @@ -669,13 +666,22 @@ func (db *mysql) CreateTableSQL(table *schemas.Table, tableName string) ([]strin charset = db.URI().Charset } if len(charset) != 0 { - sql += " DEFAULT CHARSET " + charset + b.WriteString(" DEFAULT CHARSET ") + b.WriteString(charset) } if db.rowFormat != "" { - sql += " ROW_FORMAT=" + db.rowFormat + b.WriteString(" ROW_FORMAT=") + b.WriteString(db.rowFormat) } - return []string{sql}, true + + if table.Comment != "" { + b.WriteString(" COMMENT='") + b.WriteString(table.Comment) + b.WriteString("'") + } + + return b.String(), true, nil } func (db *mysql) Filters() []Filter { @@ -769,7 +775,7 @@ func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) { // Parse protocol part of URI p := strings.SplitN(pd[0], ":", 2) if len(p) != 2 { - return nil, errors.New("Wrong protocol part of URI") + return nil, errors.New("wrong protocol part of URI") } uri.Proto = p[0] options := strings.Split(p[1], ",") @@ -792,7 +798,7 @@ func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*URI, error) { } uri.Timeout = to default: - return nil, errors.New("Unknown option: " + k) + return nil, errors.New("unknown option: " + k) } } // Remove protocol part diff --git a/dialects/oracle.go b/dialects/oracle.go index 11a6653b..04652bd6 100644 --- a/dialects/oracle.go +++ b/dialects/oracle.go @@ -539,6 +539,12 @@ func (db *oracle) Version(ctx context.Context, queryer core.Queryer) (*schemas.V }, nil } +func (db *oracle) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: SequenceAutoincrMode, + } +} + func (db *oracle) SQLType(c *schemas.Column) string { var res string switch t := c.SQLType.Name; t { @@ -599,7 +605,7 @@ func (db *oracle) DropTableSQL(tableName string) (string, bool) { return fmt.Sprintf("DROP TABLE `%s`", tableName), false } -func (db *oracle) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { +func (db *oracle) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { var sql = "CREATE TABLE " if tableName == "" { tableName = table.Name @@ -629,7 +635,7 @@ func (db *oracle) CreateTableSQL(table *schemas.Table, tableName string) ([]stri } sql = sql[:len(sql)-2] + ")" - return []string{sql}, false + return sql, false, nil } func (db *oracle) SetQuotePolicy(quotePolicy QuotePolicy) { diff --git a/dialects/postgres.go b/dialects/postgres.go index 96ebfc85..a5b080aa 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -941,6 +941,12 @@ func (db *postgres) SQLType(c *schemas.Column) string { return res } +func (db *postgres) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *postgres) ColumnTypeKind(t string) int { switch strings.ToUpper(t) { case "DATETIME", "TIMESTAMP": @@ -965,41 +971,6 @@ func (db *postgres) AutoIncrStr() string { return "" } -func (db *postgres) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql string - sql = "CREATE TABLE IF NOT EXISTS " - if tableName == "" { - tableName = table.Name - } - - quoter := db.Quoter() - sql += quoter.Quote(tableName) - sql += " (" - - if len(table.ColumnsSeq()) > 0 { - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { - col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - sql += ", " - } - - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += quoter.Join(pkList, ",") - sql += " ), " - } - - sql = sql[:len(sql)-2] - } - sql += ")" - - return []string{sql}, true -} - func (db *postgres) IndexCheckSQL(tableName, idxName string) (string, []interface{}) { if len(db.getSchema()) == 0 { args := []interface{}{tableName, idxName} @@ -1020,13 +991,37 @@ func (db *postgres) IsTableExist(queryer core.Queryer, ctx context.Context, tabl db.getSchema(), tableName) } -func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string { +func (db *postgres) AddColumnSQL(tableName string, col *schemas.Column) string { + s, _ := ColumnString(db.dialect, col, true) + + quoter := db.dialect.Quoter() + addColumnSQL := "" + commentSQL := "; " if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") { - return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", - tableName, col.Name, db.SQLType(col)) + addColumnSQL = fmt.Sprintf("ALTER TABLE %s ADD %s", quoter.Quote(tableName), s) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return addColumnSQL + commentSQL } - return fmt.Sprintf("alter table %s.%s ALTER COLUMN %s TYPE %s", - db.getSchema(), tableName, col.Name, db.SQLType(col)) + + addColumnSQL = fmt.Sprintf("ALTER TABLE %s.%s ADD %s", quoter.Quote(db.getSchema()), quoter.Quote(tableName), s) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s.%s IS '%s'", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return addColumnSQL + commentSQL +} + +func (db *postgres) ModifyColumnSQL(tableName string, col *schemas.Column) string { + quoter := db.dialect.Quoter() + modifyColumnSQL := "" + commentSQL := "; " + + if len(db.getSchema()) == 0 || strings.Contains(tableName, ".") { + modifyColumnSQL = fmt.Sprintf("ALTER TABLE %s ALTER COLUMN %s TYPE %s", quoter.Quote(tableName), quoter.Quote(col.Name), db.SQLType(col)) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return modifyColumnSQL + commentSQL + } + + modifyColumnSQL = fmt.Sprintf("ALTER TABLE %s.%s ALTER COLUMN %s TYPE %s", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), db.SQLType(col)) + commentSQL += fmt.Sprintf("COMMENT ON COLUMN %s.%s.%s IS '%s'", quoter.Quote(db.getSchema()), quoter.Quote(tableName), quoter.Quote(col.Name), col.Comment) + return modifyColumnSQL + commentSQL } func (db *postgres) DropIndexSQL(tableName string, index *schemas.Index) string { @@ -1211,9 +1206,7 @@ WHERE n.nspname= s.table_schema AND c.relkind = 'r'::char AND c.relname = $1%s A col.Default = "'" + col.Default + "'" } } else if col.SQLType.IsTime() { - if strings.HasSuffix(col.Default, "::timestamp without time zone") { - col.Default = strings.TrimSuffix(col.Default, "::timestamp without time zone") - } + col.Default = strings.TrimSuffix(col.Default, "::timestamp without time zone") } } cols[col.Name] = col @@ -1274,7 +1267,7 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN s := "SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1" if len(db.getSchema()) != 0 { args = append(args, db.getSchema()) - s = s + " AND schemaname=$2" + s += " AND schemaname=$2" } rows, err := queryer.QueryContext(ctx, s, args...) @@ -1307,6 +1300,19 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN indexType = schemas.IndexType } colNames = getIndexColName(indexdef) + + isSkip := false + //Oid It's a special index. You can't put it in + for _, element := range colNames { + if "oid" == element { + isSkip = true + break + } + } + if isSkip { + continue + } + var isRegular bool if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { newIdxName := indexName[5+len(tableName):] @@ -1331,6 +1337,26 @@ func (db *postgres) GetIndexes(queryer core.Queryer, ctx context.Context, tableN return indexes, nil } +func (db *postgres) CreateTableSQL(ctx context.Context, queryer core.Queryer, table *schemas.Table, tableName string) (string, bool, error) { + quoter := db.dialect.Quoter() + if len(db.getSchema()) != 0 && !strings.Contains(tableName, ".") { + tableName = fmt.Sprintf("%s.%s", db.getSchema(), tableName) + } + + createTableSQL, ok, err := db.Base.CreateTableSQL(ctx, queryer, table, tableName) + if err != nil { + return "", ok, err + } + + commentSQL := "; " + if table.Comment != "" { + // support schema.table -> "schema"."table" + commentSQL += fmt.Sprintf("COMMENT ON TABLE %s IS '%s'", quoter.Quote(tableName), table.Comment) + } + + return createTableSQL + commentSQL, true, nil +} + func (db *postgres) Filters() []Filter { return []Filter{&SeqFilter{Prefix: "$", Start: 1}} } diff --git a/dialects/sqlite3.go b/dialects/sqlite3.go index ac17fd92..4ff9a39e 100644 --- a/dialects/sqlite3.go +++ b/dialects/sqlite3.go @@ -184,6 +184,12 @@ func (db *sqlite3) Version(ctx context.Context, queryer core.Queryer) (*schemas. }, nil } +func (db *sqlite3) Features() *DialectFeatures { + return &DialectFeatures{ + AutoincrMode: IncrAutoincrMode, + } +} + func (db *sqlite3) SetQuotePolicy(quotePolicy QuotePolicy) { switch quotePolicy { case QuotePolicyNone: @@ -285,41 +291,6 @@ func (db *sqlite3) DropIndexSQL(tableName string, index *schemas.Index) string { return fmt.Sprintf("DROP INDEX %v", db.Quoter().Quote(idxName)) } -func (db *sqlite3) CreateTableSQL(table *schemas.Table, tableName string) ([]string, bool) { - var sql string - sql = "CREATE TABLE IF NOT EXISTS " - if tableName == "" { - tableName = table.Name - } - - quoter := db.Quoter() - sql += quoter.Quote(tableName) - sql += " (" - - if len(table.ColumnsSeq()) > 0 { - pkList := table.PrimaryKeys - - for _, colName := range table.ColumnsSeq() { - col := table.GetColumn(colName) - s, _ := ColumnString(db, col, col.IsPrimaryKey && len(pkList) == 1) - sql += s - sql = strings.TrimSpace(sql) - sql += ", " - } - - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += quoter.Join(pkList, ",") - sql += " ), " - } - - sql = sql[:len(sql)-2] - } - sql += ")" - - return []string{sql}, true -} - func (db *sqlite3) ForUpdateSQL(query string) string { return query } diff --git a/dialects/table_name.go b/dialects/table_name.go index e190cd4b..48b44de2 100644 --- a/dialects/table_name.go +++ b/dialects/table_name.go @@ -17,8 +17,7 @@ import ( func TableNameWithSchema(dialect Dialect, tableName string) string { // Add schema name as prefix of table name. // Only for postgres database. - if dialect.URI().Schema != "" && - strings.Index(tableName, ".") == -1 { + if dialect.URI().Schema != "" && !strings.Contains(tableName, ".") { return fmt.Sprintf("%s.%s", dialect.URI().Schema, tableName) } return tableName @@ -27,20 +26,18 @@ func TableNameWithSchema(dialect Dialect, tableName string) string { // TableNameNoSchema returns table name with given tableName func TableNameNoSchema(dialect Dialect, mapper names.Mapper, tableName interface{}) string { quote := dialect.Quoter().Quote - switch tableName.(type) { + switch tt := tableName.(type) { case []string: - t := tableName.([]string) - if len(t) > 1 { - return fmt.Sprintf("%v AS %v", quote(t[0]), quote(t[1])) - } else if len(t) == 1 { - return quote(t[0]) + if len(tt) > 1 { + return fmt.Sprintf("%v AS %v", quote(tt[0]), quote(tt[1])) + } else if len(tt) == 1 { + return quote(tt[0]) } case []interface{}: - t := tableName.([]interface{}) - l := len(t) + l := len(tt) var table string if l > 0 { - f := t[0] + f := tt[0] switch f.(type) { case string: table = f.(string) @@ -57,7 +54,7 @@ func TableNameNoSchema(dialect Dialect, mapper names.Mapper, tableName interface } } if l > 1 { - return fmt.Sprintf("%v AS %v", quote(table), quote(fmt.Sprintf("%v", t[1]))) + return fmt.Sprintf("%v AS %v", quote(table), quote(fmt.Sprintf("%v", tt[1]))) } else if l == 1 { return quote(table) } diff --git a/doc.go b/doc.go index d0653232..a1565806 100644 --- a/doc.go +++ b/doc.go @@ -67,6 +67,11 @@ There are 8 major ORM methods and many helpful methods to use to operate databas has, err := engine.Table("user").Where("name = ?", name).Get(&id) // SELECT id FROM user WHERE name = ? LIMIT 1 + var id int64 + var name string + has, err := engine.Table(&user).Cols("id", "name").Get(&id, &name) + // SELECT id, name FROM user LIMIT 1 + 3. Query multiple records from database var sliceOfStructs []Struct @@ -97,6 +102,17 @@ another is Rows err = rows.Scan(bean) } +or + + rows, err := engine.Cols("name", "age").Rows(...) + // SELECT * FROM user + defer rows.Close() + for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) + } + 5. Update one or more records affected, err := engine.ID(...).Update(&user) diff --git a/engine.go b/engine.go index ec066109..b7dcf5a2 100644 --- a/engine.go +++ b/engine.go @@ -7,12 +7,13 @@ package xorm import ( "context" "database/sql" - "errors" "fmt" "io" "os" "reflect" + "regexp" "runtime" + "strconv" "strings" "time" @@ -248,11 +249,6 @@ func (engine *Engine) SQLType(c *schemas.Column) string { return engine.dialect.SQLType(c) } -// AutoIncrStr Database's autoincrement statement -func (engine *Engine) AutoIncrStr() string { - return engine.dialect.AutoIncrStr() -} - // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. func (engine *Engine) SetConnMaxLifetime(d time.Duration) { engine.DB().SetConnMaxLifetime(d) @@ -441,23 +437,23 @@ func (engine *Engine) DumpTablesToFile(tables []*schemas.Table, fp string, tp .. // DumpTables dump specify tables to io.Writer func (engine *Engine) DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { - return engine.dumpTables(tables, w, tp...) + return engine.dumpTables(context.Background(), tables, w, tp...) } -func formatBool(s string, dstDialect dialects.Dialect) string { - if dstDialect.URI().DBType == schemas.MSSQL { - switch s { - case "true": +func formatBool(s bool, dstDialect dialects.Dialect) string { + if dstDialect.URI().DBType != schemas.POSTGRES { + if s { return "1" - case "false": - return "0" } + return "0" } - return s + return strconv.FormatBool(s) } +var controlCharactersRe = regexp.MustCompile(`[\x00-\x1f\x7f]+`) + // dumpTables dump database all table structs and data to w with specify db type -func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { +func (engine *Engine) dumpTables(ctx context.Context, tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error { var dstDialect dialects.Dialect if len(tp) == 0 { dstDialect = engine.dialect @@ -471,9 +467,14 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch destURI := dialects.URI{ DBType: tp[0], DBName: uri.DBName, - Schema: uri.Schema, + // DO NOT SET SCHEMA HERE + } + if tp[0] == schemas.POSTGRES { + destURI.Schema = engine.dialect.URI().Schema + } + if err := dstDialect.Init(&destURI); err != nil { + return err } - dstDialect.Init(&destURI) } cacherMgr := caches.NewManager() dstTableCache := tags.NewParser("xorm", dstDialect, engine.GetTableMapper(), engine.GetColumnMapper(), cacherMgr) @@ -484,6 +485,13 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch return err } + if dstDialect.URI().DBType == schemas.MYSQL { + // For MySQL set NO_BACKLASH_ESCAPES so that strings work properly + if _, err := io.WriteString(w, "SET sql_mode='NO_BACKSLASH_ESCAPES';\n"); err != nil { + return err + } + } + for i, table := range tables { dstTable := table if table.Type != nil { @@ -494,9 +502,12 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch } } - dstTableName := dstTable.Name + var dstTableName = dstTable.Name + var quoter = dstDialect.Quoter().Quote + var quotedDstTableName = quoter(dstTable.Name) if dstDialect.URI().Schema != "" { dstTableName = fmt.Sprintf("%s.%s", dstDialect.URI().Schema, dstTable.Name) + quotedDstTableName = fmt.Sprintf("%s.%s", quoter(dstDialect.URI().Schema), quoter(dstTable.Name)) } originalTableName := table.Name if engine.dialect.URI().Schema != "" { @@ -509,13 +520,26 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch } } - sqls, _ := dstDialect.CreateTableSQL(dstTable, dstTableName) - for _, s := range sqls { - _, err = io.WriteString(w, s+";\n") + if dstTable.AutoIncrement != "" && dstDialect.Features().AutoincrMode == dialects.SequenceAutoincrMode { + sqlstr, err := dstDialect.CreateSequenceSQL(ctx, engine.db, utils.SeqName(dstTableName)) + if err != nil { + return err + } + _, err = io.WriteString(w, sqlstr+";\n") if err != nil { return err } } + + sqlstr, _, err := dstDialect.CreateTableSQL(ctx, engine.db, dstTable, dstTableName) + if err != nil { + return err + } + _, err = io.WriteString(w, sqlstr+";\n") + if err != nil { + return err + } + if len(dstTable.PKColumns()) > 0 && dstDialect.URI().DBType == schemas.MSSQL { fmt.Fprintf(w, "SET IDENTITY_INSERT [%s] ON;\n", dstTable.Name) } @@ -552,7 +576,7 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch sess := engine.NewSession() defer sess.Close() for rows.Next() { - _, err = io.WriteString(w, "INSERT INTO "+dstDialect.Quoter().Quote(dstTableName)+" ("+destColNames+") VALUES (") + _, err = io.WriteString(w, "INSERT INTO "+quotedDstTableName+" ("+destColNames+") VALUES (") if err != nil { return err } @@ -563,36 +587,208 @@ func (engine *Engine) dumpTables(tables []*schemas.Table, w io.Writer, tp ...sch } for i, scanResult := range scanResults { stp := schemas.SQLType{Name: types[i].DatabaseTypeName()} - if stp.IsNumeric() { - s := scanResult.(*sql.NullString) - if s.Valid { - if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil { - return err - } - } else { - if _, err = io.WriteString(w, "NULL"); err != nil { - return err - } - } - } else if stp.IsBool() { - s := scanResult.(*sql.NullString) - if s.Valid { - if _, err = io.WriteString(w, formatBool(s.String, dstDialect)); err != nil { - return err - } - } else { - if _, err = io.WriteString(w, "NULL"); err != nil { - return err - } + s := scanResult.(*sql.NullString) + if !s.Valid { + if _, err = io.WriteString(w, "NULL"); err != nil { + return err } } else { - s := scanResult.(*sql.NullString) - if s.Valid { - if _, err = io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { + if table.Columns()[i].SQLType.IsBool() || stp.IsBool() || (dstDialect.URI().DBType == schemas.MSSQL && strings.EqualFold(stp.Name, schemas.Bit)) { + val, err := strconv.ParseBool(s.String) + if err != nil { return err } + + if _, err = io.WriteString(w, formatBool(val, dstDialect)); err != nil { + return err + } + } else if stp.IsNumeric() { + if _, err = io.WriteString(w, s.String); err != nil { + return err + } + } else if sess.engine.dialect.URI().DBType == schemas.DAMENG && stp.IsTime() && len(s.String) == 25 { + r := strings.ReplaceAll(s.String[:19], "T", " ") + if _, err = io.WriteString(w, "'"+r+"'"); err != nil { + return err + } + } else if len(s.String) == 0 { + if _, err := io.WriteString(w, "''"); err != nil { + return err + } + } else if dstDialect.URI().DBType == schemas.POSTGRES { + if dstTable.Columns()[i].SQLType.IsBlob() { + // Postgres has the escape format and we should use that for bytea data + if _, err := fmt.Fprintf(w, "'\\x%x'", s.String); err != nil { + return err + } + } else { + // Postgres concatentates strings using || (NOTE: a NUL byte in a text segment will fail) + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"'"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"' || "); err != nil { + return err + } + } + if _, err := io.WriteString(w, "e'"); err != nil { + return err + } + for i := loc[0]; i < loc[1]; i++ { + if _, err := fmt.Fprintf(w, "\\x%02x", toCheck[i]); err != nil { + return err + } + } + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, "' || "); err != nil { + return err + } + } else { + if _, err := io.WriteString(w, "'"); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.MYSQL { + loc := controlCharactersRe.FindStringIndex(s.String) + if loc == nil { + if _, err := io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { + return err + } + } else { + if _, err := io.WriteString(w, "CONCAT("); err != nil { + return err + } + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"')"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"', "); err != nil { + return err + } + } + for i := loc[0]; i < loc[1]-1; i++ { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(toCheck[i]))+"), "); err != nil { + return err + } + } + char := toCheck[loc[1]-1] + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"), "); err != nil { + return err + } + } else { + if _, err = io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"))"); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.SQLITE { + if dstTable.Columns()[i].SQLType.IsBlob() { + // SQLite has its escape format + if _, err := fmt.Fprintf(w, "X'%x'", s.String); err != nil { + return err + } + } else { + // SQLite concatentates strings using || (NOTE: a NUL byte in a text segment will fail) + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"'"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"' || "); err != nil { + return err + } + } + if _, err := fmt.Fprintf(w, "X'%x'", toCheck[loc[0]:loc[1]]); err != nil { + return err + } + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, " || "); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.DAMENG || dstDialect.URI().DBType == schemas.ORACLE { + if dstTable.Columns()[i].SQLType.IsBlob() { + // ORACLE/DAMENG uses HEXTORAW + if _, err := fmt.Fprintf(w, "HEXTORAW('%x')", s.String); err != nil { + return err + } + } else { + // ORACLE/DAMENG concatentates strings in multiple ways but uses CHAR and has CONCAT + // (NOTE: a NUL byte in a text segment will fail) + if _, err := io.WriteString(w, "CONCAT("); err != nil { + return err + } + toCheck := strings.ReplaceAll(s.String, "'", "''") + for len(toCheck) > 0 { + loc := controlCharactersRe.FindStringIndex(toCheck) + if loc == nil { + if _, err := io.WriteString(w, "'"+toCheck+"')"); err != nil { + return err + } + break + } + if loc[0] > 0 { + if _, err := io.WriteString(w, "'"+toCheck[:loc[0]]+"', "); err != nil { + return err + } + } + for i := loc[0]; i < loc[1]-1; i++ { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(toCheck[i]))+"), "); err != nil { + return err + } + } + char := toCheck[loc[1]-1] + toCheck = toCheck[loc[1]:] + if len(toCheck) > 0 { + if _, err := io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"), "); err != nil { + return err + } + } else { + if _, err = io.WriteString(w, "CHAR("+strconv.Itoa(int(char))+"))"); err != nil { + return err + } + } + } + } + } else if dstDialect.URI().DBType == schemas.MSSQL { + if dstTable.Columns()[i].SQLType.IsBlob() { + // MSSQL uses CONVERT(VARBINARY(MAX), '0xDEADBEEF', 1) + if _, err := fmt.Fprintf(w, "CONVERT(VARBINARY(MAX), '0x%x', 1)", s.String); err != nil { + return err + } + } else { + if _, err = io.WriteString(w, "N'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { + return err + } + } } else { - if _, err = io.WriteString(w, "NULL"); err != nil { + if _, err = io.WriteString(w, "'"+strings.ReplaceAll(s.String, "'", "''")+"'"); err != nil { return err } } @@ -923,104 +1119,13 @@ func (engine *Engine) UnMapType(t reflect.Type) { func (engine *Engine) Sync(beans ...interface{}) error { session := engine.NewSession() defer session.Close() - - for _, bean := range beans { - v := utils.ReflectValue(bean) - tableNameNoSchema := dialects.FullTableName(engine.dialect, engine.GetTableMapper(), bean) - table, err := engine.tagParser.ParseWithCache(v) - if err != nil { - return err - } - - isExist, err := session.Table(bean).isTableExist(tableNameNoSchema) - if err != nil { - return err - } - if !isExist { - err = session.createTable(bean) - if err != nil { - return err - } - } - /*isEmpty, err := engine.IsEmptyTable(bean) - if err != nil { - return err - }*/ - var isEmpty bool - if isEmpty { - err = session.dropTable(bean) - if err != nil { - return err - } - err = session.createTable(bean) - if err != nil { - return err - } - } else { - for _, col := range table.Columns() { - isExist, err := engine.dialect.IsColumnExist(engine.db, session.ctx, tableNameNoSchema, col.Name) - if err != nil { - return err - } - if !isExist { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - err = session.addColumn(col.Name) - if err != nil { - return err - } - } - } - - for name, index := range table.Indexes { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - if index.Type == schemas.UniqueType { - isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, true) - if err != nil { - return err - } - if !isExist { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - - err = session.addUnique(tableNameNoSchema, name) - if err != nil { - return err - } - } - } else if index.Type == schemas.IndexType { - isExist, err := session.isIndexExist2(tableNameNoSchema, index.Cols, false) - if err != nil { - return err - } - if !isExist { - if err := session.statement.SetRefBean(bean); err != nil { - return err - } - - err = session.addIndex(tableNameNoSchema, name) - if err != nil { - return err - } - } - } else { - return errors.New("unknow index type") - } - } - } - } - return nil + return session.Sync(beans...) } // Sync2 synchronize structs to database tables +// Depricated func (engine *Engine) Sync2(beans ...interface{}) error { - s := engine.NewSession() - defer s.Close() - return s.Sync2(beans...) + return engine.Sync(beans...) } // CreateTables create tabls according bean @@ -1036,7 +1141,7 @@ func (engine *Engine) CreateTables(beans ...interface{}) error { for _, bean := range beans { err = session.createTable(bean) if err != nil { - session.Rollback() + _ = session.Rollback() return err } } @@ -1056,7 +1161,7 @@ func (engine *Engine) DropTables(beans ...interface{}) error { for _, bean := range beans { err = session.dropTable(bean) if err != nil { - session.Rollback() + _ = session.Rollback() return err } } @@ -1133,10 +1238,10 @@ func (engine *Engine) Delete(beans ...interface{}) (int64, error) { // Get retrieve one record from table, bean's non-empty fields // are conditions -func (engine *Engine) Get(bean interface{}) (bool, error) { +func (engine *Engine) Get(beans ...interface{}) (bool, error) { session := engine.NewSession() defer session.Close() - return session.Get(bean) + return session.Get(beans...) } // Exist returns true if the record exist otherwise return false diff --git a/go.mod b/go.mod index d645011c..0764d73a 100644 --- a/go.mod +++ b/go.mod @@ -3,17 +3,19 @@ module xorm.io/xorm go 1.13 require ( + gitee.com/travelliu/dm v1.8.11192 github.com/denisenkom/go-mssqldb v0.10.0 github.com/go-sql-driver/mysql v1.6.0 - github.com/goccy/go-json v0.7.4 + github.com/goccy/go-json v0.8.1 + github.com/golang/snappy v0.0.4 // indirect github.com/jackc/pgx/v4 v4.12.0 - github.com/json-iterator/go v1.1.11 + github.com/json-iterator/go v1.1.12 github.com/lib/pq v1.10.2 - github.com/mattn/go-sqlite3 v1.14.8 + github.com/mattn/go-sqlite3 v1.14.9 github.com/shopspring/decimal v1.2.0 github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.0 github.com/ziutek/mymysql v1.5.4 - modernc.org/sqlite v1.11.2 + modernc.org/sqlite v1.14.2 xorm.io/builder v0.3.9 ) diff --git a/go.sum b/go.sum index e8024945..8e7ac44b 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +gitee.com/travelliu/dm v1.8.11192 h1:aqJT0xhodZjRutIfEXxKYv0CxqmHUHzsbz6SFaRL6OY= +gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= @@ -76,8 +78,8 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k= -github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= +github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -95,8 +97,10 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -107,6 +111,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -203,8 +209,8 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -242,9 +248,8 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= -github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -258,8 +263,9 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -457,9 +463,10 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -539,34 +546,117 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.33.6 h1:r63dgSzVzRxUpAJFPQWHy1QeZeY1ydNENUDaBx1GqYc= modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.5 h1:dEuUSf8WN51rDkprFuAqjfchKEzN0WttP/Py3enBwjk= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8= +modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.11 h1:QUxZMs48Ahg2F7SN41aERvMfGLY2HU/ADnB9DC4Yts8= modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.0 h1:GCjoRaBew8ECCKINQA2nYjzvufFW9YiEuuB+rQ9bn2E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.11.2 h1:ShWQpeD3ag/bmx6TqidBlIWonWmQaSQKls3aenCbt+w= -modernc.org/sqlite v1.11.2/go.mod h1:+mhs/P1ONd+6G7hcAs6irwDi/bjTQ7nLW6LHRBsEa3A= +modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE= +modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8= modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.5.5 h1:N03RwthgTR/l/eQvz3UjfYnvVVj1G2sZqzFGfoD4HE4= -modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI= +modernc.org/tcl v1.8.13 h1:V0sTNBw0Re86PvXZxuCub3oO9WrSTqALgrwNZNvLFGw= +modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc= -modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= +modernc.org/z v1.2.19 h1:BGyRFWhDVn5LFS5OcX4Yd/MlpRTOc7hOPTdcIpCiUao= +modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc= diff --git a/integrations/cache_test.go b/integrations/cache_test.go index 44e817b1..2caeaa34 100644 --- a/integrations/cache_test.go +++ b/integrations/cache_test.go @@ -26,7 +26,7 @@ func TestCacheFind(t *testing.T) { cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) - assert.NoError(t, testEngine.Sync2(new(MailBox))) + assert.NoError(t, testEngine.Sync(new(MailBox))) var inserts = []*MailBox{ { @@ -62,7 +62,8 @@ func TestCacheFind(t *testing.T) { } boxes = make([]MailBox, 0, 2) - assert.NoError(t, testEngine.Alias("a").Where("a.id > -1").Asc("a.id").Find(&boxes)) + assert.NoError(t, testEngine.Alias("a").Where("`a`.`id`> -1"). + Asc("`a`.`id`").Find(&boxes)) assert.EqualValues(t, 2, len(boxes)) for i, box := range boxes { assert.Equal(t, inserts[i].Id, box.Id) @@ -77,7 +78,8 @@ func TestCacheFind(t *testing.T) { } boxes2 := make([]MailBox4, 0, 2) - assert.NoError(t, testEngine.Table("mail_box").Where("mail_box.id > -1").Asc("mail_box.id").Find(&boxes2)) + assert.NoError(t, testEngine.Table("mail_box").Where("`mail_box`.`id` > -1"). + Asc("mail_box.id").Find(&boxes2)) assert.EqualValues(t, 2, len(boxes2)) for i, box := range boxes2 { assert.Equal(t, inserts[i].Id, box.Id) @@ -101,7 +103,7 @@ func TestCacheFind2(t *testing.T) { cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) - assert.NoError(t, testEngine.Sync2(new(MailBox2))) + assert.NoError(t, testEngine.Sync(new(MailBox2))) var inserts = []*MailBox2{ { @@ -152,7 +154,7 @@ func TestCacheGet(t *testing.T) { cacher := caches.NewLRUCacher2(caches.NewMemoryStore(), time.Hour, 10000) testEngine.SetDefaultCacher(cacher) - assert.NoError(t, testEngine.Sync2(new(MailBox3))) + assert.NoError(t, testEngine.Sync(new(MailBox3))) var inserts = []*MailBox3{ { @@ -164,14 +166,14 @@ func TestCacheGet(t *testing.T) { assert.NoError(t, err) var box1 MailBox3 - has, err := testEngine.Where("id = ?", inserts[0].Id).Get(&box1) + has, err := testEngine.Where("`id` = ?", inserts[0].Id).Get(&box1) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "user1", box1.Username) assert.EqualValues(t, "pass1", box1.Password) var box2 MailBox3 - has, err = testEngine.Where("id = ?", inserts[0].Id).Get(&box2) + has, err = testEngine.Where("`id` = ?", inserts[0].Id).Get(&box2) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "user1", box2.Username) diff --git a/integrations/engine_dm_test.go b/integrations/engine_dm_test.go new file mode 100644 index 00000000..6c2f6103 --- /dev/null +++ b/integrations/engine_dm_test.go @@ -0,0 +1,13 @@ +// Copyright 2021 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build dm + +package integrations + +import "xorm.io/xorm/schemas" + +func init() { + dbtypes = append(dbtypes, schemas.DAMENG) +} diff --git a/integrations/engine_test.go b/integrations/engine_test.go index 02b35a2c..cdcdd6be 100644 --- a/integrations/engine_test.go +++ b/integrations/engine_test.go @@ -14,6 +14,7 @@ import ( "xorm.io/xorm" "xorm.io/xorm/schemas" + _ "gitee.com/travelliu/dm" _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" _ "github.com/jackc/pgx/v4/stdlib" @@ -52,17 +53,18 @@ func TestAutoTransaction(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestTx))) + assert.NoError(t, testEngine.Sync(new(TestTx))) engine := testEngine.(*xorm.Engine) // will success - engine.Transaction(func(session *xorm.Session) (interface{}, error) { + _, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) { _, err := session.Insert(TestTx{Msg: "hi"}) assert.NoError(t, err) return nil, nil }) + assert.NoError(t, err) has, err := engine.Exist(&TestTx{Msg: "hi"}) assert.NoError(t, err) @@ -86,7 +88,7 @@ func assertSync(t *testing.T, beans ...interface{}) { for _, bean := range beans { t.Run(testEngine.TableName(bean, true), func(t *testing.T) { assert.NoError(t, testEngine.DropTables(bean)) - assert.NoError(t, testEngine.Sync2(bean)) + assert.NoError(t, testEngine.Sync(bean)) }) } } @@ -134,11 +136,14 @@ func TestDump(t *testing.T) { } } +var dbtypes = []schemas.DBType{schemas.SQLITE, schemas.MYSQL, schemas.POSTGRES, schemas.MSSQL} + func TestDumpTables(t *testing.T) { assert.NoError(t, PrepareEngine()) type TestDumpTableStruct struct { Id int64 + Data []byte `xorm:"BLOB"` Name string IsMan bool Created time.Time `xorm:"created"` @@ -146,13 +151,18 @@ func TestDumpTables(t *testing.T) { assertSync(t, new(TestDumpTableStruct)) - testEngine.Insert([]TestDumpTableStruct{ + _, err := testEngine.Insert([]TestDumpTableStruct{ {Name: "1", IsMan: true}, - {Name: "2\n"}, - {Name: "3;"}, - {Name: "4\n;\n''"}, - {Name: "5'\n"}, + {Name: "2\n", Data: []byte{'\000', '\001', '\002'}}, + {Name: "3;", Data: []byte("0x000102")}, + {Name: "4\n;\n''", Data: []byte("Help")}, + {Name: "5'\n", Data: []byte("0x48656c70")}, + {Name: "6\\n'\n", Data: []byte("48656c70")}, + {Name: "7\\n'\r\n", Data: []byte("7\\n'\r\n")}, + {Name: "x0809ee"}, + {Name: "090a10"}, }) + assert.NoError(t, err) fp := fmt.Sprintf("%v-table.sql", testEngine.Dialect().URI().DBType) os.Remove(fp) @@ -169,7 +179,7 @@ func TestDumpTables(t *testing.T) { assert.NoError(t, err) assert.NoError(t, sess.Commit()) - for _, tp := range []schemas.DBType{schemas.SQLITE, schemas.MYSQL, schemas.POSTGRES, schemas.MSSQL} { + for _, tp := range dbtypes { name := fmt.Sprintf("dump_%v-table.sql", tp) t.Run(name, func(t *testing.T) { assert.NoError(t, testEngine.(*xorm.Engine).DumpTablesToFile([]*schemas.Table{tb}, name, tp)) diff --git a/integrations/performance_test.go b/integrations/performance_test.go index 4b54b40c..49183717 100644 --- a/integrations/performance_test.go +++ b/integrations/performance_test.go @@ -21,7 +21,7 @@ func BenchmarkGetVars(b *testing.B) { Name string } - assert.NoError(b, testEngine.Sync2(new(BenchmarkGetVars))) + assert.NoError(b, testEngine.Sync(new(BenchmarkGetVars))) var v = BenchmarkGetVars{ Name: "myname", @@ -32,7 +32,7 @@ func BenchmarkGetVars(b *testing.B) { b.StartTimer() var myname string for i := 0; i < b.N; i++ { - has, err := testEngine.Cols("name").Table("benchmark_get_vars").Where("id=?", v.Id).Get(&myname) + has, err := testEngine.Cols("name").Table("benchmark_get_vars").Where("`id`=?", v.Id).Get(&myname) b.StopTimer() myname = "" assert.True(b, has) @@ -52,7 +52,7 @@ func BenchmarkGetStruct(b *testing.B) { Name string } - assert.NoError(b, testEngine.Sync2(new(BenchmarkGetStruct))) + assert.NoError(b, testEngine.Sync(new(BenchmarkGetStruct))) var v = BenchmarkGetStruct{ Name: "myname", @@ -84,7 +84,7 @@ func BenchmarkFindStruct(b *testing.B) { Name string } - assert.NoError(b, testEngine.Sync2(new(BenchmarkFindStruct))) + assert.NoError(b, testEngine.Sync(new(BenchmarkFindStruct))) var v = BenchmarkFindStruct{ Name: "myname", @@ -92,8 +92,8 @@ func BenchmarkFindStruct(b *testing.B) { _, err := testEngine.Insert(&v) assert.NoError(b, err) - b.StartTimer() var mynames = make([]BenchmarkFindStruct, 0, 1) + b.StartTimer() for i := 0; i < b.N; i++ { err := testEngine.Find(&mynames) b.StopTimer() diff --git a/integrations/processors_test.go b/integrations/processors_test.go index e349988d..4c383437 100644 --- a/integrations/processors_test.go +++ b/integrations/processors_test.go @@ -23,7 +23,7 @@ func TestBefore_Get(t *testing.T) { Val string `xorm:"-"` } - assert.NoError(t, testEngine.Sync2(new(BeforeTable))) + assert.NoError(t, testEngine.Sync(new(BeforeTable))) cnt, err := testEngine.Insert(&BeforeTable{ Name: "test", @@ -50,7 +50,7 @@ func TestBefore_Find(t *testing.T) { Val string `xorm:"-"` } - assert.NoError(t, testEngine.Sync2(new(BeforeTable2))) + assert.NoError(t, testEngine.Sync(new(BeforeTable2))) cnt, err := testEngine.Insert([]BeforeTable2{ {Name: "test1"}, @@ -104,7 +104,7 @@ func (p *ProcessorsStruct) BeforeDelete() { } func (p *ProcessorsStruct) BeforeSet(col string, cell xorm.Cell) { - p.BeforeSetFlag = p.BeforeSetFlag + 1 + p.BeforeSetFlag++ } func (p *ProcessorsStruct) AfterInsert() { @@ -120,7 +120,7 @@ func (p *ProcessorsStruct) AfterDelete() { } func (p *ProcessorsStruct) AfterSet(col string, cell xorm.Cell) { - p.AfterSetFlag = p.AfterSetFlag + 1 + p.AfterSetFlag++ } func TestProcessors(t *testing.T) { diff --git a/integrations/rows_test.go b/integrations/rows_test.go index f68030a4..10f11453 100644 --- a/integrations/rows_test.go +++ b/integrations/rows_test.go @@ -18,7 +18,7 @@ func TestRows(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserRows))) + assert.NoError(t, testEngine.Sync(new(UserRows))) cnt, err := testEngine.Insert(&UserRows{ IsMan: true, @@ -94,7 +94,7 @@ func TestRowsMyTableName(t *testing.T) { var tableName = "user_rows_my_table_name" - assert.NoError(t, testEngine.Table(tableName).Sync2(new(UserRowsMyTable))) + assert.NoError(t, testEngine.Table(tableName).Sync(new(UserRowsMyTable))) cnt, err := testEngine.Table(tableName).Insert(&UserRowsMyTable{ IsMan: true, @@ -141,7 +141,7 @@ func (UserRowsSpecTable) TableName() string { func TestRowsSpecTableName(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(UserRowsSpecTable))) + assert.NoError(t, testEngine.Sync(new(UserRowsSpecTable))) cnt, err := testEngine.Insert(&UserRowsSpecTable{ IsMan: true, @@ -160,5 +160,49 @@ func TestRowsSpecTableName(t *testing.T) { assert.NoError(t, err) cnt++ } + assert.NoError(t, rows.Err()) assert.EqualValues(t, 1, cnt) } + +func TestRowsScanVars(t *testing.T) { + type RowsScanVars struct { + Id int64 + Name string + Age int + } + + assert.NoError(t, PrepareEngine()) + assert.NoError(t, testEngine.Sync2(new(RowsScanVars))) + + cnt, err := testEngine.Insert(&RowsScanVars{ + Name: "xlw", + Age: 42, + }, &RowsScanVars{ + Name: "xlw2", + Age: 24, + }) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + rows, err := testEngine.Cols("name", "age").Rows(new(RowsScanVars)) + assert.NoError(t, err) + defer rows.Close() + + cnt = 0 + for rows.Next() { + var name string + var age int + err = rows.Scan(&name, &age) + assert.NoError(t, err) + if cnt == 0 { + assert.EqualValues(t, "xlw", name) + assert.EqualValues(t, 42, age) + } else if cnt == 1 { + assert.EqualValues(t, "xlw2", name) + assert.EqualValues(t, 24, age) + } + cnt++ + } + assert.NoError(t, rows.Err()) + assert.EqualValues(t, 2, cnt) +} diff --git a/integrations/session_cols_test.go b/integrations/session_cols_test.go index b74c6f8a..462ea7c7 100644 --- a/integrations/session_cols_test.go +++ b/integrations/session_cols_test.go @@ -20,7 +20,7 @@ func TestSetExpr(t *testing.T) { Title string } - assert.NoError(t, testEngine.Sync2(new(UserExprIssue))) + assert.NoError(t, testEngine.Sync(new(UserExprIssue))) var issue = UserExprIssue{ Title: "my issue", @@ -36,7 +36,7 @@ func TestSetExpr(t *testing.T) { Show bool } - assert.NoError(t, testEngine.Sync2(new(UserExpr))) + assert.NoError(t, testEngine.Sync(new(UserExpr))) cnt, err = testEngine.Insert(&UserExpr{ Show: true, @@ -45,7 +45,7 @@ func TestSetExpr(t *testing.T) { assert.EqualValues(t, 1, cnt) var not = "NOT" - if testEngine.Dialect().URI().DBType == schemas.MSSQL { + if testEngine.Dialect().URI().DBType == schemas.MSSQL || testEngine.Dialect().URI().DBType == schemas.DAMENG { not = "~" } cnt, err = testEngine.SetExpr("show", not+" `show`").ID(1).Update(new(UserExpr)) @@ -54,9 +54,9 @@ func TestSetExpr(t *testing.T) { tableName := testEngine.TableName(new(UserExprIssue), true) cnt, err = testEngine.SetExpr("issue_id", - builder.Select("id"). - From(tableName). - Where(builder.Eq{"id": issue.Id})). + builder.Select("`id`"). + From(testEngine.Quote(tableName)). + Where(builder.Eq{"`id`": issue.Id})). ID(1). Update(new(UserExpr)) assert.NoError(t, err) diff --git a/integrations/session_cond_test.go b/integrations/session_cond_test.go index a0a91cad..0597d74e 100644 --- a/integrations/session_cond_test.go +++ b/integrations/session_cond_test.go @@ -37,49 +37,50 @@ func TestBuilder(t *testing.T) { assert.NoError(t, err) var cond Condition - has, err := testEngine.Where(builder.Eq{"col_name": "col1"}).Get(&cond) + var q = testEngine.Quote + has, err := testEngine.Where(builder.Eq{q("col_name"): "col1"}).Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Eq{"op": OpEqual})). + has, err = testEngine.Where(builder.Eq{q("col_name"): "col1"}. + And(builder.Eq{q("op"): OpEqual})). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1", "op": OpEqual, "value": "1"}). + has, err = testEngine.Where(builder.Eq{q("col_name"): "col1", q("op"): OpEqual, q("value"): "1"}). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, true, has, "records should exist") - has, err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Neq{"op": OpEqual})). + has, err = testEngine.Where(builder.Eq{q("col_name"): "col1"}. + And(builder.Neq{q("op"): OpEqual})). NoAutoCondition(). Get(&cond) assert.NoError(t, err) assert.Equal(t, false, has, "records should not exist") var conds []Condition - err = testEngine.Where(builder.Eq{"col_name": "col1"}. - And(builder.Eq{"op": OpEqual})). + err = testEngine.Where(builder.Eq{q("col_name"): "col1"}. + And(builder.Eq{q("op"): OpEqual})). Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.Like{"col_name", "col"}).Find(&conds) + err = testEngine.Where(builder.Like{q("col_name"), "col"}).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.Expr("col_name = ?", "col1")).Find(&conds) + err = testEngine.Where(builder.Expr(q("col_name")+" = ?", "col1")).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") conds = make([]Condition, 0) - err = testEngine.Where(builder.In("col_name", "col1", "col2")).Find(&conds) + err = testEngine.Where(builder.In(q("col_name"), "col1", "col2")).Find(&conds) assert.NoError(t, err) assert.EqualValues(t, 1, len(conds), "records should exist") @@ -91,8 +92,8 @@ func TestBuilder(t *testing.T) { // complex condtions var where = builder.NewCond() if true { - where = where.And(builder.Eq{"col_name": "col1"}) - where = where.Or(builder.And(builder.In("col_name", "col1", "col2"), builder.Expr("col_name = ?", "col1"))) + where = where.And(builder.Eq{q("col_name"): "col1"}) + where = where.Or(builder.And(builder.In(q("col_name"), "col1", "col2"), builder.Expr(q("col_name")+" = ?", "col1"))) } conds = make([]Condition, 0) @@ -103,7 +104,7 @@ func TestBuilder(t *testing.T) { func TestIn(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(Userinfo))) + assert.NoError(t, testEngine.Sync(new(Userinfo))) cnt, err := testEngine.Insert([]Userinfo{ { @@ -202,7 +203,7 @@ func TestFindAndCount(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(FindAndCount))) + assert.NoError(t, testEngine.Sync(new(FindAndCount))) _, err := testEngine.Insert([]FindAndCount{ { @@ -215,7 +216,7 @@ func TestFindAndCount(t *testing.T) { assert.NoError(t, err) var results []FindAndCount - sess := testEngine.Where("name = ?", "test1") + sess := testEngine.Where("`name` = ?", "test1") conds := sess.Conds() err = sess.Find(&results) assert.NoError(t, err) diff --git a/integrations/session_count_test.go b/integrations/session_count_test.go index 1517dede..13d84edb 100644 --- a/integrations/session_count_test.go +++ b/integrations/session_count_test.go @@ -17,7 +17,7 @@ func TestCount(t *testing.T) { type UserinfoCount struct { Departname string } - assert.NoError(t, testEngine.Sync2(new(UserinfoCount))) + assert.NoError(t, testEngine.Sync(new(UserinfoCount))) colName := testEngine.GetColumnMapper().Obj2Table("Departname") var cond builder.Cond = builder.Eq{ @@ -63,7 +63,7 @@ func TestSQLCount(t *testing.T) { assertSync(t, new(UserinfoCount2), new(UserinfoBooks)) - total, err := testEngine.SQL("SELECT count(id) FROM " + testEngine.TableName("userinfo_count2", true)). + total, err := testEngine.SQL("SELECT count(`id`) FROM " + testEngine.Quote(testEngine.TableName("userinfo_count2", true))). Count() assert.NoError(t, err) assert.EqualValues(t, 0, total) @@ -89,7 +89,7 @@ func TestCountWithOthers(t *testing.T) { }) assert.NoError(t, err) - total, err := testEngine.OrderBy("id desc").Limit(1).Count(new(CountWithOthers)) + total, err := testEngine.OrderBy("`id` desc").Limit(1).Count(new(CountWithOthers)) assert.NoError(t, err) assert.EqualValues(t, 2, total) } @@ -118,11 +118,11 @@ func TestWithTableName(t *testing.T) { }) assert.NoError(t, err) - total, err := testEngine.OrderBy("id desc").Count(new(CountWithTableName)) + total, err := testEngine.OrderBy("`id` desc").Count(new(CountWithTableName)) assert.NoError(t, err) assert.EqualValues(t, 2, total) - total, err = testEngine.OrderBy("id desc").Count(CountWithTableName{}) + total, err = testEngine.OrderBy("`id` desc").Count(CountWithTableName{}) assert.NoError(t, err) assert.EqualValues(t, 2, total) } @@ -146,7 +146,7 @@ func TestCountWithSelectCols(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, total) - total, err = testEngine.Select("count(id)").Count(CountWithTableName{}) + total, err = testEngine.Select("count(`id`)").Count(CountWithTableName{}) assert.NoError(t, err) assert.EqualValues(t, 2, total) } @@ -166,7 +166,7 @@ func TestCountWithGroupBy(t *testing.T) { }) assert.NoError(t, err) - cnt, err := testEngine.GroupBy("name").Count(new(CountWithTableName)) + cnt, err := testEngine.GroupBy("`name`").Count(new(CountWithTableName)) assert.NoError(t, err) assert.EqualValues(t, 2, cnt) } diff --git a/integrations/session_delete_test.go b/integrations/session_delete_test.go index 56f6f5b8..b4e40edb 100644 --- a/integrations/session_delete_test.go +++ b/integrations/session_delete_test.go @@ -22,7 +22,7 @@ func TestDelete(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserinfoDelete))) + assert.NoError(t, testEngine.Sync(new(UserinfoDelete))) session := testEngine.NewSession() defer session.Close() @@ -213,7 +213,7 @@ func TestUnscopeDelete(t *testing.T) { cnt, err = testEngine.ID(1).Delete(&s) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - assert.EqualValues(t, nowUnix, s.DeletedAt.Unix()) + assert.LessOrEqual(t, int(s.DeletedAt.Unix()-nowUnix), 1) var s1 UnscopeDeleteStruct has, err := testEngine.ID(1).Get(&s1) @@ -225,7 +225,7 @@ func TestUnscopeDelete(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "test", s2.Name) - assert.EqualValues(t, nowUnix, s2.DeletedAt.Unix()) + assert.LessOrEqual(t, int(s2.DeletedAt.Unix()-nowUnix), 1) cnt, err = testEngine.ID(1).Unscoped().Delete(new(UnscopeDeleteStruct)) assert.NoError(t, err) @@ -250,7 +250,7 @@ func TestDelete2(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserinfoDelete2))) + assert.NoError(t, testEngine.Sync(new(UserinfoDelete2))) user := UserinfoDelete2{} cnt, err := testEngine.Insert(&user) diff --git a/integrations/session_exist_test.go b/integrations/session_exist_test.go index 29546376..ca1e66ad 100644 --- a/integrations/session_exist_test.go +++ b/integrations/session_exist_test.go @@ -48,19 +48,19 @@ func TestExistStruct(t *testing.T) { assert.NoError(t, err) assert.False(t, has) - has, err = testEngine.Where("name = ?", "test1").Exist(&RecordExist{}) + has, err = testEngine.Where("`name` = ?", "test1").Exist(&RecordExist{}) assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.Where("name = ?", "test2").Exist(&RecordExist{}) + has, err = testEngine.Where("`name` = ?", "test2").Exist(&RecordExist{}) assert.NoError(t, err) assert.False(t, has) - has, err = testEngine.SQL("select * from "+testEngine.TableName("record_exist", true)+" where name = ?", "test1").Exist() + has, err = testEngine.SQL("select * from "+testEngine.Quote(testEngine.TableName("record_exist", true))+" where `name` = ?", "test1").Exist() assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.SQL("select * from "+testEngine.TableName("record_exist", true)+" where name = ?", "test2").Exist() + has, err = testEngine.SQL("select * from "+testEngine.Quote(testEngine.TableName("record_exist", true))+" where `name` = ?", "test2").Exist() assert.NoError(t, err) assert.False(t, has) @@ -68,11 +68,11 @@ func TestExistStruct(t *testing.T) { assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.Table("record_exist").Where("name = ?", "test1").Exist() + has, err = testEngine.Table("record_exist").Where("`name` = ?", "test1").Exist() assert.NoError(t, err) assert.True(t, has) - has, err = testEngine.Table("record_exist").Where("name = ?", "test2").Exist() + has, err = testEngine.Table("record_exist").Where("`name` = ?", "test2").Exist() assert.NoError(t, err) assert.False(t, has) @@ -99,7 +99,7 @@ func TestExistStructForJoin(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(Number), new(OrderList), new(Player))) + assert.NoError(t, testEngine.Sync(new(Number), new(OrderList), new(Player))) var ply Player cnt, err := testEngine.Insert(&ply) @@ -124,43 +124,43 @@ func TestExistStructForJoin(t *testing.T) { defer session.Close() session.Table("number"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("number.lid = ?", 1) + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`number`.`lid` = ?", 1) has, err := session.Exist() assert.NoError(t, err) assert.True(t, has) session.Table("number"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("number.lid = ?", 2) + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`number`.`lid` = ?", 2) has, err = session.Exist() assert.NoError(t, err) assert.False(t, has) session.Table("number"). - Select("order_list.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("order_list.id = ?", 1) + Select("`order_list`.`id`"). + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`order_list`.`id` = ?", 1) has, err = session.Exist() assert.NoError(t, err) assert.True(t, has) session.Table("number"). Select("player.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid"). - Where("player.id = ?", 2) + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`"). + Where("`player`.`id` = ?", 2) has, err = session.Exist() assert.NoError(t, err) assert.False(t, has) session.Table("number"). Select("player.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid") + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`") has, err = session.Exist() assert.NoError(t, err) assert.True(t, has) @@ -174,15 +174,15 @@ func TestExistStructForJoin(t *testing.T) { session.Table("number"). Select("player.id"). - Join("INNER", "order_list", "order_list.id = number.lid"). - Join("LEFT", "player", "player.id = order_list.eid") + Join("INNER", "order_list", "`order_list`.`id` = `number`.`lid`"). + Join("LEFT", "player", "`player`.`id` = `order_list`.`eid`") has, err = session.Exist() assert.Error(t, err) assert.False(t, has) session.Table("number"). Select("player.id"). - Join("LEFT", "player", "player.id = number.lid") + Join("LEFT", "player", "`player`.`id` = `number`.`lid`") has, err = session.Exist() assert.NoError(t, err) assert.True(t, has) diff --git a/integrations/session_find_test.go b/integrations/session_find_test.go index 1cbf5e42..0e0c7bb4 100644 --- a/integrations/session_find_test.go +++ b/integrations/session_find_test.go @@ -33,7 +33,7 @@ func TestJoinLimit(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(Salary), new(CheckList), new(Empsetting))) + assert.NoError(t, testEngine.Sync(new(Salary), new(CheckList), new(Empsetting))) var emp Empsetting cnt, err := testEngine.Insert(&emp) @@ -56,8 +56,8 @@ func TestJoinLimit(t *testing.T) { var salaries []Salary err = testEngine.Table("salary"). - Join("INNER", "check_list", "check_list.id = salary.lid"). - Join("LEFT", "empsetting", "empsetting.id = check_list.eid"). + Join("INNER", "check_list", "`check_list`.`id` = `salary`.`lid`"). + Join("LEFT", "empsetting", "`empsetting`.`id` = `check_list`.`eid`"). Limit(10, 0). Find(&salaries) assert.NoError(t, err) @@ -69,10 +69,10 @@ func TestWhere(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.Where("id > ?", 2).Find(&users) + err := testEngine.Where("`id` > ?", 2).Find(&users) assert.NoError(t, err) - err = testEngine.Where("id > ?", 2).And("id < ?", 10).Find(&users) + err = testEngine.Where("`id` > ?", 2).And("`id` < ?", 10).Find(&users) assert.NoError(t, err) } @@ -121,54 +121,54 @@ func (TeamUser) TableName() string { func TestFind3(t *testing.T) { var teamUser = new(TeamUser) assert.NoError(t, PrepareEngine()) - err := testEngine.Sync2(new(Team), teamUser) + err := testEngine.Sync(new(Team), teamUser) assert.NoError(t, err) var teams []Team - err = testEngine.Cols("`team`.id"). - Where("`team_user`.org_id=?", 1). - And("`team_user`.uid=?", 2). - Join("INNER", "`team_user`", "`team_user`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`team_user`.`org_id`=?", 1). + And("`team_user`.`uid`=?", 2). + Join("INNER", "`team_user`", "`team_user`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) err = testEngine.Cols("`team`.id"). - Where("`team_user`.org_id=?", 1). - And("`team_user`.uid=?", 2). - Join("INNER", teamUser, "`team_user`.team_id=`team`.id"). + Where("`team_user`.`org_id`=?", 1). + And("`team_user`.`uid`=?", 2). + Join("INNER", teamUser, "`team_user`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`team_user`.org_id=?", 1). - And("`team_user`.uid=?", 2). - Join("INNER", []interface{}{teamUser}, "`team_user`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`team_user`.`org_id`=?", 1). + And("`team_user`.`uid`=?", 2). + Join("INNER", []interface{}{teamUser}, "`team_user`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`tu`.org_id=?", 1). - And("`tu`.uid=?", 2). - Join("INNER", []string{"team_user", "tu"}, "`tu`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`tu`.`org_id`=?", 1). + And("`tu`.`uid`=?", 2). + Join("INNER", []string{"team_user", "tu"}, "`tu`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`tu`.org_id=?", 1). - And("`tu`.uid=?", 2). - Join("INNER", []interface{}{"team_user", "tu"}, "`tu`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`tu`.`org_id`=?", 1). + And("`tu`.`uid`=?", 2). + Join("INNER", []interface{}{"team_user", "tu"}, "`tu`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) teams = make([]Team, 0) - err = testEngine.Cols("`team`.id"). - Where("`tu`.org_id=?", 1). - And("`tu`.uid=?", 2). - Join("INNER", []interface{}{teamUser, "tu"}, "`tu`.team_id=`team`.id"). + err = testEngine.Cols("`team`.`id`"). + Where("`tu`.`org_id`=?", 1). + And("`tu`.`uid`=?", 2). + Join("INNER", []interface{}{teamUser, "tu"}, "`tu`.`team_id`=`team`.`id`"). Find(&teams) assert.NoError(t, err) } @@ -241,7 +241,7 @@ func TestOrder(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.OrderBy("id desc").Find(&users) + err := testEngine.OrderBy("`id` desc").Find(&users) assert.NoError(t, err) users2 := make([]Userinfo, 0) @@ -254,7 +254,7 @@ func TestGroupBy(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.GroupBy("id, username").Find(&users) + err := testEngine.GroupBy("`id`, `username`").Find(&users) assert.NoError(t, err) } @@ -263,7 +263,7 @@ func TestHaving(t *testing.T) { assertSync(t, new(Userinfo)) users := make([]Userinfo, 0) - err := testEngine.GroupBy("username").Having("username='xlw'").Find(&users) + err := testEngine.GroupBy("`username`").Having("`username`='xlw'").Find(&users) assert.NoError(t, err) } @@ -499,7 +499,7 @@ func TestFindAndCountOneFunc(t *testing.T) { assert.EqualValues(t, 2, cnt) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).FindAndCount(&results) + cnt, err = testEngine.Where("`msg` = ?", true).FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, 1, cnt) @@ -549,21 +549,21 @@ func TestFindAndCountOneFunc(t *testing.T) { }, results[0]) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).Select("id, content, msg"). + cnt, err = testEngine.Where("`msg` = ?", true).Select("`id`, `content`, `msg`"). Limit(1).FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, 1, cnt) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).Cols("id", "content", "msg"). + cnt, err = testEngine.Where("`msg` = ?", true).Cols("id", "content", "msg"). Limit(1).FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, 1, cnt) results = make([]FindAndCountStruct, 0, 1) - cnt, err = testEngine.Where("msg = ?", true).Desc("id"). + cnt, err = testEngine.Where("`msg` = ?", true).Desc("id"). Limit(1).Cols("content").FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) @@ -649,7 +649,7 @@ func TestFindAndCount2(t *testing.T) { cnt, err = testEngine. Table(new(TestFindAndCountHotel)). Alias("t"). - Where("t.region like '6501%'"). + Where("`t`.`region` like '6501%'"). Limit(10, 0). FindAndCount(&hotels) assert.NoError(t, err) @@ -690,7 +690,7 @@ func TestFindAndCountWithGroupBy(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(FindAndCountWithGroupBy))) + assert.NoError(t, testEngine.Sync(new(FindAndCountWithGroupBy))) _, err := testEngine.Insert([]FindAndCountWithGroupBy{ { @@ -705,7 +705,37 @@ func TestFindAndCountWithGroupBy(t *testing.T) { assert.NoError(t, err) var results []FindAndCountWithGroupBy - cnt, err := testEngine.GroupBy("age").FindAndCount(&results) + cnt, err := testEngine.GroupBy("`age`").FindAndCount(&results) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + assert.EqualValues(t, 2, len(results)) +} + +func TestFindAndCountWithDistinct(t *testing.T) { + assert.NoError(t, PrepareEngine()) + + type FindAndCountWithDistinct struct { + Id int64 + Age int `xorm:"index"` + Name string + } + + assert.NoError(t, testEngine.Sync(new(FindAndCountWithDistinct))) + + _, err := testEngine.Insert([]FindAndCountWithDistinct{ + { + Name: "test1", + Age: 10, + }, + { + Name: "test2", + Age: 20, + }, + }) + assert.NoError(t, err) + + var results []FindAndCountWithDistinct + cnt, err := testEngine.Distinct("`age`").FindAndCount(&results) assert.NoError(t, err) assert.EqualValues(t, 2, cnt) assert.EqualValues(t, 2, len(results)) @@ -735,14 +765,14 @@ func TestFindMapStringId(t *testing.T) { deviceMaps := make(map[string]*FindMapDevice, len(deviceIDs)) err = testEngine. - Where("status = ?", 1). + Where("`status` = ?", 1). In("deviceid", deviceIDs). Find(&deviceMaps) assert.NoError(t, err) deviceMaps2 := make(map[string]FindMapDevice, len(deviceIDs)) err = testEngine. - Where("status = ?", 1). + Where("`status` = ?", 1). In("deviceid", deviceIDs). Find(&deviceMaps2) assert.NoError(t, err) @@ -919,17 +949,17 @@ func TestFindJoin(t *testing.T) { assertSync(t, new(SceneItem), new(DeviceUserPrivrels), new(Order)) var scenes []SceneItem - err := testEngine.Join("LEFT OUTER", "device_user_privrels", "device_user_privrels.device_id=scene_item.device_id"). - Where("scene_item.type=?", 3).Or("device_user_privrels.user_id=?", 339).Find(&scenes) + err := testEngine.Join("LEFT OUTER", "device_user_privrels", "`device_user_privrels`.`device_id`=`scene_item`.`device_id`"). + Where("`scene_item`.`type`=?", 3).Or("`device_user_privrels`.`user_id`=?", 339).Find(&scenes) assert.NoError(t, err) scenes = make([]SceneItem, 0) - err = testEngine.Join("LEFT OUTER", new(DeviceUserPrivrels), "device_user_privrels.device_id=scene_item.device_id"). - Where("scene_item.type=?", 3).Or("device_user_privrels.user_id=?", 339).Find(&scenes) + err = testEngine.Join("LEFT OUTER", new(DeviceUserPrivrels), "`device_user_privrels`.`device_id`=`scene_item`.`device_id`"). + Where("`scene_item`.`type`=?", 3).Or("`device_user_privrels`.`user_id`=?", 339).Find(&scenes) assert.NoError(t, err) scenes = make([]SceneItem, 0) - err = testEngine.Join("INNER", "order", "`scene_item`.device_id=`order`.id").Find(&scenes) + err = testEngine.Join("INNER", "order", "`scene_item`.`device_id`=`order`.`id`").Find(&scenes) assert.NoError(t, err) } @@ -949,7 +979,7 @@ func TestJoinFindLimit(t *testing.T) { assertSync(t, new(JoinFindLimit1), new(JoinFindLimit2)) var finds []JoinFindLimit1 - err := testEngine.Join("INNER", new(JoinFindLimit2), "join_find_limit2.eid=join_find_limit1.id"). + err := testEngine.Join("INNER", new(JoinFindLimit2), "`join_find_limit2`.`eid`=`join_find_limit1`.`id`"). Limit(10, 10).Find(&finds) assert.NoError(t, err) } @@ -981,9 +1011,9 @@ func TestMoreExtends(t *testing.T) { assertSync(t, new(MoreExtendsUsers), new(MoreExtendsBooks)) var books []MoreExtendsBooksExtend - err := testEngine.Table("more_extends_books").Select("more_extends_books.*, more_extends_users.*"). - Join("INNER", "more_extends_users", "more_extends_books.user_id = more_extends_users.id"). - Where("more_extends_books.name LIKE ?", "abc"). + err := testEngine.Table("more_extends_books").Select("`more_extends_books`.*, `more_extends_users`.*"). + Join("INNER", "more_extends_users", "`more_extends_books`.`user_id` = `more_extends_users`.`id`"). + Where("`more_extends_books`.`name` LIKE ?", "abc"). Limit(10, 10). Find(&books) assert.NoError(t, err) @@ -991,9 +1021,9 @@ func TestMoreExtends(t *testing.T) { books = make([]MoreExtendsBooksExtend, 0, len(books)) err = testEngine.Table("more_extends_books"). Alias("m"). - Select("m.*, more_extends_users.*"). - Join("INNER", "more_extends_users", "m.user_id = more_extends_users.id"). - Where("m.name LIKE ?", "abc"). + Select("`m`.*, `more_extends_users`.*"). + Join("INNER", "more_extends_users", "`m`.`user_id` = `more_extends_users`.`id`"). + Where("`m`.`name` LIKE ?", "abc"). Limit(10, 10). Find(&books) assert.NoError(t, err) @@ -1038,11 +1068,11 @@ func TestUpdateFind(t *testing.T) { } _, err := session.Insert(&tuf) assert.NoError(t, err) - _, err = session.Where("id = ?", tuf.Id).Update(&TestUpdateFind{}) + _, err = session.Where("`id` = ?", tuf.Id).Update(&TestUpdateFind{}) assert.EqualError(t, xorm.ErrNoColumnsTobeUpdated, err.Error()) var tufs []TestUpdateFind - err = session.Where("id = ?", tuf.Id).Find(&tufs) + err = session.Where("`id` = ?", tuf.Id).Find(&tufs) assert.NoError(t, err) } diff --git a/integrations/session_get_test.go b/integrations/session_get_test.go index 99ecd462..5d1558f4 100644 --- a/integrations/session_get_test.go +++ b/integrations/session_get_test.go @@ -15,6 +15,7 @@ import ( "xorm.io/xorm" "xorm.io/xorm/contexts" "xorm.io/xorm/convert" + "xorm.io/xorm/dialects" "xorm.io/xorm/schemas" "github.com/shopspring/decimal" @@ -32,7 +33,7 @@ func TestGetVar(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(GetVar))) + assert.NoError(t, testEngine.Sync(new(GetVar))) var data = GetVar{ Msg: "hi", @@ -55,15 +56,15 @@ func TestGetVar(t *testing.T) { assert.Equal(t, 28, age) var ageMax int - has, err = testEngine.SQL("SELECT max(age) FROM "+testEngine.TableName("get_var", true)+" WHERE `id` = ?", data.Id).Get(&ageMax) + has, err = testEngine.SQL("SELECT max(`age`) FROM "+testEngine.Quote(testEngine.TableName("get_var", true))+" WHERE `id` = ?", data.Id).Get(&ageMax) assert.NoError(t, err) assert.Equal(t, true, has) assert.Equal(t, 28, ageMax) var age2 int64 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age2) assert.NoError(t, err) assert.Equal(t, true, has) @@ -77,8 +78,8 @@ func TestGetVar(t *testing.T) { var age4 int16 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age4) assert.NoError(t, err) assert.Equal(t, true, has) @@ -86,8 +87,8 @@ func TestGetVar(t *testing.T) { var age5 int32 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age5) assert.NoError(t, err) assert.Equal(t, true, has) @@ -101,8 +102,8 @@ func TestGetVar(t *testing.T) { var age7 int64 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age7) assert.NoError(t, err) assert.Equal(t, true, has) @@ -116,8 +117,8 @@ func TestGetVar(t *testing.T) { var age9 int16 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age9) assert.NoError(t, err) assert.Equal(t, true, has) @@ -125,8 +126,8 @@ func TestGetVar(t *testing.T) { var age10 int32 has, err = testEngine.Table("get_var").Cols("age"). - Where("age > ?", 20). - And("age < ?", 30). + Where("`age` > ?", 20). + And("`age` < ?", 30). Get(&age10) assert.NoError(t, err) assert.Equal(t, true, has) @@ -161,16 +162,16 @@ func TestGetVar(t *testing.T) { var money2 float64 if testEngine.Dialect().URI().DBType == schemas.MSSQL { - has, err = testEngine.SQL("SELECT TOP 1 money FROM " + testEngine.TableName("get_var", true)).Get(&money2) + has, err = testEngine.SQL("SELECT TOP 1 `money` FROM " + testEngine.Quote(testEngine.TableName("get_var", true))).Get(&money2) } else { - has, err = testEngine.SQL("SELECT money FROM " + testEngine.TableName("get_var", true) + " LIMIT 1").Get(&money2) + has, err = testEngine.SQL("SELECT `money` FROM " + testEngine.Quote(testEngine.TableName("get_var", true)) + " LIMIT 1").Get(&money2) } assert.NoError(t, err) assert.Equal(t, true, has) assert.Equal(t, "1.5", fmt.Sprintf("%.1f", money2)) var money3 float64 - has, err = testEngine.SQL("SELECT money FROM " + testEngine.TableName("get_var", true) + " WHERE money > 20").Get(&money3) + has, err = testEngine.SQL("SELECT `money` FROM " + testEngine.Quote(testEngine.TableName("get_var", true)) + " WHERE `money` > 20").Get(&money3) assert.NoError(t, err) assert.Equal(t, false, has) @@ -187,7 +188,7 @@ func TestGetVar(t *testing.T) { // for mymysql driver, interface{} will be []byte, so ignore it currently if testEngine.DriverName() != "mymysql" { var valuesInter = make(map[string]interface{}) - has, err = testEngine.Table("get_var").Where("id = ?", 1).Select("*").Get(&valuesInter) + has, err = testEngine.Table("get_var").Where("`id` = ?", 1).Select("*").Get(&valuesInter) assert.NoError(t, err) assert.Equal(t, true, has) assert.Equal(t, 5, len(valuesInter)) @@ -234,7 +235,7 @@ func TestGetStruct(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserinfoGet))) + assert.NoError(t, testEngine.Sync(new(UserinfoGet))) session := testEngine.NewSession() defer session.Close() @@ -243,7 +244,7 @@ func TestGetStruct(t *testing.T) { if testEngine.Dialect().URI().DBType == schemas.MSSQL { err = session.Begin() assert.NoError(t, err) - _, err = session.Exec("SET IDENTITY_INSERT userinfo_get ON") + _, err = session.Exec("SET IDENTITY_INSERT `userinfo_get` ON") assert.NoError(t, err) } cnt, err := session.Insert(&UserinfoGet{Uid: 2}) @@ -265,7 +266,7 @@ func TestGetStruct(t *testing.T) { Total int64 } - assert.NoError(t, testEngine.Sync2(&NoIdUser{})) + assert.NoError(t, testEngine.Sync(&NoIdUser{})) userCol := testEngine.GetColumnMapper().Obj2Table("User") _, err = testEngine.Where("`"+userCol+"` = ?", "xlw").Delete(&NoIdUser{}) @@ -300,6 +301,11 @@ func TestGetSlice(t *testing.T) { func TestGetMap(t *testing.T) { assert.NoError(t, PrepareEngine()) + if testEngine.Dialect().Features().AutoincrMode == dialects.SequenceAutoincrMode { + t.SkipNow() + return + } + type UserinfoMap struct { Uid int `xorm:"pk autoincr"` IsMan bool @@ -308,7 +314,7 @@ func TestGetMap(t *testing.T) { assertSync(t, new(UserinfoMap)) tableName := testEngine.Quote(testEngine.TableName("userinfo_map", true)) - _, err := testEngine.Exec(fmt.Sprintf("INSERT INTO %s (is_man) VALUES (NULL)", tableName)) + _, err := testEngine.Exec(fmt.Sprintf("INSERT INTO %s (`is_man`) VALUES (NULL)", tableName)) assert.NoError(t, err) var valuesString = make(map[string]string) @@ -479,7 +485,7 @@ func TestGetStructId(t *testing.T) { //var id int64 var maxid maxidst - sql := "select max(id) as id from " + testEngine.TableName(&TestGetStruct{}, true) + sql := "select max(`id`) as id from " + testEngine.Quote(testEngine.TableName(&TestGetStruct{}, true)) has, err := testEngine.SQL(sql).Get(&maxid) assert.NoError(t, err) assert.True(t, has) @@ -570,7 +576,7 @@ func (MyGetCustomTableImpletation) TableName() string { func TestGetCustomTableInterface(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Table(getCustomTableName).Sync2(new(MyGetCustomTableImpletation))) + assert.NoError(t, testEngine.Table(getCustomTableName).Sync(new(MyGetCustomTableImpletation))) exist, err := testEngine.IsTableExist(getCustomTableName) assert.NoError(t, err) @@ -597,73 +603,78 @@ func TestGetNullVar(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestGetNullVarStruct)) - affected, err := testEngine.Exec("insert into " + testEngine.TableName(new(TestGetNullVarStruct), true) + " (name,age) values (null,null)") + if testEngine.Dialect().Features().AutoincrMode == dialects.SequenceAutoincrMode { + t.SkipNow() + return + } + + affected, err := testEngine.Exec("insert into " + testEngine.Quote(testEngine.TableName(new(TestGetNullVarStruct), true)) + " (`name`,`age`) values (null,null)") assert.NoError(t, err) a, _ := affected.RowsAffected() assert.EqualValues(t, 1, a) var name string - has, err := testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("name").Get(&name) + has, err := testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("name").Get(&name) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, "", name) var age int - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age) var age2 int8 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age2) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age2) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age2) var age3 int16 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age3) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age3) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age3) var age4 int32 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age4) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age4) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age4) var age5 int64 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age5) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age5) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age5) var age6 uint - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age6) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age6) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age6) var age7 uint8 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age7) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age7) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age7) var age8 int16 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age8) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age8) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age8) var age9 int32 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age9) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age9) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age9) var age10 int64 - has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("id = ?", 1).Cols("age").Get(&age10) + has, err = testEngine.Table(new(TestGetNullVarStruct)).Where("`id` = ?", 1).Cols("age").Get(&age10) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 0, age10) @@ -697,7 +708,7 @@ func TestCustomTypes(t *testing.T) { assert.EqualValues(t, "test", name) var age MyInt - has, err = testEngine.Table(new(TestCustomizeStruct)).ID(s.Id).Select("age").Get(&age) + has, err = testEngine.Table(new(TestCustomizeStruct)).ID(s.Id).Select("`age`").Get(&age) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, 32, age) @@ -759,7 +770,7 @@ func TestGetBigFloat(t *testing.T) { assert.NoError(t, err) var m big.Float - has, err := testEngine.Table("get_big_float").Cols("money").Where("id=?", gf.Id).Get(&m) + has, err := testEngine.Table("get_big_float").Cols("money").Where("`id`=?", gf.Id).Get(&m) assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) @@ -785,7 +796,7 @@ func TestGetBigFloat(t *testing.T) { assert.NoError(t, err) var m2 big.Float - has, err := testEngine.Table("get_big_float2").Cols("money").Where("id=?", gf2.Id).Get(&m2) + has, err := testEngine.Table("get_big_float2").Cols("money").Where("`id`=?", gf2.Id).Get(&m2) assert.NoError(t, err) assert.True(t, has) assert.True(t, m2.String() == gf2.Money.String(), "%v != %v", m2.String(), gf2.Money.String()) @@ -825,7 +836,7 @@ func TestGetDecimal(t *testing.T) { assert.NoError(t, err) var m decimal.Decimal - has, err := testEngine.Table("get_decimal").Cols("money").Where("id=?", gf.Id).Get(&m) + has, err := testEngine.Table("get_decimal").Cols("money").Where("`id`=?", gf.Id).Get(&m) assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) @@ -850,7 +861,7 @@ func TestGetDecimal(t *testing.T) { assert.NoError(t, err) var m decimal.Decimal - has, err := testEngine.Table("get_decimal2").Cols("money").Where("id=?", gf.Id).Get(&m) + has, err := testEngine.Table("get_decimal2").Cols("money").Where("`id`=?", gf.Id).Get(&m) assert.NoError(t, err) assert.True(t, has) assert.True(t, m.String() == gf.Money.String(), "%v != %v", m.String(), gf.Money.String()) @@ -879,3 +890,89 @@ func TestGetTime(t *testing.T) { assert.True(t, has) assert.EqualValues(t, gts.CreateTime.Format(time.RFC3339), gn.Format(time.RFC3339)) } + +func TestGetVars(t *testing.T) { + type GetVars struct { + Id int64 + Name string + Age int + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(GetVars)) + + _, err := testEngine.Insert(&GetVars{ + Name: "xlw", + Age: 42, + }) + assert.NoError(t, err) + + var name string + var age int + has, err := testEngine.Table(new(GetVars)).Cols("name", "age").Get(&name, &age) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", name) + assert.EqualValues(t, 42, age) +} + +func TestGetWithPrepare(t *testing.T) { + type GetVarsWithPrepare struct { + Id int64 + Name string + Age int + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(GetVarsWithPrepare)) + + _, err := testEngine.Insert(&GetVarsWithPrepare{ + Name: "xlw", + Age: 42, + }) + assert.NoError(t, err) + + var v1 GetVarsWithPrepare + has, err := testEngine.Prepare().ID(1).Get(&v1) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", v1.Name) + assert.EqualValues(t, 42, v1.Age) + + sess := testEngine.NewSession() + defer sess.Close() + + var v2 GetVarsWithPrepare + has, err = sess.Prepare().ID(1).Get(&v2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", v2.Name) + assert.EqualValues(t, 42, v2.Age) + + var v3 GetVarsWithPrepare + has, err = sess.Prepare().ID(1).Get(&v3) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "xlw", v3.Name) + assert.EqualValues(t, 42, v3.Age) + + err = sess.Begin() + assert.NoError(t, err) + + cnt, err := sess.Prepare().Insert(&GetVarsWithPrepare{ + Name: "xlw2", + Age: 12, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + cnt, err = sess.Prepare().Insert(&GetVarsWithPrepare{ + Name: "xlw3", + Age: 13, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + err = sess.Commit() + assert.NoError(t, err) +} diff --git a/integrations/session_insert_test.go b/integrations/session_insert_test.go index cd56a958..2495c1df 100644 --- a/integrations/session_insert_test.go +++ b/integrations/session_insert_test.go @@ -11,6 +11,7 @@ import ( "time" "xorm.io/xorm" + "xorm.io/xorm/schemas" "github.com/stretchr/testify/assert" ) @@ -24,7 +25,7 @@ func TestInsertOne(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(Test))) + assert.NoError(t, testEngine.Sync(new(Test))) data := Test{Msg: "hi"} _, err := testEngine.InsertOne(data) @@ -38,7 +39,7 @@ func TestInsertMulti(t *testing.T) { Name string `xorm:"varchar(255)"` } - assert.NoError(t, testEngine.Sync2(new(TestMulti))) + assert.NoError(t, testEngine.Sync(new(TestMulti))) num, err := insertMultiDatas(1, append([]TestMulti{}, TestMulti{1, "test1"}, TestMulti{2, "test2"}, TestMulti{3, "test3"})) @@ -100,7 +101,7 @@ func callbackLooper(datas interface{}, step int, actionFunc func(interface{}) er if err = actionFunc(tempInterface); err != nil { return } - processedLen = processedLen - step + processedLen -= step } return } @@ -114,7 +115,7 @@ func TestInsertOneIfPkIsPoint(t *testing.T) { Created *time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestPoint))) + assert.NoError(t, testEngine.Sync(new(TestPoint))) msg := "hi" data := TestPoint{Msg: &msg} _, err := testEngine.InsertOne(&data) @@ -130,7 +131,7 @@ func TestInsertOneIfPkIsPointRename(t *testing.T) { Created *time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestPoint2))) + assert.NoError(t, testEngine.Sync(new(TestPoint2))) msg := "hi" data := TestPoint2{Msg: &msg} _, err := testEngine.InsertOne(&data) @@ -180,7 +181,7 @@ func TestInsertDefault(t *testing.T) { } di := new(DefaultInsert) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) var di2 = DefaultInsert{Name: "test"} @@ -191,8 +192,8 @@ func TestInsertDefault(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, -1, di.Status) - assert.EqualValues(t, di2.Updated.Unix(), di.Updated.Unix()) - assert.EqualValues(t, di2.Created.Unix(), di.Created.Unix()) + assert.EqualValues(t, di2.Updated.Unix(), di.Updated.Unix(), di.Updated) + assert.EqualValues(t, di2.Created.Unix(), di.Created.Unix(), di.Created) } func TestInsertDefault2(t *testing.T) { @@ -206,7 +207,7 @@ func TestInsertDefault2(t *testing.T) { } di := new(DefaultInsert2) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) var di2 = DefaultInsert2{Name: "test"} @@ -257,7 +258,7 @@ func TestInsertCreated(t *testing.T) { assert.NoError(t, PrepareEngine()) di := new(CreatedInsert) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) ci := &CreatedInsert{} @@ -270,7 +271,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci.Created.Unix(), di.Created.Unix()) di2 := new(CreatedInsert2) - err = testEngine.Sync2(di2) + err = testEngine.Sync(di2) assert.NoError(t, err) ci2 := &CreatedInsert2{} @@ -283,7 +284,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci2.Created, di2.Created) di3 := new(CreatedInsert3) - err = testEngine.Sync2(di3) + err = testEngine.Sync(di3) assert.NoError(t, err) ci3 := &CreatedInsert3{} @@ -296,7 +297,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci3.Created, di3.Created) di4 := new(CreatedInsert4) - err = testEngine.Sync2(di4) + err = testEngine.Sync(di4) assert.NoError(t, err) ci4 := &CreatedInsert4{} @@ -309,7 +310,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci4.Created, di4.Created) di5 := new(CreatedInsert5) - err = testEngine.Sync2(di5) + err = testEngine.Sync(di5) assert.NoError(t, err) ci5 := &CreatedInsert5{} @@ -322,7 +323,7 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci5.Created.Unix(), di5.Created.Unix()) di6 := new(CreatedInsert6) - err = testEngine.Sync2(di6) + err = testEngine.Sync(di6) assert.NoError(t, err) oldTime := time.Now().Add(-time.Hour) @@ -336,6 +337,42 @@ func TestInsertCreated(t *testing.T) { assert.EqualValues(t, ci6.Created.Unix(), di6.Created.Unix()) } +func TestInsertTime(t *testing.T) { + type InsertTimeStruct struct { + Id int64 + CreatedAt time.Time `xorm:"created"` + UpdatedAt time.Time `xorm:"updated"` + DeletedAt time.Time `xorm:"deleted"` + Stime time.Time + Etime time.Time + } + + assert.NoError(t, PrepareEngine()) + assertSync(t, new(InsertTimeStruct)) + + its := &InsertTimeStruct{ + Stime: time.Now(), + Etime: time.Now(), + } + cnt, err := testEngine.Insert(its) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var itsGet InsertTimeStruct + has, err := testEngine.ID(1).Get(&itsGet) + assert.NoError(t, err) + assert.True(t, has) + assert.False(t, itsGet.Stime.IsZero()) + assert.False(t, itsGet.Etime.IsZero()) + + var itsFind []*InsertTimeStruct + err = testEngine.Find(&itsFind) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(itsFind)) + assert.False(t, itsFind[0].Stime.IsZero()) + assert.False(t, itsFind[0].Etime.IsZero()) +} + type JSONTime time.Time func (j JSONTime) format() string { @@ -389,7 +426,7 @@ func TestCreatedJsonTime(t *testing.T) { assert.NoError(t, PrepareEngine()) di5 := new(MyJSONTime) - err := testEngine.Sync2(di5) + err := testEngine.Sync(di5) assert.NoError(t, err) ci5 := &MyJSONTime{} @@ -488,7 +525,7 @@ func TestInsertCreatedInt64(t *testing.T) { Created int64 `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(TestCreatedInt64))) + assert.NoError(t, testEngine.Sync(new(TestCreatedInt64))) data := TestCreatedInt64{Msg: "hi"} now := time.Now() @@ -624,6 +661,11 @@ func TestAnonymousStruct(t *testing.T) { } func TestInsertMap(t *testing.T) { + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + type InsertMap struct { Id int64 Width uint32 @@ -727,7 +769,7 @@ func TestInsertWhere(t *testing.T) { } inserted, err := testEngine.SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). - Where("repo_id=?", 1). + Where("`repo_id`=?", 1). Insert(&i) assert.NoError(t, err) assert.EqualValues(t, 1, inserted) @@ -740,7 +782,12 @@ func TestInsertWhere(t *testing.T) { i.Index = 1 assert.EqualValues(t, i, j) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). Insert(map[string]interface{}{ "repo_id": 1, @@ -761,7 +808,7 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, "trest2", j2.Name) assert.EqualValues(t, 2, j2.Index) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). SetExpr("repo_id", "1"). Insert(map[string]string{ @@ -777,7 +824,7 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, "trest3", j3.Name) assert.EqualValues(t, 3, j3.Index) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). Insert(map[string]interface{}{ "repo_id": 1, @@ -793,7 +840,7 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, "10';delete * from insert_where; --", j4.Name) assert.EqualValues(t, 4, j4.Index) - inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + inserted, err = testEngine.Table(new(InsertWhere)).Where("`repo_id`=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). Insert(map[string]interface{}{ "repo_id": 1, @@ -846,6 +893,11 @@ func TestInsertExpr2(t *testing.T) { assert.EqualValues(t, 1, ie2.RepoId) assert.EqualValues(t, true, ie2.IsTag) + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + inserted, err = testEngine.Table(new(InsertExprsRelease)). SetExpr("is_draft", true). SetExpr("num_commits", 0). @@ -880,7 +932,7 @@ func TestMultipleInsertTableName(t *testing.T) { assert.NoError(t, PrepareEngine()) tableName := `prd_nightly_rate_16` - assert.NoError(t, testEngine.Table(tableName).Sync2(new(NightlyRate))) + assert.NoError(t, testEngine.Table(tableName).Sync(new(NightlyRate))) trans := testEngine.NewSession() defer trans.Close() @@ -916,7 +968,7 @@ func TestInsertMultiWithOmit(t *testing.T) { Omitted string `xorm:"varchar(255) 'omitted'"` } - assert.NoError(t, testEngine.Sync2(new(TestMultiOmit))) + assert.NoError(t, testEngine.Sync(new(TestMultiOmit))) l := []interface{}{ TestMultiOmit{Id: 1, Name: "1", Omitted: "1"}, @@ -961,7 +1013,7 @@ func TestInsertTwice(t *testing.T) { FieldB int } - assert.NoError(t, testEngine.Sync2(new(InsertStructA), new(InsertStructB))) + assert.NoError(t, testEngine.Sync(new(InsertStructA), new(InsertStructB))) var sliceA []InsertStructA // sliceA is empty sliceB := []InsertStructB{ @@ -992,7 +1044,7 @@ func TestInsertIntSlice(t *testing.T) { NameIDs []int `xorm:"json notnull"` } - assert.NoError(t, testEngine.Sync2(new(InsertIntSlice))) + assert.NoError(t, testEngine.Sync(new(InsertIntSlice))) var v = InsertIntSlice{ NameIDs: []int{1, 2}, @@ -1033,7 +1085,7 @@ func TestInsertDeleted(t *testing.T) { DeletedAt time.Time `xorm:"'DELETED_AT' deleted notnull"` } // notnull tag will be ignored - err := testEngine.Sync2(new(InsertDeletedStructNotRight)) + err := testEngine.Sync(new(InsertDeletedStructNotRight)) assert.NoError(t, err) type InsertDeletedStruct struct { @@ -1041,7 +1093,7 @@ func TestInsertDeleted(t *testing.T) { DeletedAt time.Time `xorm:"'DELETED_AT' deleted"` } - assert.NoError(t, testEngine.Sync2(new(InsertDeletedStruct))) + assert.NoError(t, testEngine.Sync(new(InsertDeletedStruct))) var v InsertDeletedStruct _, err = testEngine.Insert(&v) @@ -1067,6 +1119,11 @@ func TestInsertDeleted(t *testing.T) { } func TestInsertMultipleMap(t *testing.T) { + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + type InsertMultipleMap struct { Id int64 Width uint32 diff --git a/integrations/session_iterate_test.go b/integrations/session_iterate_test.go index 564f457b..c5ecc593 100644 --- a/integrations/session_iterate_test.go +++ b/integrations/session_iterate_test.go @@ -18,7 +18,7 @@ func TestIterate(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserIterate))) + assert.NoError(t, testEngine.Sync(new(UserIterate))) cnt, err := testEngine.Insert(&UserIterate{ IsMan: true, @@ -26,16 +26,27 @@ func TestIterate(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) + cnt, err = testEngine.Insert(&UserIterate{ + IsMan: false, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + cnt = 0 err = testEngine.Iterate(new(UserIterate), func(i int, bean interface{}) error { user := bean.(*UserIterate) - assert.EqualValues(t, 1, user.Id) - assert.EqualValues(t, true, user.IsMan) + if cnt == 0 { + assert.EqualValues(t, 1, user.Id) + assert.EqualValues(t, true, user.IsMan) + } else { + assert.EqualValues(t, 2, user.Id) + assert.EqualValues(t, false, user.IsMan) + } cnt++ return nil }) assert.NoError(t, err) - assert.EqualValues(t, 1, cnt) + assert.EqualValues(t, 2, cnt) } func TestBufferIterate(t *testing.T) { @@ -46,7 +57,7 @@ func TestBufferIterate(t *testing.T) { IsMan bool } - assert.NoError(t, testEngine.Sync2(new(UserBufferIterate))) + assert.NoError(t, testEngine.Sync(new(UserBufferIterate))) var size = 20 for i := 0; i < size; i++ { @@ -91,7 +102,7 @@ func TestBufferIterate(t *testing.T) { assert.EqualValues(t, 7, cnt) cnt = 0 - err = testEngine.Where("id <= 10").BufferSize(2).Iterate(new(UserBufferIterate), func(i int, bean interface{}) error { + err = testEngine.Where("`id` <= 10").BufferSize(2).Iterate(new(UserBufferIterate), func(i int, bean interface{}) error { user := bean.(*UserBufferIterate) assert.EqualValues(t, cnt+1, user.Id) assert.EqualValues(t, true, user.IsMan) diff --git a/integrations/session_pk_test.go b/integrations/session_pk_test.go index 8f7dcb55..0244937f 100644 --- a/integrations/session_pk_test.go +++ b/integrations/session_pk_test.go @@ -121,7 +121,7 @@ func TestInt16Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[int16]Int16Id, 0) + beans2 := make(map[int16]Int16Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -154,7 +154,7 @@ func TestInt32Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[int32]Int32Id, 0) + beans2 := make(map[int32]Int32Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -205,7 +205,7 @@ func TestUintId(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 3, len(beans)) - beans2 := make(map[uint]UintId, 0) + beans2 := make(map[uint]UintId) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 3, len(beans2)) @@ -239,7 +239,7 @@ func TestUint16Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[uint16]Uint16Id, 0) + beans2 := make(map[uint16]Uint16Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -273,7 +273,7 @@ func TestUint32Id(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(beans)) - beans2 := make(map[uint32]Uint32Id, 0) + beans2 := make(map[uint32]Uint32Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -310,7 +310,7 @@ func TestUint64Id(t *testing.T) { assert.EqualValues(t, 1, len(beans)) assert.EqualValues(t, *bean, beans[0]) - beans2 := make(map[uint64]Uint64Id, 0) + beans2 := make(map[uint64]Uint64Id) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -533,7 +533,7 @@ func TestMyIntId(t *testing.T) { assert.EqualValues(t, 1, len(beans)) assert.EqualValues(t, *bean, beans[0]) - beans2 := make(map[ID]MyIntPK, 0) + beans2 := make(map[ID]MyIntPK) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -570,7 +570,7 @@ func TestMyStringId(t *testing.T) { assert.EqualValues(t, 1, len(beans)) assert.EqualValues(t, *bean, beans[0]) - beans2 := make(map[StrID]MyStringPK, 0) + beans2 := make(map[StrID]MyStringPK) err = testEngine.Find(&beans2) assert.NoError(t, err) assert.EqualValues(t, 1, len(beans2)) @@ -607,7 +607,7 @@ func TestCompositePK(t *testing.T) { assert.NoError(t, err) assertSync(t, new(TaskSolution)) - assert.NoError(t, testEngine.Sync2(new(TaskSolution))) + assert.NoError(t, testEngine.Sync(new(TaskSolution))) tables2, err := testEngine.DBMetas() assert.NoError(t, err) diff --git a/integrations/session_query_test.go b/integrations/session_query_test.go index ef2ccdd6..b72f7ef2 100644 --- a/integrations/session_query_test.go +++ b/integrations/session_query_test.go @@ -5,7 +5,6 @@ package integrations import ( - "fmt" "strconv" "testing" "time" @@ -27,7 +26,7 @@ func TestQueryString(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(GetVar2))) + assert.NoError(t, testEngine.Sync(new(GetVar2))) var data = GetVar2{ Msg: "hi", @@ -37,7 +36,7 @@ func TestQueryString(t *testing.T) { _, err := testEngine.InsertOne(data) assert.NoError(t, err) - records, err := testEngine.QueryString("select * from " + testEngine.TableName("get_var2", true)) + records, err := testEngine.QueryString("select * from " + testEngine.Quote(testEngine.TableName("get_var2", true))) assert.NoError(t, err) assert.Equal(t, 1, len(records)) assert.Equal(t, 5, len(records[0])) @@ -55,7 +54,7 @@ func TestQueryString2(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar3))) + assert.NoError(t, testEngine.Sync(new(GetVar3))) var data = GetVar3{ Msg: false, @@ -63,7 +62,7 @@ func TestQueryString2(t *testing.T) { _, err := testEngine.Insert(data) assert.NoError(t, err) - records, err := testEngine.QueryString("select * from " + testEngine.TableName("get_var3", true)) + records, err := testEngine.QueryString("select * from " + testEngine.Quote(testEngine.TableName("get_var3", true))) assert.NoError(t, err) assert.Equal(t, 1, len(records)) assert.Equal(t, 2, len(records[0])) @@ -71,42 +70,6 @@ func TestQueryString2(t *testing.T) { assert.True(t, "0" == records[0]["msg"] || "false" == records[0]["msg"]) } -func toString(i interface{}) string { - switch i.(type) { - case []byte: - return string(i.([]byte)) - case string: - return i.(string) - } - return fmt.Sprintf("%v", i) -} - -func toInt64(i interface{}) int64 { - switch i.(type) { - case []byte: - n, _ := strconv.ParseInt(string(i.([]byte)), 10, 64) - return n - case int: - return int64(i.(int)) - case int64: - return i.(int64) - } - return 0 -} - -func toFloat64(i interface{}) float64 { - switch i.(type) { - case []byte: - n, _ := strconv.ParseFloat(string(i.([]byte)), 64) - return n - case float64: - return i.(float64) - case float32: - return float64(i.(float32)) - } - return 0 -} - func toBool(i interface{}) bool { switch t := i.(type) { case int32: @@ -128,7 +91,7 @@ func TestQueryInterface(t *testing.T) { Created time.Time `xorm:"created"` } - assert.NoError(t, testEngine.Sync2(new(GetVarInterface))) + assert.NoError(t, testEngine.Sync(new(GetVarInterface))) var data = GetVarInterface{ Msg: "hi", @@ -138,7 +101,7 @@ func TestQueryInterface(t *testing.T) { _, err := testEngine.InsertOne(data) assert.NoError(t, err) - records, err := testEngine.QueryInterface("select * from " + testEngine.TableName("get_var_interface", true)) + records, err := testEngine.QueryInterface("select * from " + testEngine.Quote(testEngine.TableName("get_var_interface", true))) assert.NoError(t, err) assert.Equal(t, 1, len(records)) assert.Equal(t, 5, len(records[0])) @@ -161,7 +124,7 @@ func TestQueryNoParams(t *testing.T) { testEngine.ShowSQL(true) - assert.NoError(t, testEngine.Sync2(new(QueryNoParams))) + assert.NoError(t, testEngine.Sync(new(QueryNoParams))) var q = QueryNoParams{ Msg: "message", @@ -192,7 +155,7 @@ func TestQueryNoParams(t *testing.T) { assert.NoError(t, err) assertResult(t, results) - results, err = testEngine.SQL("select * from " + testEngine.TableName("query_no_params", true)).Query() + results, err = testEngine.SQL("select * from " + testEngine.Quote(testEngine.TableName("query_no_params", true))).Query() assert.NoError(t, err) assertResult(t, results) } @@ -205,7 +168,7 @@ func TestQueryStringNoParam(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar4))) + assert.NoError(t, testEngine.Sync(new(GetVar4))) var data = GetVar4{ Msg: false, @@ -223,7 +186,7 @@ func TestQueryStringNoParam(t *testing.T) { assert.EqualValues(t, "0", records[0]["msg"]) } - records, err = testEngine.Table("get_var4").Where(builder.Eq{"id": 1}).QueryString() + records, err = testEngine.Table("get_var4").Where(builder.Eq{"`id`": 1}).QueryString() assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0]["id"]) @@ -242,7 +205,7 @@ func TestQuerySliceStringNoParam(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar6))) + assert.NoError(t, testEngine.Sync(new(GetVar6))) var data = GetVar6{ Msg: false, @@ -260,7 +223,7 @@ func TestQuerySliceStringNoParam(t *testing.T) { assert.EqualValues(t, "0", records[0][1]) } - records, err = testEngine.Table("get_var6").Where(builder.Eq{"id": 1}).QuerySliceString() + records, err = testEngine.Table("get_var6").Where(builder.Eq{"`id`": 1}).QuerySliceString() assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0][0]) @@ -279,7 +242,7 @@ func TestQueryInterfaceNoParam(t *testing.T) { Msg bool } - assert.NoError(t, testEngine.Sync2(new(GetVar5))) + assert.NoError(t, testEngine.Sync(new(GetVar5))) var data = GetVar5{ Msg: false, @@ -293,7 +256,7 @@ func TestQueryInterfaceNoParam(t *testing.T) { assert.EqualValues(t, 1, records[0]["id"]) assert.False(t, toBool(records[0]["msg"])) - records, err = testEngine.Table("get_var5").Where(builder.Eq{"id": 1}).QueryInterface() + records, err = testEngine.Table("get_var5").Where(builder.Eq{"`id`": 1}).QueryInterface() assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, 1, records[0]["id"]) @@ -313,7 +276,7 @@ func TestQueryWithBuilder(t *testing.T) { testEngine.ShowSQL(true) - assert.NoError(t, testEngine.Sync2(new(QueryWithBuilder))) + assert.NoError(t, testEngine.Sync(new(QueryWithBuilder))) var q = QueryWithBuilder{ Msg: "message", @@ -340,7 +303,7 @@ func TestQueryWithBuilder(t *testing.T) { assert.EqualValues(t, 3000, money) } - results, err := testEngine.Query(builder.Select("*").From(testEngine.TableName("query_with_builder", true))) + results, err := testEngine.Query(builder.Select("*").From(testEngine.Quote(testEngine.TableName("query_with_builder", true)))) assert.NoError(t, err) assertResult(t, results) } @@ -362,7 +325,7 @@ func TestJoinWithSubQuery(t *testing.T) { testEngine.ShowSQL(true) - assert.NoError(t, testEngine.Sync2(new(JoinWithSubQuery1), new(JoinWithSubQueryDepart))) + assert.NoError(t, testEngine.Sync(new(JoinWithSubQuery1), new(JoinWithSubQueryDepart))) var depart = JoinWithSubQueryDepart{ Name: "depart1", @@ -383,14 +346,14 @@ func TestJoinWithSubQuery(t *testing.T) { tbName := testEngine.Quote(testEngine.TableName("join_with_sub_query_depart", true)) var querys []JoinWithSubQuery1 - err = testEngine.Join("INNER", builder.Select("id").From(tbName), - "join_with_sub_query_depart.id = join_with_sub_query1.depart_id").Find(&querys) + err = testEngine.Join("INNER", builder.Select("`id`").From(tbName), + "`join_with_sub_query_depart`.`id` = `join_with_sub_query1`.`depart_id`").Find(&querys) assert.NoError(t, err) assert.EqualValues(t, 1, len(querys)) assert.EqualValues(t, q, querys[0]) querys = make([]JoinWithSubQuery1, 0, 1) - err = testEngine.Join("INNER", "(SELECT id FROM "+tbName+") join_with_sub_query_depart", "join_with_sub_query_depart.id = join_with_sub_query1.depart_id"). + err = testEngine.Join("INNER", "(SELECT `id` FROM "+tbName+") `a`", "`a`.`id` = `join_with_sub_query1`.`depart_id`"). Find(&querys) assert.NoError(t, err) assert.EqualValues(t, 1, len(querys)) @@ -412,7 +375,7 @@ func TestQueryStringWithLimit(t *testing.T) { Money float32 } - assert.NoError(t, testEngine.Sync2(new(QueryWithLimit))) + assert.NoError(t, testEngine.Sync(new(QueryWithLimit))) data, err := testEngine.Table("query_with_limit").Limit(20, 20).QueryString() assert.NoError(t, err) diff --git a/integrations/session_raw_test.go b/integrations/session_raw_test.go index 36677683..5fa48d6e 100644 --- a/integrations/session_raw_test.go +++ b/integrations/session_raw_test.go @@ -20,15 +20,15 @@ func TestExecAndQuery(t *testing.T) { Name string } - assert.NoError(t, testEngine.Sync2(new(UserinfoQuery))) + assert.NoError(t, testEngine.Sync(new(UserinfoQuery))) - res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_query`", true)+" (uid, name) VALUES (?, ?)", 1, "user") + res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_query`", true)+" (`uid`, `name`) VALUES (?, ?)", 1, "user") assert.NoError(t, err) cnt, err := res.RowsAffected() assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - results, err := testEngine.Query("select * from " + testEngine.TableName("userinfo_query", true)) + results, err := testEngine.Query("select * from " + testEngine.Quote(testEngine.TableName("userinfo_query", true))) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) id, err := strconv.Atoi(string(results[0]["uid"])) @@ -46,21 +46,21 @@ func TestExecTime(t *testing.T) { Created time.Time } - assert.NoError(t, testEngine.Sync2(new(UserinfoExecTime))) + assert.NoError(t, testEngine.Sync(new(UserinfoExecTime))) now := time.Now() - res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_exec_time`", true)+" (uid, name, created) VALUES (?, ?, ?)", 1, "user", now) + res, err := testEngine.Exec("INSERT INTO "+testEngine.TableName("`userinfo_exec_time`", true)+" (`uid`, `name`, `created`) VALUES (?, ?, ?)", 1, "user", now) assert.NoError(t, err) cnt, err := res.RowsAffected() assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - results, err := testEngine.QueryString("SELECT * FROM " + testEngine.TableName("`userinfo_exec_time`", true)) + results, err := testEngine.QueryString("SELECT * FROM " + testEngine.Quote(testEngine.TableName("userinfo_exec_time", true))) assert.NoError(t, err) assert.EqualValues(t, 1, len(results)) assert.EqualValues(t, now.In(testEngine.GetTZLocation()).Format("2006-01-02 15:04:05"), results[0]["created"]) var uet UserinfoExecTime - has, err := testEngine.Where("uid=?", 1).Get(&uet) + has, err := testEngine.Where("`uid`=?", 1).Get(&uet) assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, now.In(testEngine.GetTZLocation()).Format("2006-01-02 15:04:05"), uet.Created.Format("2006-01-02 15:04:05")) diff --git a/integrations/session_schema_test.go b/integrations/session_schema_test.go index 9cbebcbf..7dc0af76 100644 --- a/integrations/session_schema_test.go +++ b/integrations/session_schema_test.go @@ -173,7 +173,7 @@ func (s *SyncTable3) TableName() string { func TestSyncTable(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable1))) + assert.NoError(t, testEngine.Sync(new(SyncTable1))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -183,7 +183,7 @@ func TestSyncTable(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, testEngine.Dialect().SQLType(tables[0].GetColumn("name")), testEngine.Dialect().SQLType(tableInfo.GetColumn("name"))) - assert.NoError(t, testEngine.Sync2(new(SyncTable2))) + assert.NoError(t, testEngine.Sync(new(SyncTable2))) tables, err = testEngine.DBMetas() assert.NoError(t, err) @@ -193,7 +193,7 @@ func TestSyncTable(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, testEngine.Dialect().SQLType(tables[0].GetColumn("name")), testEngine.Dialect().SQLType(tableInfo.GetColumn("name"))) - assert.NoError(t, testEngine.Sync2(new(SyncTable3))) + assert.NoError(t, testEngine.Sync(new(SyncTable3))) tables, err = testEngine.DBMetas() assert.NoError(t, err) @@ -207,7 +207,7 @@ func TestSyncTable(t *testing.T) { func TestSyncTable2(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Table("sync_tablex").Sync2(new(SyncTable1))) + assert.NoError(t, testEngine.Table("sync_tablex").Sync(new(SyncTable1))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -220,7 +220,7 @@ func TestSyncTable2(t *testing.T) { NewCol string } - assert.NoError(t, testEngine.Table("sync_tablex").Sync2(new(SyncTable4))) + assert.NoError(t, testEngine.Table("sync_tablex").Sync(new(SyncTable4))) tables, err = testEngine.DBMetas() assert.NoError(t, err) assert.EqualValues(t, 1, len(tables)) @@ -241,7 +241,7 @@ func TestSyncTable3(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable5))) + assert.NoError(t, testEngine.Sync(new(SyncTable5))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -272,7 +272,7 @@ func TestSyncTable3(t *testing.T) { }() assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable5))) + assert.NoError(t, testEngine.Sync(new(SyncTable5))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -294,9 +294,9 @@ func TestSyncTable4(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SyncTable6))) + assert.NoError(t, testEngine.Sync(new(SyncTable6))) - assert.NoError(t, testEngine.Sync2(new(SyncTable6))) + assert.NoError(t, testEngine.Sync(new(SyncTable6))) } func TestIsTableExist(t *testing.T) { @@ -328,14 +328,14 @@ func TestIsTableEmpty(t *testing.T) { Created time.Time `xorm:"created"` ILike int PageView int - From_url string + From_url string // nolint Pre_url string `xorm:"unique"` //pre view image's url Uid int64 } assert.NoError(t, testEngine.DropTables(&PictureEmpty{}, &NumericEmpty{})) - assert.NoError(t, testEngine.Sync2(new(PictureEmpty), new(NumericEmpty))) + assert.NoError(t, testEngine.Sync(new(PictureEmpty), new(NumericEmpty))) isEmpty, err := testEngine.IsTableEmpty(&PictureEmpty{}) assert.NoError(t, err) @@ -393,7 +393,7 @@ func TestIndexAndUnique(t *testing.T) { func TestMetaInfo(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(CustomTableName), new(IndexOrUnique))) + assert.NoError(t, testEngine.Sync(new(CustomTableName), new(IndexOrUnique))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -423,8 +423,8 @@ func TestSync2_1(t *testing.T) { assert.NoError(t, PrepareEngine()) assert.NoError(t, testEngine.DropTables("wx_test")) - assert.NoError(t, testEngine.Sync2(new(WxTest))) - assert.NoError(t, testEngine.Sync2(new(WxTest))) + assert.NoError(t, testEngine.Sync(new(WxTest))) + assert.NoError(t, testEngine.Sync(new(WxTest))) } func TestUnique_1(t *testing.T) { @@ -440,7 +440,7 @@ func TestUnique_1(t *testing.T) { assert.NoError(t, PrepareEngine()) assert.NoError(t, testEngine.DropTables("user_unique")) - assert.NoError(t, testEngine.Sync2(new(UserUnique))) + assert.NoError(t, testEngine.Sync(new(UserUnique))) assert.NoError(t, testEngine.DropTables("user_unique")) assert.NoError(t, testEngine.CreateTables(new(UserUnique))) @@ -459,7 +459,7 @@ func TestSync2_2(t *testing.T) { for i := 0; i < 10; i++ { tableName := fmt.Sprintf("test_sync2_index_%d", i) tableNames[tableName] = true - assert.NoError(t, testEngine.Table(tableName).Sync2(new(TestSync2Index))) + assert.NoError(t, testEngine.Table(tableName).Sync(new(TestSync2Index))) exist, err := testEngine.IsTableExist(tableName) assert.NoError(t, err) @@ -484,7 +484,7 @@ func TestSync2_Default(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestSync2Default)) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default))) } func TestSync2_Default2(t *testing.T) { @@ -497,9 +497,9 @@ func TestSync2_Default2(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(TestSync2Default2)) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default2))) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default2))) - assert.NoError(t, testEngine.Sync2(new(TestSync2Default2))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) + assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) assert.NoError(t, testEngine.Sync(new(TestSync2Default2))) @@ -526,8 +526,9 @@ func TestModifyColum(t *testing.T) { SQLType: schemas.SQLType{ Name: "VARCHAR", }, - Length: 16, - Nullable: false, + Length: 16, + Nullable: false, + DefaultIsEmpty: true, }) _, err := testEngine.Exec(alterSQL) assert.NoError(t, err) diff --git a/integrations/session_sum_test.go b/integrations/session_sum_test.go index b447c699..e000233b 100644 --- a/integrations/session_sum_test.go +++ b/integrations/session_sum_test.go @@ -23,7 +23,7 @@ func TestSum(t *testing.T) { } assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SumStruct))) + assert.NoError(t, testEngine.Sync(new(SumStruct))) var ( cases = []SumStruct{ @@ -82,7 +82,7 @@ func (s SumStructWithTableName) TableName() string { func TestSumWithTableName(t *testing.T) { assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(SumStructWithTableName))) + assert.NoError(t, testEngine.Sync(new(SumStructWithTableName))) var ( cases = []SumStructWithTableName{ @@ -146,7 +146,7 @@ func TestSumCustomColumn(t *testing.T) { } ) - assert.NoError(t, testEngine.Sync2(new(SumStruct2))) + assert.NoError(t, testEngine.Sync(new(SumStruct2))) cnt, err := testEngine.Insert(cases) assert.NoError(t, err) diff --git a/integrations/session_test.go b/integrations/session_test.go index c3ef0513..a36b81bf 100644 --- a/integrations/session_test.go +++ b/integrations/session_test.go @@ -18,7 +18,7 @@ func TestClose(t *testing.T) { sess1.Close() assert.True(t, sess1.IsClosed()) - sess2 := testEngine.Where("a = ?", 1) + sess2 := testEngine.Where("`a` = ?", 1) sess2.Close() assert.True(t, sess2.IsClosed()) } @@ -32,7 +32,7 @@ func TestNullFloatStruct(t *testing.T) { } assert.NoError(t, PrepareEngine()) - assert.NoError(t, testEngine.Sync2(new(MyNullFloatStruct))) + assert.NoError(t, testEngine.Sync(new(MyNullFloatStruct))) _, err := testEngine.Insert(&MyNullFloatStruct{ Uuid: "111111", diff --git a/integrations/session_tx_test.go b/integrations/session_tx_test.go index 4cff5610..8d6519d0 100644 --- a/integrations/session_tx_test.go +++ b/integrations/session_tx_test.go @@ -37,7 +37,7 @@ func TestTransaction(t *testing.T) { assert.NoError(t, err) user2 := Userinfo{Username: "yyy"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) _, err = session.Delete(&user2) @@ -70,10 +70,10 @@ func TestCombineTransaction(t *testing.T) { assert.NoError(t, err) user2 := Userinfo{Username: "zzz"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) - _, err = session.Exec("delete from "+testEngine.TableName("userinfo", true)+" where username = ?", user2.Username) + _, err = session.Exec("delete from "+testEngine.Quote(testEngine.TableName("userinfo", true))+" where `username` = ?", user2.Username) assert.NoError(t, err) err = session.Commit() @@ -113,10 +113,10 @@ func TestCombineTransactionSameMapper(t *testing.T) { assert.NoError(t, err) user2 := Userinfo{Username: "zzz"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) - _, err = session.Exec("delete from "+testEngine.TableName("`Userinfo`", true)+" where `Username` = ?", user2.Username) + _, err = session.Exec("delete from "+testEngine.Quote(testEngine.TableName("Userinfo", true))+" where `Username` = ?", user2.Username) assert.NoError(t, err) err = session.Commit() @@ -144,7 +144,7 @@ func TestMultipleTransaction(t *testing.T) { assert.NoError(t, err) user2 := MultipleTransaction{Name: "zzz"} - _, err = session.Where("id = ?", 0).Update(&user2) + _, err = session.Where("`id` = ?", 0).Update(&user2) assert.NoError(t, err) err = session.Commit() @@ -158,7 +158,7 @@ func TestMultipleTransaction(t *testing.T) { err = session.Begin() assert.NoError(t, err) - _, err = session.Where("id=?", m1.Id).Delete(new(MultipleTransaction)) + _, err = session.Where("`id`=?", m1.Id).Delete(new(MultipleTransaction)) assert.NoError(t, err) err = session.Commit() diff --git a/integrations/session_update_test.go b/integrations/session_update_test.go index bbcc7600..45338cad 100644 --- a/integrations/session_update_test.go +++ b/integrations/session_update_test.go @@ -27,7 +27,7 @@ func TestUpdateMap(t *testing.T) { Age int } - assert.NoError(t, testEngine.Sync2(new(UpdateTable))) + assert.NoError(t, testEngine.Sync(new(UpdateTable))) var tb = UpdateTable{ Name: "test", Age: 35, @@ -35,7 +35,7 @@ func TestUpdateMap(t *testing.T) { _, err := testEngine.Insert(&tb) assert.NoError(t, err) - cnt, err := testEngine.Table("update_table").Where("id = ?", tb.Id).Update(map[string]interface{}{ + cnt, err := testEngine.Table("update_table").Where("`id` = ?", tb.Id).Update(map[string]interface{}{ "name": "test2", "age": 36, }) @@ -78,7 +78,7 @@ func TestUpdateLimit(t *testing.T) { Age int } - assert.NoError(t, testEngine.Sync2(new(UpdateTable2))) + assert.NoError(t, testEngine.Sync(new(UpdateTable2))) var tb = UpdateTable2{ Name: "test1", Age: 35, @@ -93,7 +93,12 @@ func TestUpdateLimit(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - cnt, err = testEngine.OrderBy("name desc").Limit(1).Update(&UpdateTable2{ + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + + cnt, err = testEngine.OrderBy("`name` desc").Limit(1).Update(&UpdateTable2{ Age: 30, }) assert.NoError(t, err) @@ -166,7 +171,7 @@ func TestForUpdate(t *testing.T) { // use lock fList := make([]ForUpdate, 0) session1.ForUpdate() - session1.Where("id = ?", 1) + session1.Where("`id` = ?", 1) err = session1.Find(&fList) switch { case err != nil: @@ -187,7 +192,7 @@ func TestForUpdate(t *testing.T) { wg.Add(1) go func() { f2 := new(ForUpdate) - session2.Where("id = ?", 1).ForUpdate() + session2.Where("`id` = ?", 1).ForUpdate() has, err := session2.Get(f2) // wait release lock switch { case err != nil: @@ -207,7 +212,7 @@ func TestForUpdate(t *testing.T) { wg2.Add(1) go func() { f3 := new(ForUpdate) - session3.Where("id = ?", 1) + session3.Where("`id` = ?", 1) has, err := session3.Get(f3) // wait release lock switch { case err != nil: @@ -225,15 +230,13 @@ func TestForUpdate(t *testing.T) { f := new(ForUpdate) f.Name = "updated by session1" - session1.Where("id = ?", 1) - session1.Update(f) + session1.Where("`id` = ?", 1) + _, err = session1.Update(f) + assert.NoError(t, err) // release lock err = session1.Commit() - if err != nil { - t.Error(err) - return - } + assert.NoError(t, err) wg.Wait() } @@ -248,7 +251,7 @@ func TestWithIn(t *testing.T) { assert.NoError(t, PrepareEngine()) assert.NoError(t, testEngine.Sync(new(temp3))) - testEngine.Insert(&[]temp3{ + _, err := testEngine.Insert(&[]temp3{ { Name: "user1", }, @@ -259,6 +262,7 @@ func TestWithIn(t *testing.T) { Name: "user1", }, }) + assert.NoError(t, err) cnt, err := testEngine.In("Id", 1, 2, 3, 4).Update(&temp3{Name: "aa"}, &temp3{Name: "user1"}) assert.NoError(t, err) @@ -300,7 +304,7 @@ func TestUpdateMap2(t *testing.T) { assert.NoError(t, PrepareEngine()) assertSync(t, new(UpdateMustCols)) - _, err := testEngine.Table("update_must_cols").Where("id =?", 1).Update(map[string]interface{}{ + _, err := testEngine.Table("update_must_cols").Where("`id` =?", 1).Update(map[string]interface{}{ "bool": true, }) assert.NoError(t, err) @@ -313,6 +317,7 @@ func TestUpdate1(t *testing.T) { _, err := testEngine.Insert(&Userinfo{ Username: "user1", }) + assert.NoError(t, err) var ori Userinfo has, err := testEngine.Get(&ori) @@ -345,11 +350,11 @@ func TestUpdate1(t *testing.T) { userID := user.Uid has, err := testEngine.ID(userID). - And("username = ?", user.Username). - And("height = ?", user.Height). - And("departname = ?", ""). - And("detail_id = ?", 0). - And("is_man = ?", false). + And("`username` = ?", user.Username). + And("`height` = ?", user.Height). + And("`departname` = ?", ""). + And("`detail_id` = ?", 0). + And("`is_man` = ?", false). Get(&Userinfo{}) assert.NoError(t, err) assert.True(t, has, "cannot insert properly") @@ -362,12 +367,12 @@ func TestUpdate1(t *testing.T) { assert.EqualValues(t, 1, cnt, "update not returned 1") has, err = testEngine.ID(userID). - And("username = ?", updatedUser.Username). - And("height IS NULL"). - And("departname IS NULL"). - And("is_man IS NULL"). - And("created IS NULL"). - And("detail_id = ?", 0). + And("`username` = ?", updatedUser.Username). + And("`height` IS NULL"). + And("`departname` IS NULL"). + And("`is_man` IS NULL"). + And("`created` IS NULL"). + And("`detail_id` = ?", 0). Get(&Userinfo{}) assert.NoError(t, err) assert.True(t, has, "cannot update with null properly") @@ -377,7 +382,7 @@ func TestUpdate1(t *testing.T) { assert.EqualValues(t, 1, cnt, "delete not returned 1") } - err = testEngine.StoreEngine("Innodb").Sync2(&Article{}) + err = testEngine.StoreEngine("Innodb").Sync(&Article{}) assert.NoError(t, err) defer func() { @@ -508,7 +513,7 @@ func TestUpdateUpdated(t *testing.T) { assert.NoError(t, PrepareEngine()) di := new(UpdatedUpdate) - err := testEngine.Sync2(di) + err := testEngine.Sync(di) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate{}) @@ -524,7 +529,7 @@ func TestUpdateUpdated(t *testing.T) { assert.EqualValues(t, ci.Updated.Unix(), di.Updated.Unix()) di2 := new(UpdatedUpdate2) - err = testEngine.Sync2(di2) + err = testEngine.Sync(di2) assert.NoError(t, err) now := time.Now() @@ -551,7 +556,7 @@ func TestUpdateUpdated(t *testing.T) { assert.True(t, ci2.Updated >= di21.Updated) di3 := new(UpdatedUpdate3) - err = testEngine.Sync2(di3) + err = testEngine.Sync(di3) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate3{}) @@ -567,7 +572,7 @@ func TestUpdateUpdated(t *testing.T) { assert.EqualValues(t, ci3.Updated, di3.Updated) di4 := new(UpdatedUpdate4) - err = testEngine.Sync2(di4) + err = testEngine.Sync(di4) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate4{}) @@ -583,7 +588,7 @@ func TestUpdateUpdated(t *testing.T) { assert.EqualValues(t, ci4.Updated, di4.Updated) di5 := new(UpdatedUpdate5) - err = testEngine.Sync2(di5) + err = testEngine.Sync(di5) assert.NoError(t, err) _, err = testEngine.Insert(&UpdatedUpdate5{}) @@ -825,7 +830,7 @@ func TestNewUpdate(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 0, af) - af, err = testEngine.Table(new(TbUserInfo)).Where("phone=?", "13126564922").Update(&changeUsr) + af, err = testEngine.Table(new(TbUserInfo)).Where("`phone`=?", "13126564922").Update(&changeUsr) assert.NoError(t, err) assert.EqualValues(t, 0, af) } @@ -920,6 +925,7 @@ func TestDeletedUpdate(t *testing.T) { var s1 DeletedUpdatedStruct has, err := testEngine.ID(s.Id).Get(&s1) + assert.NoError(t, err) assert.EqualValues(t, true, has) cnt, err = testEngine.ID(s.Id).Delete(&DeletedUpdatedStruct{}) @@ -936,6 +942,7 @@ func TestDeletedUpdate(t *testing.T) { var s2 DeletedUpdatedStruct has, err = testEngine.ID(s.Id).Get(&s2) + assert.NoError(t, err) assert.EqualValues(t, true, has) } @@ -1166,7 +1173,7 @@ func TestUpdateExprs(t *testing.T) { }) assert.NoError(t, err) - _, err = testEngine.SetExpr("num_issues", "num_issues+1").AllCols().Update(&UpdateExprs{ + _, err = testEngine.SetExpr("num_issues", "`num_issues`+1").AllCols().Update(&UpdateExprs{ NumIssues: 3, Name: "lunny xiao", }) @@ -1197,7 +1204,7 @@ func TestUpdateAlias(t *testing.T) { }) assert.NoError(t, err) - _, err = testEngine.Alias("ua").Where("ua.id = ?", 1).Update(&UpdateAlias{ + _, err = testEngine.Alias("ua").Where("ua.`id` = ?", 1).Update(&UpdateAlias{ NumIssues: 2, Name: "lunny xiao", }) @@ -1237,7 +1244,7 @@ func TestUpdateExprs2(t *testing.T) { assert.EqualValues(t, 1, inserted) updated, err := testEngine. - Where("repo_id = ? AND is_tag = ?", 1, false). + Where("`repo_id` = ? AND `is_tag` = ?", 1, false). SetExpr("is_draft", true). SetExpr("num_commits", 0). SetExpr("sha1", ""). @@ -1257,6 +1264,11 @@ func TestUpdateExprs2(t *testing.T) { } func TestUpdateMap3(t *testing.T) { + if testEngine.Dialect().URI().DBType == schemas.DAMENG { + t.SkipNow() + return + } + assert.NoError(t, PrepareEngine()) type UpdateMapUser struct { @@ -1308,7 +1320,7 @@ func TestUpdateIgnoreOnlyFromDBFields(t *testing.T) { assertGetRecord := func() *TestOnlyFromDBField { var record TestOnlyFromDBField - has, err := testEngine.Where("id = ?", 1).Get(&record) + has, err := testEngine.Where("`id` = ?", 1).Get(&record) assert.NoError(t, err) assert.EqualValues(t, true, has) assert.EqualValues(t, "", record.OnlyFromDBField) diff --git a/integrations/tags_test.go b/integrations/tags_test.go index fc7b505e..247a64e8 100644 --- a/integrations/tags_test.go +++ b/integrations/tags_test.go @@ -458,7 +458,7 @@ func TestExtends5(t *testing.T) { list := make([]Book, 0) err = session. Select(fmt.Sprintf( - "%s.%s, sc.%s AS %s, sc.%s AS %s, s.%s, s.%s", + "%s.%s, `sc`.%s AS %s, `sc`.%s AS %s, `s`.%s, `s`.%s", quote(bookTableName), quote("id"), quote("Width"), @@ -472,12 +472,12 @@ func TestExtends5(t *testing.T) { Join( "LEFT", sizeTableName+" AS `sc`", - bookTableName+".`SizeClosed`=sc.`id`", + bookTableName+".`SizeClosed`=`sc`.`id`", ). Join( "LEFT", sizeTableName+" AS `s`", - bookTableName+".`Size`=s.`id`", + bookTableName+".`Size`=`s`.`id`", ). Find(&list) assert.NoError(t, err) @@ -673,7 +673,7 @@ func TestCreatedUpdated(t *testing.T) { Updated time.Time `xorm:"updated"` } - err := testEngine.Sync2(&CreatedUpdated{}) + err := testEngine.Sync(&CreatedUpdated{}) assert.NoError(t, err) c := &CreatedUpdated{Name: "test"} @@ -728,9 +728,9 @@ type Lowercase struct { func TestLowerCase(t *testing.T) { assert.NoError(t, PrepareEngine()) - err := testEngine.Sync2(&Lowercase{}) + err := testEngine.Sync(&Lowercase{}) assert.NoError(t, err) - _, err = testEngine.Where("id > 0").Delete(&Lowercase{}) + _, err = testEngine.Where("`id` > 0").Delete(&Lowercase{}) assert.NoError(t, err) _, err = testEngine.Insert(&Lowercase{ended: 1}) @@ -827,7 +827,7 @@ func TestTagComment(t *testing.T) { assert.True(t, cols[0].DefaultIsEmpty) assert.EqualValues(t, "", cols[0].Default) - assert.NoError(t, testEngine.Sync2(new(TestComment1))) + assert.NoError(t, testEngine.Sync(new(TestComment1))) tables, err := testEngine.DBMetas() assert.NoError(t, err) @@ -851,7 +851,7 @@ func TestTagComment(t *testing.T) { assert.True(t, cols[0].DefaultIsEmpty) assert.EqualValues(t, "", cols[0].Default) - assert.NoError(t, testEngine.Sync2(new(TestComment2))) + assert.NoError(t, testEngine.Sync(new(TestComment2))) tables, err = testEngine.DBMetas() assert.NoError(t, err) @@ -1202,7 +1202,7 @@ func TestTagTime(t *testing.T) { assert.NoError(t, err) assert.True(t, has) assert.EqualValues(t, s.Created.UTC().Format("2006-01-02 15:04:05"), - strings.Replace(strings.Replace(tm, "T", " ", -1), "Z", "", -1)) + strings.ReplaceAll(strings.ReplaceAll(tm, "T", " "), "Z", "")) } func TestTagAutoIncr(t *testing.T) { @@ -1287,7 +1287,7 @@ func TestVersion1(t *testing.T) { assert.EqualValues(t, newVer.Ver, 2) newVer = new(VersionS) - has, err = testEngine.ID(ver.Id).Get(newVer) + _, err = testEngine.ID(ver.Id).Get(newVer) assert.NoError(t, err) assert.EqualValues(t, newVer.Ver, 2) } @@ -1345,7 +1345,7 @@ func TestVersion3(t *testing.T) { assert.EqualValues(t, newVer.Ver, 2) newVer = new(VersionUintS) - has, err = testEngine.ID(ver.Id).Get(newVer) + _, err = testEngine.ID(ver.Id).Get(newVer) assert.NoError(t, err) assert.EqualValues(t, newVer.Ver, 2) } diff --git a/integrations/tests.go b/integrations/tests.go index 8b14b0f4..59f4b29a 100644 --- a/integrations/tests.go +++ b/integrations/tests.go @@ -51,7 +51,7 @@ func createEngine(dbType, connStr string) error { if !*cluster { switch schemas.DBType(strings.ToLower(dbType)) { case schemas.MSSQL: - db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "master", -1)) + db, err := sql.Open(dbType, strings.ReplaceAll(connStr, "xorm_test", "master")) if err != nil { return err } @@ -61,7 +61,7 @@ func createEngine(dbType, connStr string) error { db.Close() *ignoreSelectUpdate = true case schemas.POSTGRES: - db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "postgres", -1)) + db, err := sql.Open(dbType, strings.ReplaceAll(connStr, "xorm_test", "postgres")) if err != nil { return err } @@ -90,7 +90,7 @@ func createEngine(dbType, connStr string) error { db.Close() *ignoreSelectUpdate = true case schemas.MYSQL: - db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "mysql", -1)) + db, err := sql.Open(dbType, strings.ReplaceAll(connStr, "xorm_test", "mysql")) if err != nil { return err } diff --git a/integrations/time_test.go b/integrations/time_test.go index cd2e879f..a8447eea 100644 --- a/integrations/time_test.go +++ b/integrations/time_test.go @@ -324,7 +324,7 @@ func TestTimeUserDeleted(t *testing.T) { fmt.Println("user2 str", user2.CreatedAtStr, user2.UpdatedAtStr) var user3 UserDeleted - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(user3.DeletedAt)) @@ -386,7 +386,7 @@ func TestTimeUserDeletedDiffLoc(t *testing.T) { fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) var user3 UserDeleted2 - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(user3.DeletedAt)) @@ -457,7 +457,7 @@ func TestCustomTimeUserDeleted(t *testing.T) { fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) var user3 UserDeleted3 - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(time.Time(user3.DeletedAt))) @@ -519,7 +519,7 @@ func TestCustomTimeUserDeletedDiffLoc(t *testing.T) { fmt.Println("user2", user2.CreatedAt, user2.UpdatedAt, user2.DeletedAt) var user3 UserDeleted4 - cnt, err = testEngine.Where("id = ?", "lunny").Delete(&user3) + cnt, err = testEngine.Where("`id` = ?", "lunny").Delete(&user3) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) assert.True(t, !utils.IsTimeZero(time.Time(user3.DeletedAt))) diff --git a/integrations/types_null_test.go b/integrations/types_null_test.go index 86ce1939..8d98b456 100644 --- a/integrations/types_null_test.go +++ b/integrations/types_null_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" ) -type NullType struct { +type NullStruct struct { Id int `xorm:"pk autoincr"` Name sql.NullString Age sql.NullInt64 @@ -65,26 +65,26 @@ func (m CustomStruct) Value() (driver.Value, error) { func TestCreateNullStructTable(t *testing.T) { assert.NoError(t, PrepareEngine()) - err := testEngine.CreateTables(new(NullType)) + err := testEngine.CreateTables(new(NullStruct)) assert.NoError(t, err) } func TestDropNullStructTable(t *testing.T) { assert.NoError(t, PrepareEngine()) - err := testEngine.DropTables(new(NullType)) + err := testEngine.DropTables(new(NullStruct)) assert.NoError(t, err) } func TestNullStructInsert(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - item1 := new(NullType) + item1 := new(NullStruct) _, err := testEngine.Insert(item1) assert.NoError(t, err) assert.EqualValues(t, 1, item1.Id) - item := NullType{ + item := NullStruct{ Name: sql.NullString{String: "haolei", Valid: true}, Age: sql.NullInt64{Int64: 34, Valid: true}, Height: sql.NullFloat64{Float64: 1.72, Valid: true}, @@ -95,9 +95,9 @@ func TestNullStructInsert(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, item.Id) - items := []NullType{} + items := []NullStruct{} for i := 0; i < 5; i++ { - item := NullType{ + item := NullStruct{ Name: sql.NullString{String: "haolei_" + fmt.Sprint(i+1), Valid: true}, Age: sql.NullInt64{Int64: 30 + int64(i), Valid: true}, Height: sql.NullFloat64{Float64: 1.5 + 1.1*float64(i), Valid: true}, @@ -111,7 +111,7 @@ func TestNullStructInsert(t *testing.T) { _, err = testEngine.Insert(&items) assert.NoError(t, err) - items = make([]NullType, 0, 7) + items = make([]NullStruct, 0, 7) err = testEngine.Find(&items) assert.NoError(t, err) assert.EqualValues(t, 7, len(items)) @@ -119,9 +119,9 @@ func TestNullStructInsert(t *testing.T) { func TestNullStructUpdate(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - _, err := testEngine.Insert([]NullType{ + _, err := testEngine.Insert([]NullStruct{ { Name: sql.NullString{ String: "name1", @@ -150,7 +150,7 @@ func TestNullStructUpdate(t *testing.T) { assert.NoError(t, err) if true { // 测试可插入NULL - item := new(NullType) + item := new(NullStruct) item.Age = sql.NullInt64{Int64: 23, Valid: true} item.Height = sql.NullFloat64{Float64: 0, Valid: false} // update to NULL @@ -160,7 +160,7 @@ func TestNullStructUpdate(t *testing.T) { } if true { // 测试In update - item := new(NullType) + item := new(NullStruct) item.Age = sql.NullInt64{Int64: 23, Valid: true} affected, err := testEngine.In("id", 3, 4).Cols("age", "height", "is_man").Update(item) assert.NoError(t, err) @@ -168,17 +168,17 @@ func TestNullStructUpdate(t *testing.T) { } if true { // 测试where - item := new(NullType) + item := new(NullStruct) item.Name = sql.NullString{String: "nullname", Valid: true} item.IsMan = sql.NullBool{Bool: true, Valid: true} item.Age = sql.NullInt64{Int64: 34, Valid: true} - _, err := testEngine.Where("age > ?", 34).Update(item) + _, err := testEngine.Where("`age` > ?", 34).Update(item) assert.NoError(t, err) } if true { // 修改全部时,插入空值 - item := &NullType{ + item := &NullStruct{ Name: sql.NullString{String: "winxxp", Valid: true}, Age: sql.NullInt64{Int64: 30, Valid: true}, Height: sql.NullFloat64{Float64: 1.72, Valid: true}, @@ -192,9 +192,9 @@ func TestNullStructUpdate(t *testing.T) { func TestNullStructFind(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - _, err := testEngine.Insert([]NullType{ + _, err := testEngine.Insert([]NullStruct{ { Name: sql.NullString{ String: "name1", @@ -223,7 +223,7 @@ func TestNullStructFind(t *testing.T) { assert.NoError(t, err) if true { - item := new(NullType) + item := new(NullStruct) has, err := testEngine.ID(1).Get(item) assert.NoError(t, err) assert.True(t, has) @@ -235,7 +235,7 @@ func TestNullStructFind(t *testing.T) { } if true { - item := new(NullType) + item := new(NullStruct) item.Id = 2 has, err := testEngine.Get(item) assert.NoError(t, err) @@ -243,13 +243,13 @@ func TestNullStructFind(t *testing.T) { } if true { - item := make([]NullType, 0) + item := make([]NullStruct, 0) err := testEngine.ID(2).Find(&item) assert.NoError(t, err) } if true { - item := make([]NullType, 0) + item := make([]NullStruct, 0) err := testEngine.Asc("age").Find(&item) assert.NoError(t, err) } @@ -257,12 +257,12 @@ func TestNullStructFind(t *testing.T) { func TestNullStructIterate(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) if true { - err := testEngine.Where("age IS NOT NULL").OrderBy("age").Iterate(new(NullType), + err := testEngine.Where("`age` IS NOT NULL").OrderBy("age").Iterate(new(NullStruct), func(i int, bean interface{}) error { - nultype := bean.(*NullType) + nultype := bean.(*NullStruct) fmt.Println(i, nultype) return nil }) @@ -272,21 +272,21 @@ func TestNullStructIterate(t *testing.T) { func TestNullStructCount(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) if true { - item := new(NullType) - _, err := testEngine.Where("age IS NOT NULL").Count(item) + item := new(NullStruct) + _, err := testEngine.Where("`age` IS NOT NULL").Count(item) assert.NoError(t, err) } } func TestNullStructRows(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - item := new(NullType) - rows, err := testEngine.Where("id > ?", 1).Rows(item) + item := new(NullStruct) + rows, err := testEngine.Where("`id` > ?", 1).Rows(item) assert.NoError(t, err) defer rows.Close() @@ -298,13 +298,13 @@ func TestNullStructRows(t *testing.T) { func TestNullStructDelete(t *testing.T) { assert.NoError(t, PrepareEngine()) - assertSync(t, new(NullType)) + assertSync(t, new(NullStruct)) - item := new(NullType) + item := new(NullStruct) _, err := testEngine.ID(1).Delete(item) assert.NoError(t, err) - _, err = testEngine.Where("id > ?", 1).Delete(item) + _, err = testEngine.Where("`id` > ?", 1).Delete(item) assert.NoError(t, err) } diff --git a/integrations/types_test.go b/integrations/types_test.go index b08dfec5..d166845e 100644 --- a/integrations/types_test.go +++ b/integrations/types_test.go @@ -28,7 +28,7 @@ func TestArrayField(t *testing.T) { Name [20]byte `xorm:"char(80)"` } - assert.NoError(t, testEngine.Sync2(new(ArrayStruct))) + assert.NoError(t, testEngine.Sync(new(ArrayStruct))) var as = ArrayStruct{ Name: [20]byte{ @@ -90,7 +90,7 @@ func TestGetBytes(t *testing.T) { Data []byte `xorm:"VARBINARY(250)"` } - err := testEngine.Sync2(new(Varbinary)) + err := testEngine.Sync(new(Varbinary)) assert.NoError(t, err) cnt, err := testEngine.Insert(&Varbinary{ @@ -193,7 +193,7 @@ func TestConversion(t *testing.T) { c := new(ConvStruct) assert.NoError(t, testEngine.DropTables(c)) - assert.NoError(t, testEngine.Sync2(c)) + assert.NoError(t, testEngine.Sync(c)) var s ConvString = "sssss" c.Conv = "tttt" @@ -428,7 +428,7 @@ func TestUnsignedUint64(t *testing.T) { assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) case schemas.MYSQL: assert.EqualValues(t, "UNSIGNED BIGINT", tables[0].Columns()[0].SQLType.Name) - case schemas.POSTGRES: + case schemas.POSTGRES, schemas.DAMENG: assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) case schemas.MSSQL: assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) @@ -472,9 +472,7 @@ func TestUnsignedUint32(t *testing.T) { assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) case schemas.MYSQL: assert.EqualValues(t, "UNSIGNED INT", tables[0].Columns()[0].SQLType.Name) - case schemas.POSTGRES: - assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) - case schemas.MSSQL: + case schemas.POSTGRES, schemas.MSSQL, schemas.DAMENG: assert.EqualValues(t, "BIGINT", tables[0].Columns()[0].SQLType.Name) default: assert.False(t, true, "Unsigned is not implemented") @@ -507,7 +505,7 @@ func TestUnsignedTinyInt(t *testing.T) { assert.EqualValues(t, 1, len(tables[0].Columns())) switch testEngine.Dialect().URI().DBType { - case schemas.SQLITE: + case schemas.SQLITE, schemas.DAMENG: assert.EqualValues(t, "INTEGER", tables[0].Columns()[0].SQLType.Name) case schemas.MYSQL: assert.EqualValues(t, "UNSIGNED TINYINT", tables[0].Columns()[0].SQLType.Name) @@ -516,7 +514,7 @@ func TestUnsignedTinyInt(t *testing.T) { case schemas.MSSQL: assert.EqualValues(t, "INT", tables[0].Columns()[0].SQLType.Name) default: - assert.False(t, true, "Unsigned is not implemented") + assert.False(t, true, fmt.Sprintf("Unsigned is not implemented, returned %s", tables[0].Columns()[0].SQLType.Name)) } cnt, err := testEngine.Insert(&MyUnsignedTinyIntStruct{ diff --git a/interface.go b/interface.go index 5d68f536..b9e88505 100644 --- a/interface.go +++ b/interface.go @@ -37,7 +37,7 @@ type Interface interface { Exist(bean ...interface{}) (bool, error) Find(interface{}, ...interface{}) error FindAndCount(interface{}, ...interface{}) (int64, error) - Get(interface{}) (bool, error) + Get(...interface{}) (bool, error) GroupBy(keys string) *Session ID(interface{}) *Session In(string, ...interface{}) *Session @@ -99,6 +99,7 @@ type EngineInterface interface { MapCacher(interface{}, caches.Cacher) error NewSession() *Session NoAutoTime() *Session + Prepare() *Session Quote(string) string SetCacher(string, caches.Cacher) SetConnMaxLifetime(time.Duration) diff --git a/internal/statements/insert.go b/internal/statements/insert.go index 84547cdf..91a33319 100644 --- a/internal/statements/insert.go +++ b/internal/statements/insert.go @@ -10,6 +10,7 @@ import ( "strings" "xorm.io/builder" + "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -42,7 +43,19 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } - if len(colNames) <= 0 { + var hasInsertColumns = len(colNames) > 0 + var needSeq = len(table.AutoIncrement) > 0 && (statement.dialect.URI().DBType == schemas.ORACLE || statement.dialect.URI().DBType == schemas.DAMENG) + if needSeq { + for _, col := range colNames { + if strings.EqualFold(col, table.AutoIncrement) { + needSeq = false + break + } + } + } + + if !hasInsertColumns && statement.dialect.URI().DBType != schemas.ORACLE && + statement.dialect.URI().DBType != schemas.DAMENG { if statement.dialect.URI().DBType == schemas.MYSQL { if _, err := buf.WriteString(" VALUES ()"); err != nil { return "", nil, err @@ -60,6 +73,10 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } + if needSeq { + colNames = append(colNames, table.AutoIncrement) + } + if err := statement.dialect.Quoter().JoinWrite(buf.Builder, append(colNames, exprs.ColNames()...), ","); err != nil { return "", nil, err } @@ -80,13 +97,23 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } + if needSeq { + if len(args) > 0 { + if _, err := buf.WriteString(","); err != nil { + return "", nil, err + } + } + if _, err := buf.WriteString(utils.SeqName(tableName) + ".nextval"); err != nil { + return "", nil, err + } + } if len(exprs) > 0 { if _, err := buf.WriteString(","); err != nil { return "", nil, err } - } - if err := exprs.WriteArgs(buf); err != nil { - return "", nil, err + if err := exprs.WriteArgs(buf); err != nil { + return "", nil, err + } } if _, err := buf.WriteString(" FROM "); err != nil { @@ -113,6 +140,18 @@ func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) return "", nil, err } + // Insert tablename (id) Values(seq_tablename.nextval) + if needSeq { + if hasInsertColumns { + if _, err := buf.WriteString(","); err != nil { + return "", nil, err + } + } + if _, err := buf.WriteString(utils.SeqName(tableName) + ".nextval"); err != nil { + return "", nil, err + } + } + if len(exprs) > 0 { if _, err := buf.WriteString(","); err != nil { return "", nil, err diff --git a/internal/statements/query.go b/internal/statements/query.go index 76946cbd..8b383866 100644 --- a/internal/statements/query.go +++ b/internal/statements/query.go @@ -79,7 +79,9 @@ func (statement *Statement) GenSumSQL(bean interface{}, columns ...string) (stri return statement.GenRawSQL(), statement.RawParams, nil } - statement.SetRefBean(bean) + if err := statement.SetRefBean(bean); err != nil { + return "", nil, err + } var sumStrs = make([]string, 0, len(columns)) for _, colName := range columns { @@ -111,7 +113,9 @@ func (statement *Statement) GenGetSQL(bean interface{}) (string, []interface{}, v := rValue(bean) isStruct = v.Kind() == reflect.Struct if isStruct { - statement.SetRefBean(bean) + if err := statement.SetRefBean(bean); err != nil { + return "", nil, err + } } } @@ -168,7 +172,9 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa var condArgs []interface{} var err error if len(beans) > 0 { - statement.SetRefBean(beans[0]) + if err := statement.SetRefBean(beans[0]); err != nil { + return "", nil, err + } if err := statement.mergeConds(beans[0]); err != nil { return "", nil, err } @@ -203,14 +209,42 @@ func (statement *Statement) GenCountSQL(beans ...interface{}) (string, []interfa return sqlStr, append(statement.joinArgs, condArgs...), nil } +func (statement *Statement) fromBuilder() *strings.Builder { + var builder strings.Builder + var quote = statement.quote + var dialect = statement.dialect + + builder.WriteString(" FROM ") + + if dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") { + builder.WriteString(statement.TableName()) + } else { + builder.WriteString(quote(statement.TableName())) + } + + if statement.TableAlias != "" { + if dialect.URI().DBType == schemas.ORACLE { + builder.WriteString(" ") + } else { + builder.WriteString(" AS ") + } + builder.WriteString(quote(statement.TableAlias)) + } + if statement.JoinStr != "" { + builder.WriteString(" ") + builder.WriteString(statement.JoinStr) + } + return &builder +} + func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderBy bool) (string, []interface{}, error) { var ( distinct string dialect = statement.dialect - quote = statement.quote - fromStr = " FROM " + fromStr = statement.fromBuilder().String() top, mssqlCondi, whereStr string ) + if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { distinct = "DISTINCT " } @@ -220,24 +254,7 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB return "", nil, err } if len(condSQL) > 0 { - whereStr = " WHERE " + condSQL - } - - if dialect.URI().DBType == schemas.MSSQL && strings.Contains(statement.TableName(), "..") { - fromStr += statement.TableName() - } else { - fromStr += quote(statement.TableName()) - } - - if statement.TableAlias != "" { - if dialect.URI().DBType == schemas.ORACLE { - fromStr += " " + quote(statement.TableAlias) - } else { - fromStr += " AS " + quote(statement.TableAlias) - } - } - if statement.JoinStr != "" { - fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr) + whereStr = fmt.Sprintf(" WHERE %s", condSQL) } pLimitN := statement.LimitN @@ -266,20 +283,20 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB } if statement.needTableName() { if len(statement.TableAlias) > 0 { - column = statement.TableAlias + "." + column + column = fmt.Sprintf("%s.%s", statement.TableAlias, column) } else { - column = statement.TableName() + "." + column + column = fmt.Sprintf("%s.%s", statement.TableName(), column) } } var orderStr string if needOrderBy && len(statement.OrderStr) > 0 { - orderStr = " ORDER BY " + statement.OrderStr + orderStr = fmt.Sprintf(" ORDER BY %s", statement.OrderStr) } var groupStr string if len(statement.GroupByStr) > 0 { - groupStr = " GROUP BY " + statement.GroupByStr + groupStr = fmt.Sprintf(" GROUP BY %s", statement.GroupByStr) } mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s%s))", column, statement.Start, column, fromStr, whereStr, orderStr, groupStr) @@ -311,13 +328,13 @@ func (statement *Statement) genSelectSQL(columnStr string, needLimit, needOrderB if pLimitN != nil { fmt.Fprintf(&buf, " LIMIT %v OFFSET %v", *pLimitN, statement.Start) } else { - fmt.Fprintf(&buf, "LIMIT 0 OFFSET %v", statement.Start) + fmt.Fprintf(&buf, " LIMIT 0 OFFSET %v", statement.Start) } } else if pLimitN != nil { fmt.Fprint(&buf, " LIMIT ", *pLimitN) } } else if dialect.URI().DBType == schemas.ORACLE { - if statement.Start != 0 && pLimitN != nil { + if pLimitN != nil { oldString := buf.String() buf.Reset() rawColStr := columnStr @@ -381,7 +398,7 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac } else if statement.dialect.URI().DBType == schemas.ORACLE { sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) %s AND ROWNUM=1", tableName, joinStr, condSQL) } else { - sqlStr = fmt.Sprintf("SELECT * FROM %s %s WHERE %s LIMIT 1", tableName, joinStr, condSQL) + sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s WHERE %s LIMIT 1", tableName, joinStr, condSQL) } args = condArgs } else { @@ -390,7 +407,7 @@ func (statement *Statement) GenExistSQL(bean ...interface{}) (string, []interfac } else if statement.dialect.URI().DBType == schemas.ORACLE { sqlStr = fmt.Sprintf("SELECT * FROM %s %s WHERE ROWNUM=1", tableName, joinStr) } else { - sqlStr = fmt.Sprintf("SELECT * FROM %s %s LIMIT 1", tableName, joinStr) + sqlStr = fmt.Sprintf("SELECT 1 FROM %s %s LIMIT 1", tableName, joinStr) } args = []interface{}{} } diff --git a/internal/statements/statement.go b/internal/statements/statement.go index 1fcc0bba..2a7ae8b0 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -308,7 +308,7 @@ func (statement *Statement) colName(col *schemas.Column, tableName string) strin if len(statement.TableAlias) > 0 { nm = statement.TableAlias } - return statement.quote(nm) + "." + statement.quote(col.Name) + return fmt.Sprintf("%s.%s", statement.quote(nm), statement.quote(col.Name)) } return statement.quote(col.Name) } @@ -473,7 +473,7 @@ func (statement *Statement) Desc(colNames ...string) *Statement { if i > 0 { fmt.Fprint(&buf, ", ") } - statement.dialect.Quoter().QuoteTo(&buf, col) + _ = statement.dialect.Quoter().QuoteTo(&buf, col) fmt.Fprint(&buf, " DESC") } statement.OrderStr = buf.String() @@ -490,7 +490,7 @@ func (statement *Statement) Asc(colNames ...string) *Statement { if i > 0 { fmt.Fprint(&buf, ", ") } - statement.dialect.Quoter().QuoteTo(&buf, col) + _ = statement.dialect.Quoter().QuoteTo(&buf, col) fmt.Fprint(&buf, " ASC") } statement.OrderStr = buf.String() @@ -539,7 +539,7 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) aliasName = schemas.CommonQuoter.Trim(aliasName) - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), aliasName, statement.ReplaceQuote(condition)) + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) statement.joinArgs = append(statement.joinArgs, subQueryArgs...) case *builder.Builder: subSQL, subQueryArgs, err := tp.ToSQL() @@ -552,14 +552,16 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition aliasName := statement.dialect.Quoter().Trim(fields[len(fields)-1]) aliasName = schemas.CommonQuoter.Trim(aliasName) - fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), aliasName, statement.ReplaceQuote(condition)) + fmt.Fprintf(&buf, "(%s) %s ON %v", statement.ReplaceQuote(subSQL), statement.quote(aliasName), statement.ReplaceQuote(condition)) statement.joinArgs = append(statement.joinArgs, subQueryArgs...) default: tbName := dialects.FullTableName(statement.dialect, statement.tagParser.GetTableMapper(), tablename, true) if !utils.IsSubQuery(tbName) { var buf strings.Builder - statement.dialect.Quoter().QuoteTo(&buf, tbName) + _ = statement.dialect.Quoter().QuoteTo(&buf, tbName) tbName = buf.String() + } else { + tbName = statement.ReplaceQuote(tbName) } fmt.Fprintf(&buf, "%s ON %v", tbName, statement.ReplaceQuote(condition)) } @@ -569,15 +571,6 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition return statement } -// tbNameNoSchema get some table's table name -func (statement *Statement) tbNameNoSchema(table *schemas.Table) string { - if len(statement.AltTableName) > 0 { - return statement.AltTableName - } - - return table.Name -} - // GroupBy generate "Group By keys" statement func (statement *Statement) GroupBy(keys string) *Statement { statement.GroupByStr = statement.ReplaceQuote(keys) @@ -642,14 +635,6 @@ func (statement *Statement) genColumnStr() string { return buf.String() } -// GenCreateTableSQL generated create table SQL -func (statement *Statement) GenCreateTableSQL() []string { - statement.RefTable.StoreEngine = statement.StoreEngine - statement.RefTable.Charset = statement.Charset - s, _ := statement.dialect.CreateTableSQL(statement.RefTable, statement.TableName()) - return s -} - // GenIndexSQL generated create index SQL func (statement *Statement) GenIndexSQL() []string { var sqls []string @@ -863,9 +848,6 @@ func (statement *Statement) buildConds2(table *schemas.Table, bean interface{}, fieldValuePtr, err := col.ValueOf(bean) if err != nil { - if !strings.Contains(err.Error(), "is not valid") { - //engine.logger.Warn(err) - } continue } else if fieldValuePtr == nil { continue @@ -968,9 +950,9 @@ func (statement *Statement) convertSQLOrArgs(sqlOrArgs ...interface{}) (string, if len(sqlOrArgs) > 1 { var newArgs = make([]interface{}, 0, len(sqlOrArgs)-1) for _, arg := range sqlOrArgs[1:] { - if v, ok := arg.(*time.Time); ok { + if v, ok := arg.(time.Time); ok { newArgs = append(newArgs, v.In(statement.defaultTimeZone).Format("2006-01-02 15:04:05")) - } else if v, ok := arg.(time.Time); ok { + } else if v, ok := arg.(*time.Time); ok && v != nil { newArgs = append(newArgs, v.In(statement.defaultTimeZone).Format("2006-01-02 15:04:05")) } else { newArgs = append(newArgs, arg) diff --git a/internal/statements/statement_test.go b/internal/statements/statement_test.go index ba92330e..31428efa 100644 --- a/internal/statements/statement_test.go +++ b/internal/statements/statement_test.go @@ -5,6 +5,7 @@ package statements import ( + "os" "reflect" "strings" "testing" @@ -37,6 +38,7 @@ func TestMain(m *testing.M) { panic("tags parser is nil") } m.Run() + os.Exit(0) } var colStrTests = []struct { @@ -77,6 +79,23 @@ func TestColumnsStringGeneration(t *testing.T) { } } +func TestConvertSQLOrArgs(t *testing.T) { + statement, err := createTestStatement() + assert.NoError(t, err) + + // example orm struct + // type Table struct { + // ID int + // del *time.Time `xorm:"deleted"` + // } + args := []interface{}{ + "INSERT `table` (`id`, `del`) VALUES (?, ?)", 1, (*time.Time)(nil), + } + // before fix, here will panic + _, _, err = statement.convertSQLOrArgs(args...) + assert.NoError(t, err) +} + func BenchmarkGetFlagForColumnWithICKey_ContainsKey(b *testing.B) { b.StopTimer() diff --git a/internal/utils/name.go b/internal/utils/name.go index 840dd9e8..aeef683d 100644 --- a/internal/utils/name.go +++ b/internal/utils/name.go @@ -6,9 +6,15 @@ package utils import ( "fmt" + "strings" ) // IndexName returns index name func IndexName(tableName, idxName string) string { return fmt.Sprintf("IDX_%v_%v", tableName, idxName) } + +// SeqName returns sequence name for some table +func SeqName(tableName string) string { + return "SEQ_" + strings.ToUpper(tableName) +} diff --git a/internal/utils/slice.go b/internal/utils/slice.go index 89685706..82289b1a 100644 --- a/internal/utils/slice.go +++ b/internal/utils/slice.go @@ -11,8 +11,8 @@ func SliceEq(left, right []string) bool { if len(left) != len(right) { return false } - sort.Sort(sort.StringSlice(left)) - sort.Sort(sort.StringSlice(right)) + sort.Strings(left) + sort.Strings(right) for i := 0; i < len(left); i++ { if left[i] != right[i] { return false @@ -20,3 +20,13 @@ func SliceEq(left, right []string) bool { } return true } + +// IndexSlice search c in slice s and return the index, return -1 if s don't contain c +func IndexSlice(s []string, c string) int { + for i, ss := range s { + if c == ss { + return i + } + } + return -1 +} diff --git a/log/logger.go b/log/logger.go index 3b6db34e..b8798c3f 100644 --- a/log/logger.go +++ b/log/logger.go @@ -130,56 +130,56 @@ func NewSimpleLogger3(out io.Writer, prefix string, flag int, l LogLevel) *Simpl // Error implement ILogger func (s *SimpleLogger) Error(v ...interface{}) { if s.level <= LOG_ERR { - s.ERR.Output(2, fmt.Sprintln(v...)) + _ = s.ERR.Output(2, fmt.Sprintln(v...)) } } // Errorf implement ILogger func (s *SimpleLogger) Errorf(format string, v ...interface{}) { if s.level <= LOG_ERR { - s.ERR.Output(2, fmt.Sprintf(format, v...)) + _ = s.ERR.Output(2, fmt.Sprintf(format, v...)) } } // Debug implement ILogger func (s *SimpleLogger) Debug(v ...interface{}) { if s.level <= LOG_DEBUG { - s.DEBUG.Output(2, fmt.Sprintln(v...)) + _ = s.DEBUG.Output(2, fmt.Sprintln(v...)) } } // Debugf implement ILogger func (s *SimpleLogger) Debugf(format string, v ...interface{}) { if s.level <= LOG_DEBUG { - s.DEBUG.Output(2, fmt.Sprintf(format, v...)) + _ = s.DEBUG.Output(2, fmt.Sprintf(format, v...)) } } // Info implement ILogger func (s *SimpleLogger) Info(v ...interface{}) { if s.level <= LOG_INFO { - s.INFO.Output(2, fmt.Sprintln(v...)) + _ = s.INFO.Output(2, fmt.Sprintln(v...)) } } // Infof implement ILogger func (s *SimpleLogger) Infof(format string, v ...interface{}) { if s.level <= LOG_INFO { - s.INFO.Output(2, fmt.Sprintf(format, v...)) + _ = s.INFO.Output(2, fmt.Sprintf(format, v...)) } } // Warn implement ILogger func (s *SimpleLogger) Warn(v ...interface{}) { if s.level <= LOG_WARNING { - s.WARN.Output(2, fmt.Sprintln(v...)) + _ = s.WARN.Output(2, fmt.Sprintln(v...)) } } // Warnf implement ILogger func (s *SimpleLogger) Warnf(format string, v ...interface{}) { if s.level <= LOG_WARNING { - s.WARN.Output(2, fmt.Sprintf(format, v...)) + _ = s.WARN.Output(2, fmt.Sprintf(format, v...)) } } diff --git a/log/syslogger.go b/log/syslogger.go index 0b3e381c..44272586 100644 --- a/log/syslogger.go +++ b/log/syslogger.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !windows && !nacl && !plan9 // +build !windows,!nacl,!plan9 package log @@ -26,42 +27,42 @@ func NewSyslogLogger(w *syslog.Writer) *SyslogLogger { // Debug log content as Debug func (s *SyslogLogger) Debug(v ...interface{}) { - s.w.Debug(fmt.Sprint(v...)) + _ = s.w.Debug(fmt.Sprint(v...)) } // Debugf log content as Debug and format func (s *SyslogLogger) Debugf(format string, v ...interface{}) { - s.w.Debug(fmt.Sprintf(format, v...)) + _ = s.w.Debug(fmt.Sprintf(format, v...)) } // Error log content as Error func (s *SyslogLogger) Error(v ...interface{}) { - s.w.Err(fmt.Sprint(v...)) + _ = s.w.Err(fmt.Sprint(v...)) } // Errorf log content as Errorf and format func (s *SyslogLogger) Errorf(format string, v ...interface{}) { - s.w.Err(fmt.Sprintf(format, v...)) + _ = s.w.Err(fmt.Sprintf(format, v...)) } // Info log content as Info func (s *SyslogLogger) Info(v ...interface{}) { - s.w.Info(fmt.Sprint(v...)) + _ = s.w.Info(fmt.Sprint(v...)) } // Infof log content as Infof and format func (s *SyslogLogger) Infof(format string, v ...interface{}) { - s.w.Info(fmt.Sprintf(format, v...)) + _ = s.w.Info(fmt.Sprintf(format, v...)) } // Warn log content as Warn func (s *SyslogLogger) Warn(v ...interface{}) { - s.w.Warning(fmt.Sprint(v...)) + _ = s.w.Warning(fmt.Sprint(v...)) } // Warnf log content as Warnf and format func (s *SyslogLogger) Warnf(format string, v ...interface{}) { - s.w.Warning(fmt.Sprintf(format, v...)) + _ = s.w.Warning(fmt.Sprintf(format, v...)) } // Level shows log level diff --git a/migrate/migrate.go b/migrate/migrate.go index 19b4afe0..5c259627 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -203,7 +203,7 @@ func (m *Migrate) migrationDidRun(mig *Migration) (bool, error) { func (m *Migrate) isFirstRun() bool { row := m.db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", m.options.TableName)) var count int - row.Scan(&count) + _ = row.Scan(&count) return count == 0 } diff --git a/migrate/migrate_test.go b/migrate/migrate_test.go index 3d7aa189..750afb28 100644 --- a/migrate/migrate_test.go +++ b/migrate/migrate_test.go @@ -31,7 +31,7 @@ var ( { ID: "201608301400", Migrate: func(tx *xorm.Engine) error { - return tx.Sync2(&Person{}) + return tx.Sync(&Person{}) }, Rollback: func(tx *xorm.Engine) error { return tx.DropTables(&Person{}) @@ -40,7 +40,7 @@ var ( { ID: "201608301430", Migrate: func(tx *xorm.Engine) error { - return tx.Sync2(&Pet{}) + return tx.Sync(&Pet{}) }, Rollback: func(tx *xorm.Engine) error { return tx.DropTables(&Pet{}) @@ -103,10 +103,10 @@ func TestInitSchema(t *testing.T) { m := New(db, DefaultOptions, migrations) m.InitSchema(func(tx *xorm.Engine) error { - if err := tx.Sync2(&Person{}); err != nil { + if err := tx.Sync(&Person{}); err != nil { return err } - return tx.Sync2(&Pet{}) + return tx.Sync(&Pet{}) }) err = m.Migrate() @@ -142,6 +142,6 @@ func TestMissingID(t *testing.T) { func tableCount(db *xorm.Engine, tableName string) (count int) { row := db.DB().QueryRow(fmt.Sprintf("SELECT COUNT(*) FROM %s", tableName)) - row.Scan(&count) + _ = row.Scan(&count) return } diff --git a/names/table_name.go b/names/table_name.go index cc0e9274..d7d71b51 100644 --- a/names/table_name.go +++ b/names/table_name.go @@ -14,9 +14,15 @@ type TableName interface { TableName() string } +type TableComment interface { + TableComment() string +} + var ( - tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() - tvCache sync.Map + tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() + tpTableComment = reflect.TypeOf((*TableComment)(nil)).Elem() + tvCache sync.Map + tcCache sync.Map ) // GetTableName returns table name @@ -55,3 +61,40 @@ func GetTableName(mapper Mapper, v reflect.Value) string { return mapper.Obj2Table(v.Type().Name()) } + +// GetTableComment returns table comment +func GetTableComment(v reflect.Value) string { + if v.Type().Implements(tpTableComment) { + return v.Interface().(TableComment).TableComment() + } + + if v.Kind() == reflect.Ptr { + v = v.Elem() + if v.Type().Implements(tpTableComment) { + return v.Interface().(TableComment).TableComment() + } + } else if v.CanAddr() { + v1 := v.Addr() + if v1.Type().Implements(tpTableComment) { + return v1.Interface().(TableComment).TableComment() + } + } else { + comment, ok := tcCache.Load(v.Type()) + if ok { + if comment.(string) != "" { + return comment.(string) + } + } else { + v2 := reflect.New(v.Type()) + if v2.Type().Implements(tpTableComment) { + tableComment := v2.Interface().(TableComment).TableComment() + tcCache.Store(v.Type(), tableComment) + return tableComment + } + + tcCache.Store(v.Type(), "") + } + } + + return "" +} diff --git a/rows.go b/rows.go index 8e7cc075..4801c300 100644 --- a/rows.go +++ b/rows.go @@ -11,7 +11,6 @@ import ( "xorm.io/builder" "xorm.io/xorm/core" - "xorm.io/xorm/internal/utils" ) // Rows rows wrapper a rows to @@ -41,7 +40,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { return nil, err } - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return nil, ErrTableNotFound } @@ -84,26 +83,41 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { // Next move cursor to next record, return false if end has reached func (rows *Rows) Next() bool { - return rows.rows.Next() + if rows.rows != nil { + return rows.rows.Next() + } + return false } // Err returns the error, if any, that was encountered during iteration. Err may be called after an explicit or implicit Close. func (rows *Rows) Err() error { - return rows.rows.Err() + if rows.rows != nil { + return rows.rows.Err() + } + return nil } // Scan row record to bean properties -func (rows *Rows) Scan(bean interface{}) error { +func (rows *Rows) Scan(beans ...interface{}) error { if rows.Err() != nil { return rows.Err() } - if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { - return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) + var bean = beans[0] + var tp = reflect.TypeOf(bean) + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() } + var beanKind = tp.Kind() - if err := rows.session.statement.SetRefBean(bean); err != nil { - return err + if len(beans) == 1 { + if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { + return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) + } + + if err := rows.session.statement.SetRefBean(bean); err != nil { + return err + } } fields, err := rows.rows.Columns() @@ -115,14 +129,7 @@ func (rows *Rows) Scan(bean interface{}) error { return err } - scanResults, err := rows.session.row2Slice(rows.rows, fields, types, bean) - if err != nil { - return err - } - - dataStruct := utils.ReflectValue(bean) - _, err = rows.session.slice2Bean(scanResults, fields, bean, &dataStruct, rows.session.statement.RefTable) - if err != nil { + if err := rows.session.scan(rows.rows, rows.session.statement.RefTable, beanKind, beans, types, fields); err != nil { return err } @@ -139,5 +146,5 @@ func (rows *Rows) Close() error { return rows.rows.Close() } - return rows.Err() + return nil } diff --git a/scan.go b/scan.go index 56d3c9d6..10988bdb 100644 --- a/scan.go +++ b/scan.go @@ -129,57 +129,6 @@ func genScanResultsByBean(bean interface{}) (interface{}, bool, error) { } } -func (engine *Engine) row2mapStr(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]string, error) { - var scanResults = make([]interface{}, len(fields)) - for i := 0; i < len(fields); i++ { - var s sql.NullString - scanResults[i] = &s - } - - if err := rows.Scan(scanResults...); err != nil { - return nil, err - } - - result := make(map[string]string, len(fields)) - for i, key := range fields { - s := scanResults[i].(*sql.NullString) - if s.String == "" { - result[key] = "" - continue - } - - if schemas.TIME_TYPE == engine.dialect.ColumnTypeKind(types[i].DatabaseTypeName()) { - t, err := convert.String2Time(s.String, engine.DatabaseTZ, engine.TZLocation) - if err != nil { - return nil, err - } - result[key] = t.Format("2006-01-02 15:04:05") - } else { - result[key] = s.String - } - } - return result, nil -} - -func row2mapBytes(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string][]byte, error) { - var scanResults = make([]interface{}, len(fields)) - for i := 0; i < len(fields); i++ { - var s sql.NullString - scanResults[i] = &s - } - - if err := rows.Scan(scanResults...); err != nil { - return nil, err - } - - result := make(map[string][]byte, len(fields)) - for ii, key := range fields { - s := scanResults[ii].(*sql.NullString) - result[key] = []byte(s.String) - } - return result, nil -} - func (engine *Engine) scanStringInterface(rows *core.Rows, fields []string, types []*sql.ColumnType) ([]interface{}, error) { var scanResults = make([]interface{}, len(types)) for i := 0; i < len(types); i++ { @@ -259,41 +208,8 @@ func (engine *Engine) scanInterfaces(rows *core.Rows, fields []string, types []* return scanResultContainers, nil } -func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fields []string) ([]string, error) { - scanResults, err := engine.scanStringInterface(rows, fields, types) - if err != nil { - return nil, err - } - - var results = make([]string, 0, len(fields)) - for i := 0; i < len(fields); i++ { - results = append(results, scanResults[i].(*sql.NullString).String) - } - return results, nil -} - -func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) { - fields, err := rows.Columns() - if err != nil { - return nil, err - } - types, err := rows.ColumnTypes() - if err != nil { - return nil, err - } - for rows.Next() { - result, err := row2mapBytes(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} +//////////////////// +// row -> map[string]interface{} func (engine *Engine) row2mapInterface(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]interface{}, error) { var resultsMap = make(map[string]interface{}, len(fields)) @@ -318,3 +234,206 @@ func (engine *Engine) row2mapInterface(rows *core.Rows, types []*sql.ColumnType, } return resultsMap, nil } + +// ScanInterfaceMap scan result from *core.Rows and return a map +func (engine *Engine) ScanInterfaceMap(rows *core.Rows) (map[string]interface{}, error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + + return engine.row2mapInterface(rows, types, fields) +} + +// ScanInterfaceMaps scan results from *core.Rows and return a slice of map +func (engine *Engine) ScanInterfaceMaps(rows *core.Rows) (resultsSlice []map[string]interface{}, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + for rows.Next() { + result, err := engine.row2mapInterface(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, result) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} + +//////////////////// +// row -> map[string]string + +func (engine *Engine) row2mapStr(rows *core.Rows, types []*sql.ColumnType, fields []string) (map[string]string, error) { + var scanResults = make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var s sql.NullString + scanResults[i] = &s + } + + if err := engine.driver.Scan(&dialects.ScanContext{ + DBLocation: engine.DatabaseTZ, + UserLocation: engine.TZLocation, + }, rows, types, scanResults...); err != nil { + return nil, err + } + + result := make(map[string]string, len(fields)) + for i, key := range fields { + s := scanResults[i].(*sql.NullString) + if s.String == "" { + result[key] = "" + continue + } + + if schemas.TIME_TYPE == engine.dialect.ColumnTypeKind(types[i].DatabaseTypeName()) { + t, err := convert.String2Time(s.String, engine.DatabaseTZ, engine.TZLocation) + if err != nil { + return nil, err + } + result[key] = t.Format("2006-01-02 15:04:05") + } else { + result[key] = s.String + } + } + return result, nil +} + +// ScanStringMap scan results from *core.Rows and return a map +func (engine *Engine) ScanStringMap(rows *core.Rows) (map[string]string, error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + return engine.row2mapStr(rows, types, fields) +} + +// ScanStringMaps scan results from *core.Rows and return a slice of map +func (engine *Engine) ScanStringMaps(rows *core.Rows) (resultsSlice []map[string]string, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + + for rows.Next() { + result, err := engine.row2mapStr(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, result) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} + +//////////////////// +// row -> map[string][]byte + +func convertMapStr2Bytes(m map[string]string) map[string][]byte { + var r = make(map[string][]byte, len(m)) + for k, v := range m { + r[k] = []byte(v) + } + return r +} + +func (engine *Engine) scanByteMaps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + for rows.Next() { + result, err := engine.row2mapStr(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, convertMapStr2Bytes(result)) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} + +//////////////////// +// row -> []string + +func (engine *Engine) row2sliceStr(rows *core.Rows, types []*sql.ColumnType, fields []string) ([]string, error) { + scanResults, err := engine.scanStringInterface(rows, fields, types) + if err != nil { + return nil, err + } + + var results = make([]string, 0, len(fields)) + for i := 0; i < len(fields); i++ { + results = append(results, scanResults[i].(*sql.NullString).String) + } + return results, nil +} + +// ScanStringSlice scan results from *core.Rows and return a slice of one row +func (engine *Engine) ScanStringSlice(rows *core.Rows) ([]string, error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + + return engine.row2sliceStr(rows, types, fields) +} + +// ScanStringSlices scan results from *core.Rows and return a slice of all rows +func (engine *Engine) ScanStringSlices(rows *core.Rows) (resultsSlice [][]string, err error) { + fields, err := rows.Columns() + if err != nil { + return nil, err + } + types, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + + for rows.Next() { + record, err := engine.row2sliceStr(rows, types, fields) + if err != nil { + return nil, err + } + resultsSlice = append(resultsSlice, record) + } + if rows.Err() != nil { + return nil, rows.Err() + } + + return resultsSlice, nil +} diff --git a/schemas/index.go b/schemas/index.go index 8f31af52..47027ea4 100644 --- a/schemas/index.go +++ b/schemas/index.go @@ -32,7 +32,7 @@ func NewIndex(name string, indexType int) *Index { func (index *Index) XName(tableName string) string { if !strings.HasPrefix(index.Name, "UQE_") && !strings.HasPrefix(index.Name, "IDX_") { - tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".") + tableParts := strings.Split(strings.ReplaceAll(tableName, `"`, ""), ".") tableName = tableParts[len(tableParts)-1] if index.Type == UniqueType { return fmt.Sprintf("UQE_%v_%v", tableName, index.Name) diff --git a/schemas/quote.go b/schemas/quote.go index 71040ad9..4cab30fe 100644 --- a/schemas/quote.go +++ b/schemas/quote.go @@ -37,7 +37,7 @@ func (q Quoter) IsEmpty() bool { // Quote quote a string func (q Quoter) Quote(s string) string { var buf strings.Builder - q.QuoteTo(&buf, s) + _ = q.QuoteTo(&buf, s) return buf.String() } @@ -64,7 +64,7 @@ func (q Quoter) Trim(s string) string { // Join joins a slice with quoters func (q Quoter) Join(a []string, sep string) string { var b strings.Builder - q.JoinWrite(&b, a, sep) + _ = q.JoinWrite(&b, a, sep) return b.String() } @@ -86,7 +86,9 @@ func (q Quoter) JoinWrite(b *strings.Builder, a []string, sep string) error { return err } } - q.QuoteTo(b, strings.TrimSpace(s)) + if err := q.QuoteTo(b, strings.TrimSpace(s)); err != nil { + return err + } } return nil } @@ -121,7 +123,7 @@ func findStart(value string, start int) int { } if (value[k] == 'A' || value[k] == 'a') && (value[k+1] == 'S' || value[k+1] == 's') { - k = k + 2 + k += 2 } for j := k; j < len(value); j++ { diff --git a/schemas/quote_test.go b/schemas/quote_test.go index 8e351dc0..f84dfb7d 100644 --- a/schemas/quote_test.go +++ b/schemas/quote_test.go @@ -45,7 +45,8 @@ func TestAlwaysQuoteTo(t *testing.T) { for _, v := range kases { t.Run(v.value, func(t *testing.T) { buf := &strings.Builder{} - quoter.QuoteTo(buf, v.value) + err := quoter.QuoteTo(buf, v.value) + assert.NoError(t, err) assert.EqualValues(t, v.expected, buf.String()) }) } @@ -54,10 +55,7 @@ func TestAlwaysQuoteTo(t *testing.T) { func TestReversedQuoteTo(t *testing.T) { var ( quoter = Quoter{'[', ']', func(s string) bool { - if s == "mytable" { - return true - } - return false + return s == "mytable" }} kases = []struct { expected string @@ -118,7 +116,8 @@ func TestNoQuoteTo(t *testing.T) { for _, v := range kases { t.Run(v.value, func(t *testing.T) { buf := &strings.Builder{} - quoter.QuoteTo(buf, v.value) + err := quoter.QuoteTo(buf, v.value) + assert.NoError(t, err) assert.EqualValues(t, v.expected, buf.String()) }) } @@ -176,6 +175,10 @@ func TestReplace(t *testing.T) { "UPDATE table SET `a` = ~ `a`, `b`='abc`'", "UPDATE table SET [a] = ~ [a], [b]='abc`'", }, + { + "INSERT INTO `insert_where` (`height`,`name`,`repo_id`,`width`,`index`) SELECT $1,$2,$3,$4,coalesce(MAX(`index`),0)+1 FROM `insert_where` WHERE (`repo_id`=$5)", + "INSERT INTO [insert_where] ([height],[name],[repo_id],[width],[index]) SELECT $1,$2,$3,$4,coalesce(MAX([index]),0)+1 FROM [insert_where] WHERE ([repo_id]=$5)", + }, } for _, kase := range kases { diff --git a/schemas/type.go b/schemas/type.go index cf730134..8702862a 100644 --- a/schemas/type.go +++ b/schemas/type.go @@ -22,6 +22,7 @@ const ( MYSQL DBType = "mysql" MSSQL DBType = "mssql" ORACLE DBType = "oracle" + DAMENG DBType = "dameng" ) // SQLType represents SQL types @@ -105,12 +106,14 @@ var ( Integer = "INTEGER" BigInt = "BIGINT" UnsignedBigInt = "UNSIGNED BIGINT" + Number = "NUMBER" Enum = "ENUM" Set = "SET" Char = "CHAR" Varchar = "VARCHAR" + VARCHAR2 = "VARCHAR2" NChar = "NCHAR" NVarchar = "NVARCHAR" TinyText = "TINYTEXT" @@ -174,6 +177,7 @@ var ( Integer: NUMERIC_TYPE, BigInt: NUMERIC_TYPE, UnsignedBigInt: NUMERIC_TYPE, + Number: NUMERIC_TYPE, Enum: TEXT_TYPE, Set: TEXT_TYPE, @@ -185,6 +189,7 @@ var ( Char: TEXT_TYPE, NChar: TEXT_TYPE, Varchar: TEXT_TYPE, + VARCHAR2: TEXT_TYPE, NVarchar: TEXT_TYPE, TinyText: TEXT_TYPE, Text: TEXT_TYPE, @@ -327,6 +332,10 @@ func SQLType2Type(st SQLType) reflect.Type { return IntType case BigInt, BigSerial: return Int64Type + case UnsignedBit, UnsignedTinyInt, UnsignedSmallInt, UnsignedMediumInt, UnsignedInt: + return UintType + case UnsignedBigInt: + return Uint64Type case Float, Real: return Float32Type case Double: diff --git a/session.go b/session.go index 563dfaf2..21bbe6e1 100644 --- a/session.go +++ b/session.go @@ -79,7 +79,8 @@ type Session struct { afterClosures []func(interface{}) afterProcessors []executedProcessor - stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) + stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) + txStmtCache map[uint32]*core.Stmt // for tx statement lastSQL string lastSQLArgs []interface{} @@ -123,13 +124,14 @@ func newSession(engine *Engine) *Session { autoResetStatement: true, prepareStmt: false, - afterInsertBeans: make(map[interface{}]*[]func(interface{}), 0), - afterUpdateBeans: make(map[interface{}]*[]func(interface{}), 0), - afterDeleteBeans: make(map[interface{}]*[]func(interface{}), 0), + afterInsertBeans: make(map[interface{}]*[]func(interface{})), + afterUpdateBeans: make(map[interface{}]*[]func(interface{})), + afterDeleteBeans: make(map[interface{}]*[]func(interface{})), beforeClosures: make([]func(interface{}), 0), afterClosures: make([]func(interface{}), 0), afterProcessors: make([]executedProcessor, 0), stmtCache: make(map[uint32]*core.Stmt), + txStmtCache: make(map[uint32]*core.Stmt), lastSQL: "", lastSQLArgs: make([]interface{}, 0), @@ -150,6 +152,12 @@ func (session *Session) Close() error { } } + for _, v := range session.txStmtCache { + if err := v.Close(); err != nil { + return err + } + } + if !session.isClosed { // When Close be called, if session is a transaction and do not call // Commit or Rollback, then call Rollback. @@ -160,6 +168,7 @@ func (session *Session) Close() error { } session.tx = nil session.stmtCache = nil + session.txStmtCache = nil session.isClosed = true } return nil @@ -174,6 +183,11 @@ func (session *Session) Engine() *Engine { return session.engine } +// Tx returns session tx +func (session *Session) Tx() *core.Tx { + return session.tx +} + func (session *Session) getQueryer() core.Queryer { if session.tx != nil { return session.tx @@ -195,6 +209,7 @@ func (session *Session) IsClosed() bool { func (session *Session) resetStatement() { if session.autoResetStatement { session.statement.Reset() + session.prepareStmt = false } } @@ -365,7 +380,22 @@ func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, return } -func (session *Session) getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) { +func (session *Session) doPrepareTx(sqlStr string) (stmt *core.Stmt, err error) { + crc := crc32.ChecksumIEEE([]byte(sqlStr)) + // TODO try hash(sqlStr+len(sqlStr)) + var has bool + stmt, has = session.txStmtCache[crc] + if !has { + stmt, err = session.tx.PrepareContext(session.ctx, sqlStr) + if err != nil { + return nil, err + } + session.txStmtCache[crc] = stmt + } + return +} + +func getField(dataStruct *reflect.Value, table *schemas.Table, colName string, idx int) (*schemas.Column, *reflect.Value, error) { var col = table.GetColumnIdx(colName, idx) if col == nil { return nil, nil, ErrFieldIsNotExist{colName, table.Name} @@ -435,7 +465,7 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, types []*sql return scanResults, nil } -func (session *Session) setJSON(fieldValue *reflect.Value, fieldType reflect.Type, scanResult interface{}) error { +func setJSON(fieldValue *reflect.Value, fieldType reflect.Type, scanResult interface{}) error { bs, ok := convert.AsBytes(scanResult) if !ok { return fmt.Errorf("unsupported database data type: %#v", scanResult) @@ -519,6 +549,9 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec if !ok { return fmt.Errorf("cannot convert %#v as bytes", scanResult) } + if data == nil { + return nil + } return structConvert.FromDB(data) } } @@ -543,7 +576,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec fieldType := fieldValue.Type() if col.IsJSON { - return session.setJSON(fieldValue, fieldType, scanResult) + return setJSON(fieldValue, fieldType, scanResult) } switch fieldType.Kind() { @@ -562,7 +595,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } return nil case reflect.Complex64, reflect.Complex128: - return session.setJSON(fieldValue, fieldType, scanResult) + return setJSON(fieldValue, fieldType, scanResult) case reflect.Slice, reflect.Array: bs, ok := convert.AsBytes(scanResult) if ok && fieldType.Elem().Kind() == reflect.Uint8 { @@ -671,18 +704,17 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b if idx, ok = tempMap[lKey]; !ok { idx = 0 } else { - idx = idx + 1 + idx++ } tempMap[lKey] = idx - col, fieldValue, err := session.getField(dataStruct, table, colName, idx) - if err != nil { - if _, ok := err.(ErrFieldIsNotExist); ok { - continue - } else { - return nil, err - } + col, fieldValue, err := getField(dataStruct, table, colName, idx) + if _, ok := err.(ErrFieldIsNotExist); ok { + continue + } else if err != nil { + return nil, err } + if fieldValue == nil { continue } @@ -725,6 +757,12 @@ func (session *Session) incrVersionFieldValue(fieldValue *reflect.Value) { // Context sets the context on this session func (session *Session) Context(ctx context.Context) *Session { + if session.ctx != nil { + ctx = context.WithValue(ctx, log.SessionIDKey, session.ctx.Value(log.SessionIDKey)) + ctx = context.WithValue(ctx, log.SessionKey, session.ctx.Value(log.SessionKey)) + ctx = context.WithValue(ctx, log.SessionShowSQLKey, session.ctx.Value(log.SessionShowSQLKey)) + } + session.ctx = ctx return session } diff --git a/session_delete.go b/session_delete.go index 37b9c1cd..a0f420b1 100644 --- a/session_delete.go +++ b/session_delete.go @@ -40,7 +40,13 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri pkColumns := table.PKColumns() ids, err := caches.GetCacheSql(cacher, tableName, newsql, args) if err != nil { - resultsSlice, err := session.queryBytes(newsql, args...) + rows, err := session.queryRows(newsql, args...) + if err != nil { + return err + } + defer rows.Close() + + resultsSlice, err := session.engine.ScanStringMaps(rows) if err != nil { return err } @@ -53,9 +59,9 @@ func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr stri if v, ok := data[col.Name]; !ok { return errors.New("no id") } else if col.SQLType.IsText() { - pk = append(pk, string(v)) + pk = append(pk, v) } else if col.SQLType.IsNumeric() { - id, err = strconv.ParseInt(string(v), 10, 64) + id, err = strconv.ParseInt(v, 10, 64) if err != nil { return err } @@ -226,7 +232,7 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { } if cacher := session.engine.GetCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache { - session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) + _ = session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) } session.statement.RefTable = table diff --git a/session_find.go b/session_find.go index 47a3d308..caf79ee3 100644 --- a/session_find.go +++ b/session_find.go @@ -57,7 +57,7 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte if session.statement.SelectStr != "" { session.statement.SelectStr = "" } - if len(session.statement.ColumnMap) > 0 { + if len(session.statement.ColumnMap) > 0 && !session.statement.IsDistinct { session.statement.ColumnMap = []string{} } if session.statement.OrderStr != "" { @@ -71,7 +71,11 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte } // session has stored the conditions so we use `unscoped` to avoid duplicated condition. - return session.Unscoped().Count(reflect.New(sliceElementType).Interface()) + if sliceElementType.Kind() == reflect.Struct { + return session.Unscoped().Count(reflect.New(sliceElementType).Interface()) + } + + return session.Unscoped().Count() } func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { @@ -152,7 +156,6 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) if err != ErrCacheFailed { return err } - err = nil // !nashtsai! reset err to nil for ErrCacheFailed session.engine.logger.Warnf("Cache Find Failed") } } @@ -251,9 +254,9 @@ func (session *Session) noCacheFind(table *schemas.Table, containerValue reflect switch elemType.Kind() { case reflect.Slice: - err = rows.ScanSlice(bean) + err = session.getSlice(rows, types, fields, bean) case reflect.Map: - err = rows.ScanMap(bean) + err = session.getMap(rows, types, fields, bean) default: err = rows.Scan(bean) } diff --git a/session_get.go b/session_get.go index 08172524..9bb92a8b 100644 --- a/session_get.go +++ b/session_get.go @@ -6,7 +6,6 @@ package xorm import ( "database/sql" - "database/sql/driver" "errors" "fmt" "math/big" @@ -28,11 +27,11 @@ var ( // Get retrieve one record from database, bean's non-empty fields // will be as conditions -func (session *Session) Get(bean interface{}) (bool, error) { +func (session *Session) Get(beans ...interface{}) (bool, error) { if session.isAutoClose { defer session.Close() } - return session.get(bean) + return session.get(beans...) } func isPtrOfTime(v interface{}) bool { @@ -48,14 +47,17 @@ func isPtrOfTime(v interface{}) bool { return el.Type().ConvertibleTo(schemas.TimeType) } -func (session *Session) get(bean interface{}) (bool, error) { +func (session *Session) get(beans ...interface{}) (bool, error) { defer session.resetStatement() if session.statement.LastError != nil { return false, session.statement.LastError } + if len(beans) == 0 { + return false, errors.New("needs at least one parameter for get") + } - beanValue := reflect.ValueOf(bean) + beanValue := reflect.ValueOf(beans[0]) if beanValue.Kind() != reflect.Ptr { return false, errors.New("needs a pointer to a value") } else if beanValue.Elem().Kind() == reflect.Ptr { @@ -64,8 +66,9 @@ func (session *Session) get(bean interface{}) (bool, error) { return false, ErrObjectIsNil } - if beanValue.Elem().Kind() == reflect.Struct && !isPtrOfTime(bean) { - if err := session.statement.SetRefBean(bean); err != nil { + var isStruct = beanValue.Elem().Kind() == reflect.Struct && !isPtrOfTime(beans[0]) + if isStruct { + if err := session.statement.SetRefBean(beans[0]); err != nil { return false, err } } @@ -75,11 +78,11 @@ func (session *Session) get(bean interface{}) (bool, error) { var err error if session.statement.RawSQL == "" { - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return false, ErrTableNotFound } session.statement.Limit(1) - sqlStr, args, err = session.statement.GenGetSQL(bean) + sqlStr, args, err = session.statement.GenGetSQL(beans[0]) if err != nil { return false, err } @@ -90,10 +93,10 @@ func (session *Session) get(bean interface{}) (bool, error) { table := session.statement.RefTable - if session.statement.ColumnMap.IsEmpty() && session.canCache() && beanValue.Elem().Kind() == reflect.Struct { + if session.statement.ColumnMap.IsEmpty() && session.canCache() && isStruct { if cacher := session.engine.GetCacher(session.statement.TableName()); cacher != nil && !session.statement.GetUnscoped() { - has, err := session.cacheGet(bean, sqlStr, args...) + has, err := session.cacheGet(beans[0], sqlStr, args...) if err != ErrCacheFailed { return has, err } @@ -101,12 +104,12 @@ func (session *Session) get(bean interface{}) (bool, error) { } context := session.statement.Context - if context != nil { + if context != nil && isStruct { res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args)) if res != nil { session.engine.logger.Debugf("hit context cache: %s", sqlStr) - structValue := reflect.Indirect(reflect.ValueOf(bean)) + structValue := reflect.Indirect(reflect.ValueOf(beans[0])) structValue.Set(reflect.Indirect(reflect.ValueOf(res))) session.lastSQL = "" session.lastSQLArgs = nil @@ -114,29 +117,18 @@ func (session *Session) get(bean interface{}) (bool, error) { } } - has, err := session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...) + has, err := session.nocacheGet(beanValue.Elem().Kind(), table, beans, sqlStr, args...) if err != nil || !has { return has, err } - if context != nil { - context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean) + if context != nil && isStruct { + context.Put(fmt.Sprintf("%v-%v", sqlStr, args), beans[0]) } return true, nil } -var ( - valuerTypePlaceHolder driver.Valuer - valuerType = reflect.TypeOf(&valuerTypePlaceHolder).Elem() - - scannerTypePlaceHolder sql.Scanner - scannerType = reflect.TypeOf(&scannerTypePlaceHolder).Elem() - - conversionTypePlaceHolder convert.Conversion - conversionType = reflect.TypeOf(&conversionTypePlaceHolder).Elem() -) - func isScannableStruct(bean interface{}, typeLen int) bool { switch bean.(type) { case *time.Time: @@ -151,7 +143,7 @@ func isScannableStruct(bean interface{}, typeLen int) bool { return true } -func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) { +func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, beans []interface{}, sqlStr string, args ...interface{}) (bool, error) { rows, err := session.queryRows(sqlStr, args...) if err != nil { return false, err @@ -171,27 +163,51 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, if err != nil { return true, err } - switch beanKind { - case reflect.Struct: - if !isScannableStruct(bean, len(types)) { - break - } - return session.getStruct(rows, types, fields, table, bean) - case reflect.Slice: - return session.getSlice(rows, types, fields, bean) - case reflect.Map: - return session.getMap(rows, types, fields, bean) - } - return session.getVars(rows, types, fields, bean) + if err := session.scan(rows, table, beanKind, beans, types, fields); err != nil { + return true, err + } + rows.Close() + + return true, session.executeProcessors() } -func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) (bool, error) { +func (session *Session) scan(rows *core.Rows, table *schemas.Table, firstBeanKind reflect.Kind, beans []interface{}, types []*sql.ColumnType, fields []string) error { + if len(beans) == 1 { + bean := beans[0] + switch firstBeanKind { + case reflect.Struct: + if !isScannableStruct(bean, len(types)) { + break + } + scanResults, err := session.row2Slice(rows, fields, types, bean) + if err != nil { + return err + } + + dataStruct := utils.ReflectValue(bean) + _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table) + return err + case reflect.Slice: + return session.getSlice(rows, types, fields, bean) + case reflect.Map: + return session.getMap(rows, types, fields, bean) + } + } + + if len(beans) != len(types) { + return fmt.Errorf("expected columns %d, but only %d variables", len(types), len(beans)) + } + + return session.engine.scan(rows, fields, types, beans...) +} + +func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) error { switch t := bean.(type) { case *[]string: res, err := session.engine.scanStringInterface(rows, fields, types) if err != nil { - return true, err + return err } var needAppend = len(*t) == 0 // both support slice is empty or has been initlized @@ -202,17 +218,17 @@ func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, field (*t)[i] = r.(*sql.NullString).String } } - return true, nil + return nil case *[]interface{}: scanResults, err := session.engine.scanInterfaces(rows, fields, types) if err != nil { - return true, err + return err } var needAppend = len(*t) == 0 for ii := range fields { s, err := convert.Interface2Interface(session.engine.DatabaseTZ, scanResults[ii]) if err != nil { - return true, err + return err } if needAppend { *t = append(*t, s) @@ -220,67 +236,41 @@ func (session *Session) getSlice(rows *core.Rows, types []*sql.ColumnType, field (*t)[ii] = s } } - return true, nil + return nil default: - return true, fmt.Errorf("unspoorted slice type: %t", t) + return fmt.Errorf("unspoorted slice type: %t", t) } } -func (session *Session) getMap(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) (bool, error) { +func (session *Session) getMap(rows *core.Rows, types []*sql.ColumnType, fields []string, bean interface{}) error { switch t := bean.(type) { case *map[string]string: scanResults, err := session.engine.scanStringInterface(rows, fields, types) if err != nil { - return true, err + return err } for ii, key := range fields { (*t)[key] = scanResults[ii].(*sql.NullString).String } - return true, nil + return nil case *map[string]interface{}: scanResults, err := session.engine.scanInterfaces(rows, fields, types) if err != nil { - return true, err + return err } for ii, key := range fields { s, err := convert.Interface2Interface(session.engine.DatabaseTZ, scanResults[ii]) if err != nil { - return true, err + return err } (*t)[key] = s } - return true, nil + return nil default: - return true, fmt.Errorf("unspoorted map type: %t", t) + return fmt.Errorf("unspoorted map type: %t", t) } } -func (session *Session) getVars(rows *core.Rows, types []*sql.ColumnType, fields []string, beans ...interface{}) (bool, error) { - if len(beans) != len(types) { - return false, fmt.Errorf("expected columns %d, but only %d variables", len(types), len(beans)) - } - - err := session.engine.scan(rows, fields, types, beans...) - return true, err -} - -func (session *Session) getStruct(rows *core.Rows, types []*sql.ColumnType, fields []string, table *schemas.Table, bean interface{}) (bool, error) { - scanResults, err := session.row2Slice(rows, fields, types, bean) - if err != nil { - return false, err - } - // close it before convert data - rows.Close() - - dataStruct := utils.ReflectValue(bean) - _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table) - if err != nil { - return true, err - } - - return true, session.executeProcessors() -} - func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) { // if has no reftable, then don't use cache currently if !session.canCache() { @@ -357,7 +347,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf cacheBean := cacher.GetBean(tableName, sid) if cacheBean == nil { cacheBean = bean - has, err = session.nocacheGet(reflect.Struct, table, cacheBean, sqlStr, args...) + has, err = session.nocacheGet(reflect.Struct, table, []interface{}{cacheBean}, sqlStr, args...) if err != nil || !has { return has, err } diff --git a/session_insert.go b/session_insert.go index a8f365c7..fc025613 100644 --- a/session_insert.go +++ b/session_insert.go @@ -80,7 +80,7 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -90,7 +90,6 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e colNames []string colMultiPlaces []string args []interface{} - cols []*schemas.Column ) for i := 0; i < size; i++ { @@ -123,6 +122,12 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e } fieldValue := *ptrFieldValue if col.IsAutoIncrement && utils.IsZero(fieldValue.Interface()) { + if session.engine.dialect.Features().AutoincrMode == dialects.SequenceAutoincrMode { + if i == 0 { + colNames = append(colNames, col.Name) + } + colPlaces = append(colPlaces, utils.SeqName(tableName)+".nextval") + } continue } if col.MapType == schemas.ONLYFROMDB { @@ -137,6 +142,13 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e if len(session.statement.ColumnMap) > 0 && !session.statement.ColumnMap.Contain(col.Name) { continue } + // !satorunooshie! set fieldValue as nil when column is nullable and zero-value + if _, ok := getFlagForColumn(session.statement.NullableMap, col); ok { + if col.Nullable && utils.IsValueZero(fieldValue) { + var nilValue *int + fieldValue = reflect.ValueOf(nilValue) + } + } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { val, t, err := session.engine.nowTime(col) if err != nil { @@ -166,7 +178,6 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e if i == 0 { colNames = append(colNames, col.Name) - cols = append(cols, col) } colPlaces = append(colPlaces, "?") } @@ -197,7 +208,7 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e return 0, err } - session.cacheInsert(tableName) + _ = session.cacheInsert(tableName) lenAfterClosures := len(session.afterClosures) for i := 0; i < size; i++ { @@ -251,7 +262,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { if err := session.statement.SetRefBean(bean); err != nil { return 0, err } - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return 0, ErrTableNotFound } @@ -277,6 +288,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { if err != nil { return 0, err } + sqlStr = session.engine.dialect.Quoter().Replace(sqlStr) handleAfterInsertProcessorFunc := func(bean interface{}) { if session.isAutoCommit { @@ -307,20 +319,53 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { // if there is auto increment column and driver don't support return it if len(table.AutoIncrement) > 0 && !session.engine.driver.Features().SupportReturnInsertedID { - var sql = sqlStr - if session.engine.dialect.URI().DBType == schemas.ORACLE { - sql = "select seq_atable.currval from dual" + var sql string + var newArgs []interface{} + var needCommit bool + var id int64 + if session.engine.dialect.URI().DBType == schemas.ORACLE || session.engine.dialect.URI().DBType == schemas.DAMENG { + if session.isAutoCommit { // if it's not in transaction + if err := session.Begin(); err != nil { + return 0, err + } + needCommit = true + } + _, err := session.exec(sqlStr, args...) + if err != nil { + return 0, err + } + i := utils.IndexSlice(colNames, table.AutoIncrement) + if i > -1 { + id, err = convert.AsInt64(args[i]) + if err != nil { + return 0, err + } + } else { + sql = fmt.Sprintf("select %s.currval from dual", utils.SeqName(tableName)) + } + } else { + sql = sqlStr + newArgs = args } - rows, err := session.queryRows(sql, args...) - if err != nil { - return 0, err + if id == 0 { + err := session.queryRow(sql, newArgs...).Scan(&id) + if err != nil { + return 0, err + } + if needCommit { + if err := session.Commit(); err != nil { + return 0, err + } + } + if id == 0 { + return 0, errors.New("insert successfully but not returned id") + } } - defer rows.Close() defer handleAfterInsertProcessorFunc(bean) - session.cacheInsert(tableName) + _ = session.cacheInsert(tableName) if table.Version != "" && session.statement.CheckVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -331,16 +376,6 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { } } - var id int64 - if !rows.Next() { - if rows.Err() != nil { - return 0, rows.Err() - } - return 0, errors.New("insert successfully but not returned id") - } - if err := rows.Scan(&id); err != nil { - return 1, err - } aiValue, err := table.AutoIncrColumn().ValueOf(bean) if err != nil { session.engine.logger.Errorf("%v", err) @@ -360,7 +395,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { defer handleAfterInsertProcessorFunc(bean) - session.cacheInsert(tableName) + _ = session.cacheInsert(tableName) if table.Version != "" && session.statement.CheckVersion { verValue, err := table.VersionColumn().ValueOf(bean) @@ -400,6 +435,7 @@ func (session *Session) insertStruct(bean interface{}) (int64, error) { // InsertOne insert only one struct into database as a record. // The in parameter bean must a struct or a point to struct. The return // parameter is inserted and error +// Deprecated: Please use Insert directly func (session *Session) InsertOne(bean interface{}) (int64, error) { if session.isAutoClose { defer session.Close() @@ -507,7 +543,7 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -529,12 +565,12 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err } func (session *Session) insertMultipleMapInterface(maps []map[string]interface{}) (int64, error) { - if len(maps) <= 0 { + if len(maps) == 0 { return 0, ErrNoElementsOnSlice } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -565,7 +601,7 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -588,12 +624,12 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { } func (session *Session) insertMultipleMapString(maps []map[string]string) (int64, error) { - if len(maps) <= 0 { + if len(maps) == 0 { return 0, ErrNoElementsOnSlice } tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -620,7 +656,7 @@ func (session *Session) insertMultipleMapString(maps []map[string]string) (int64 func (session *Session) insertMap(columns []string, args []interface{}) (int64, error) { tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -628,6 +664,7 @@ func (session *Session) insertMap(columns []string, args []interface{}) (int64, if err != nil { return 0, err } + sql = session.engine.dialect.Quoter().Replace(sql) if err := session.cacheInsert(tableName); err != nil { return 0, err @@ -646,7 +683,7 @@ func (session *Session) insertMap(columns []string, args []interface{}) (int64, func (session *Session) insertMultipleMap(columns []string, argss [][]interface{}) (int64, error) { tableName := session.statement.TableName() - if len(tableName) <= 0 { + if len(tableName) == 0 { return 0, ErrTableNotFound } @@ -654,6 +691,7 @@ func (session *Session) insertMultipleMap(columns []string, argss [][]interface{ if err != nil { return 0, err } + sql = session.engine.dialect.Quoter().Replace(sql) if err := session.cacheInsert(tableName); err != nil { return 0, err diff --git a/session_iterate.go b/session_iterate.go index f6301009..afb9a7cc 100644 --- a/session_iterate.go +++ b/session_iterate.go @@ -95,7 +95,7 @@ func (session *Session) bufferIterate(bean interface{}, fun IterFunc) error { break } - start = start + slice.Elem().Len() + start += slice.Elem().Len() if pLimitN != nil && start+bufferSize > *pLimitN { bufferSize = *pLimitN - start } diff --git a/session_query.go b/session_query.go deleted file mode 100644 index a4070985..00000000 --- a/session_query.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "xorm.io/xorm/core" -) - -// Query runs a raw sql and return records as []map[string][]byte -func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - return session.queryBytes(sqlStr, args...) -} - -func (session *Session) rows2Strings(rows *core.Rows) (resultsSlice []map[string]string, err error) { - fields, err := rows.Columns() - if err != nil { - return nil, err - } - types, err := rows.ColumnTypes() - if err != nil { - return nil, err - } - - for rows.Next() { - result, err := session.engine.row2mapStr(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} - -func (session *Session) rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) { - fields, err := rows.Columns() - if err != nil { - return nil, err - } - types, err := rows.ColumnTypes() - if err != nil { - return nil, err - } - - for rows.Next() { - record, err := session.engine.row2sliceStr(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, record) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} - -// QueryString runs a raw sql and return records as []map[string]string -func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - rows, err := session.queryRows(sqlStr, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return session.rows2Strings(rows) -} - -// QuerySliceString runs a raw sql and return records as [][]string -func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - rows, err := session.queryRows(sqlStr, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return session.rows2SliceString(rows) -} - -func (session *Session) rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, err error) { - fields, err := rows.Columns() - if err != nil { - return nil, err - } - types, err := rows.ColumnTypes() - if err != nil { - return nil, err - } - for rows.Next() { - result, err := session.engine.row2mapInterface(rows, types, fields) - if err != nil { - return nil, err - } - resultsSlice = append(resultsSlice, result) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return resultsSlice, nil -} - -// QueryInterface runs a raw sql and return records as []map[string]interface{} -func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { - if session.isAutoClose { - defer session.Close() - } - - sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) - if err != nil { - return nil, err - } - - rows, err := session.queryRows(sqlStr, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return session.rows2Interfaces(rows) -} diff --git a/session_raw.go b/session_raw.go index 2b488988..add584d0 100644 --- a/session_raw.go +++ b/session_raw.go @@ -33,7 +33,7 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row if session.isAutoCommit { var db *core.DB - if session.sessionType == groupSession && strings.EqualFold(sqlStr[:6], "select") { + if session.sessionType == groupSession && strings.EqualFold(strings.TrimSpace(sqlStr)[:6], "select") && !session.statement.IsForUpdate { db = session.engine.engineGroup.Slave().DB() } else { db = session.DB() @@ -46,39 +46,106 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row return nil, err } - rows, err := stmt.QueryContext(session.ctx, args...) - if err != nil { - return nil, err - } - return rows, nil + return stmt.QueryContext(session.ctx, args...) } - rows, err := db.QueryContext(session.ctx, sqlStr, args...) + return db.QueryContext(session.ctx, sqlStr, args...) + } + + if session.prepareStmt { + stmt, err := session.doPrepareTx(sqlStr) if err != nil { return nil, err } - return rows, nil + + return stmt.QueryContext(session.ctx, args...) } - rows, err := session.tx.QueryContext(session.ctx, sqlStr, args...) - if err != nil { - return nil, err - } - return rows, nil + return session.tx.QueryContext(session.ctx, sqlStr, args...) } func (session *Session) queryRow(sqlStr string, args ...interface{}) *core.Row { return core.NewRow(session.queryRows(sqlStr, args...)) } -func (session *Session) queryBytes(sqlStr string, args ...interface{}) ([]map[string][]byte, error) { +// Query runs a raw sql and return records as []map[string][]byte +func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + rows, err := session.queryRows(sqlStr, args...) if err != nil { return nil, err } defer rows.Close() - return rows2maps(rows) + return session.engine.scanByteMaps(rows) +} + +// QueryString runs a raw sql and return records as []map[string]string +func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return session.engine.ScanStringMaps(rows) +} + +// QuerySliceString runs a raw sql and return records as [][]string +func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return session.engine.ScanStringSlices(rows) +} + +// QueryInterface runs a raw sql and return records as []map[string]interface{} +func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { + if session.isAutoClose { + defer session.Close() + } + + sqlStr, args, err := session.statement.GenQuerySQL(sqlOrArgs...) + if err != nil { + return nil, err + } + + rows, err := session.queryRows(sqlStr, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + return session.engine.ScanInterfaceMaps(rows) } func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { @@ -90,6 +157,13 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er session.lastSQLArgs = args if !session.isAutoCommit { + if session.prepareStmt { + stmt, err := session.doPrepareTx(sqlStr) + if err != nil { + return nil, err + } + return stmt.ExecContext(session.ctx, args...) + } return session.tx.ExecContext(session.ctx, sqlStr, args...) } @@ -98,12 +172,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er if err != nil { return nil, err } - - res, err := stmt.ExecContext(session.ctx, args...) - if err != nil { - return nil, err - } - return res, nil + return stmt.ExecContext(session.ctx, args...) } return session.DB().ExecContext(session.ctx, sqlStr, args...) diff --git a/session_schema.go b/session_schema.go index 2e64350f..e66c3b42 100644 --- a/session_schema.go +++ b/session_schema.go @@ -6,12 +6,14 @@ package xorm import ( "bufio" + "context" "database/sql" "fmt" "io" "os" "strings" + "xorm.io/xorm/dialects" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" ) @@ -40,13 +42,28 @@ func (session *Session) createTable(bean interface{}) error { return err } - sqlStrs := session.statement.GenCreateTableSQL() - for _, s := range sqlStrs { - _, err := session.exec(s) + session.statement.RefTable.StoreEngine = session.statement.StoreEngine + session.statement.RefTable.Charset = session.statement.Charset + tableName := session.statement.TableName() + refTable := session.statement.RefTable + if refTable.AutoIncrement != "" && session.engine.dialect.Features().AutoincrMode == dialects.SequenceAutoincrMode { + sqlStr, err := session.engine.dialect.CreateSequenceSQL(context.Background(), session.engine.db, utils.SeqName(tableName)) if err != nil { return err } + if _, err := session.exec(sqlStr); err != nil { + return err + } } + + sqlStr, _, err := session.engine.dialect.CreateTableSQL(context.Background(), session.engine.db, refTable, tableName) + if err != nil { + return err + } + if _, err := session.exec(sqlStr); err != nil { + return err + } + return nil } @@ -141,11 +158,32 @@ func (session *Session) dropTable(beanOrTableName interface{}) error { checkIfExist = exist } - if checkIfExist { - _, err := session.exec(sqlStr) + if !checkIfExist { + return nil + } + if _, err := session.exec(sqlStr); err != nil { return err } - return nil + + if session.engine.dialect.Features().AutoincrMode == dialects.IncrAutoincrMode { + return nil + } + + var seqName = utils.SeqName(tableName) + exist, err := session.engine.dialect.IsSequenceExist(session.ctx, session.getQueryer(), seqName) + if err != nil { + return err + } + if !exist { + return nil + } + + sqlStr, err = session.engine.dialect.DropSequenceSQL(seqName) + if err != nil { + return err + } + _, err = session.exec(sqlStr) + return err } // IsTableExist if a table is exist @@ -185,24 +223,6 @@ func (session *Session) isTableEmpty(tableName string) (bool, error) { return total == 0, nil } -// find if index is exist according cols -func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) { - indexes, err := session.engine.dialect.GetIndexes(session.getQueryer(), session.ctx, tableName) - if err != nil { - return false, err - } - - for _, index := range indexes { - if utils.SliceEq(index.Cols, cols) { - if unique { - return index.Type == schemas.UniqueType, nil - } - return index.Type == schemas.IndexType, nil - } - } - return false, nil -} - func (session *Session) addColumn(colName string) error { col := session.statement.RefTable.GetColumn(colName) sql := session.engine.dialect.AddColumnSQL(session.statement.TableName(), col) @@ -225,7 +245,13 @@ func (session *Session) addUnique(tableName, uqeName string) error { } // Sync2 synchronize structs to database tables +// Depricated func (session *Session) Sync2(beans ...interface{}) error { + return session.Sync(beans...) +} + +// Sync synchronize structs to database tables +func (session *Session) Sync(beans ...interface{}) error { engine := session.engine if session.isAutoClose { @@ -350,6 +376,8 @@ func (session *Session) Sync2(beans ...interface{}) error { _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) } } + } else if col.Comment != oriCol.Comment { + _, err = session.exec(engine.dialect.ModifyColumnSQL(tbNameWithSchema, col)) } if col.Default != oriCol.Default { diff --git a/session_stats.go b/session_stats.go index 17d0a675..5d0da5e9 100644 --- a/session_stats.go +++ b/session_stats.go @@ -70,12 +70,12 @@ func (session *Session) SumInt(bean interface{}, columnName string) (res int64, // Sums call sum some columns. bean's non-empty fields are conditions. func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) { - var res = make([]float64, len(columnNames), len(columnNames)) + var res = make([]float64, len(columnNames)) return res, session.sum(&res, bean, columnNames...) } // SumsInt sum specify columns and return as []int64 instead of []float64 func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { - var res = make([]int64, len(columnNames), len(columnNames)) + var res = make([]int64, len(columnNames)) return res, session.sum(&res, bean, columnNames...) } diff --git a/session_tx.go b/session_tx.go index 8763784c..4fa56891 100644 --- a/session_tx.go +++ b/session_tx.go @@ -75,7 +75,7 @@ func (session *Session) Commit() error { } cleanUpFunc := func(slices *map[interface{}]*[]func(interface{})) { if len(*slices) > 0 { - *slices = make(map[interface{}]*[]func(interface{}), 0) + *slices = make(map[interface{}]*[]func(interface{})) } } cleanUpFunc(&session.afterInsertBeans) diff --git a/session_update.go b/session_update.go index 7d91346e..fefbee90 100644 --- a/session_update.go +++ b/session_update.go @@ -22,6 +22,7 @@ var ( ErrNoColumnsTobeUpdated = errors.New("no columns found to be updated") ) +//revive:disable func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr string, args ...interface{}) error { if table == nil || session.tx != nil { @@ -39,7 +40,7 @@ func (session *Session) cacheUpdate(table *schemas.Table, tableName, sqlStr stri var nStart int if len(args) > 0 { - if strings.Index(sqlStr, "?") > -1 { + if strings.Contains(sqlStr, "?") { nStart = strings.Count(oldhead, "?") } else { // only for pq, TODO: if any other databse? @@ -182,7 +183,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return 0, err } - if len(session.statement.TableName()) <= 0 { + if len(session.statement.TableName()) == 0 { return 0, ErrTableNotFound } @@ -278,7 +279,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 condBeanIsStruct := false if len(condiBean) > 0 { if c, ok := condiBean[0].(map[string]interface{}); ok { - autoCond = builder.Eq(c) + var eq = make(builder.Eq) + for k, v := range c { + eq[session.engine.Quote(k)] = v + } + autoCond = builder.Eq(eq) } else { ct := reflect.TypeOf(condiBean[0]) k := ct.Kind() @@ -338,7 +343,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - if len(colNames) <= 0 { + if len(colNames) == 0 { return 0, ErrNoColumnsTobeUpdated } @@ -352,7 +357,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } if st.OrderStr != "" { - condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr) + condSQL += fmt.Sprintf(" ORDER BY %v", st.OrderStr) } var tableName = session.statement.TableName() @@ -362,7 +367,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 limitValue := *st.LimitN switch session.engine.dialect.URI().DBType { case schemas.MYSQL: - condSQL = condSQL + fmt.Sprintf(" LIMIT %d", limitValue) + condSQL += fmt.Sprintf(" LIMIT %d", limitValue) case schemas.SQLITE: tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", limitValue) cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", diff --git a/tags/parser.go b/tags/parser.go index efee11e7..83026862 100644 --- a/tags/parser.go +++ b/tags/parser.go @@ -316,6 +316,7 @@ func (parser *Parser) Parse(v reflect.Value) (*schemas.Table, error) { table := schemas.NewEmptyTable() table.Type = t table.Name = names.GetTableName(parser.tableMapper, v) + table.Comment = names.GetTableComment(v) for i := 0; i < t.NumField(); i++ { col, err := parser.parseField(table, i, t.Field(i), v.Field(i)) diff --git a/tags/parser_test.go b/tags/parser_test.go index 70c57692..83c81a1e 100644 --- a/tags/parser_test.go +++ b/tags/parser_test.go @@ -26,6 +26,20 @@ func (p ParseTableName2) TableName() string { return "p_parseTableName" } +type ParseTableComment struct{} + +type ParseTableComment1 struct{} + +type ParseTableComment2 struct{} + +func (p ParseTableComment1) TableComment() string { + return "p_parseTableComment1" +} + +func (p *ParseTableComment2) TableComment() string { + return "p_parseTableComment2" +} + func TestParseTableName(t *testing.T) { parser := NewParser( "xorm", @@ -47,6 +61,36 @@ func TestParseTableName(t *testing.T) { assert.EqualValues(t, "p_parseTableName", table.Name) } +func TestParseTableComment(t *testing.T) { + parser := NewParser( + "xorm", + dialects.QueryDialect("mysql"), + names.SnakeMapper{}, + names.SnakeMapper{}, + caches.NewManager(), + ) + + table, err := parser.Parse(reflect.ValueOf(new(ParseTableComment))) + assert.NoError(t, err) + assert.EqualValues(t, "", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(new(ParseTableComment1))) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment1", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(ParseTableComment1{})) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment1", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(new(ParseTableComment2))) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment2", table.Comment) + + table, err = parser.Parse(reflect.ValueOf(ParseTableComment2{})) + assert.NoError(t, err) + assert.EqualValues(t, "p_parseTableComment2", table.Comment) +} + func TestUnexportField(t *testing.T) { parser := NewParser( "xorm",