diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..c8f64282 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,61 @@ +# Golang CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-go/ for more details +version: 2 +jobs: + build: + docker: + # specify the version + - image: circleci/golang:1.10 + + - image: circleci/mysql:5.7 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: true + MYSQL_DATABASE: xorm_test + MYSQL_HOST: 127.0.0.1 + MYSQL_ROOT_HOST: '%' + MYSQL_USER: root + + # CircleCI PostgreSQL images available at: https://hub.docker.com/r/circleci/postgres/ + - image: circleci/postgres:9.6.2-alpine + environment: + POSTGRES_USER: circleci + POSTGRES_DB: xorm_test + + - image: microsoft/mssql-server-linux:latest + environment: + ACCEPT_EULA: Y + SA_PASSWORD: yourStrong(!)Password + MSSQL_PID: Developer + + - image: pingcap/tidb:v2.1.2 + + working_directory: /go/src/github.com/go-xorm/xorm + steps: + - checkout + + - run: go get -t -d -v ./... + - run: go get -u xorm.io/core + - run: go get -u xorm.io/builder + - run: GO111MODULE=off go build -v + - run: GO111MODULE=on go build -v + + - run: go get -u github.com/wadey/gocovmerge + + - run: go test -v -race -db="sqlite3" -conn_str="./test.db" -coverprofile=coverage1-1.txt -covermode=atomic + - run: go test -v -race -db="sqlite3" -conn_str="./test.db" -cache=true -coverprofile=coverage1-2.txt -covermode=atomic + - run: go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -coverprofile=coverage2-1.txt -covermode=atomic + - run: go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic + - run: go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -coverprofile=coverage3-1.txt -covermode=atomic + - run: go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic + - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -coverprofile=coverage4-1.txt -covermode=atomic + - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic + - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic + - run: go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic + - run: go test -v -race -db="mssql" -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" -coverprofile=coverage6-1.txt -covermode=atomic + - run: go test -v -race -db="mssql" -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic + - run: go test -v -race -db="mysql" -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic + - run: go test -v -race -db="mysql" -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic + - run: gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt coverage6-1.txt coverage6-2.txt coverage7-1.txt coverage7-2.txt > coverage.txt + + - run: bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/.drone.yml b/.drone.yml index 52f9ef6c..33ce0a92 100644 --- a/.drone.yml +++ b/.drone.yml @@ -65,8 +65,8 @@ pipeline: image: golang:${GO_VERSION} commands: - go get -t -d -v ./... - - go get -u github.com/go-xorm/core - - go get -u github.com/go-xorm/builder + - go get -u xorm.io/core + - go get -u xorm.io/builder - go build -v when: event: [ push, pull_request ] diff --git a/README.md b/README.md index 6a57606e..62b40ba3 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Xorm is a simple and powerful ORM for Go. * Optimistic Locking support -* SQL Builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder) +* SQL Builder support via [xorm.io/builder](https://xorm.io/builder) * Automatical Read/Write seperatelly @@ -151,20 +151,20 @@ has, err := engine.Where("name = ?", name).Desc("id").Get(&user) // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 var name string -has, err := engine.Where("id = ?", id).Cols("name").Get(&name) +has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name) // SELECT name FROM user WHERE id = ? var id int64 -has, err := engine.Where("name = ?", name).Cols("id").Get(&id) +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 valuesMap = make(map[string]string) -has, err := engine.Where("id = ?", id).Get(&valuesMap) +has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) // SELECT * FROM user WHERE id = ? var valuesSlice = make([]interface{}, len(cols)) -has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) +has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice) // SELECT col1, col2, col3 FROM user WHERE id = ? ``` @@ -284,6 +284,13 @@ counts, err := engine.Count(&user) // SELECT count(*) AS total FROM user ``` +* `FindAndCount` combines function `Find` with `Count` which is usually used in query by page + +```Go +var users []User +counts, err := engine.FindAndCount(&users) +``` + * `Sum` sum functions ```Go @@ -363,7 +370,7 @@ return session.Commit() * Or you can use `Transaction` to replace above codes. ```Go -res, err := engine.Transaction(func(sess *xorm.Session) (interface{}, error) { +res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) { user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} if _, err := session.Insert(&user1); err != nil { return nil, err @@ -493,4 +500,4 @@ Support this project by becoming a sponsor. Your logo will show up here with a l ## LICENSE -BSD License [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) \ No newline at end of file +BSD License [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/README_CN.md b/README_CN.md index baf14b85..0cec6ed5 100644 --- a/README_CN.md +++ b/README_CN.md @@ -153,20 +153,20 @@ has, err := engine.Where("name = ?", name).Desc("id").Get(&user) // SELECT * FROM user WHERE name = ? ORDER BY id DESC LIMIT 1 var name string -has, err := engine.Where("id = ?", id).Cols("name").Get(&name) +has, err := engine.Table(&user).Where("id = ?", id).Cols("name").Get(&name) // SELECT name FROM user WHERE id = ? var id int64 -has, err := engine.Where("name = ?", name).Cols("id").Get(&id) +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 valuesMap = make(map[string]string) -has, err := engine.Where("id = ?", id).Get(&valuesMap) +has, err := engine.Table(&user).Where("id = ?", id).Get(&valuesMap) // SELECT * FROM user WHERE id = ? var valuesSlice = make([]interface{}, len(cols)) -has, err := engine.Where("id = ?", id).Cols(cols...).Get(&valuesSlice) +has, err := engine.Table(&user).Where("id = ?", id).Cols(cols...).Get(&valuesSlice) // SELECT col1, col2, col3 FROM user WHERE id = ? ``` @@ -362,10 +362,10 @@ if _, err := session.Exec("delete from userinfo where username = ?", user2.Usern return session.Commit() ``` -* 事物的简写方法 +* 事务的简写方法 ```Go -res, err := engine.Transaction(func(sess *xorm.Session) (interface{}, error) { +res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error) { user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} if _, err := session.Insert(&user1); err != nil { return nil, err @@ -383,7 +383,7 @@ res, err := engine.Transaction(func(sess *xorm.Session) (interface{}, error) { }) ``` -* Context Cache, if enabled, current query result will be cached on session and be used by next same statement on the same session. +* 上下文缓存,如果启用,那么针对单个对象的查询将会被缓存到系统中,可以被下一个查询使用。 ```Go sess := engine.NewSession() diff --git a/cache_lru.go b/cache_lru.go index c9672ceb..ab948bd2 100644 --- a/cache_lru.go +++ b/cache_lru.go @@ -10,7 +10,7 @@ import ( "sync" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) // LRUCacher implments cache object facilities diff --git a/cache_lru_test.go b/cache_lru_test.go index 28854474..7da36f00 100644 --- a/cache_lru_test.go +++ b/cache_lru_test.go @@ -7,7 +7,7 @@ package xorm import ( "testing" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) diff --git a/cache_memory_store.go b/cache_memory_store.go index 36853b19..0c483f45 100644 --- a/cache_memory_store.go +++ b/cache_memory_store.go @@ -7,7 +7,7 @@ package xorm import ( "sync" - "github.com/go-xorm/core" + "xorm.io/core" ) var _ core.CacheStore = NewMemoryStore() diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 8fde3169..00000000 --- a/circle.yml +++ /dev/null @@ -1,41 +0,0 @@ -dependencies: - override: - # './...' is a relative pattern which means all subdirectories - - go get -t -d -v ./... - - go get -t -d -v github.com/go-xorm/tests - - go get -u github.com/go-xorm/core - - go get -u github.com/go-xorm/builder - - go build -v - -database: - override: - - mysql -u root -e "CREATE DATABASE xorm_test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" - - mysql -u root -e "CREATE DATABASE xorm_test1 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" - - mysql -u root -e "CREATE DATABASE xorm_test2 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" - - mysql -u root -e "CREATE DATABASE xorm_test3 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" - - createdb -p 5432 -e -U postgres xorm_test - - createdb -p 5432 -e -U postgres xorm_test1 - - createdb -p 5432 -e -U postgres xorm_test2 - - createdb -p 5432 -e -U postgres xorm_test3 - - psql xorm_test postgres -c "create schema xorm" - -test: - override: - # './...' is a relative pattern which means all subdirectories - - go get -u github.com/wadey/gocovmerge - - go test -v -race -db="sqlite3" -conn_str="./test.db" -coverprofile=coverage1-1.txt -covermode=atomic - - go test -v -race -db="sqlite3" -conn_str="./test.db" -cache=true -coverprofile=coverage1-2.txt -covermode=atomic - - go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -coverprofile=coverage2-1.txt -covermode=atomic - - go test -v -race -db="mysql" -conn_str="root:@/xorm_test" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic - - go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -coverprofile=coverage3-1.txt -covermode=atomic - - go test -v -race -db="mymysql" -conn_str="xorm_test/root/" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic - - go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -coverprofile=coverage4-1.txt -covermode=atomic - - go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic - - go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic - - go test -v -race -db="postgres" -conn_str="dbname=xorm_test sslmode=disable" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic - - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt - - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./sqlite3.sh - - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./mysql.sh - - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./postgres.sh - post: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/dialect_mssql.go b/dialect_mssql.go index 6d2291dc..61061cb2 100644 --- a/dialect_mssql.go +++ b/dialect_mssql.go @@ -7,199 +7,200 @@ package xorm import ( "errors" "fmt" + "net/url" "strconv" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) var ( mssqlReservedWords = map[string]bool{ - "ADD": true, - "EXTERNAL": true, - "PROCEDURE": true, - "ALL": true, - "FETCH": true, - "PUBLIC": true, - "ALTER": true, - "FILE": true, - "RAISERROR": true, - "AND": true, - "FILLFACTOR": true, - "READ": true, - "ANY": true, - "FOR": true, - "READTEXT": true, - "AS": true, - "FOREIGN": true, - "RECONFIGURE": true, - "ASC": true, - "FREETEXT": true, - "REFERENCES": true, - "AUTHORIZATION": true, - "FREETEXTTABLE": true, - "REPLICATION": true, - "BACKUP": true, - "FROM": true, - "RESTORE": true, - "BEGIN": true, - "FULL": true, - "RESTRICT": true, - "BETWEEN": true, - "FUNCTION": true, - "RETURN": true, - "BREAK": true, - "GOTO": true, - "REVERT": true, - "BROWSE": true, - "GRANT": true, - "REVOKE": true, - "BULK": true, - "GROUP": true, - "RIGHT": true, - "BY": true, - "HAVING": true, - "ROLLBACK": true, - "CASCADE": true, - "HOLDLOCK": true, - "ROWCOUNT": true, - "CASE": true, - "IDENTITY": true, - "ROWGUIDCOL": true, - "CHECK": true, - "IDENTITY_INSERT": true, - "RULE": true, - "CHECKPOINT": true, - "IDENTITYCOL": true, - "SAVE": true, - "CLOSE": true, - "IF": true, - "SCHEMA": true, - "CLUSTERED": true, - "IN": true, - "SECURITYAUDIT": true, - "COALESCE": true, - "INDEX": true, - "SELECT": true, - "COLLATE": true, - "INNER": true, - "SEMANTICKEYPHRASETABLE": true, - "COLUMN": true, - "INSERT": true, + "ADD": true, + "EXTERNAL": true, + "PROCEDURE": true, + "ALL": true, + "FETCH": true, + "PUBLIC": true, + "ALTER": true, + "FILE": true, + "RAISERROR": true, + "AND": true, + "FILLFACTOR": true, + "READ": true, + "ANY": true, + "FOR": true, + "READTEXT": true, + "AS": true, + "FOREIGN": true, + "RECONFIGURE": true, + "ASC": true, + "FREETEXT": true, + "REFERENCES": true, + "AUTHORIZATION": true, + "FREETEXTTABLE": true, + "REPLICATION": true, + "BACKUP": true, + "FROM": true, + "RESTORE": true, + "BEGIN": true, + "FULL": true, + "RESTRICT": true, + "BETWEEN": true, + "FUNCTION": true, + "RETURN": true, + "BREAK": true, + "GOTO": true, + "REVERT": true, + "BROWSE": true, + "GRANT": true, + "REVOKE": true, + "BULK": true, + "GROUP": true, + "RIGHT": true, + "BY": true, + "HAVING": true, + "ROLLBACK": true, + "CASCADE": true, + "HOLDLOCK": true, + "ROWCOUNT": true, + "CASE": true, + "IDENTITY": true, + "ROWGUIDCOL": true, + "CHECK": true, + "IDENTITY_INSERT": true, + "RULE": true, + "CHECKPOINT": true, + "IDENTITYCOL": true, + "SAVE": true, + "CLOSE": true, + "IF": true, + "SCHEMA": true, + "CLUSTERED": true, + "IN": true, + "SECURITYAUDIT": true, + "COALESCE": true, + "INDEX": true, + "SELECT": true, + "COLLATE": true, + "INNER": true, + "SEMANTICKEYPHRASETABLE": true, + "COLUMN": true, + "INSERT": true, "SEMANTICSIMILARITYDETAILSTABLE": true, - "COMMIT": true, - "INTERSECT": true, - "SEMANTICSIMILARITYTABLE": true, - "COMPUTE": true, - "INTO": true, - "SESSION_USER": true, - "CONSTRAINT": true, - "IS": true, - "SET": true, - "CONTAINS": true, - "JOIN": true, - "SETUSER": true, - "CONTAINSTABLE": true, - "KEY": true, - "SHUTDOWN": true, - "CONTINUE": true, - "KILL": true, - "SOME": true, - "CONVERT": true, - "LEFT": true, - "STATISTICS": true, - "CREATE": true, - "LIKE": true, - "SYSTEM_USER": true, - "CROSS": true, - "LINENO": true, - "TABLE": true, - "CURRENT": true, - "LOAD": true, - "TABLESAMPLE": true, - "CURRENT_DATE": true, - "MERGE": true, - "TEXTSIZE": true, - "CURRENT_TIME": true, - "NATIONAL": true, - "THEN": true, - "CURRENT_TIMESTAMP": true, - "NOCHECK": true, - "TO": true, - "CURRENT_USER": true, - "NONCLUSTERED": true, - "TOP": true, - "CURSOR": true, - "NOT": true, - "TRAN": true, - "DATABASE": true, - "NULL": true, - "TRANSACTION": true, - "DBCC": true, - "NULLIF": true, - "TRIGGER": true, - "DEALLOCATE": true, - "OF": true, - "TRUNCATE": true, - "DECLARE": true, - "OFF": true, - "TRY_CONVERT": true, - "DEFAULT": true, - "OFFSETS": true, - "TSEQUAL": true, - "DELETE": true, - "ON": true, - "UNION": true, - "DENY": true, - "OPEN": true, - "UNIQUE": true, - "DESC": true, - "OPENDATASOURCE": true, - "UNPIVOT": true, - "DISK": true, - "OPENQUERY": true, - "UPDATE": true, - "DISTINCT": true, - "OPENROWSET": true, - "UPDATETEXT": true, - "DISTRIBUTED": true, - "OPENXML": true, - "USE": true, - "DOUBLE": true, - "OPTION": true, - "USER": true, - "DROP": true, - "OR": true, - "VALUES": true, - "DUMP": true, - "ORDER": true, - "VARYING": true, - "ELSE": true, - "OUTER": true, - "VIEW": true, - "END": true, - "OVER": true, - "WAITFOR": true, - "ERRLVL": true, - "PERCENT": true, - "WHEN": true, - "ESCAPE": true, - "PIVOT": true, - "WHERE": true, - "EXCEPT": true, - "PLAN": true, - "WHILE": true, - "EXEC": true, - "PRECISION": true, - "WITH": true, - "EXECUTE": true, - "PRIMARY": true, - "WITHIN": true, - "EXISTS": true, - "PRINT": true, - "WRITETEXT": true, - "EXIT": true, - "PROC": true, + "COMMIT": true, + "INTERSECT": true, + "SEMANTICSIMILARITYTABLE": true, + "COMPUTE": true, + "INTO": true, + "SESSION_USER": true, + "CONSTRAINT": true, + "IS": true, + "SET": true, + "CONTAINS": true, + "JOIN": true, + "SETUSER": true, + "CONTAINSTABLE": true, + "KEY": true, + "SHUTDOWN": true, + "CONTINUE": true, + "KILL": true, + "SOME": true, + "CONVERT": true, + "LEFT": true, + "STATISTICS": true, + "CREATE": true, + "LIKE": true, + "SYSTEM_USER": true, + "CROSS": true, + "LINENO": true, + "TABLE": true, + "CURRENT": true, + "LOAD": true, + "TABLESAMPLE": true, + "CURRENT_DATE": true, + "MERGE": true, + "TEXTSIZE": true, + "CURRENT_TIME": true, + "NATIONAL": true, + "THEN": true, + "CURRENT_TIMESTAMP": true, + "NOCHECK": true, + "TO": true, + "CURRENT_USER": true, + "NONCLUSTERED": true, + "TOP": true, + "CURSOR": true, + "NOT": true, + "TRAN": true, + "DATABASE": true, + "NULL": true, + "TRANSACTION": true, + "DBCC": true, + "NULLIF": true, + "TRIGGER": true, + "DEALLOCATE": true, + "OF": true, + "TRUNCATE": true, + "DECLARE": true, + "OFF": true, + "TRY_CONVERT": true, + "DEFAULT": true, + "OFFSETS": true, + "TSEQUAL": true, + "DELETE": true, + "ON": true, + "UNION": true, + "DENY": true, + "OPEN": true, + "UNIQUE": true, + "DESC": true, + "OPENDATASOURCE": true, + "UNPIVOT": true, + "DISK": true, + "OPENQUERY": true, + "UPDATE": true, + "DISTINCT": true, + "OPENROWSET": true, + "UPDATETEXT": true, + "DISTRIBUTED": true, + "OPENXML": true, + "USE": true, + "DOUBLE": true, + "OPTION": true, + "USER": true, + "DROP": true, + "OR": true, + "VALUES": true, + "DUMP": true, + "ORDER": true, + "VARYING": true, + "ELSE": true, + "OUTER": true, + "VIEW": true, + "END": true, + "OVER": true, + "WAITFOR": true, + "ERRLVL": true, + "PERCENT": true, + "WHEN": true, + "ESCAPE": true, + "PIVOT": true, + "WHERE": true, + "EXCEPT": true, + "PLAN": true, + "WHILE": true, + "EXEC": true, + "PRECISION": true, + "WITH": true, + "EXECUTE": true, + "PRIMARY": true, + "WITHIN": true, + "EXISTS": true, + "PRINT": true, + "WRITETEXT": true, + "EXIT": true, + "PROC": true, } ) @@ -218,7 +219,7 @@ func (db *mssql) SqlType(c *core.Column) string { res = core.Bit if strings.EqualFold(c.Default, "true") { c.Default = "1" - } else { + } else if strings.EqualFold(c.Default, "false") { c.Default = "0" } case core.Serial: @@ -285,10 +286,6 @@ func (db *mssql) Quote(name string) string { return "\"" + name + "\"" } -func (db *mssql) QuoteStr() string { - return "\"" -} - func (db *mssql) SupportEngine() bool { return false } @@ -506,7 +503,7 @@ func (db *mssql) CreateTableSql(table *core.Table, tableName, storeEngine, chars sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE " - sql += db.QuoteStr() + tableName + db.QuoteStr() + " (" + sql += db.Quote(tableName) + " (" pkList := table.PrimaryKeys @@ -544,14 +541,23 @@ type odbcDriver struct { } func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - kv := strings.Split(dataSourceName, ";") var dbName string - for _, c := range kv { - vv := strings.Split(strings.TrimSpace(c), "=") - if len(vv) == 2 { - switch strings.ToLower(vv[0]) { - case "database": - dbName = vv[1] + + if strings.HasPrefix(dataSourceName, "sqlserver://") { + u, err := url.Parse(dataSourceName) + if err != nil { + return nil, err + } + dbName = u.Query().Get("database") + } else { + kv := strings.Split(dataSourceName, ";") + for _, c := range kv { + vv := strings.Split(strings.TrimSpace(c), "=") + if len(vv) == 2 { + switch strings.ToLower(vv[0]) { + case "database": + dbName = vv[1] + } } } } diff --git a/dialect_mssql_test.go b/dialect_mssql_test.go new file mode 100644 index 00000000..acd1d059 --- /dev/null +++ b/dialect_mssql_test.go @@ -0,0 +1,35 @@ +// Copyright 2019 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 ( + "reflect" + "testing" + + "xorm.io/core" +) + +func TestParseMSSQL(t *testing.T) { + tests := []struct { + in string + expected string + valid bool + }{ + {"sqlserver://sa:yourStrong(!)Password@localhost:1433?database=db&connection+timeout=30", "db", true}, + {"server=localhost;user id=sa;password=yourStrong(!)Password;database=db", "db", true}, + } + + driver := core.QueryDriver("mssql") + + for _, test := range tests { + uri, err := driver.Parse("mssql", test.in) + + if err != nil && test.valid { + t.Errorf("%q got unexpected error: %s", test.in, err) + } else if err == nil && !reflect.DeepEqual(test.expected, uri.DbName) { + t.Errorf("%q got: %#v want: %#v", test.in, uri.DbName, test.expected) + } + } +} diff --git a/dialect_mysql.go b/dialect_mysql.go index 9f5ae3b2..a108b81f 100644 --- a/dialect_mysql.go +++ b/dialect_mysql.go @@ -13,7 +13,7 @@ import ( "strings" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) var ( @@ -220,7 +220,7 @@ func (db *mysql) SqlType(c *core.Column) string { case core.TimeStampz: res = core.Char c.Length = 64 - case core.Enum: //mysql enum + case core.Enum: // mysql enum res = core.Enum res += "(" opts := "" @@ -229,7 +229,7 @@ func (db *mysql) SqlType(c *core.Column) string { } res += strings.TrimLeft(opts, ",") res += ")" - case core.Set: //mysql set + case core.Set: // mysql set res = core.Set res += "(" opts := "" @@ -278,10 +278,6 @@ func (db *mysql) Quote(name string) string { return "`" + name + "`" } -func (db *mysql) QuoteStr() string { - return "`" -} - func (db *mysql) SupportEngine() bool { return true } @@ -360,7 +356,7 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column var len1, len2 int if len(cts) == 2 { idx := strings.Index(cts[1], ")") - if colType == core.Enum && cts[1][0] == '\'' { //enum + if colType == core.Enum && cts[1][0] == '\'' { // enum options := strings.Split(cts[1][0:idx], ",") col.EnumOptions = make(map[string]int) for k, v := range options { @@ -393,6 +389,9 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column if colType == "FLOAT UNSIGNED" { colType = "FLOAT" } + if colType == "DOUBLE UNSIGNED" { + colType = "DOUBLE" + } col.Length = len1 col.Length2 = len2 if _, ok := core.SqlTypes[colType]; ok { @@ -405,7 +404,7 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column col.IsPrimaryKey = true } if colKey == "UNI" { - //col.is + // col.is } if extra == "auto_increment" { @@ -551,12 +550,10 @@ func (db *mysql) CreateTableSql(table *core.Table, tableName, storeEngine, chars if len(charset) == 0 { charset = db.URI().Charset - } + } if len(charset) != 0 { sql += " DEFAULT CHARSET " + charset } - - if db.rowFormat != "" { sql += " ROW_FORMAT=" + db.rowFormat @@ -630,7 +627,7 @@ func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error `\/(?P.*?)` + // /dbname `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] matches := dsnPattern.FindStringSubmatch(dataSourceName) - //tlsConfigRegister := make(map[string]*tls.Config) + // tlsConfigRegister := make(map[string]*tls.Config) names := dsnPattern.SubexpNames() uri := &core.Uri{DbType: core.MYSQL} diff --git a/dialect_oracle.go b/dialect_oracle.go index ac0081b3..15010ca5 100644 --- a/dialect_oracle.go +++ b/dialect_oracle.go @@ -11,7 +11,7 @@ import ( "strconv" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) var ( @@ -230,271 +230,271 @@ var ( "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, + "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, } ) @@ -552,11 +552,7 @@ func (db *oracle) IsReserved(name string) bool { } func (db *oracle) Quote(name string) string { - return "\"" + name + "\"" -} - -func (db *oracle) QuoteStr() string { - return "\"" + return "[" + name + "]" } func (db *oracle) SupportEngine() bool { @@ -596,7 +592,7 @@ func (db *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, char sql += col.String(b.dialect) } else {*/ sql += col.StringNoPk(db) - //} + // } sql = strings.TrimSpace(sql) sql += ", " } @@ -865,7 +861,7 @@ func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, e `\/(?P.*?)` + // /dbname `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] matches := dsnPattern.FindStringSubmatch(dataSourceName) - //tlsConfigRegister := make(map[string]*tls.Config) + // tlsConfigRegister := make(map[string]*tls.Config) names := dsnPattern.SubexpNames() for i, match := range matches { @@ -883,8 +879,8 @@ func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, e type oci8Driver struct { } -//dataSourceName=user/password@ipv4:port/dbname -//dataSourceName=user/password@[ipv6]:port/dbname +// dataSourceName=user/password@ipv4:port/dbname +// dataSourceName=user/password@[ipv6]:port/dbname func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { db := &core.Uri{DbType: core.ORACLE} dsnPattern := regexp.MustCompile( diff --git a/dialect_postgres.go b/dialect_postgres.go index 1f74bd31..e1c377a0 100644 --- a/dialect_postgres.go +++ b/dialect_postgres.go @@ -11,38 +11,38 @@ import ( "strconv" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) // from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html var ( postgresReservedWords = map[string]bool{ - "A": true, - "ABORT": true, - "ABS": true, - "ABSENT": true, - "ABSOLUTE": true, - "ACCESS": true, - "ACCORDING": true, - "ACTION": true, - "ADA": true, - "ADD": true, - "ADMIN": true, - "AFTER": true, - "AGGREGATE": true, - "ALL": true, - "ALLOCATE": true, - "ALSO": true, - "ALTER": true, - "ALWAYS": true, - "ANALYSE": true, - "ANALYZE": true, - "AND": true, - "ANY": true, - "ARE": true, - "ARRAY": true, - "ARRAY_AGG": true, - "ARRAY_MAX_CARDINALITY": true, + "A": true, + "ABORT": true, + "ABS": true, + "ABSENT": true, + "ABSOLUTE": true, + "ACCESS": true, + "ACCORDING": true, + "ACTION": true, + "ADA": true, + "ADD": true, + "ADMIN": true, + "AFTER": true, + "AGGREGATE": true, + "ALL": true, + "ALLOCATE": true, + "ALSO": true, + "ALTER": true, + "ALWAYS": true, + "ANALYSE": true, + "ANALYZE": true, + "AND": true, + "ANY": true, + "ARE": true, + "ARRAY": true, + "ARRAY_AGG": true, + "ARRAY_MAX_CARDINALITY": true, "AS": true, "ASC": true, "ASENSITIVE": true, @@ -171,598 +171,598 @@ var ( "DATABASE": true, "DATALINK": true, "DATE": true, - "DATETIME_INTERVAL_CODE": true, - "DATETIME_INTERVAL_PRECISION": true, - "DAY": true, - "DB": true, - "DEALLOCATE": true, - "DEC": true, - "DECIMAL": true, - "DECLARE": true, - "DEFAULT": true, - "DEFAULTS": true, - "DEFERRABLE": true, - "DEFERRED": true, - "DEFINED": true, - "DEFINER": true, - "DEGREE": true, - "DELETE": true, - "DELIMITER": true, - "DELIMITERS": true, - "DENSE_RANK": true, - "DEPTH": true, - "DEREF": true, - "DERIVED": true, - "DESC": true, - "DESCRIBE": true, - "DESCRIPTOR": true, - "DETERMINISTIC": true, - "DIAGNOSTICS": true, - "DICTIONARY": true, - "DISABLE": true, - "DISCARD": true, - "DISCONNECT": true, - "DISPATCH": true, - "DISTINCT": true, - "DLNEWCOPY": true, - "DLPREVIOUSCOPY": true, - "DLURLCOMPLETE": true, - "DLURLCOMPLETEONLY": true, - "DLURLCOMPLETEWRITE": true, - "DLURLPATH": true, - "DLURLPATHONLY": true, - "DLURLPATHWRITE": true, - "DLURLSCHEME": true, - "DLURLSERVER": true, - "DLVALUE": true, - "DO": true, - "DOCUMENT": true, - "DOMAIN": true, - "DOUBLE": true, - "DROP": true, - "DYNAMIC": true, - "DYNAMIC_FUNCTION": true, - "DYNAMIC_FUNCTION_CODE": true, - "EACH": true, - "ELEMENT": true, - "ELSE": true, - "EMPTY": true, - "ENABLE": true, - "ENCODING": true, - "ENCRYPTED": true, - "END": true, - "END-EXEC": true, - "END_FRAME": true, - "END_PARTITION": true, - "ENFORCED": true, - "ENUM": true, - "EQUALS": true, - "ESCAPE": true, - "EVENT": true, - "EVERY": true, - "EXCEPT": true, - "EXCEPTION": true, - "EXCLUDE": true, - "EXCLUDING": true, - "EXCLUSIVE": true, - "EXEC": true, - "EXECUTE": true, - "EXISTS": true, - "EXP": true, - "EXPLAIN": true, - "EXPRESSION": true, - "EXTENSION": true, - "EXTERNAL": true, - "EXTRACT": true, - "FALSE": true, - "FAMILY": true, - "FETCH": true, - "FILE": true, - "FILTER": true, - "FINAL": true, - "FIRST": true, - "FIRST_VALUE": true, - "FLAG": true, - "FLOAT": true, - "FLOOR": true, - "FOLLOWING": true, - "FOR": true, - "FORCE": true, - "FOREIGN": true, - "FORTRAN": true, - "FORWARD": true, - "FOUND": true, - "FRAME_ROW": true, - "FREE": true, - "FREEZE": true, - "FROM": true, - "FS": true, - "FULL": true, - "FUNCTION": true, - "FUNCTIONS": true, - "FUSION": true, - "G": true, - "GENERAL": true, - "GENERATED": true, - "GET": true, - "GLOBAL": true, - "GO": true, - "GOTO": true, - "GRANT": true, - "GRANTED": true, - "GREATEST": true, - "GROUP": true, - "GROUPING": true, - "GROUPS": true, - "HANDLER": true, - "HAVING": true, - "HEADER": true, - "HEX": true, - "HIERARCHY": true, - "HOLD": true, - "HOUR": true, - "ID": true, - "IDENTITY": true, - "IF": true, - "IGNORE": true, - "ILIKE": true, - "IMMEDIATE": true, - "IMMEDIATELY": true, - "IMMUTABLE": true, - "IMPLEMENTATION": true, - "IMPLICIT": true, - "IMPORT": true, - "IN": true, - "INCLUDING": true, - "INCREMENT": true, - "INDENT": true, - "INDEX": true, - "INDEXES": true, - "INDICATOR": true, - "INHERIT": true, - "INHERITS": true, - "INITIALLY": true, - "INLINE": true, - "INNER": true, - "INOUT": true, - "INPUT": true, - "INSENSITIVE": true, - "INSERT": true, - "INSTANCE": true, - "INSTANTIABLE": true, - "INSTEAD": true, - "INT": true, - "INTEGER": true, - "INTEGRITY": true, - "INTERSECT": true, - "INTERSECTION": true, - "INTERVAL": true, - "INTO": true, - "INVOKER": true, - "IS": true, - "ISNULL": true, - "ISOLATION": true, - "JOIN": true, - "K": true, - "KEY": true, - "KEY_MEMBER": true, - "KEY_TYPE": true, - "LABEL": true, - "LAG": true, - "LANGUAGE": true, - "LARGE": true, - "LAST": true, - "LAST_VALUE": true, - "LATERAL": true, - "LC_COLLATE": true, - "LC_CTYPE": true, - "LEAD": true, - "LEADING": true, - "LEAKPROOF": true, - "LEAST": true, - "LEFT": true, - "LENGTH": true, - "LEVEL": true, - "LIBRARY": true, - "LIKE": true, - "LIKE_REGEX": true, - "LIMIT": true, - "LINK": true, - "LISTEN": true, - "LN": true, - "LOAD": true, - "LOCAL": true, - "LOCALTIME": true, - "LOCALTIMESTAMP": true, - "LOCATION": true, - "LOCATOR": true, - "LOCK": true, - "LOWER": true, - "M": true, - "MAP": true, - "MAPPING": true, - "MATCH": true, - "MATCHED": true, - "MATERIALIZED": true, - "MAX": true, - "MAXVALUE": true, - "MAX_CARDINALITY": true, - "MEMBER": true, - "MERGE": true, - "MESSAGE_LENGTH": true, - "MESSAGE_OCTET_LENGTH": true, - "MESSAGE_TEXT": true, - "METHOD": true, - "MIN": true, - "MINUTE": true, - "MINVALUE": true, - "MOD": true, - "MODE": true, - "MODIFIES": true, - "MODULE": true, - "MONTH": true, - "MORE": true, - "MOVE": true, - "MULTISET": true, - "MUMPS": true, - "NAME": true, - "NAMES": true, - "NAMESPACE": true, - "NATIONAL": true, - "NATURAL": true, - "NCHAR": true, - "NCLOB": true, - "NESTING": true, - "NEW": true, - "NEXT": true, - "NFC": true, - "NFD": true, - "NFKC": true, - "NFKD": true, - "NIL": true, - "NO": true, - "NONE": true, - "NORMALIZE": true, - "NORMALIZED": true, - "NOT": true, - "NOTHING": true, - "NOTIFY": true, - "NOTNULL": true, - "NOWAIT": true, - "NTH_VALUE": true, - "NTILE": true, - "NULL": true, - "NULLABLE": true, - "NULLIF": true, - "NULLS": true, - "NUMBER": true, - "NUMERIC": true, - "OBJECT": true, - "OCCURRENCES_REGEX": true, - "OCTETS": true, - "OCTET_LENGTH": true, - "OF": true, - "OFF": true, - "OFFSET": true, - "OIDS": true, - "OLD": true, - "ON": true, - "ONLY": true, - "OPEN": true, - "OPERATOR": true, - "OPTION": true, - "OPTIONS": true, - "OR": true, - "ORDER": true, - "ORDERING": true, - "ORDINALITY": true, - "OTHERS": true, - "OUT": true, - "OUTER": true, - "OUTPUT": true, - "OVER": true, - "OVERLAPS": true, - "OVERLAY": true, - "OVERRIDING": true, - "OWNED": true, - "OWNER": true, - "P": true, - "PAD": true, - "PARAMETER": true, - "PARAMETER_MODE": true, - "PARAMETER_NAME": true, - "PARAMETER_ORDINAL_POSITION": true, - "PARAMETER_SPECIFIC_CATALOG": true, - "PARAMETER_SPECIFIC_NAME": true, - "PARAMETER_SPECIFIC_SCHEMA": true, - "PARSER": true, - "PARTIAL": true, - "PARTITION": true, - "PASCAL": true, - "PASSING": true, - "PASSTHROUGH": true, - "PASSWORD": true, - "PATH": true, - "PERCENT": true, - "PERCENTILE_CONT": true, - "PERCENTILE_DISC": true, - "PERCENT_RANK": true, - "PERIOD": true, - "PERMISSION": true, - "PLACING": true, - "PLANS": true, - "PLI": true, - "PORTION": true, - "POSITION": true, - "POSITION_REGEX": true, - "POWER": true, - "PRECEDES": true, - "PRECEDING": true, - "PRECISION": true, - "PREPARE": true, - "PREPARED": true, - "PRESERVE": true, - "PRIMARY": true, - "PRIOR": true, - "PRIVILEGES": true, - "PROCEDURAL": true, - "PROCEDURE": true, - "PROGRAM": true, - "PUBLIC": true, - "QUOTE": true, - "RANGE": true, - "RANK": true, - "READ": true, - "READS": true, - "REAL": true, - "REASSIGN": true, - "RECHECK": true, - "RECOVERY": true, - "RECURSIVE": true, - "REF": true, - "REFERENCES": true, - "REFERENCING": true, - "REFRESH": true, - "REGR_AVGX": true, - "REGR_AVGY": true, - "REGR_COUNT": true, - "REGR_INTERCEPT": true, - "REGR_R2": true, - "REGR_SLOPE": true, - "REGR_SXX": true, - "REGR_SXY": true, - "REGR_SYY": true, - "REINDEX": true, - "RELATIVE": true, - "RELEASE": true, - "RENAME": true, - "REPEATABLE": true, - "REPLACE": true, - "REPLICA": true, - "REQUIRING": true, - "RESET": true, - "RESPECT": true, - "RESTART": true, - "RESTORE": true, - "RESTRICT": true, - "RESULT": true, - "RETURN": true, - "RETURNED_CARDINALITY": true, - "RETURNED_LENGTH": true, - "RETURNED_OCTET_LENGTH": true, - "RETURNED_SQLSTATE": true, - "RETURNING": true, - "RETURNS": true, - "REVOKE": true, - "RIGHT": true, - "ROLE": true, - "ROLLBACK": true, - "ROLLUP": true, - "ROUTINE": true, - "ROUTINE_CATALOG": true, - "ROUTINE_NAME": true, - "ROUTINE_SCHEMA": true, - "ROW": true, - "ROWS": true, - "ROW_COUNT": true, - "ROW_NUMBER": true, - "RULE": true, - "SAVEPOINT": true, - "SCALE": true, - "SCHEMA": true, - "SCHEMA_NAME": true, - "SCOPE": true, - "SCOPE_CATALOG": true, - "SCOPE_NAME": true, - "SCOPE_SCHEMA": true, - "SCROLL": true, - "SEARCH": true, - "SECOND": true, - "SECTION": true, - "SECURITY": true, - "SELECT": true, - "SELECTIVE": true, - "SELF": true, - "SENSITIVE": true, - "SEQUENCE": true, - "SEQUENCES": true, - "SERIALIZABLE": true, - "SERVER": true, - "SERVER_NAME": true, - "SESSION": true, - "SESSION_USER": true, - "SET": true, - "SETOF": true, - "SETS": true, - "SHARE": true, - "SHOW": true, - "SIMILAR": true, - "SIMPLE": true, - "SIZE": true, - "SMALLINT": true, - "SNAPSHOT": true, - "SOME": true, - "SOURCE": true, - "SPACE": true, - "SPECIFIC": true, - "SPECIFICTYPE": true, - "SPECIFIC_NAME": true, - "SQL": true, - "SQLCODE": true, - "SQLERROR": true, - "SQLEXCEPTION": true, - "SQLSTATE": true, - "SQLWARNING": true, - "SQRT": true, - "STABLE": true, - "STANDALONE": true, - "START": true, - "STATE": true, - "STATEMENT": true, - "STATIC": true, - "STATISTICS": true, - "STDDEV_POP": true, - "STDDEV_SAMP": true, - "STDIN": true, - "STDOUT": true, - "STORAGE": true, - "STRICT": true, - "STRIP": true, - "STRUCTURE": true, - "STYLE": true, - "SUBCLASS_ORIGIN": true, - "SUBMULTISET": true, - "SUBSTRING": true, - "SUBSTRING_REGEX": true, - "SUCCEEDS": true, - "SUM": true, - "SYMMETRIC": true, - "SYSID": true, - "SYSTEM": true, - "SYSTEM_TIME": true, - "SYSTEM_USER": true, - "T": true, - "TABLE": true, - "TABLES": true, - "TABLESAMPLE": true, - "TABLESPACE": true, - "TABLE_NAME": true, - "TEMP": true, - "TEMPLATE": true, - "TEMPORARY": true, - "TEXT": true, - "THEN": true, - "TIES": true, - "TIME": true, - "TIMESTAMP": true, - "TIMEZONE_HOUR": true, - "TIMEZONE_MINUTE": true, - "TO": true, - "TOKEN": true, - "TOP_LEVEL_COUNT": true, - "TRAILING": true, - "TRANSACTION": true, - "TRANSACTIONS_COMMITTED": true, - "TRANSACTIONS_ROLLED_BACK": true, - "TRANSACTION_ACTIVE": true, - "TRANSFORM": true, - "TRANSFORMS": true, - "TRANSLATE": true, - "TRANSLATE_REGEX": true, - "TRANSLATION": true, - "TREAT": true, - "TRIGGER": true, - "TRIGGER_CATALOG": true, - "TRIGGER_NAME": true, - "TRIGGER_SCHEMA": true, - "TRIM": true, - "TRIM_ARRAY": true, - "TRUE": true, - "TRUNCATE": true, - "TRUSTED": true, - "TYPE": true, - "TYPES": true, - "UESCAPE": true, - "UNBOUNDED": true, - "UNCOMMITTED": true, - "UNDER": true, - "UNENCRYPTED": true, - "UNION": true, - "UNIQUE": true, - "UNKNOWN": true, - "UNLINK": true, - "UNLISTEN": true, - "UNLOGGED": true, - "UNNAMED": true, - "UNNEST": true, - "UNTIL": true, - "UNTYPED": true, - "UPDATE": true, - "UPPER": true, - "URI": true, - "USAGE": true, - "USER": true, - "USER_DEFINED_TYPE_CATALOG": true, - "USER_DEFINED_TYPE_CODE": true, - "USER_DEFINED_TYPE_NAME": true, - "USER_DEFINED_TYPE_SCHEMA": true, - "USING": true, - "VACUUM": true, - "VALID": true, - "VALIDATE": true, - "VALIDATOR": true, - "VALUE": true, - "VALUES": true, - "VALUE_OF": true, - "VARBINARY": true, - "VARCHAR": true, - "VARIADIC": true, - "VARYING": true, - "VAR_POP": true, - "VAR_SAMP": true, - "VERBOSE": true, - "VERSION": true, - "VERSIONING": true, - "VIEW": true, - "VOLATILE": true, - "WHEN": true, - "WHENEVER": true, - "WHERE": true, - "WHITESPACE": true, - "WIDTH_BUCKET": true, - "WINDOW": true, - "WITH": true, - "WITHIN": true, - "WITHOUT": true, - "WORK": true, - "WRAPPER": true, - "WRITE": true, - "XML": true, - "XMLAGG": true, - "XMLATTRIBUTES": true, - "XMLBINARY": true, - "XMLCAST": true, - "XMLCOMMENT": true, - "XMLCONCAT": true, - "XMLDECLARATION": true, - "XMLDOCUMENT": true, - "XMLELEMENT": true, - "XMLEXISTS": true, - "XMLFOREST": true, - "XMLITERATE": true, - "XMLNAMESPACES": true, - "XMLPARSE": true, - "XMLPI": true, - "XMLQUERY": true, - "XMLROOT": true, - "XMLSCHEMA": true, - "XMLSERIALIZE": true, - "XMLTABLE": true, - "XMLTEXT": true, - "XMLVALIDATE": true, - "YEAR": true, - "YES": true, - "ZONE": true, + "DATETIME_INTERVAL_CODE": true, + "DATETIME_INTERVAL_PRECISION": true, + "DAY": true, + "DB": true, + "DEALLOCATE": true, + "DEC": true, + "DECIMAL": true, + "DECLARE": true, + "DEFAULT": true, + "DEFAULTS": true, + "DEFERRABLE": true, + "DEFERRED": true, + "DEFINED": true, + "DEFINER": true, + "DEGREE": true, + "DELETE": true, + "DELIMITER": true, + "DELIMITERS": true, + "DENSE_RANK": true, + "DEPTH": true, + "DEREF": true, + "DERIVED": true, + "DESC": true, + "DESCRIBE": true, + "DESCRIPTOR": true, + "DETERMINISTIC": true, + "DIAGNOSTICS": true, + "DICTIONARY": true, + "DISABLE": true, + "DISCARD": true, + "DISCONNECT": true, + "DISPATCH": true, + "DISTINCT": true, + "DLNEWCOPY": true, + "DLPREVIOUSCOPY": true, + "DLURLCOMPLETE": true, + "DLURLCOMPLETEONLY": true, + "DLURLCOMPLETEWRITE": true, + "DLURLPATH": true, + "DLURLPATHONLY": true, + "DLURLPATHWRITE": true, + "DLURLSCHEME": true, + "DLURLSERVER": true, + "DLVALUE": true, + "DO": true, + "DOCUMENT": true, + "DOMAIN": true, + "DOUBLE": true, + "DROP": true, + "DYNAMIC": true, + "DYNAMIC_FUNCTION": true, + "DYNAMIC_FUNCTION_CODE": true, + "EACH": true, + "ELEMENT": true, + "ELSE": true, + "EMPTY": true, + "ENABLE": true, + "ENCODING": true, + "ENCRYPTED": true, + "END": true, + "END-EXEC": true, + "END_FRAME": true, + "END_PARTITION": true, + "ENFORCED": true, + "ENUM": true, + "EQUALS": true, + "ESCAPE": true, + "EVENT": true, + "EVERY": true, + "EXCEPT": true, + "EXCEPTION": true, + "EXCLUDE": true, + "EXCLUDING": true, + "EXCLUSIVE": true, + "EXEC": true, + "EXECUTE": true, + "EXISTS": true, + "EXP": true, + "EXPLAIN": true, + "EXPRESSION": true, + "EXTENSION": true, + "EXTERNAL": true, + "EXTRACT": true, + "FALSE": true, + "FAMILY": true, + "FETCH": true, + "FILE": true, + "FILTER": true, + "FINAL": true, + "FIRST": true, + "FIRST_VALUE": true, + "FLAG": true, + "FLOAT": true, + "FLOOR": true, + "FOLLOWING": true, + "FOR": true, + "FORCE": true, + "FOREIGN": true, + "FORTRAN": true, + "FORWARD": true, + "FOUND": true, + "FRAME_ROW": true, + "FREE": true, + "FREEZE": true, + "FROM": true, + "FS": true, + "FULL": true, + "FUNCTION": true, + "FUNCTIONS": true, + "FUSION": true, + "G": true, + "GENERAL": true, + "GENERATED": true, + "GET": true, + "GLOBAL": true, + "GO": true, + "GOTO": true, + "GRANT": true, + "GRANTED": true, + "GREATEST": true, + "GROUP": true, + "GROUPING": true, + "GROUPS": true, + "HANDLER": true, + "HAVING": true, + "HEADER": true, + "HEX": true, + "HIERARCHY": true, + "HOLD": true, + "HOUR": true, + "ID": true, + "IDENTITY": true, + "IF": true, + "IGNORE": true, + "ILIKE": true, + "IMMEDIATE": true, + "IMMEDIATELY": true, + "IMMUTABLE": true, + "IMPLEMENTATION": true, + "IMPLICIT": true, + "IMPORT": true, + "IN": true, + "INCLUDING": true, + "INCREMENT": true, + "INDENT": true, + "INDEX": true, + "INDEXES": true, + "INDICATOR": true, + "INHERIT": true, + "INHERITS": true, + "INITIALLY": true, + "INLINE": true, + "INNER": true, + "INOUT": true, + "INPUT": true, + "INSENSITIVE": true, + "INSERT": true, + "INSTANCE": true, + "INSTANTIABLE": true, + "INSTEAD": true, + "INT": true, + "INTEGER": true, + "INTEGRITY": true, + "INTERSECT": true, + "INTERSECTION": true, + "INTERVAL": true, + "INTO": true, + "INVOKER": true, + "IS": true, + "ISNULL": true, + "ISOLATION": true, + "JOIN": true, + "K": true, + "KEY": true, + "KEY_MEMBER": true, + "KEY_TYPE": true, + "LABEL": true, + "LAG": true, + "LANGUAGE": true, + "LARGE": true, + "LAST": true, + "LAST_VALUE": true, + "LATERAL": true, + "LC_COLLATE": true, + "LC_CTYPE": true, + "LEAD": true, + "LEADING": true, + "LEAKPROOF": true, + "LEAST": true, + "LEFT": true, + "LENGTH": true, + "LEVEL": true, + "LIBRARY": true, + "LIKE": true, + "LIKE_REGEX": true, + "LIMIT": true, + "LINK": true, + "LISTEN": true, + "LN": true, + "LOAD": true, + "LOCAL": true, + "LOCALTIME": true, + "LOCALTIMESTAMP": true, + "LOCATION": true, + "LOCATOR": true, + "LOCK": true, + "LOWER": true, + "M": true, + "MAP": true, + "MAPPING": true, + "MATCH": true, + "MATCHED": true, + "MATERIALIZED": true, + "MAX": true, + "MAXVALUE": true, + "MAX_CARDINALITY": true, + "MEMBER": true, + "MERGE": true, + "MESSAGE_LENGTH": true, + "MESSAGE_OCTET_LENGTH": true, + "MESSAGE_TEXT": true, + "METHOD": true, + "MIN": true, + "MINUTE": true, + "MINVALUE": true, + "MOD": true, + "MODE": true, + "MODIFIES": true, + "MODULE": true, + "MONTH": true, + "MORE": true, + "MOVE": true, + "MULTISET": true, + "MUMPS": true, + "NAME": true, + "NAMES": true, + "NAMESPACE": true, + "NATIONAL": true, + "NATURAL": true, + "NCHAR": true, + "NCLOB": true, + "NESTING": true, + "NEW": true, + "NEXT": true, + "NFC": true, + "NFD": true, + "NFKC": true, + "NFKD": true, + "NIL": true, + "NO": true, + "NONE": true, + "NORMALIZE": true, + "NORMALIZED": true, + "NOT": true, + "NOTHING": true, + "NOTIFY": true, + "NOTNULL": true, + "NOWAIT": true, + "NTH_VALUE": true, + "NTILE": true, + "NULL": true, + "NULLABLE": true, + "NULLIF": true, + "NULLS": true, + "NUMBER": true, + "NUMERIC": true, + "OBJECT": true, + "OCCURRENCES_REGEX": true, + "OCTETS": true, + "OCTET_LENGTH": true, + "OF": true, + "OFF": true, + "OFFSET": true, + "OIDS": true, + "OLD": true, + "ON": true, + "ONLY": true, + "OPEN": true, + "OPERATOR": true, + "OPTION": true, + "OPTIONS": true, + "OR": true, + "ORDER": true, + "ORDERING": true, + "ORDINALITY": true, + "OTHERS": true, + "OUT": true, + "OUTER": true, + "OUTPUT": true, + "OVER": true, + "OVERLAPS": true, + "OVERLAY": true, + "OVERRIDING": true, + "OWNED": true, + "OWNER": true, + "P": true, + "PAD": true, + "PARAMETER": true, + "PARAMETER_MODE": true, + "PARAMETER_NAME": true, + "PARAMETER_ORDINAL_POSITION": true, + "PARAMETER_SPECIFIC_CATALOG": true, + "PARAMETER_SPECIFIC_NAME": true, + "PARAMETER_SPECIFIC_SCHEMA": true, + "PARSER": true, + "PARTIAL": true, + "PARTITION": true, + "PASCAL": true, + "PASSING": true, + "PASSTHROUGH": true, + "PASSWORD": true, + "PATH": true, + "PERCENT": true, + "PERCENTILE_CONT": true, + "PERCENTILE_DISC": true, + "PERCENT_RANK": true, + "PERIOD": true, + "PERMISSION": true, + "PLACING": true, + "PLANS": true, + "PLI": true, + "PORTION": true, + "POSITION": true, + "POSITION_REGEX": true, + "POWER": true, + "PRECEDES": true, + "PRECEDING": true, + "PRECISION": true, + "PREPARE": true, + "PREPARED": true, + "PRESERVE": true, + "PRIMARY": true, + "PRIOR": true, + "PRIVILEGES": true, + "PROCEDURAL": true, + "PROCEDURE": true, + "PROGRAM": true, + "PUBLIC": true, + "QUOTE": true, + "RANGE": true, + "RANK": true, + "READ": true, + "READS": true, + "REAL": true, + "REASSIGN": true, + "RECHECK": true, + "RECOVERY": true, + "RECURSIVE": true, + "REF": true, + "REFERENCES": true, + "REFERENCING": true, + "REFRESH": true, + "REGR_AVGX": true, + "REGR_AVGY": true, + "REGR_COUNT": true, + "REGR_INTERCEPT": true, + "REGR_R2": true, + "REGR_SLOPE": true, + "REGR_SXX": true, + "REGR_SXY": true, + "REGR_SYY": true, + "REINDEX": true, + "RELATIVE": true, + "RELEASE": true, + "RENAME": true, + "REPEATABLE": true, + "REPLACE": true, + "REPLICA": true, + "REQUIRING": true, + "RESET": true, + "RESPECT": true, + "RESTART": true, + "RESTORE": true, + "RESTRICT": true, + "RESULT": true, + "RETURN": true, + "RETURNED_CARDINALITY": true, + "RETURNED_LENGTH": true, + "RETURNED_OCTET_LENGTH": true, + "RETURNED_SQLSTATE": true, + "RETURNING": true, + "RETURNS": true, + "REVOKE": true, + "RIGHT": true, + "ROLE": true, + "ROLLBACK": true, + "ROLLUP": true, + "ROUTINE": true, + "ROUTINE_CATALOG": true, + "ROUTINE_NAME": true, + "ROUTINE_SCHEMA": true, + "ROW": true, + "ROWS": true, + "ROW_COUNT": true, + "ROW_NUMBER": true, + "RULE": true, + "SAVEPOINT": true, + "SCALE": true, + "SCHEMA": true, + "SCHEMA_NAME": true, + "SCOPE": true, + "SCOPE_CATALOG": true, + "SCOPE_NAME": true, + "SCOPE_SCHEMA": true, + "SCROLL": true, + "SEARCH": true, + "SECOND": true, + "SECTION": true, + "SECURITY": true, + "SELECT": true, + "SELECTIVE": true, + "SELF": true, + "SENSITIVE": true, + "SEQUENCE": true, + "SEQUENCES": true, + "SERIALIZABLE": true, + "SERVER": true, + "SERVER_NAME": true, + "SESSION": true, + "SESSION_USER": true, + "SET": true, + "SETOF": true, + "SETS": true, + "SHARE": true, + "SHOW": true, + "SIMILAR": true, + "SIMPLE": true, + "SIZE": true, + "SMALLINT": true, + "SNAPSHOT": true, + "SOME": true, + "SOURCE": true, + "SPACE": true, + "SPECIFIC": true, + "SPECIFICTYPE": true, + "SPECIFIC_NAME": true, + "SQL": true, + "SQLCODE": true, + "SQLERROR": true, + "SQLEXCEPTION": true, + "SQLSTATE": true, + "SQLWARNING": true, + "SQRT": true, + "STABLE": true, + "STANDALONE": true, + "START": true, + "STATE": true, + "STATEMENT": true, + "STATIC": true, + "STATISTICS": true, + "STDDEV_POP": true, + "STDDEV_SAMP": true, + "STDIN": true, + "STDOUT": true, + "STORAGE": true, + "STRICT": true, + "STRIP": true, + "STRUCTURE": true, + "STYLE": true, + "SUBCLASS_ORIGIN": true, + "SUBMULTISET": true, + "SUBSTRING": true, + "SUBSTRING_REGEX": true, + "SUCCEEDS": true, + "SUM": true, + "SYMMETRIC": true, + "SYSID": true, + "SYSTEM": true, + "SYSTEM_TIME": true, + "SYSTEM_USER": true, + "T": true, + "TABLE": true, + "TABLES": true, + "TABLESAMPLE": true, + "TABLESPACE": true, + "TABLE_NAME": true, + "TEMP": true, + "TEMPLATE": true, + "TEMPORARY": true, + "TEXT": true, + "THEN": true, + "TIES": true, + "TIME": true, + "TIMESTAMP": true, + "TIMEZONE_HOUR": true, + "TIMEZONE_MINUTE": true, + "TO": true, + "TOKEN": true, + "TOP_LEVEL_COUNT": true, + "TRAILING": true, + "TRANSACTION": true, + "TRANSACTIONS_COMMITTED": true, + "TRANSACTIONS_ROLLED_BACK": true, + "TRANSACTION_ACTIVE": true, + "TRANSFORM": true, + "TRANSFORMS": true, + "TRANSLATE": true, + "TRANSLATE_REGEX": true, + "TRANSLATION": true, + "TREAT": true, + "TRIGGER": true, + "TRIGGER_CATALOG": true, + "TRIGGER_NAME": true, + "TRIGGER_SCHEMA": true, + "TRIM": true, + "TRIM_ARRAY": true, + "TRUE": true, + "TRUNCATE": true, + "TRUSTED": true, + "TYPE": true, + "TYPES": true, + "UESCAPE": true, + "UNBOUNDED": true, + "UNCOMMITTED": true, + "UNDER": true, + "UNENCRYPTED": true, + "UNION": true, + "UNIQUE": true, + "UNKNOWN": true, + "UNLINK": true, + "UNLISTEN": true, + "UNLOGGED": true, + "UNNAMED": true, + "UNNEST": true, + "UNTIL": true, + "UNTYPED": true, + "UPDATE": true, + "UPPER": true, + "URI": true, + "USAGE": true, + "USER": true, + "USER_DEFINED_TYPE_CATALOG": true, + "USER_DEFINED_TYPE_CODE": true, + "USER_DEFINED_TYPE_NAME": true, + "USER_DEFINED_TYPE_SCHEMA": true, + "USING": true, + "VACUUM": true, + "VALID": true, + "VALIDATE": true, + "VALIDATOR": true, + "VALUE": true, + "VALUES": true, + "VALUE_OF": true, + "VARBINARY": true, + "VARCHAR": true, + "VARIADIC": true, + "VARYING": true, + "VAR_POP": true, + "VAR_SAMP": true, + "VERBOSE": true, + "VERSION": true, + "VERSIONING": true, + "VIEW": true, + "VOLATILE": true, + "WHEN": true, + "WHENEVER": true, + "WHERE": true, + "WHITESPACE": true, + "WIDTH_BUCKET": true, + "WINDOW": true, + "WITH": true, + "WITHIN": true, + "WITHOUT": true, + "WORK": true, + "WRAPPER": true, + "WRITE": true, + "XML": true, + "XMLAGG": true, + "XMLATTRIBUTES": true, + "XMLBINARY": true, + "XMLCAST": true, + "XMLCOMMENT": true, + "XMLCONCAT": true, + "XMLDECLARATION": true, + "XMLDOCUMENT": true, + "XMLELEMENT": true, + "XMLEXISTS": true, + "XMLFOREST": true, + "XMLITERATE": true, + "XMLNAMESPACES": true, + "XMLPARSE": true, + "XMLPI": true, + "XMLQUERY": true, + "XMLROOT": true, + "XMLSCHEMA": true, + "XMLSERIALIZE": true, + "XMLTABLE": true, + "XMLTEXT": true, + "XMLVALIDATE": true, + "YEAR": true, + "YES": true, + "ZONE": true, } // DefaultPostgresSchema default postgres schema @@ -822,7 +822,7 @@ func (db *postgres) SqlType(c *core.Column) string { case core.NVarchar: res = core.Varchar case core.Uuid: - res = core.Uuid + return core.Uuid case core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob: return core.Bytea case core.Double: @@ -834,6 +834,10 @@ func (db *postgres) SqlType(c *core.Column) string { res = t } + if strings.EqualFold(res, "bool") { + // for bool, we don't need length information + return res + } hasLen1 := (c.Length > 0) hasLen2 := (c.Length2 > 0) @@ -859,10 +863,6 @@ func (db *postgres) Quote(name string) string { return "\"" + name + "\"" } -func (db *postgres) QuoteStr() string { - return "\"" -} - func (db *postgres) AutoIncrStr() string { return "" } @@ -994,7 +994,7 @@ WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.att return nil, nil, err } - //fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, numPrecision, numRadix, isPK, isUnique) + // fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, numPrecision, numRadix, isPK, isUnique) var maxLen int if maxLenStr != nil { maxLen, err = strconv.Atoi(*maxLenStr) @@ -1089,6 +1089,17 @@ func (db *postgres) GetTables() ([]*core.Table, error) { return tables, nil } +func getIndexColName(indexdef string) []string { + var colNames []string + + cs := strings.Split(indexdef, "(") + for _, v := range strings.Split(strings.Split(cs[1], ")")[0], ",") { + colNames = append(colNames, strings.Split(strings.TrimLeft(v, " "), " ")[0]) + } + + return colNames +} + func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) { args := []interface{}{tableName} s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE tablename=$1") @@ -1122,8 +1133,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) } else { indexType = core.IndexType } - cs := strings.Split(indexdef, "(") - colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") + colNames = getIndexColName(indexdef) var isRegular bool if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { newIdxName := indexName[5+len(tableName):] diff --git a/dialect_postgres_test.go b/dialect_postgres_test.go index 6e6c44bb..ebc079e1 100644 --- a/dialect_postgres_test.go +++ b/dialect_postgres_test.go @@ -4,8 +4,9 @@ import ( "reflect" "testing" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/jackc/pgx/stdlib" + "github.com/stretchr/testify/assert" ) func TestParsePostgres(t *testing.T) { @@ -84,3 +85,37 @@ func TestParsePgx(t *testing.T) { } } + +func TestGetIndexColName(t *testing.T) { + t.Run("Index", func(t *testing.T) { + s := "CREATE INDEX test2_mm_idx ON test2 (major);" + colNames := getIndexColName(s) + assert.Equal(t, []string{"major"}, colNames) + }) + + t.Run("Multicolumn indexes", func(t *testing.T) { + s := "CREATE INDEX test2_mm_idx ON test2 (major, minor);" + colNames := getIndexColName(s) + assert.Equal(t, []string{"major", "minor"}, colNames) + }) + + t.Run("Indexes and ORDER BY", func(t *testing.T) { + s := "CREATE INDEX test2_mm_idx ON test2 (major NULLS FIRST, minor DESC NULLS LAST);" + colNames := getIndexColName(s) + assert.Equal(t, []string{"major", "minor"}, colNames) + }) + + t.Run("Combining Multiple Indexes", func(t *testing.T) { + s := "CREATE INDEX test2_mm_cm_idx ON public.test2 USING btree (major, minor) WHERE ((major <> 5) AND (minor <> 6))" + colNames := getIndexColName(s) + assert.Equal(t, []string{"major", "minor"}, colNames) + }) + + t.Run("unique", func(t *testing.T) { + s := "CREATE UNIQUE INDEX test2_mm_uidx ON test2 (major);" + colNames := getIndexColName(s) + assert.Equal(t, []string{"major"}, colNames) + }) + + t.Run("Indexes on Expressions", func(t *testing.T) {}) +} diff --git a/dialect_sqlite3.go b/dialect_sqlite3.go index e1294814..60f07295 100644 --- a/dialect_sqlite3.go +++ b/dialect_sqlite3.go @@ -11,7 +11,7 @@ import ( "regexp" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) var ( @@ -202,10 +202,6 @@ func (db *sqlite3) Quote(name string) string { return "`" + name + "`" } -func (db *sqlite3) QuoteStr() string { - return "`" -} - func (db *sqlite3) AutoIncrStr() string { return "AUTOINCREMENT" } diff --git a/engine.go b/engine.go index 89a96d9f..ebcab91b 100644 --- a/engine.go +++ b/engine.go @@ -7,6 +7,7 @@ package xorm import ( "bufio" "bytes" + "context" "database/sql" "encoding/gob" "errors" @@ -19,8 +20,8 @@ import ( "sync" "time" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) // Engine is the major struct of xorm, it means a database manager. @@ -52,6 +53,8 @@ type Engine struct { cachers map[string]core.Cacher cacherLock sync.RWMutex + + defaultContext context.Context } func (engine *Engine) setCacher(tableName string, cacher core.Cacher) { @@ -122,6 +125,7 @@ func (engine *Engine) Logger() core.ILogger { // SetLogger set the new logger func (engine *Engine) SetLogger(logger core.ILogger) { engine.logger = logger + engine.showSQL = logger.IsShowSQL() engine.dialect.SetLogger(logger) } @@ -171,12 +175,6 @@ func (engine *Engine) SupportInsertMany() bool { return engine.dialect.SupportInsertMany() } -// QuoteStr Engine's database use which character as quote. -// mysql, sqlite use ` and postgres use " -func (engine *Engine) QuoteStr() string { - return engine.dialect.QuoteStr() -} - func (engine *Engine) quoteColumns(columnStr string) string { columns := strings.Split(columnStr, ",") for i := 0; i < len(columns); i++ { @@ -192,13 +190,10 @@ func (engine *Engine) Quote(value string) string { return value } - if string(value[0]) == engine.dialect.QuoteStr() || value[0] == '`' { - return value - } + buf := builder.StringBuilder{} + engine.QuoteTo(&buf, value) - value = strings.Replace(value, ".", engine.dialect.QuoteStr()+"."+engine.dialect.QuoteStr(), -1) - - return engine.dialect.QuoteStr() + value + engine.dialect.QuoteStr() + return buf.String() } // QuoteTo quotes string and writes into the buffer @@ -212,20 +207,30 @@ func (engine *Engine) QuoteTo(buf *builder.StringBuilder, value string) { return } - if string(value[0]) == engine.dialect.QuoteStr() || value[0] == '`' { - buf.WriteString(value) + quotePair := engine.dialect.Quote("") + + if value[0] == '`' || len(quotePair) < 2 || value[0] == quotePair[0] { // no quote + _, _ = buf.WriteString(value) return + } else { + prefix, suffix := quotePair[0], quotePair[1] + + _ = buf.WriteByte(prefix) + for i := 0; i < len(value); i++ { + if value[i] == '.' { + _ = buf.WriteByte(suffix) + _ = buf.WriteByte('.') + _ = buf.WriteByte(prefix) + } else { + _ = buf.WriteByte(value[i]) + } + } + _ = buf.WriteByte(suffix) } - - value = strings.Replace(value, ".", engine.dialect.QuoteStr()+"."+engine.dialect.QuoteStr(), -1) - - buf.WriteString(engine.dialect.QuoteStr()) - buf.WriteString(value) - buf.WriteString(engine.dialect.QuoteStr()) } func (engine *Engine) quote(sql string) string { - return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr() + return engine.dialect.Quote(sql) } // SqlType will be deprecated, please use SQLType instead @@ -481,7 +486,8 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D } cols := table.ColumnsSeq() - colNames := dialect.Quote(strings.Join(cols, dialect.Quote(", "))) + colNames := engine.dialect.Quote(strings.Join(cols, engine.dialect.Quote(", "))) + destColNames := dialect.Quote(strings.Join(cols, dialect.Quote(", "))) rows, err := engine.DB().Query("SELECT " + colNames + " FROM " + engine.Quote(table.Name)) if err != nil { @@ -496,7 +502,7 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D return err } - _, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+colNames+") VALUES (") + _, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+destColNames+") VALUES (") if err != nil { return err } @@ -526,7 +532,11 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D } else if col.SQLType.IsNumeric() { switch reflect.TypeOf(d).Kind() { case reflect.Slice: - temp += fmt.Sprintf(", %s", string(d.([]byte))) + if col.SQLType.Name == core.Bool { + temp += fmt.Sprintf(", %v", strconv.FormatBool(d.([]byte)[0] != byte('0'))) + } else { + temp += fmt.Sprintf(", %s", string(d.([]byte))) + } case reflect.Int16, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int: if col.SQLType.Name == core.Bool { temp += fmt.Sprintf(", %v", strconv.FormatBool(reflect.ValueOf(d).Int() > 0)) @@ -563,7 +573,7 @@ func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.D // FIXME: Hack for postgres if string(dialect.DBType()) == core.POSTGRES && table.AutoIncrColumn() != nil { - _, err = io.WriteString(w, "SELECT setval('table_id_seq', COALESCE((SELECT MAX("+table.AutoIncrColumn().Name+") FROM "+dialect.Quote(table.Name)+"), 1), false);\n") + _, err = io.WriteString(w, "SELECT setval('"+table.Name+"_id_seq', COALESCE((SELECT MAX("+table.AutoIncrColumn().Name+") + 1 FROM "+dialect.Quote(table.Name)+"), 1), false);\n") if err != nil { return err } @@ -914,7 +924,16 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { engine: engine, } - if strings.ToUpper(tags[0]) == "EXTENDS" { + if strings.HasPrefix(strings.ToUpper(tags[0]), "EXTENDS") { + pStart := strings.Index(tags[0], "(") + if pStart > -1 && strings.HasSuffix(tags[0], ")") { + var tagPrefix = strings.TrimFunc(tags[0][pStart+1:len(tags[0])-1], func(r rune) bool { + return r == '\'' || r == '"' + }) + + ctx.params = []string{tagPrefix} + } + if err := ExtendsTagHandler(&ctx); err != nil { return nil, err } @@ -1346,31 +1365,31 @@ func (engine *Engine) DropIndexes(bean interface{}) error { } // Exec raw sql -func (engine *Engine) Exec(sqlorArgs ...interface{}) (sql.Result, error) { +func (engine *Engine) Exec(sqlOrArgs ...interface{}) (sql.Result, error) { session := engine.NewSession() defer session.Close() - return session.Exec(sqlorArgs...) + return session.Exec(sqlOrArgs...) } // Query a raw sql and return records as []map[string][]byte -func (engine *Engine) Query(sqlorArgs ...interface{}) (resultsSlice []map[string][]byte, err error) { +func (engine *Engine) Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) { session := engine.NewSession() defer session.Close() - return session.Query(sqlorArgs...) + return session.Query(sqlOrArgs...) } // QueryString runs a raw sql and return records as []map[string]string -func (engine *Engine) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) { +func (engine *Engine) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { session := engine.NewSession() defer session.Close() - return session.QueryString(sqlorArgs...) + return session.QueryString(sqlOrArgs...) } // QueryInterface runs a raw sql and return records as []map[string]interface{} -func (engine *Engine) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) { +func (engine *Engine) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { session := engine.NewSession() defer session.Close() - return session.QueryInterface(sqlorArgs...) + return session.QueryInterface(sqlOrArgs...) } // Insert one or more records @@ -1563,7 +1582,7 @@ func (engine *Engine) formatColTime(col *core.Column, t time.Time) (v interface{ func (engine *Engine) formatTime(sqlTypeName string, t time.Time) (v interface{}) { switch sqlTypeName { case core.Time: - s := t.Format("2006-01-02 15:04:05") //time.RFC3339 + s := t.Format("2006-01-02 15:04:05") // time.RFC3339 v = s[11:19] case core.Date: v = t.Format("2006-01-02") diff --git a/engine_cond.go b/engine_cond.go index 4dde8662..702ac804 100644 --- a/engine_cond.go +++ b/engine_cond.go @@ -6,14 +6,13 @@ package xorm import ( "database/sql/driver" - "encoding/json" "fmt" "reflect" "strings" "time" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) func (engine *Engine) buildConds(table *core.Table, bean interface{}, @@ -147,7 +146,7 @@ func (engine *Engine) buildConds(table *core.Table, bean interface{}, } else { if col.SQLType.IsJson() { if col.SQLType.IsText() { - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { engine.logger.Error(err) continue @@ -156,7 +155,7 @@ func (engine *Engine) buildConds(table *core.Table, bean interface{}, } else if col.SQLType.IsBlob() { var bytes []byte var err error - bytes, err = json.Marshal(fieldValue.Interface()) + bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { engine.logger.Error(err) continue @@ -195,7 +194,7 @@ func (engine *Engine) buildConds(table *core.Table, bean interface{}, } if col.SQLType.IsText() { - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { engine.logger.Error(err) continue @@ -212,7 +211,7 @@ func (engine *Engine) buildConds(table *core.Table, bean interface{}, continue } } else { - bytes, err = json.Marshal(fieldValue.Interface()) + bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { engine.logger.Error(err) continue diff --git a/engine_context.go b/engine_context.go new file mode 100644 index 00000000..c6cbb76c --- /dev/null +++ b/engine_context.go @@ -0,0 +1,28 @@ +// Copyright 2019 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 go1.8 + +package xorm + +import "context" + +// Context creates a session with the context +func (engine *Engine) Context(ctx context.Context) *Session { + session := engine.NewSession() + session.isAutoClose = true + return session.Context(ctx) +} + +// SetDefaultContext set the default context +func (engine *Engine) SetDefaultContext(ctx context.Context) { + engine.defaultContext = ctx +} + +// PingContext tests if database is alive +func (engine *Engine) PingContext(ctx context.Context) error { + session := engine.NewSession() + defer session.Close() + return session.PingContext(ctx) +} diff --git a/context_test.go b/engine_context_test.go similarity index 68% rename from context_test.go rename to engine_context_test.go index 17437af5..1a3276ce 100644 --- a/context_test.go +++ b/engine_context_test.go @@ -17,9 +17,12 @@ import ( func TestPingContext(t *testing.T) { assert.NoError(t, prepareEngine()) - ctx, canceled := context.WithTimeout(context.Background(), 10*time.Second) + ctx, canceled := context.WithTimeout(context.Background(), time.Nanosecond) defer canceled() + time.Sleep(time.Nanosecond) + err := testEngine.(*Engine).PingContext(ctx) - assert.NoError(t, err) + assert.Error(t, err) + assert.Contains(t, err.Error(), "context deadline exceeded") } diff --git a/engine_group.go b/engine_group.go index 5eee3e61..42d49eca 100644 --- a/engine_group.go +++ b/engine_group.go @@ -5,9 +5,10 @@ package xorm import ( + "context" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) // EngineGroup defines an engine group @@ -74,6 +75,20 @@ func (eg *EngineGroup) Close() error { return nil } +// Context returned a group session +func (eg *EngineGroup) Context(ctx context.Context) *Session { + sess := eg.NewSession() + sess.isAutoClose = true + return sess.Context(ctx) +} + +// NewSession returned a group session +func (eg *EngineGroup) NewSession() *Session { + sess := eg.Engine.NewSession() + sess.sessionType = groupSession + return sess +} + // Master returns the master engine func (eg *EngineGroup) Master() *Engine { return eg.Engine diff --git a/engine_table.go b/engine_table.go index 94871a4b..eb5aa850 100644 --- a/engine_table.go +++ b/engine_table.go @@ -9,10 +9,10 @@ import ( "reflect" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) -// TableNameWithSchema will automatically add schema prefix on table name +// tbNameWithSchema will automatically add schema prefix on table name func (engine *Engine) tbNameWithSchema(v string) string { // Add schema name as prefix of table name. // Only for postgres database. diff --git a/error.go b/error.go index a223fc4a..a67527ac 100644 --- a/error.go +++ b/error.go @@ -26,6 +26,8 @@ var ( ErrNotImplemented = errors.New("Not implemented") // ErrConditionType condition type unsupported ErrConditionType = errors.New("Unsupported condition type") + // ErrUnSupportedSQLType parameter of SQL is not supported + ErrUnSupportedSQLType = errors.New("unsupported sql type") ) // ErrFieldIsNotExist columns does not exist diff --git a/go.mod b/go.mod index 4510e93c..9a30e797 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,19 @@ -module "github.com/go-xorm/xorm" +module github.com/go-xorm/xorm require ( - "github.com/go-xorm/builder" v0.0.0-20180322150003-a9b7ffcca3f0 - "github.com/go-xorm/core" v0.0.0-20180322150003-0177c08cee88 + github.com/cockroachdb/apd v1.1.0 // indirect + github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 + github.com/go-sql-driver/mysql v1.4.1 + github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect + github.com/jackc/pgx v3.3.0+incompatible + github.com/kr/pretty v0.1.0 // indirect + github.com/lib/pq v1.0.0 + github.com/mattn/go-sqlite3 v1.10.0 + github.com/pkg/errors v0.8.1 // indirect + github.com/satori/go.uuid v1.2.0 // indirect + github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect + github.com/stretchr/testify v1.3.0 + github.com/ziutek/mymysql v1.5.4 + xorm.io/builder v0.3.5 + xorm.io/core v0.7.0 ) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..f00ff971 --- /dev/null +++ b/go.sum @@ -0,0 +1,159 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o= +github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= +github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +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= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90= +github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +xorm.io/builder v0.3.5 h1:EilU39fvWDxjb1cDaELpYhsF+zziRBhew8xk4pngO+A= +xorm.io/builder v0.3.5/go.mod h1:ZFbByS/KxZI1FKRjL05PyJ4YrK2bcxlUaAxdum5aTR8= +xorm.io/core v0.6.3 h1:n1NhVZt1s2oLw1BZfX2ocIJsHyso259uPgg63BGr37M= +xorm.io/core v0.6.3/go.mod h1:8kz/C6arVW/O9vk3PgCiMJO2hIAm1UcuOL3dSPyZ2qo= diff --git a/helpers.go b/helpers.go index f1705782..a31e922c 100644 --- a/helpers.go +++ b/helpers.go @@ -12,7 +12,7 @@ import ( "strconv" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) // str2PK convert string value to primary key value according to tp @@ -281,7 +281,7 @@ func rValue(bean interface{}) reflect.Value { func rType(bean interface{}) reflect.Type { sliceValue := reflect.Indirect(reflect.ValueOf(bean)) - //return reflect.TypeOf(sliceValue.Interface()) + // return reflect.TypeOf(sliceValue.Interface()) return sliceValue.Type() } @@ -309,3 +309,24 @@ func sliceEq(left, right []string) bool { func indexName(tableName, idxName string) string { return fmt.Sprintf("IDX_%v_%v", tableName, idxName) } + +func eraseAny(value string, strToErase ...string) string { + if len(strToErase) == 0 { + return value + } + var replaceSeq []string + for _, s := range strToErase { + replaceSeq = append(replaceSeq, s, "") + } + + replacer := strings.NewReplacer(replaceSeq...) + + return replacer.Replace(value) +} + +func quoteColumns(cols []string, quoteFunc func(string) string, sep string) string { + for i := range cols { + cols[i] = quoteFunc(cols[i]) + } + return strings.Join(cols, sep+" ") +} diff --git a/helpers_test.go b/helpers_test.go index d57c54ae..7e317126 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -4,7 +4,11 @@ package xorm -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/assert" +) func TestSplitTag(t *testing.T) { var cases = []struct { @@ -24,3 +28,19 @@ func TestSplitTag(t *testing.T) { } } } + +func TestEraseAny(t *testing.T) { + raw := "SELECT * FROM `table`.[table_name]" + assert.EqualValues(t, raw, eraseAny(raw)) + assert.EqualValues(t, "SELECT * FROM table.[table_name]", eraseAny(raw, "`")) + assert.EqualValues(t, "SELECT * FROM table.table_name", eraseAny(raw, "`", "[", "]")) +} + +func TestQuoteColumns(t *testing.T) { + cols := []string{"f1", "f2", "f3"} + quoteFunc := func(value string) string { + return "[" + value + "]" + } + + assert.EqualValues(t, "[f1], [f2], [f3]", quoteColumns(cols, quoteFunc, ",")) +} diff --git a/interface.go b/interface.go index 33d2078e..0928f66a 100644 --- a/interface.go +++ b/interface.go @@ -5,11 +5,12 @@ package xorm import ( + "context" "database/sql" "reflect" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) // Interface defines the interface which Engine, EngineGroup and Session will implementate. @@ -27,7 +28,7 @@ type Interface interface { Delete(interface{}) (int64, error) Distinct(columns ...string) *Session DropIndexes(bean interface{}) error - Exec(sqlOrAgrs ...interface{}) (sql.Result, error) + Exec(sqlOrArgs ...interface{}) (sql.Result, error) Exist(bean ...interface{}) (bool, error) Find(interface{}, ...interface{}) error FindAndCount(interface{}, ...interface{}) (int64, error) @@ -49,9 +50,9 @@ type Interface interface { Omit(columns ...string) *Session OrderBy(order string) *Session Ping() error - Query(sqlOrAgrs ...interface{}) (resultsSlice []map[string][]byte, err error) - QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) - QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) + Query(sqlOrArgs ...interface{}) (resultsSlice []map[string][]byte, err error) + QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) + QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) Rows(bean interface{}) (*Rows, error) SetExpr(string, string) *Session SQL(interface{}, ...interface{}) *Session @@ -73,6 +74,7 @@ type EngineInterface interface { Before(func(interface{})) *Session Charset(charset string) *Session ClearCache(...interface{}) error + Context(context.Context) *Session CreateTables(...interface{}) error DBMetas() ([]*core.Table, error) Dialect() core.Dialect diff --git a/json.go b/json.go new file mode 100644 index 00000000..fdb6ce56 --- /dev/null +++ b/json.go @@ -0,0 +1,31 @@ +// Copyright 2019 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 "encoding/json" + +// JSONInterface represents an interface to handle json data +type JSONInterface interface { + Marshal(v interface{}) ([]byte, error) + Unmarshal(data []byte, v interface{}) error +} + +var ( + // DefaultJSONHandler default json handler + DefaultJSONHandler JSONInterface = StdJSON{} +) + +// StdJSON implements JSONInterface via encoding/json +type StdJSON struct{} + +// Marshal implements JSONInterface +func (StdJSON) Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +// Unmarshal implements JSONInterface +func (StdJSON) Unmarshal(data []byte, v interface{}) error { + return json.Unmarshal(data, v) +} diff --git a/logger.go b/logger.go index 727d030a..7b26e77f 100644 --- a/logger.go +++ b/logger.go @@ -9,7 +9,7 @@ import ( "io" "log" - "github.com/go-xorm/core" + "xorm.io/core" ) // default log options diff --git a/migrate/migrate_test.go b/migrate/migrate_test.go index 086707ec..2452cce2 100644 --- a/migrate/migrate_test.go +++ b/migrate/migrate_test.go @@ -8,7 +8,7 @@ import ( "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" - "gopkg.in/stretchr/testify.v1/assert" + "github.com/stretchr/testify/assert" ) type Person struct { diff --git a/processors_test.go b/processors_test.go index e8c27e89..d1efc047 100644 --- a/processors_test.go +++ b/processors_test.go @@ -154,118 +154,86 @@ func TestProcessors(t *testing.T) { } _, err = testEngine.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedViaExt == 0 { - t.Error(errors.New("AfterInsertedViaExt not set")) - } - } + assert.NoError(t, err) + assert.True(t, p.Id > 0, "Inserted ID not set") + assert.True(t, p.B4InsertFlag > 0, "B4InsertFlag not set") + assert.True(t, p.AfterInsertedFlag > 0, "B4InsertFlag not set") + assert.True(t, p.B4InsertViaExt > 0, "B4InsertFlag not set") + assert.True(t, p.AfterInsertedViaExt > 0, "AfterInsertedViaExt not set") p2 := &ProcessorsStruct{} - _, err = testEngine.ID(p.Id).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p2.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p2.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p2.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - if p2.BeforeSetFlag != 9 { - t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p2.BeforeSetFlag)) - } - if p2.AfterSetFlag != 9 { - t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p2.BeforeSetFlag)) - } - } + has, err := testEngine.ID(p.Id).Get(p2) + assert.NoError(t, err) + assert.True(t, has) + assert.True(t, p2.B4InsertFlag > 0, "B4InsertFlag not set") + assert.True(t, p2.AfterInsertedFlag == 0, "AfterInsertedFlag is set") + assert.True(t, p2.B4InsertViaExt > 0, "B4InsertViaExt not set") + assert.True(t, p2.AfterInsertedViaExt == 0, "AfterInsertedViaExt is set") + assert.True(t, p2.BeforeSetFlag == 9, fmt.Sprintf("BeforeSetFlag is %d not 9", p2.BeforeSetFlag)) + assert.True(t, p2.AfterSetFlag == 9, fmt.Sprintf("AfterSetFlag is %d not 9", p2.BeforeSetFlag)) // -- // test find processors var p2Find []*ProcessorsStruct err = testEngine.Find(&p2Find) - if err != nil { + assert.NoError(t, err) + + if len(p2Find) != 1 { + err = errors.New("Should get 1") t.Error(err) - panic(err) - } else { - if len(p2Find) != 1 { - err = errors.New("Should get 1") - t.Error(err) - } - p21 := p2Find[0] - if p21.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p21.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p21.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p21.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - if p21.BeforeSetFlag != 9 { - t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p21.BeforeSetFlag)) - } - if p21.AfterSetFlag != 9 { - t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p21.BeforeSetFlag)) - } + } + p21 := p2Find[0] + if p21.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p21.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p21.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p21.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } + if p21.BeforeSetFlag != 9 { + t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p21.BeforeSetFlag)) + } + if p21.AfterSetFlag != 9 { + t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p21.BeforeSetFlag)) } // -- // test find map processors var p2FindMap = make(map[int64]*ProcessorsStruct) err = testEngine.Find(&p2FindMap) - if err != nil { - t.Error(err) - panic(err) - } else { - if len(p2FindMap) != 1 { - err = errors.New("Should get 1") - t.Error(err) - } - var p22 *ProcessorsStruct - for _, v := range p2FindMap { - p22 = v - } + assert.NoError(t, err) - if p22.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p22.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p22.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p22.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - if p22.BeforeSetFlag != 9 { - t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p22.BeforeSetFlag)) - } - if p22.AfterSetFlag != 9 { - t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p22.BeforeSetFlag)) - } + if len(p2FindMap) != 1 { + err = errors.New("Should get 1") + t.Error(err) + } + var p22 *ProcessorsStruct + for _, v := range p2FindMap { + p22 = v + } + + if p22.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p22.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p22.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p22.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } + if p22.BeforeSetFlag != 9 { + t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p22.BeforeSetFlag)) + } + if p22.AfterSetFlag != 9 { + t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p22.BeforeSetFlag)) } // -- @@ -289,48 +257,43 @@ func TestProcessors(t *testing.T) { p = p2 // reset _, err = testEngine.Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag == 0 { - t.Error(errors.New("AfterUpdatedFlag not set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt == 0 { - t.Error(errors.New("AfterUpdatedViaExt not set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag == 0 { + t.Error(errors.New("AfterUpdatedFlag not set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt == 0 { + t.Error(errors.New("AfterUpdatedViaExt not set")) } p2 = &ProcessorsStruct{} - _, err = testEngine.ID(p.Id).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p2.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set: " + string(p.AfterUpdatedFlag))) - } - if p2.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p2.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set: " + string(p.AfterUpdatedViaExt))) - } - if p2.BeforeSetFlag != 9 { - t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p2.BeforeSetFlag)) - } - if p2.AfterSetFlag != 9 { - t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p2.BeforeSetFlag)) - } + has, err = testEngine.ID(p.Id).Get(p2) + assert.NoError(t, err) + assert.True(t, has) + + if p2.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p2.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set: " + string(p.AfterUpdatedFlag))) + } + if p2.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p2.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set: " + string(p.AfterUpdatedViaExt))) + } + if p2.BeforeSetFlag != 9 { + t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p2.BeforeSetFlag)) + } + if p2.AfterSetFlag != 9 { + t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p2.BeforeSetFlag)) } // -- @@ -353,22 +316,18 @@ func TestProcessors(t *testing.T) { p = p2 // reset _, err = testEngine.Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag == 0 { - t.Error(errors.New("AfterDeletedFlag not set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt == 0 { - t.Error(errors.New("AfterDeletedViaExt not set")) - } + assert.NoError(t, err) + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) + } + if p.AfterDeletedFlag == 0 { + t.Error(errors.New("AfterDeletedFlag not set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt == 0 { + t.Error(errors.New("AfterDeletedViaExt not set")) } // -- @@ -377,54 +336,46 @@ func TestProcessors(t *testing.T) { pslice = append(pslice, &ProcessorsStruct{}) pslice = append(pslice, &ProcessorsStruct{}) cnt, err := testEngine.Before(b4InsertFunc).After(afterInsertFunc).Insert(&pslice) - if err != nil { - t.Error(err) - panic(err) - } else { - if cnt != 2 { - t.Error(errors.New("incorrect insert count")) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt, "incorrect insert count") + + for _, elem := range pslice { + if elem.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) } - for _, elem := range pslice { - if elem.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if elem.AfterInsertedFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if elem.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if elem.AfterInsertedViaExt == 0 { - t.Error(errors.New("AfterInsertedViaExt not set")) - } + if elem.AfterInsertedFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if elem.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if elem.AfterInsertedViaExt == 0 { + t.Error(errors.New("AfterInsertedViaExt not set")) } } for _, elem := range pslice { p = &ProcessorsStruct{} _, err = testEngine.ID(elem.Id).Get(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p2.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p2.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p2.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - if p2.BeforeSetFlag != 9 { - t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p2.BeforeSetFlag)) - } - if p2.AfterSetFlag != 9 { - t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p2.BeforeSetFlag)) - } + assert.NoError(t, err) + + if p2.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p2.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p2.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p2.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } + if p2.BeforeSetFlag != 9 { + t.Error(fmt.Errorf("BeforeSetFlag is %d not 9", p2.BeforeSetFlag)) + } + if p2.AfterSetFlag != 9 { + t.Error(fmt.Errorf("AfterSetFlag is %d not 9", p2.BeforeSetFlag)) } } // -- @@ -434,24 +385,17 @@ func TestProcessorsTx(t *testing.T) { assert.NoError(t, prepareEngine()) err := testEngine.DropTables(&ProcessorsStruct{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.CreateTables(&ProcessorsStruct{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) // test insert processors with tx rollback session := testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) p := &ProcessorsStruct{} b4InsertFunc := func(bean interface{}) { @@ -470,133 +414,117 @@ func TestProcessorsTx(t *testing.T) { } } _, err = session.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("B4InsertFlag is set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("B4InsertFlag is set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) } err = session.Rollback() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("B4InsertFlag is set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("B4InsertFlag is set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } + session.Close() + p2 := &ProcessorsStruct{} _, err = testEngine.ID(p.Id).Get(p2) - if err != nil { + assert.NoError(t, err) + + if p2.Id > 0 { + err = errors.New("tx got committed upon insert!?") t.Error(err) panic(err) - } else { - if p2.Id > 0 { - err = errors.New("tx got committed upon insert!?") - t.Error(err) - panic(err) - } } // -- // test insert processors with tx commit session = testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) p = &ProcessorsStruct{} _, err = session.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) } err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag == 0 { - t.Error(errors.New("AfterInsertedFlag not set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt == 0 { - t.Error(errors.New("AfterInsertedViaExt not set")) - } + assert.NoError(t, err) + + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) } + if p.AfterInsertedFlag == 0 { + t.Error(errors.New("AfterInsertedFlag not set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt == 0 { + t.Error(errors.New("AfterInsertedViaExt not set")) + } + session.Close() p2 = &ProcessorsStruct{} _, err = testEngine.ID(p.Id).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p2.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p2.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p2.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } + assert.NoError(t, err) + + if p2.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) } + if p2.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p2.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p2.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } + insertedId := p2.Id // -- // test update processors with tx rollback session = testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) b4UpdateFunc := func(bean interface{}) { if v, ok := (bean).(*ProcessorsStruct); ok { @@ -617,183 +545,160 @@ func TestProcessorsTx(t *testing.T) { p = p2 // reset _, err = session.ID(insertedId).Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) + } + err = session.Rollback() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) } session.Close() p2 = &ProcessorsStruct{} _, err = testEngine.ID(insertedId).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4UpdateFlag != 0 { - t.Error(errors.New("B4UpdateFlag is set")) - } - if p2.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p2.B4UpdateViaExt != 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p2.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } + assert.NoError(t, err) + + if p2.B4UpdateFlag != 0 { + t.Error(errors.New("B4UpdateFlag is set")) + } + if p2.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p2.B4UpdateViaExt != 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p2.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) } // -- // test update processors with tx rollback session = testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) p = &ProcessorsStruct{Id: insertedId} _, err = session.Update(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag == 0 { - t.Error(errors.New("AfterUpdatedFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag == 0 { + t.Error(errors.New("AfterUpdatedFlag not set")) + } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag set")) + } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag set")) } session.Close() // test update processors with tx commit session = testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) p = &ProcessorsStruct{} _, err = session.ID(insertedId).Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) + } + err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag == 0 { - t.Error(errors.New("AfterUpdatedFlag not set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt == 0 { - t.Error(errors.New("AfterUpdatedViaExt not set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) } + if p.AfterUpdatedFlag == 0 { + t.Error(errors.New("AfterUpdatedFlag not set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt == 0 { + t.Error(errors.New("AfterUpdatedViaExt not set")) + } + session.Close() p2 = &ProcessorsStruct{} _, err = testEngine.ID(insertedId).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag == 0 { - t.Error(errors.New("AfterUpdatedFlag not set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt == 0 { - t.Error(errors.New("AfterUpdatedViaExt not set")) - } + assert.NoError(t, err) + + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag == 0 { + t.Error(errors.New("AfterUpdatedFlag not set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt == 0 { + t.Error(errors.New("AfterUpdatedViaExt not set")) } // -- // test delete processors with tx rollback session = testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) b4DeleteFunc := func(bean interface{}) { if v, ok := (bean).(*ProcessorsStruct); ok { @@ -814,152 +719,131 @@ func TestProcessorsTx(t *testing.T) { p = &ProcessorsStruct{} // reset _, err = session.ID(insertedId).Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) + } + err = session.Rollback() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } + assert.NoError(t, err) + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) + } + session.Close() p2 = &ProcessorsStruct{} _, err = testEngine.ID(insertedId).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4DeleteFlag != 0 { - t.Error(errors.New("B4DeleteFlag is set")) - } - if p2.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p2.B4DeleteViaExt != 0 { - t.Error(errors.New("B4DeleteViaExt is set")) - } - if p2.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } + assert.NoError(t, err) + + if p2.B4DeleteFlag != 0 { + t.Error(errors.New("B4DeleteFlag is set")) + } + if p2.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p2.B4DeleteViaExt != 0 { + t.Error(errors.New("B4DeleteViaExt is set")) + } + if p2.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) } // -- // test delete processors with tx commit session = testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) p = &ProcessorsStruct{} _, err = session.ID(insertedId).Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } + assert.NoError(t, err) + + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) + } + err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag == 0 { - t.Error(errors.New("AfterDeletedFlag not set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt == 0 { - t.Error(errors.New("AfterDeletedViaExt not set")) - } + assert.NoError(t, err) + + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) } + if p.AfterDeletedFlag == 0 { + t.Error(errors.New("AfterDeletedFlag not set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt == 0 { + t.Error(errors.New("AfterDeletedViaExt not set")) + } + session.Close() // test delete processors with tx commit session = testEngine.NewSession() + defer session.Close() + err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) p = &ProcessorsStruct{Id: insertedId} - fmt.Println("delete") _, err = session.Delete(p) + assert.NoError(t, err) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag == 0 { - t.Error(errors.New("AfterDeletedFlag not set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag set")) - } + assert.NoError(t, err) + + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) + } + if p.AfterDeletedFlag == 0 { + t.Error(errors.New("AfterDeletedFlag not set")) + } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag set")) + } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag set")) } session.Close() // -- diff --git a/rows.go b/rows.go index 54ec7f37..bdd44589 100644 --- a/rows.go +++ b/rows.go @@ -9,16 +9,13 @@ import ( "fmt" "reflect" - "github.com/go-xorm/core" + "xorm.io/core" ) // Rows rows wrapper a rows to type Rows struct { - NoTypeCheck bool - session *Session rows *core.Rows - fields []string beanType reflect.Type lastError error } @@ -57,13 +54,6 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { return nil, err } - rows.fields, err = rows.rows.Columns() - if err != nil { - rows.lastError = err - rows.Close() - return nil, err - } - return rows, nil } @@ -90,7 +80,7 @@ func (rows *Rows) Scan(bean interface{}) error { return rows.lastError } - if !rows.NoTypeCheck && reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { + if reflect.Indirect(reflect.ValueOf(bean)).Type() != rows.beanType { return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) } @@ -98,13 +88,18 @@ func (rows *Rows) Scan(bean interface{}) error { return err } - scanResults, err := rows.session.row2Slice(rows.rows, rows.fields, bean) + fields, err := rows.rows.Columns() + if err != nil { + return err + } + + scanResults, err := rows.session.row2Slice(rows.rows, fields, bean) if err != nil { return err } dataStruct := rValue(bean) - _, err = rows.session.slice2Bean(scanResults, rows.fields, bean, &dataStruct, rows.session.statement.RefTable) + _, err = rows.session.slice2Bean(scanResults, fields, bean, &dataStruct, rows.session.statement.RefTable) if err != nil { return err } @@ -118,17 +113,9 @@ func (rows *Rows) Close() error { defer rows.session.Close() } - if rows.lastError == nil { - if rows.rows != nil { - rows.lastError = rows.rows.Close() - if rows.lastError != nil { - return rows.lastError - } - } - } else { - if rows.rows != nil { - defer rows.rows.Close() - } + if rows.rows != nil { + return rows.rows.Close() } + return rows.lastError } diff --git a/rows_test.go b/rows_test.go index ee121c5e..af333861 100644 --- a/rows_test.go +++ b/rows_test.go @@ -38,6 +38,22 @@ func TestRows(t *testing.T) { cnt++ } assert.EqualValues(t, 1, cnt) + assert.False(t, rows.Next()) + assert.NoError(t, rows.Close()) + + rows0, err := testEngine.Where("1>1").Rows(new(UserRows)) + assert.NoError(t, err) + defer rows0.Close() + + cnt = 0 + user0 := new(UserRows) + for rows0.Next() { + err = rows0.Scan(user0) + assert.NoError(t, err) + cnt++ + } + assert.EqualValues(t, 0, cnt) + assert.NoError(t, rows0.Close()) sess := testEngine.NewSession() defer sess.Close() @@ -67,3 +83,68 @@ func TestRows(t *testing.T) { } assert.EqualValues(t, 1, cnt) } + +func TestRowsMyTableName(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type UserRowsMyTable struct { + Id int64 + IsMan bool + } + + var tableName = "user_rows_my_table_name" + + assert.NoError(t, testEngine.Table(tableName).Sync2(new(UserRowsMyTable))) + + cnt, err := testEngine.Table(tableName).Insert(&UserRowsMyTable{ + IsMan: true, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + rows, err := testEngine.Table(tableName).Rows(new(UserRowsMyTable)) + assert.NoError(t, err) + defer rows.Close() + + cnt = 0 + user := new(UserRowsMyTable) + for rows.Next() { + err = rows.Scan(user) + assert.NoError(t, err) + cnt++ + } + assert.EqualValues(t, 1, cnt) +} + +type UserRowsSpecTable struct { + Id int64 + IsMan bool +} + +func (UserRowsSpecTable) TableName() string { + return "user_rows_my_table_name" +} + +func TestRowsSpecTableName(t *testing.T) { + assert.NoError(t, prepareEngine()) + assert.NoError(t, testEngine.Sync2(new(UserRowsSpecTable))) + + cnt, err := testEngine.Insert(&UserRowsSpecTable{ + IsMan: true, + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + rows, err := testEngine.Rows(new(UserRowsSpecTable)) + assert.NoError(t, err) + defer rows.Close() + + cnt = 0 + user := new(UserRowsSpecTable) + for rows.Next() { + err = rows.Scan(user) + assert.NoError(t, err) + cnt++ + } + assert.EqualValues(t, 1, cnt) +} diff --git a/session.go b/session.go index b4750891..b33955fd 100644 --- a/session.go +++ b/session.go @@ -5,8 +5,8 @@ package xorm import ( + "context" "database/sql" - "encoding/json" "errors" "fmt" "hash/crc32" @@ -14,7 +14,14 @@ import ( "strings" "time" - "github.com/go-xorm/core" + "xorm.io/core" +) + +type sessionType int + +const ( + engineSession sessionType = iota + groupSession ) // Session keep a pointer to sql.DB and provides all execution of all @@ -51,7 +58,8 @@ type Session struct { lastSQL string lastSQLArgs []interface{} - err error + ctx context.Context + sessionType sessionType } // Clone copy all the session's content and return a new session @@ -82,6 +90,8 @@ func (session *Session) Init() { session.lastSQL = "" session.lastSQLArgs = []interface{}{} + + session.ctx = session.engine.defaultContext } // Close release the connection from pool @@ -275,7 +285,7 @@ func (session *Session) doPrepare(db *core.DB, sqlStr string) (stmt *core.Stmt, var has bool stmt, has = session.stmtCache[crc] if !has { - stmt, err = db.Prepare(sqlStr) + stmt, err = db.PrepareContext(session.ctx, sqlStr) if err != nil { return nil, err } @@ -480,13 +490,13 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b continue } if fieldValue.CanAddr() { - err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + err := DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) if err != nil { return nil, err } } else { x := reflect.New(fieldType) - err := json.Unmarshal(bs, x.Interface()) + err := DefaultJSONHandler.Unmarshal(bs, x.Interface()) if err != nil { return nil, err } @@ -510,13 +520,13 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b hasAssigned = true if len(bs) > 0 { if fieldValue.CanAddr() { - err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + err := DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) if err != nil { return nil, err } } else { x := reflect.New(fieldType) - err := json.Unmarshal(bs, x.Interface()) + err := DefaultJSONHandler.Unmarshal(bs, x.Interface()) if err != nil { return nil, err } @@ -532,7 +542,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b hasAssigned = true if col.SQLType.IsText() { x := reflect.New(fieldType) - err := json.Unmarshal(vv.Bytes(), x.Interface()) + err := DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) if err != nil { return nil, err } @@ -647,7 +657,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b hasAssigned = true x := reflect.New(fieldType) if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), x.Interface()) + err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), x.Interface()) if err != nil { return nil, err } @@ -657,7 +667,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b hasAssigned = true x := reflect.New(fieldType) if len(vv.Bytes()) > 0 { - err := json.Unmarshal(vv.Bytes(), x.Interface()) + err := DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) if err != nil { return nil, err } @@ -793,7 +803,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b case core.Complex64Type: var x complex64 if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), &x) + err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) if err != nil { return nil, err } @@ -803,7 +813,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b case core.Complex128Type: var x complex128 if len([]byte(vv.String())) > 0 { - err := json.Unmarshal([]byte(vv.String()), &x) + err := DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) if err != nil { return nil, err } @@ -845,3 +855,12 @@ func (session *Session) Unscoped() *Session { session.statement.Unscoped() return session } + +func (session *Session) incrVersionFieldValue(fieldValue *reflect.Value) { + switch fieldValue.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + fieldValue.SetInt(fieldValue.Int() + 1) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + fieldValue.SetUint(fieldValue.Uint() + 1) + } +} diff --git a/session_cols.go b/session_cols.go index 47d109c6..dc3befcf 100644 --- a/session_cols.go +++ b/session_cols.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) type incrParam struct { diff --git a/session_cols_test.go b/session_cols_test.go index 3315e6b5..5f5954c7 100644 --- a/session_cols_test.go +++ b/session_cols_test.go @@ -7,7 +7,7 @@ package xorm import ( "testing" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) diff --git a/session_cond.go b/session_cond.go index e1d528f2..b16bdea8 100644 --- a/session_cond.go +++ b/session_cond.go @@ -4,7 +4,7 @@ package xorm -import "github.com/go-xorm/builder" +import "xorm.io/builder" // Sql provides raw sql input parameter. When you have a complex SQL statement // and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. diff --git a/session_cond_test.go b/session_cond_test.go index ae4c2f82..10650484 100644 --- a/session_cond_test.go +++ b/session_cond_test.go @@ -9,7 +9,7 @@ import ( "fmt" "testing" - "github.com/go-xorm/builder" + "xorm.io/builder" "github.com/stretchr/testify/assert" ) diff --git a/context.go b/session_context.go similarity index 60% rename from context.go rename to session_context.go index 074ba35a..915f0568 100644 --- a/context.go +++ b/session_context.go @@ -1,18 +1,15 @@ -// Copyright 2017 The Xorm Authors. All rights reserved. +// Copyright 2019 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 go1.8 - package xorm import "context" -// PingContext tests if database is alive -func (engine *Engine) PingContext(ctx context.Context) error { - session := engine.NewSession() - defer session.Close() - return session.PingContext(ctx) +// Context sets the context on this session +func (session *Session) Context(ctx context.Context) *Session { + session.ctx = ctx + return session } // PingContext test if database is ok diff --git a/session_context_test.go b/session_context_test.go new file mode 100644 index 00000000..2784468d --- /dev/null +++ b/session_context_test.go @@ -0,0 +1,36 @@ +// Copyright 2019 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 ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestQueryContext(t *testing.T) { + type ContextQueryStruct struct { + Id int64 + Name string + } + + assert.NoError(t, prepareEngine()) + assertSync(t, new(ContextQueryStruct)) + + _, err := testEngine.Insert(&ContextQueryStruct{Name: "1"}) + assert.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond) + defer cancel() + + time.Sleep(time.Nanosecond) + + has, err := testEngine.Context(ctx).Exist(&ContextQueryStruct{Name: "1"}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "context deadline exceeded") + assert.False(t, has) +} diff --git a/session_convert.go b/session_convert.go index 1f9d8aa1..caff5d26 100644 --- a/session_convert.go +++ b/session_convert.go @@ -7,7 +7,6 @@ package xorm import ( "database/sql" "database/sql/driver" - "encoding/json" "errors" "fmt" "reflect" @@ -15,7 +14,7 @@ import ( "strings" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) { @@ -103,7 +102,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, case reflect.Complex64, reflect.Complex128: x := reflect.New(fieldType) if len(data) > 0 { - err := json.Unmarshal(data, x.Interface()) + err := DefaultJSONHandler.Unmarshal(data, x.Interface()) if err != nil { session.engine.logger.Error(err) return err @@ -117,7 +116,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, if col.SQLType.IsText() { x := reflect.New(fieldType) if len(data) > 0 { - err := json.Unmarshal(data, x.Interface()) + err := DefaultJSONHandler.Unmarshal(data, x.Interface()) if err != nil { session.engine.logger.Error(err) return err @@ -130,7 +129,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } else { x := reflect.New(fieldType) if len(data) > 0 { - err := json.Unmarshal(data, x.Interface()) + err := DefaultJSONHandler.Unmarshal(data, x.Interface()) if err != nil { session.engine.logger.Error(err) return err @@ -259,7 +258,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, case core.Complex64Type.Kind(): var x complex64 if len(data) > 0 { - err := json.Unmarshal(data, &x) + err := DefaultJSONHandler.Unmarshal(data, &x) if err != nil { session.engine.logger.Error(err) return err @@ -270,7 +269,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, case core.Complex128Type.Kind(): var x complex128 if len(data) > 0 { - err := json.Unmarshal(data, &x) + err := DefaultJSONHandler.Unmarshal(data, &x) if err != nil { session.engine.logger.Error(err) return err @@ -604,14 +603,14 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } if col.SQLType.IsText() { - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { session.engine.logger.Error(err) return 0, err } return string(bytes), nil } else if col.SQLType.IsBlob() { - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { session.engine.logger.Error(err) return 0, err @@ -620,7 +619,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) case reflect.Complex64, reflect.Complex128: - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { session.engine.logger.Error(err) return 0, err @@ -632,7 +631,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } if col.SQLType.IsText() { - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { session.engine.logger.Error(err) return 0, err @@ -641,11 +640,11 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } else if col.SQLType.IsBlob() { var bytes []byte var err error - if (k == reflect.Array || k == reflect.Slice) && + if (k == reflect.Slice) && (fieldValue.Type().Elem().Kind() == reflect.Uint8) { bytes = fieldValue.Bytes() } else { - bytes, err = json.Marshal(fieldValue.Interface()) + bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { session.engine.logger.Error(err) return 0, err diff --git a/session_delete.go b/session_delete.go index d9cf3ea9..675d4d8c 100644 --- a/session_delete.go +++ b/session_delete.go @@ -9,7 +9,7 @@ import ( "fmt" "strconv" - "github.com/go-xorm/core" + "xorm.io/core" ) func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error { @@ -79,6 +79,10 @@ func (session *Session) Delete(bean interface{}) (int64, error) { defer session.Close() } + if session.statement.lastError != nil { + return 0, session.statement.lastError + } + if err := session.statement.setRefBean(bean); err != nil { return 0, err } @@ -199,7 +203,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { }) } - if cacher := session.engine.getCacher(tableName); cacher != nil && session.statement.UseCache { + if cacher := session.engine.getCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache { session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) } diff --git a/session_delete_test.go b/session_delete_test.go index 916dab46..5edb0718 100644 --- a/session_delete_test.go +++ b/session_delete_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "xorm.io/core" "github.com/stretchr/testify/assert" ) @@ -21,11 +22,27 @@ func TestDelete(t *testing.T) { assert.NoError(t, testEngine.Sync2(new(UserinfoDelete))) + session := testEngine.NewSession() + defer session.Close() + + var err error + if testEngine.Dialect().DBType() == core.MSSQL { + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT userinfo_delete ON") + assert.NoError(t, err) + } + user := UserinfoDelete{Uid: 1} - cnt, err := testEngine.Insert(&user) + cnt, err := session.Insert(&user) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) + if testEngine.Dialect().DBType() == core.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } + cnt, err = testEngine.Delete(&UserinfoDelete{Uid: user.Uid}) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) @@ -40,7 +57,7 @@ func TestDelete(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, cnt) - cnt, err = testEngine.Where("id=?", user.Uid).Delete(&UserinfoDelete{}) + cnt, err = testEngine.Where("`id`=?", user.Uid).Delete(&UserinfoDelete{}) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) diff --git a/session_exist.go b/session_exist.go index 74a660e8..660cc47e 100644 --- a/session_exist.go +++ b/session_exist.go @@ -9,8 +9,8 @@ import ( "fmt" "reflect" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) // Exist returns true if the record exist otherwise return false @@ -19,6 +19,10 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) { defer session.Close() } + if session.statement.lastError != nil { + return false, session.statement.lastError + } + var sqlStr string var args []interface{} var err error @@ -30,6 +34,8 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) { return false, ErrTableNotFound } + tableName = session.statement.Engine.Quote(tableName) + if session.statement.cond.IsValid() { condSQL, condArgs, err := builder.ToSQL(session.statement.cond) if err != nil { @@ -37,14 +43,18 @@ func (session *Session) Exist(bean ...interface{}) (bool, error) { } if session.engine.dialect.DBType() == core.MSSQL { - sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s WHERE %s", tableName, condSQL) + sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s WHERE %s", tableName, condSQL) + } else if session.engine.dialect.DBType() == core.ORACLE { + sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE (%s) AND ROWNUM=1", tableName, condSQL) } else { sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT 1", tableName, condSQL) } args = condArgs } else { if session.engine.dialect.DBType() == core.MSSQL { - sqlStr = fmt.Sprintf("SELECT top 1 * FROM %s", tableName) + sqlStr = fmt.Sprintf("SELECT TOP 1 * FROM %s", tableName) + } else if session.engine.dialect.DBType() == core.ORACLE { + sqlStr = fmt.Sprintf("SELECT * FROM %s WHERE ROWNUM=1", tableName) } else { sqlStr = fmt.Sprintf("SELECT * FROM %s LIMIT 1", tableName) } diff --git a/session_find.go b/session_find.go index b75f8347..d3fc0d30 100644 --- a/session_find.go +++ b/session_find.go @@ -10,8 +10,8 @@ import ( "reflect" "strings" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) const ( @@ -63,6 +63,10 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte } func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { + if session.statement.lastError != nil { + return session.statement.lastError + } + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { return errors.New("needs a pointer to a slice or a map") @@ -176,7 +180,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) } if session.canCache() { - if cacher := session.engine.getCacher(table.Name); cacher != nil && + if cacher := session.engine.getCacher(session.statement.TableName()); cacher != nil && !session.statement.IsDistinct && !session.statement.unscoped { err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) diff --git a/session_find_test.go b/session_find_test.go index f9ebdc91..f805f06e 100644 --- a/session_find_test.go +++ b/session_find_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) diff --git a/session_get.go b/session_get.go index 887a0aeb..ad2627f4 100644 --- a/session_get.go +++ b/session_get.go @@ -11,7 +11,7 @@ import ( "reflect" "strconv" - "github.com/go-xorm/core" + "xorm.io/core" ) // Get retrieve one record from database, bean's non-empty fields @@ -24,6 +24,10 @@ func (session *Session) Get(bean interface{}) (bool, error) { } func (session *Session) get(bean interface{}) (bool, error) { + if session.statement.lastError != nil { + return false, session.statement.lastError + } + beanValue := reflect.ValueOf(bean) if beanValue.Kind() != reflect.Ptr { return false, errors.New("needs a pointer to a value") @@ -58,7 +62,7 @@ func (session *Session) get(bean interface{}) (bool, error) { table := session.statement.RefTable if session.canCache() && beanValue.Elem().Kind() == reflect.Struct { - if cacher := session.engine.getCacher(table.Name); cacher != nil && + if cacher := session.engine.getCacher(session.statement.TableName()); cacher != nil && !session.statement.unscoped { has, err := session.cacheGet(bean, sqlStr, args...) if err != ErrCacheFailed { @@ -110,6 +114,114 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea return true, rows.Scan(&bean) case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString: return true, rows.Scan(bean) + case *string: + var res sql.NullString + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*string)) = res.String + } + return true, nil + case *int: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*int)) = int(res.Int64) + } + return true, nil + case *int8: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*int8)) = int8(res.Int64) + } + return true, nil + case *int16: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*int16)) = int16(res.Int64) + } + return true, nil + case *int32: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*int32)) = int32(res.Int64) + } + return true, nil + case *int64: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*int64)) = int64(res.Int64) + } + return true, nil + case *uint: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*uint)) = uint(res.Int64) + } + return true, nil + case *uint8: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*uint8)) = uint8(res.Int64) + } + return true, nil + case *uint16: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*uint16)) = uint16(res.Int64) + } + return true, nil + case *uint32: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*uint32)) = uint32(res.Int64) + } + return true, nil + case *uint64: + var res sql.NullInt64 + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*uint64)) = uint64(res.Int64) + } + return true, nil + case *bool: + var res sql.NullBool + if err := rows.Scan(&res); err != nil { + return true, err + } + if res.Valid { + *(bean.(*bool)) = res.Bool + } + return true, nil } switch beanKind { @@ -138,6 +250,9 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea err = rows.ScanSlice(bean) case reflect.Map: err = rows.ScanMap(bean) + case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + err = rows.Scan(&bean) default: err = rows.Scan(bean) } diff --git a/session_get_test.go b/session_get_test.go index 02148df4..3af4395f 100644 --- a/session_get_test.go +++ b/session_get_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" - "github.com/go-xorm/core" "github.com/stretchr/testify/assert" + "xorm.io/core" ) func TestGetVar(t *testing.T) { @@ -47,6 +47,12 @@ func TestGetVar(t *testing.T) { assert.Equal(t, true, has) 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) + 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). @@ -56,6 +62,69 @@ func TestGetVar(t *testing.T) { assert.Equal(t, true, has) assert.EqualValues(t, 28, age2) + var age3 int8 + has, err = testEngine.Table("get_var").Cols("age").Get(&age3) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age3) + + var age4 int16 + has, err = testEngine.Table("get_var").Cols("age"). + Where("age > ?", 20). + And("age < ?", 30). + Get(&age4) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age4) + + var age5 int32 + has, err = testEngine.Table("get_var").Cols("age"). + Where("age > ?", 20). + And("age < ?", 30). + Get(&age5) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age5) + + var age6 int + has, err = testEngine.Table("get_var").Cols("age").Get(&age6) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age6) + + var age7 int64 + has, err = testEngine.Table("get_var").Cols("age"). + Where("age > ?", 20). + And("age < ?", 30). + Get(&age7) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age7) + + var age8 int8 + has, err = testEngine.Table("get_var").Cols("age").Get(&age8) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age8) + + var age9 int16 + has, err = testEngine.Table("get_var").Cols("age"). + Where("age > ?", 20). + And("age < ?", 30). + Get(&age9) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age9) + + var age10 int32 + has, err = testEngine.Table("get_var").Cols("age"). + Where("age > ?", 20). + And("age < ?", 30). + Get(&age10) + assert.NoError(t, err) + assert.Equal(t, true, has) + assert.EqualValues(t, 28, age10) + var id sql.NullInt64 has, err = testEngine.Table("get_var").Cols("id").Get(&id) assert.NoError(t, err) @@ -84,7 +153,11 @@ func TestGetVar(t *testing.T) { assert.Equal(t, "1.5", fmt.Sprintf("%.1f", money)) var money2 float64 - has, err = testEngine.SQL("SELECT money FROM " + testEngine.TableName("get_var", true) + " LIMIT 1").Get(&money2) + if testEngine.Dialect().DBType() == core.MSSQL { + has, err = testEngine.SQL("SELECT TOP 1 money FROM " + testEngine.TableName("get_var", true)).Get(&money2) + } else { + has, err = testEngine.SQL("SELECT money FROM " + 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)) @@ -156,14 +229,23 @@ func TestGetStruct(t *testing.T) { assert.NoError(t, testEngine.Sync2(new(UserinfoGet))) + session := testEngine.NewSession() + defer session.Close() + var err error if testEngine.Dialect().DBType() == core.MSSQL { - _, err = testEngine.Exec("SET IDENTITY_INSERT userinfo_get ON") + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT userinfo_get ON") assert.NoError(t, err) } - cnt, err := testEngine.Insert(&UserinfoGet{Uid: 2}) + cnt, err := session.Insert(&UserinfoGet{Uid: 2}) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) + if testEngine.Dialect().DBType() == core.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } user := UserinfoGet{Uid: 2} has, err := testEngine.Get(&user) @@ -386,3 +468,119 @@ func TestContextGet2(t *testing.T) { assert.EqualValues(t, 1, c3.Id) assert.EqualValues(t, "1", c3.Name) } + +type GetCustomTableInterface interface { + TableName() string +} + +type MyGetCustomTableImpletation struct { + Id int64 `json:"id"` + Name string `json:"name"` +} + +const getCustomTableName = "GetCustomTableInterface" + +func (m *MyGetCustomTableImpletation) TableName() string { + return getCustomTableName +} + +func TestGetCustomTableInterface(t *testing.T) { + assert.NoError(t, prepareEngine()) + assert.NoError(t, testEngine.Table(getCustomTableName).Sync2(new(MyGetCustomTableImpletation))) + + exist, err := testEngine.IsTableExist(getCustomTableName) + assert.NoError(t, err) + assert.True(t, exist) + + _, err = testEngine.Insert(&MyGetCustomTableImpletation{ + Name: "xlw", + }) + assert.NoError(t, err) + + var c GetCustomTableInterface = new(MyGetCustomTableImpletation) + has, err := testEngine.Get(c) + assert.NoError(t, err) + assert.True(t, has) +} + +func TestGetNullVar(t *testing.T) { + type TestGetNullVarStruct struct { + Id int64 + Name string + Age int + } + + assert.NoError(t, prepareEngine()) + assertSync(t, new(TestGetNullVarStruct)) + + affected, err := testEngine.Exec("insert into " + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, 0, age10) +} diff --git a/session_insert.go b/session_insert.go index 2ea58fda..71356566 100644 --- a/session_insert.go +++ b/session_insert.go @@ -8,10 +8,11 @@ import ( "errors" "fmt" "reflect" + "sort" "strconv" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) // Insert insert one or more beans @@ -24,32 +25,67 @@ func (session *Session) Insert(beans ...interface{}) (int64, error) { } for _, bean := range beans { - sliceValue := reflect.Indirect(reflect.ValueOf(bean)) - if sliceValue.Kind() == reflect.Slice { - size := sliceValue.Len() - if size > 0 { - if session.engine.SupportInsertMany() { - cnt, err := session.innerInsertMulti(bean) - if err != nil { - return affected, err - } - affected += cnt - } else { - for i := 0; i < size; i++ { - cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) - if err != nil { - return affected, err - } - affected += cnt - } - } - } - } else { - cnt, err := session.innerInsert(bean) + switch bean.(type) { + case map[string]interface{}: + cnt, err := session.insertMapInterface(bean.(map[string]interface{})) if err != nil { return affected, err } affected += cnt + case []map[string]interface{}: + s := bean.([]map[string]interface{}) + session.autoResetStatement = false + for i := 0; i < len(s); i++ { + cnt, err := session.insertMapInterface(s[i]) + if err != nil { + return affected, err + } + affected += cnt + } + case map[string]string: + cnt, err := session.insertMapString(bean.(map[string]string)) + if err != nil { + return affected, err + } + affected += cnt + case []map[string]string: + s := bean.([]map[string]string) + session.autoResetStatement = false + for i := 0; i < len(s); i++ { + cnt, err := session.insertMapString(s[i]) + if err != nil { + return affected, err + } + affected += cnt + } + default: + sliceValue := reflect.Indirect(reflect.ValueOf(bean)) + if sliceValue.Kind() == reflect.Slice { + size := sliceValue.Len() + if size > 0 { + if session.engine.SupportInsertMany() { + cnt, err := session.innerInsertMulti(bean) + if err != nil { + return affected, err + } + affected += cnt + } else { + for i := 0; i < size; i++ { + cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) + if err != nil { + return affected, err + } + affected += cnt + } + } + } + } else { + cnt, err := session.innerInsert(bean) + if err != nil { + return affected, err + } + affected += cnt + } } } @@ -206,23 +242,17 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error var sql string if session.engine.dialect.DBType() == core.ORACLE { - temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (", + temp := fmt.Sprintf(") INTO %s (%v) VALUES (", session.engine.Quote(tableName), - session.engine.QuoteStr(), - strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), - session.engine.QuoteStr()) - sql = fmt.Sprintf("INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL", + quoteColumns(colNames, session.engine.Quote, ",")) + sql = fmt.Sprintf("INSERT ALL INTO %s (%v) VALUES (%v) SELECT 1 FROM DUAL", session.engine.Quote(tableName), - session.engine.QuoteStr(), - strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), - session.engine.QuoteStr(), + quoteColumns(colNames, session.engine.Quote, ","), strings.Join(colMultiPlaces, temp)) } else { - sql = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", + sql = fmt.Sprintf("INSERT INTO %s (%v) VALUES (%v)", session.engine.Quote(tableName), - session.engine.QuoteStr(), - strings.Join(colNames, session.engine.QuoteStr()+", "+session.engine.QuoteStr()), - session.engine.QuoteStr(), + quoteColumns(colNames, session.engine.Quote, ","), strings.Join(colMultiPlaces, "),(")) } res, err := session.exec(sql, args...) @@ -337,21 +367,28 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { var sqlStr string var tableName = session.statement.TableName() + var output string + if session.engine.dialect.DBType() == core.MSSQL && len(table.AutoIncrement) > 0 { + output = fmt.Sprintf(" OUTPUT Inserted.%s", table.AutoIncrement) + } if len(colPlaces) > 0 { - sqlStr = fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", + sqlStr = fmt.Sprintf("INSERT INTO %s (%v)%s VALUES (%v)", session.engine.Quote(tableName), - session.engine.QuoteStr(), - strings.Join(colNames, session.engine.Quote(", ")), - session.engine.QuoteStr(), + quoteColumns(colNames, session.engine.Quote, ","), + output, colPlaces) } else { if session.engine.dialect.DBType() == core.MYSQL { sqlStr = fmt.Sprintf("INSERT INTO %s VALUES ()", session.engine.Quote(tableName)) } else { - sqlStr = fmt.Sprintf("INSERT INTO %s DEFAULT VALUES", session.engine.Quote(tableName)) + sqlStr = fmt.Sprintf("INSERT INTO %s%s DEFAULT VALUES", session.engine.Quote(tableName), output) } } + if len(table.AutoIncrement) > 0 && session.engine.dialect.DBType() == core.POSTGRES { + sqlStr = sqlStr + " RETURNING " + session.engine.Quote(table.AutoIncrement) + } + handleAfterInsertProcessorFunc := func(bean interface{}) { if session.isAutoCommit { for _, closure := range session.afterClosures { @@ -397,7 +434,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { if err != nil { session.engine.logger.Error(err) } else if verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(1) + session.incrVersionFieldValue(verValue) } } @@ -423,9 +460,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { aiValue.Set(int64ToIntValue(id, aiValue.Type())) return 1, nil - } else if session.engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 { - //assert table.AutoIncrement != "" - sqlStr = sqlStr + " RETURNING " + session.engine.Quote(table.AutoIncrement) + } else if len(table.AutoIncrement) > 0 && (session.engine.dialect.DBType() == core.POSTGRES || session.engine.dialect.DBType() == core.MSSQL) { res, err := session.queryBytes(sqlStr, args...) if err != nil { @@ -440,12 +475,12 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { if err != nil { session.engine.logger.Error(err) } else if verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(1) + session.incrVersionFieldValue(verValue) } } if len(res) < 1 { - return 0, errors.New("insert no error but not returned id") + return 0, errors.New("insert successfully but not returned id") } idByte := res[0][table.AutoIncrement] @@ -481,7 +516,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { if err != nil { session.engine.logger.Error(err) } else if verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(1) + session.incrVersionFieldValue(verValue) } } @@ -622,3 +657,83 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac } return colNames, args, nil } + +func (session *Session) insertMapInterface(m map[string]interface{}) (int64, error) { + if len(m) == 0 { + return 0, ErrParamsType + } + + var columns = make([]string, 0, len(m)) + for k := range m { + columns = append(columns, k) + } + sort.Strings(columns) + + qm := strings.Repeat("?,", len(columns)) + qm = "(" + qm[:len(qm)-1] + ")" + + tableName := session.statement.TableName() + if len(tableName) <= 0 { + return 0, ErrTableNotFound + } + + var sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES %s", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) + var args = make([]interface{}, 0, len(m)) + for _, colName := range columns { + args = append(args, m[colName]) + } + + if err := session.cacheInsert(tableName); err != nil { + return 0, err + } + + res, err := session.exec(sql, args...) + if err != nil { + return 0, err + } + affected, err := res.RowsAffected() + if err != nil { + return 0, err + } + return affected, nil +} + +func (session *Session) insertMapString(m map[string]string) (int64, error) { + if len(m) == 0 { + return 0, ErrParamsType + } + + var columns = make([]string, 0, len(m)) + for k := range m { + columns = append(columns, k) + } + sort.Strings(columns) + + qm := strings.Repeat("?,", len(columns)) + qm = "(" + qm[:len(qm)-1] + ")" + + tableName := session.statement.TableName() + if len(tableName) <= 0 { + return 0, ErrTableNotFound + } + + var sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES %s", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) + var args = make([]interface{}, 0, len(m)) + for _, colName := range columns { + args = append(args, m[colName]) + } + + if err := session.cacheInsert(tableName); err != nil { + return 0, err + } + + res, err := session.exec(sql, args...) + if err != nil { + return 0, err + } + affected, err := res.RowsAffected() + if err != nil { + return 0, err + } + return affected, nil +} diff --git a/session_insert_test.go b/session_insert_test.go index 50943032..8e7ffa99 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -145,41 +145,22 @@ func TestInsert(t *testing.T) { user := Userinfo{0, "xiaolunwen", "dev", "lunny", time.Now(), Userdetail{Id: 1}, 1.78, []byte{1, 2, 3}, true} cnt, err := testEngine.Insert(&user) - fmt.Println(user.Uid) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - } - - if user.Uid <= 0 { - err = errors.New("not return id error") - t.Error(err) - panic(err) - } + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt, "insert not returned 1") + assert.True(t, user.Uid > 0, "not return id error") user.Uid = 0 cnt, err = testEngine.Insert(&user) + // Username is unique, so this should return error + assert.Error(t, err, "insert should fail but no error returned") + assert.EqualValues(t, 0, cnt, "insert not returned 1") if err == nil { - err = errors.New("insert failed but no return error") - t.Error(err) - panic(err) - } - if cnt != 0 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return + panic("should return err") } } func TestInsertAutoIncr(t *testing.T) { assert.NoError(t, prepareEngine()) - assertSync(t, new(Userinfo)) // auto increment insert @@ -214,20 +195,14 @@ func TestInsertDefault(t *testing.T) { di := new(DefaultInsert) err := testEngine.Sync2(di) - if err != nil { - t.Error(err) - } + assert.NoError(t, err) var di2 = DefaultInsert{Name: "test"} _, err = testEngine.Omit(testEngine.GetColumnMapper().Obj2Table("Status")).Insert(&di2) - if err != nil { - t.Error(err) - } + assert.NoError(t, err) has, err := testEngine.Desc("(id)").Get(di) - if err != nil { - t.Error(err) - } + assert.NoError(t, err) if !has { err = errors.New("error with no data") t.Error(err) @@ -780,3 +755,82 @@ func TestAnonymousStruct(t *testing.T) { }) assert.NoError(t, err) } + +func TestInsertMap(t *testing.T) { + type InsertMap struct { + Id int64 + Width uint32 + Height uint32 + Name string + } + + assert.NoError(t, prepareEngine()) + assertSync(t, new(InsertMap)) + + cnt, err := testEngine.Table(new(InsertMap)).Insert(map[string]interface{}{ + "width": 20, + "height": 10, + "name": "lunny", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var im InsertMap + has, err := testEngine.Get(&im) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, 20, im.Width) + assert.EqualValues(t, 10, im.Height) + assert.EqualValues(t, "lunny", im.Name) + + cnt, err = testEngine.Table("insert_map").Insert(map[string]interface{}{ + "width": 30, + "height": 10, + "name": "lunny", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var ims []InsertMap + err = testEngine.Find(&ims) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(ims)) + assert.EqualValues(t, 20, ims[0].Width) + assert.EqualValues(t, 10, ims[0].Height) + assert.EqualValues(t, "lunny", ims[0].Name) + assert.EqualValues(t, 30, ims[1].Width) + assert.EqualValues(t, 10, ims[1].Height) + assert.EqualValues(t, "lunny", ims[1].Name) + + cnt, err = testEngine.Table("insert_map").Insert([]map[string]interface{}{ + { + "width": 40, + "height": 10, + "name": "lunny", + }, + { + "width": 50, + "height": 10, + "name": "lunny", + }, + }) + assert.NoError(t, err) + assert.EqualValues(t, 2, cnt) + + ims = make([]InsertMap, 0, 4) + err = testEngine.Find(&ims) + assert.NoError(t, err) + assert.EqualValues(t, 4, len(ims)) + assert.EqualValues(t, 20, ims[0].Width) + assert.EqualValues(t, 10, ims[0].Height) + assert.EqualValues(t, "lunny", ims[1].Name) + assert.EqualValues(t, 30, ims[1].Width) + assert.EqualValues(t, 10, ims[1].Height) + assert.EqualValues(t, "lunny", ims[1].Name) + assert.EqualValues(t, 40, ims[2].Width) + assert.EqualValues(t, 10, ims[2].Height) + assert.EqualValues(t, "lunny", ims[2].Name) + assert.EqualValues(t, 50, ims[3].Width) + assert.EqualValues(t, 10, ims[3].Height) + assert.EqualValues(t, "lunny", ims[3].Name) +} diff --git a/session_iterate.go b/session_iterate.go index 071fce49..ca996c28 100644 --- a/session_iterate.go +++ b/session_iterate.go @@ -23,6 +23,10 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error { defer session.Close() } + if session.statement.lastError != nil { + return session.statement.lastError + } + if session.statement.bufferSize > 0 { return session.bufferIterate(bean, fun) } diff --git a/session_pk_test.go b/session_pk_test.go index da4209b5..4c066634 100644 --- a/session_pk_test.go +++ b/session_pk_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) diff --git a/session_query.go b/session_query.go index 6d597cc4..21c00b8d 100644 --- a/session_query.go +++ b/session_query.go @@ -11,13 +11,13 @@ import ( "strings" "time" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) -func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interface{}, error) { - if len(sqlorArgs) > 0 { - return convertSQLOrArgs(sqlorArgs...) +func (session *Session) genQuerySQL(sqlOrArgs ...interface{}) (string, []interface{}, error) { + if len(sqlOrArgs) > 0 { + return convertSQLOrArgs(sqlOrArgs...) } if session.statement.RawSQL != "" { @@ -78,12 +78,12 @@ func (session *Session) genQuerySQL(sqlorArgs ...interface{}) (string, []interfa } // Query runs a raw sql and return records as []map[string][]byte -func (session *Session) Query(sqlorArgs ...interface{}) ([]map[string][]byte, error) { +func (session *Session) Query(sqlOrArgs ...interface{}) ([]map[string][]byte, error) { if session.isAutoClose { defer session.Close() } - sqlStr, args, err := session.genQuerySQL(sqlorArgs...) + sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) if err != nil { return nil, err } @@ -227,12 +227,12 @@ func rows2SliceString(rows *core.Rows) (resultsSlice [][]string, err error) { } // QueryString runs a raw sql and return records as []map[string]string -func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]string, error) { +func (session *Session) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) { if session.isAutoClose { defer session.Close() } - sqlStr, args, err := session.genQuerySQL(sqlorArgs...) + sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) if err != nil { return nil, err } @@ -247,12 +247,12 @@ func (session *Session) QueryString(sqlorArgs ...interface{}) ([]map[string]stri } // QuerySliceString runs a raw sql and return records as [][]string -func (session *Session) QuerySliceString(sqlorArgs ...interface{}) ([][]string, error) { +func (session *Session) QuerySliceString(sqlOrArgs ...interface{}) ([][]string, error) { if session.isAutoClose { defer session.Close() } - sqlStr, args, err := session.genQuerySQL(sqlorArgs...) + sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) if err != nil { return nil, err } @@ -300,12 +300,12 @@ func rows2Interfaces(rows *core.Rows) (resultsSlice []map[string]interface{}, er } // QueryInterface runs a raw sql and return records as []map[string]interface{} -func (session *Session) QueryInterface(sqlorArgs ...interface{}) ([]map[string]interface{}, error) { +func (session *Session) QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) { if session.isAutoClose { defer session.Close() } - sqlStr, args, err := session.genQuerySQL(sqlorArgs...) + sqlStr, args, err := session.genQuerySQL(sqlOrArgs...) if err != nil { return nil, err } diff --git a/session_query_test.go b/session_query_test.go index 5ce89a4a..772206a8 100644 --- a/session_query_test.go +++ b/session_query_test.go @@ -10,8 +10,8 @@ import ( "testing" "time" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" "github.com/stretchr/testify/assert" ) @@ -207,7 +207,7 @@ func TestQueryStringNoParam(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0]["id"]) - if testEngine.Dialect().URI().DbType == core.POSTGRES { + if testEngine.Dialect().DBType() == core.POSTGRES || testEngine.Dialect().DBType() == core.MSSQL { assert.EqualValues(t, "false", records[0]["msg"]) } else { assert.EqualValues(t, "0", records[0]["msg"]) @@ -217,7 +217,7 @@ func TestQueryStringNoParam(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0]["id"]) - if testEngine.Dialect().URI().DbType == core.POSTGRES { + if testEngine.Dialect().DBType() == core.POSTGRES || testEngine.Dialect().DBType() == core.MSSQL { assert.EqualValues(t, "false", records[0]["msg"]) } else { assert.EqualValues(t, "0", records[0]["msg"]) @@ -244,7 +244,7 @@ func TestQuerySliceStringNoParam(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0][0]) - if testEngine.Dialect().URI().DbType == core.POSTGRES { + if testEngine.Dialect().DBType() == core.POSTGRES || testEngine.Dialect().DBType() == core.MSSQL { assert.EqualValues(t, "false", records[0][1]) } else { assert.EqualValues(t, "0", records[0][1]) @@ -254,7 +254,7 @@ func TestQuerySliceStringNoParam(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 1, len(records)) assert.EqualValues(t, "1", records[0][0]) - if testEngine.Dialect().URI().DbType == core.POSTGRES { + if testEngine.Dialect().DBType() == core.POSTGRES || testEngine.Dialect().DBType() == core.MSSQL { assert.EqualValues(t, "false", records[0][1]) } else { assert.EqualValues(t, "0", records[0][1]) @@ -334,3 +334,47 @@ func TestQueryWithBuilder(t *testing.T) { assert.NoError(t, err) assertResult(t, results) } + +func TestJoinWithSubQuery(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type JoinWithSubQuery1 struct { + Id int64 `xorm:"autoincr pk"` + Msg string `xorm:"varchar(255)"` + DepartId int64 + Money float32 + } + + type JoinWithSubQueryDepart struct { + Id int64 `xorm:"autoincr pk"` + Name string + } + + testEngine.ShowSQL(true) + + assert.NoError(t, testEngine.Sync2(new(JoinWithSubQuery1), new(JoinWithSubQueryDepart))) + + var depart = JoinWithSubQueryDepart{ + Name: "depart1", + } + cnt, err := testEngine.Insert(&depart) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var q = JoinWithSubQuery1{ + Msg: "message", + DepartId: depart.Id, + Money: 3000, + } + + cnt, err = testEngine.Insert(&q) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + var querys []JoinWithSubQuery1 + err = testEngine.Join("INNER", builder.Select("id").From(testEngine.Quote(testEngine.TableName("join_with_sub_query_depart", true))), + "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]) +} diff --git a/session_raw.go b/session_raw.go index 47823d67..67648ef1 100644 --- a/session_raw.go +++ b/session_raw.go @@ -9,8 +9,8 @@ import ( "reflect" "time" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { @@ -49,7 +49,7 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row if session.isAutoCommit { var db *core.DB - if session.engine.engineGroup != nil { + if session.sessionType == groupSession { db = session.engine.engineGroup.Slave().DB() } else { db = session.DB() @@ -62,21 +62,21 @@ func (session *Session) queryRows(sqlStr string, args ...interface{}) (*core.Row return nil, err } - rows, err := stmt.Query(args...) + rows, err := stmt.QueryContext(session.ctx, args...) if err != nil { return nil, err } return rows, nil } - rows, err := db.Query(sqlStr, args...) + rows, err := db.QueryContext(session.ctx, sqlStr, args...) if err != nil { return nil, err } return rows, nil } - rows, err := session.tx.Query(sqlStr, args...) + rows, err := session.tx.QueryContext(session.ctx, sqlStr, args...) if err != nil { return nil, err } @@ -175,7 +175,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er } if !session.isAutoCommit { - return session.tx.Exec(sqlStr, args...) + return session.tx.ExecContext(session.ctx, sqlStr, args...) } if session.prepareStmt { @@ -184,24 +184,24 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er return nil, err } - res, err := stmt.Exec(args...) + res, err := stmt.ExecContext(session.ctx, args...) if err != nil { return nil, err } return res, nil } - return session.DB().Exec(sqlStr, args...) + return session.DB().ExecContext(session.ctx, sqlStr, args...) } -func convertSQLOrArgs(sqlorArgs ...interface{}) (string, []interface{}, error) { - switch sqlorArgs[0].(type) { +func convertSQLOrArgs(sqlOrArgs ...interface{}) (string, []interface{}, error) { + switch sqlOrArgs[0].(type) { case string: - return sqlorArgs[0].(string), sqlorArgs[1:], nil + return sqlOrArgs[0].(string), sqlOrArgs[1:], nil case *builder.Builder: - return sqlorArgs[0].(*builder.Builder).ToSQL() + return sqlOrArgs[0].(*builder.Builder).ToSQL() case builder.Builder: - bd := sqlorArgs[0].(builder.Builder) + bd := sqlOrArgs[0].(builder.Builder) return bd.ToSQL() } @@ -209,16 +209,16 @@ func convertSQLOrArgs(sqlorArgs ...interface{}) (string, []interface{}, error) { } // Exec raw sql -func (session *Session) Exec(sqlorArgs ...interface{}) (sql.Result, error) { +func (session *Session) Exec(sqlOrArgs ...interface{}) (sql.Result, error) { if session.isAutoClose { defer session.Close() } - if len(sqlorArgs) == 0 { + if len(sqlOrArgs) == 0 { return nil, ErrUnSupportedType } - sqlStr, args, err := convertSQLOrArgs(sqlorArgs...) + sqlStr, args, err := convertSQLOrArgs(sqlOrArgs...) if err != nil { return nil, err } diff --git a/session_schema.go b/session_schema.go index 369ec72a..da5c8855 100644 --- a/session_schema.go +++ b/session_schema.go @@ -9,7 +9,7 @@ import ( "fmt" "strings" - "github.com/go-xorm/core" + "xorm.io/core" ) // Ping test if database is ok @@ -19,7 +19,7 @@ func (session *Session) Ping() error { } session.engine.logger.Infof("PING DATABASE %v", session.engine.DriverName()) - return session.DB().Ping() + return session.DB().PingContext(session.ctx) } // CreateTable create a table according a bean diff --git a/session_stats_test.go b/session_stats_test.go index b66a84b4..01c76ba5 100644 --- a/session_stats_test.go +++ b/session_stats_test.go @@ -9,7 +9,7 @@ import ( "strconv" "testing" - "github.com/go-xorm/builder" + "xorm.io/builder" "github.com/stretchr/testify/assert" ) @@ -18,13 +18,14 @@ func isFloatEq(i, j float64, precision int) bool { } func TestSum(t *testing.T) { - assert.NoError(t, prepareEngine()) - type SumStruct struct { Int int Float float32 } + assert.NoError(t, prepareEngine()) + assert.NoError(t, testEngine.Sync2(new(SumStruct))) + var ( cases = []SumStruct{ {1, 6.2}, @@ -40,8 +41,6 @@ func TestSum(t *testing.T) { f += v.Float } - assert.NoError(t, testEngine.Sync2(new(SumStruct))) - cnt, err := testEngine.Insert(cases) assert.NoError(t, err) assert.EqualValues(t, 3, cnt) @@ -73,6 +72,65 @@ func TestSum(t *testing.T) { assert.EqualValues(t, i, int(sumsInt[0])) } +type SumStructWithTableName struct { + Int int + Float float32 +} + +func (s SumStructWithTableName) TableName() string { + return "sum_struct_with_table_name_1" +} + +func TestSumWithTableName(t *testing.T) { + assert.NoError(t, prepareEngine()) + assert.NoError(t, testEngine.Sync2(new(SumStructWithTableName))) + + var ( + cases = []SumStructWithTableName{ + {1, 6.2}, + {2, 5.3}, + {92, -0.2}, + } + ) + + var i int + var f float32 + for _, v := range cases { + i += v.Int + f += v.Float + } + + cnt, err := testEngine.Insert(cases) + assert.NoError(t, err) + assert.EqualValues(t, 3, cnt) + + colInt := testEngine.GetColumnMapper().Obj2Table("Int") + colFloat := testEngine.GetColumnMapper().Obj2Table("Float") + + sumInt, err := testEngine.Sum(new(SumStructWithTableName), colInt) + assert.NoError(t, err) + assert.EqualValues(t, int(sumInt), i) + + sumFloat, err := testEngine.Sum(new(SumStructWithTableName), colFloat) + assert.NoError(t, err) + assert.Condition(t, func() bool { + return isFloatEq(sumFloat, float64(f), 2) + }) + + sums, err := testEngine.Sums(new(SumStructWithTableName), colInt, colFloat) + assert.NoError(t, err) + assert.EqualValues(t, 2, len(sums)) + assert.EqualValues(t, i, int(sums[0])) + assert.Condition(t, func() bool { + return isFloatEq(sums[1], float64(f), 2) + }) + + sumsInt, err := testEngine.SumsInt(new(SumStructWithTableName), colInt) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(sumsInt)) + assert.EqualValues(t, i, int(sumsInt[0])) +} + func TestSumCustomColumn(t *testing.T) { assert.NoError(t, prepareEngine()) @@ -183,3 +241,36 @@ func TestCountWithOthers(t *testing.T) { assert.NoError(t, err) assert.EqualValues(t, 2, total) } + +type CountWithTableName struct { + Id int64 + Name string +} + +func (CountWithTableName) TableName() string { + return "count_with_table_name1" +} + +func TestWithTableName(t *testing.T) { + assert.NoError(t, prepareEngine()) + + assertSync(t, new(CountWithTableName)) + + _, err := testEngine.Insert(&CountWithTableName{ + Name: "orderby", + }) + assert.NoError(t, err) + + _, err = testEngine.Insert(CountWithTableName{ + Name: "limit", + }) + assert.NoError(t, err) + + 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{}) + assert.NoError(t, err) + assert.EqualValues(t, 2, total) +} diff --git a/session_tx.go b/session_tx.go index c8d759a3..ee3d473f 100644 --- a/session_tx.go +++ b/session_tx.go @@ -7,7 +7,7 @@ package xorm // Begin a transaction func (session *Session) Begin() error { if session.isAutoCommit { - tx, err := session.DB().Begin() + tx, err := session.DB().BeginTx(session.ctx, nil) if err != nil { return err } diff --git a/session_tx_test.go b/session_tx_test.go index ab66713e..23e1bf28 100644 --- a/session_tx_test.go +++ b/session_tx_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) diff --git a/session_update.go b/session_update.go index 42dfaacd..85b0bb0b 100644 --- a/session_update.go +++ b/session_update.go @@ -11,8 +11,8 @@ import ( "strconv" "strings" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, args ...interface{}) error { @@ -96,14 +96,15 @@ func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, return ErrCacheFailed } kvs := strings.Split(strings.TrimSpace(sqls[1]), ",") + for idx, kv := range kvs { sps := strings.SplitN(kv, "=", 2) sps2 := strings.Split(sps[0], ".") colName := sps2[len(sps2)-1] - if strings.Contains(colName, "`") { - colName = strings.TrimSpace(strings.Replace(colName, "`", "", -1)) - } else if strings.Contains(colName, session.engine.QuoteStr()) { - colName = strings.TrimSpace(strings.Replace(colName, session.engine.QuoteStr(), "", -1)) + // treat quote prefix, suffix and '`' as quotes + quotes := append(strings.Split(session.engine.Quote(""), ""), "`") + if strings.ContainsAny(colName, strings.Join(quotes, "")) { + colName = strings.TrimSpace(eraseAny(colName, quotes...)) } else { session.engine.logger.Debug("[cacheUpdate] cannot find column", tableName, colName) return ErrCacheFailed @@ -116,7 +117,7 @@ func (session *Session) cacheUpdate(table *core.Table, tableName, sqlStr string, } else { session.engine.logger.Debug("[cacheUpdate] set bean field", bean, colName, fieldValue.Interface()) if col.IsVersion && session.statement.checkVersion { - fieldValue.SetInt(fieldValue.Int() + 1) + session.incrVersionFieldValue(fieldValue) } else { fieldValue.Set(reflect.ValueOf(args[idx])) } @@ -147,6 +148,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 defer session.Close() } + if session.statement.lastError != nil { + return 0, session.statement.lastError + } + v := rValue(bean) t := v.Type() @@ -217,19 +222,19 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } - //for update action to like "column = column + ?" + // for update action to like "column = column + ?" incColumns := session.statement.getInc() for _, v := range incColumns { colNames = append(colNames, session.engine.Quote(v.colName)+" = "+session.engine.Quote(v.colName)+" + ?") args = append(args, v.arg) } - //for update action to like "column = column - ?" + // for update action to like "column = column - ?" decColumns := session.statement.getDec() for _, v := range decColumns { colNames = append(colNames, session.engine.Quote(v.colName)+" = "+session.engine.Quote(v.colName)+" - ?") args = append(args, v.arg) } - //for update action to like "column = expression" + // for update action to like "column = expression" exprColumns := session.statement.getExpr() for _, v := range exprColumns { colNames = append(colNames, session.engine.Quote(v.colName)+" = "+v.expr) @@ -240,23 +245,39 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } var autoCond builder.Cond - if !session.statement.noAutoCondition && len(condiBean) > 0 { - if c, ok := condiBean[0].(map[string]interface{}); ok { - autoCond = builder.Eq(c) - } else { - ct := reflect.TypeOf(condiBean[0]) - k := ct.Kind() - if k == reflect.Ptr { - k = ct.Elem().Kind() - } - if k == reflect.Struct { - var err error - autoCond, err = session.statement.buildConds(session.statement.RefTable, condiBean[0], true, true, false, true, false) - if err != nil { - return 0, err - } + if !session.statement.noAutoCondition { + condBeanIsStruct := false + if len(condiBean) > 0 { + if c, ok := condiBean[0].(map[string]interface{}); ok { + autoCond = builder.Eq(c) } else { - return 0, ErrConditionType + ct := reflect.TypeOf(condiBean[0]) + k := ct.Kind() + if k == reflect.Ptr { + k = ct.Elem().Kind() + } + if k == reflect.Struct { + var err error + autoCond, err = session.statement.buildConds(session.statement.RefTable, condiBean[0], true, true, false, true, false) + if err != nil { + return 0, err + } + condBeanIsStruct = true + } else { + return 0, ErrConditionType + } + } + } + + if !condBeanIsStruct && table != nil { + if col := table.DeletedColumn(); col != nil && !session.statement.unscoped { // tag "deleted" is enabled + autoCond1 := session.engine.CondDeleted(session.engine.Quote(col.Name)) + + if autoCond == nil { + autoCond = autoCond1 + } else { + autoCond = autoCond.And(autoCond1) + } } } } @@ -357,12 +378,12 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return 0, err } else if doIncVer { if verValue != nil && verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(verValue.Int() + 1) + session.incrVersionFieldValue(verValue) } } if cacher := session.engine.getCacher(tableName); cacher != nil && session.statement.UseCache { - //session.cacheUpdate(table, tableName, sqlStr, args...) + // session.cacheUpdate(table, tableName, sqlStr, args...) session.engine.logger.Debug("[cacheUpdate] clear table ", tableName) cacher.ClearIds(tableName) cacher.ClearBeans(tableName) diff --git a/session_update_test.go b/session_update_test.go index 2a7005ee..c90ec5bd 100644 --- a/session_update_test.go +++ b/session_update_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/go-xorm/core" "github.com/stretchr/testify/assert" + "xorm.io/core" ) func TestUpdateMap(t *testing.T) { @@ -110,7 +110,7 @@ func setupForUpdate(engine EngineInterface) error { } func TestForUpdate(t *testing.T) { - if testEngine.Dialect().DriverName() != "mysql" && testEngine.Dialect().DriverName() != "mymysql" { + if *ignoreSelectUpdate { return } @@ -1331,3 +1331,62 @@ func TestUpdateCondiBean(t *testing.T) { assert.NoError(t, err) assert.True(t, has) } + +func TestWhereCondErrorWhenUpdate(t *testing.T) { + type AuthRequestError struct { + ChallengeToken string + RequestToken string + } + + assert.NoError(t, prepareEngine()) + assertSync(t, new(AuthRequestError)) + + _, err := testEngine.Cols("challenge_token", "request_token", "challenge_agent", "status"). + Where(&AuthRequestError{ChallengeToken: "1"}). + Update(&AuthRequestError{ + ChallengeToken: "2", + }) + assert.Error(t, err) + assert.EqualValues(t, ErrConditionType, err) +} + +func TestUpdateDeleted(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type UpdateDeletedStruct struct { + Id int64 + Name string + DeletedAt time.Time `xorm:"deleted"` + } + + assertSync(t, new(UpdateDeletedStruct)) + + var s = UpdateDeletedStruct{ + Name: "test", + } + cnt, err := testEngine.Insert(&s) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + cnt, err = testEngine.ID(s.Id).Delete(&UpdateDeletedStruct{}) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + cnt, err = testEngine.ID(s.Id).Update(&UpdateDeletedStruct{ + Name: "test1", + }) + assert.NoError(t, err) + assert.EqualValues(t, 0, cnt) + + cnt, err = testEngine.Table(&UpdateDeletedStruct{}).ID(s.Id).Update(map[string]interface{}{ + "name": "test1", + }) + assert.NoError(t, err) + assert.EqualValues(t, 0, cnt) + + cnt, err = testEngine.ID(s.Id).Unscoped().Update(&UpdateDeletedStruct{ + Name: "test1", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) +} diff --git a/statement.go b/statement.go index a7f7010a..585378a8 100644 --- a/statement.go +++ b/statement.go @@ -6,15 +6,13 @@ package xorm import ( "database/sql/driver" - "encoding/json" - "errors" "fmt" "reflect" "strings" "time" - "github.com/go-xorm/builder" - "github.com/go-xorm/core" + "xorm.io/builder" + "xorm.io/core" ) // Statement save all the sql info for executing SQL @@ -60,6 +58,7 @@ type Statement struct { cond builder.Cond bufferSize int context ContextCache + lastError error } // Init reset all the statement's fields @@ -101,6 +100,7 @@ func (statement *Statement) Init() { statement.cond = builder.NewCond() statement.bufferSize = 0 statement.context = nil + statement.lastError = nil } // NoAutoCondition if you do not want convert bean's field as query condition, then use this function @@ -125,13 +125,13 @@ func (statement *Statement) SQL(query interface{}, args ...interface{}) *Stateme var err error statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL() if err != nil { - statement.Engine.logger.Error(err) + statement.lastError = err } case string: statement.RawSQL = query.(string) statement.RawParams = args default: - statement.Engine.logger.Error("unsupported sql type") + statement.lastError = ErrUnSupportedSQLType } return statement @@ -160,7 +160,7 @@ func (statement *Statement) And(query interface{}, args ...interface{}) *Stateme } } default: - // TODO: not support condition type + statement.lastError = ErrConditionType } return statement @@ -397,7 +397,7 @@ func (statement *Statement) buildUpdates(bean interface{}, continue } } else { - //TODO: how to handler? + // TODO: how to handler? panic("not supported") } } else { @@ -406,7 +406,7 @@ func (statement *Statement) buildUpdates(bean interface{}, } else { // Blank struct could not be as update data if requiredField || !isStructZero(fieldValue) { - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface())) } @@ -435,7 +435,7 @@ func (statement *Statement) buildUpdates(bean interface{}, } if col.SQLType.IsText() { - bytes, err := json.Marshal(fieldValue.Interface()) + bytes, err := DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { engine.logger.Error(err) continue @@ -455,7 +455,7 @@ func (statement *Statement) buildUpdates(bean interface{}, fieldType.Elem().Kind() == reflect.Uint8 { val = fieldValue.Slice(0, 0).Interface() } else { - bytes, err = json.Marshal(fieldValue.Interface()) + bytes, err = DefaultJSONHandler.Marshal(fieldValue.Interface()) if err != nil { engine.logger.Error(err) continue @@ -578,21 +578,9 @@ func (statement *Statement) getExpr() map[string]exprParam { func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { newColumns := make([]string, 0) + quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`") for _, col := range columns { - col = strings.Replace(col, "`", "", -1) - col = strings.Replace(col, statement.Engine.QuoteStr(), "", -1) - ccols := strings.Split(col, ",") - for _, c := range ccols { - fields := strings.Split(strings.TrimSpace(c), ".") - if len(fields) == 1 { - newColumns = append(newColumns, statement.Engine.quote(fields[0])) - } else if len(fields) == 2 { - newColumns = append(newColumns, statement.Engine.quote(fields[0])+"."+ - statement.Engine.quote(fields[1])) - } else { - panic(errors.New("unwanted colnames")) - } - } + newColumns = append(newColumns, statement.Engine.Quote(eraseAny(col, quotes...))) } return newColumns } @@ -755,9 +743,36 @@ func (statement *Statement) Join(joinOP string, tablename interface{}, condition fmt.Fprintf(&buf, "%v JOIN ", joinOP) } - tbName := statement.Engine.TableName(tablename, true) + switch tp := tablename.(type) { + case builder.Builder: + subSQL, subQueryArgs, err := tp.ToSQL() + if err != nil { + statement.lastError = err + return statement + } + tbs := strings.Split(tp.TableName(), ".") + quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`") + + var aliasName = strings.Trim(tbs[len(tbs)-1], strings.Join(quotes, "")) + fmt.Fprintf(&buf, "(%s) %s ON %v", subSQL, aliasName, condition) + statement.joinArgs = append(statement.joinArgs, subQueryArgs...) + case *builder.Builder: + subSQL, subQueryArgs, err := tp.ToSQL() + if err != nil { + statement.lastError = err + return statement + } + tbs := strings.Split(tp.TableName(), ".") + quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`") + + var aliasName = strings.Trim(tbs[len(tbs)-1], strings.Join(quotes, "")) + fmt.Fprintf(&buf, "(%s) %s ON %v", subSQL, aliasName, condition) + statement.joinArgs = append(statement.joinArgs, subQueryArgs...) + default: + tbName := statement.Engine.TableName(tablename, true) + fmt.Fprintf(&buf, "%s ON %v", tbName, condition) + } - fmt.Fprintf(&buf, "%s ON %v", tbName, condition) statement.JoinStr = buf.String() statement.joinArgs = append(statement.joinArgs, args...) return statement @@ -1064,7 +1079,7 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, n if dialect.DBType() == core.MSSQL { if statement.LimitN > 0 { - top = fmt.Sprintf(" TOP %d ", statement.LimitN) + top = fmt.Sprintf("TOP %d ", statement.LimitN) } if statement.Start > 0 { var column string @@ -1133,8 +1148,12 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, n if statement.Start != 0 || statement.LimitN != 0 { oldString := buf.String() buf.Reset() + rawColStr := columnStr + if rawColStr == "*" { + rawColStr = "at.*" + } fmt.Fprintf(&buf, "SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", - columnStr, columnStr, oldString, statement.Start+statement.LimitN, statement.Start) + columnStr, rawColStr, oldString, statement.Start+statement.LimitN, statement.Start) } } } @@ -1218,7 +1237,7 @@ func (statement *Statement) convertUpdateSQL(sqlStr string) (string, string) { var whereStr = sqls[1] - //TODO: for postgres only, if any other database? + // TODO: for postgres only, if any other database? var paraStr string if statement.Engine.dialect.DBType() == core.POSTGRES { paraStr = "$" diff --git a/statement_test.go b/statement_test.go index acca72da..acc542ab 100644 --- a/statement_test.go +++ b/statement_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/go-xorm/core" "github.com/stretchr/testify/assert" + "xorm.io/core" ) var colStrTests = []struct { @@ -237,3 +237,12 @@ func TestUpdateIgnoreOnlyFromDBFields(t *testing.T) { testEngine.Update(record) assertGetRecord() } + +func TestCol2NewColsWithQuote(t *testing.T) { + cols := []string{"f1", "f2", "t3.f3"} + + statement := createTestStatement() + + quotedCols := statement.col2NewColsWithQuote(cols...) + assert.EqualValues(t, []string{statement.Engine.Quote("f1"), statement.Engine.Quote("f2"), statement.Engine.Quote("t3.f3")}, quotedCols) +} diff --git a/syslogger.go b/syslogger.go index 8840635d..11ba01e7 100644 --- a/syslogger.go +++ b/syslogger.go @@ -10,7 +10,7 @@ import ( "fmt" "log/syslog" - "github.com/go-xorm/core" + "xorm.io/core" ) var _ core.ILogger = &SyslogLogger{} diff --git a/tag.go b/tag.go index e1c821fb..6feb581a 100644 --- a/tag.go +++ b/tag.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) type tagContext struct { @@ -244,6 +244,7 @@ func SQLTypeTagHandler(ctx *tagContext) error { // ExtendsTagHandler describes extends tag handler func ExtendsTagHandler(ctx *tagContext) error { var fieldValue = ctx.fieldValue + var isPtr = false switch fieldValue.Kind() { case reflect.Ptr: f := fieldValue.Type().Elem() @@ -254,6 +255,7 @@ func ExtendsTagHandler(ctx *tagContext) error { fieldValue = reflect.New(f).Elem() } } + isPtr = true fallthrough case reflect.Struct: parentTable, err := ctx.engine.mapType(fieldValue) @@ -262,6 +264,24 @@ func ExtendsTagHandler(ctx *tagContext) error { } for _, col := range parentTable.Columns() { col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName) + + var tagPrefix = ctx.col.FieldName + if len(ctx.params) > 0 { + col.Nullable = isPtr + tagPrefix = ctx.params[0] + if col.IsPrimaryKey { + col.Name = ctx.col.FieldName + col.IsPrimaryKey = false + } else { + col.Name = fmt.Sprintf("%v%v", tagPrefix, col.Name) + } + } + + if col.Nullable { + col.IsAutoIncrement = false + col.IsPrimaryKey = false + } + ctx.table.AddColumn(col) for indexName, indexType := range col.Indexes { addIndex(indexName, ctx.table, col, indexType) diff --git a/tag_extends_test.go b/tag_extends_test.go index 4a4150ba..5a8031f0 100644 --- a/tag_extends_test.go +++ b/tag_extends_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) @@ -60,63 +60,37 @@ func TestExtends(t *testing.T) { assert.NoError(t, prepareEngine()) err := testEngine.DropTables(&tempUser2{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.CreateTables(&tempUser2{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) tu := &tempUser2{tempUser{0, "extends"}, "dev depart"} _, err = testEngine.Insert(tu) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) tu2 := &tempUser2{} _, err = testEngine.Get(tu2) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) tu3 := &tempUser2{tempUser{0, "extends update"}, ""} _, err = testEngine.ID(tu2.TempUser.Id).Update(tu3) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.DropTables(&tempUser4{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.CreateTables(&tempUser4{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) tu8 := &tempUser4{tempUser2{tempUser{0, "extends"}, "dev depart"}} _, err = testEngine.Insert(tu8) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) tu9 := &tempUser4{} _, err = testEngine.Get(tu9) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) + if tu9.TempUser2.TempUser.Username != tu8.TempUser2.TempUser.Username || tu9.TempUser2.Departname != tu8.TempUser2.Departname { err = errors.New(fmt.Sprintln("not equal for", tu8, tu9)) t.Error(err) @@ -125,36 +99,22 @@ func TestExtends(t *testing.T) { tu10 := &tempUser4{tempUser2{tempUser{0, "extends update"}, ""}} _, err = testEngine.ID(tu9.TempUser2.TempUser.Id).Update(tu10) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.DropTables(&tempUser3{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.CreateTables(&tempUser3{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) tu4 := &tempUser3{&tempUser{0, "extends"}, "dev depart"} _, err = testEngine.Insert(tu4) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) tu5 := &tempUser3{} _, err = testEngine.Get(tu5) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) + if tu5.Temp == nil { err = errors.New("error get data extends") t.Error(err) @@ -169,22 +129,12 @@ func TestExtends(t *testing.T) { tu6 := &tempUser3{&tempUser{0, "extends update"}, ""} _, err = testEngine.ID(tu5.Temp.Id).Update(tu6) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) users := make([]tempUser3, 0) err = testEngine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - if len(users) != 1 { - err = errors.New("error get data not 1") - t.Error(err) - panic(err) - } + assert.NoError(t, err) + assert.EqualValues(t, 1, len(users), "error get data not 1") assertSync(t, new(Userinfo), new(Userdetail)) @@ -249,10 +199,7 @@ func TestExtends(t *testing.T) { Join("LEFT", qt(ud), qt(ui)+"."+qt("detail_id")+" = "+qt(ud)+"."+qt(uiid)). NoCascade(). Find(&infos2) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) fmt.Println(infos2) } @@ -297,25 +244,16 @@ func TestExtends2(t *testing.T) { assert.NoError(t, prepareEngine()) err := testEngine.DropTables(&Message{}, &MessageUser{}, &MessageType{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.CreateTables(&Message{}, &MessageUser{}, &MessageType{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) var sender = MessageUser{Name: "sender"} var receiver = MessageUser{Name: "receiver"} var msgtype = MessageType{Name: "type"} _, err = testEngine.Insert(&sender, &receiver, &msgtype) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) msg := Message{ MessageBase: MessageBase{ @@ -326,15 +264,24 @@ func TestExtends2(t *testing.T) { Uid: sender.Id, ToUid: receiver.Id, } + + session := testEngine.NewSession() + defer session.Close() + + // MSSQL deny insert identity column excep declare as below if testEngine.Dialect().DBType() == core.MSSQL { - _, err = testEngine.Exec("SET IDENTITY_INSERT message ON") + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT message ON") assert.NoError(t, err) } + cnt, err := session.Insert(&msg) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) - _, err = testEngine.Insert(&msg) - if err != nil { - t.Error(err) - panic(err) + if testEngine.Dialect().DBType() == core.MSSQL { + err = session.Commit() + assert.NoError(t, err) } var mapper = testEngine.GetTableMapper().Obj2Table @@ -344,23 +291,14 @@ func TestExtends2(t *testing.T) { msgTableName := quote(testEngine.TableName(mapper("Message"), true)) list := make([]Message, 0) - err = testEngine.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). + err = session.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). Join("LEFT", []string{userTableName, "receiver"}, "`receiver`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("ToUid")+"`"). Join("LEFT", []string{typeTableName, "type"}, "`type`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). Find(&list) assert.NoError(t, err) - if len(list) != 1 { - err = errors.New(fmt.Sprintln("should have 1 message, got", len(list))) - t.Error(err) - panic(err) - } - - if list[0].Id != msg.Id { - err = errors.New(fmt.Sprintln("should message equal", list[0], msg)) - t.Error(err) - panic(err) - } + assert.EqualValues(t, 1, len(list), fmt.Sprintln("should have 1 message, got", len(list))) + assert.EqualValues(t, msg.Id, list[0].Id, fmt.Sprintln("should message equal", list[0], msg)) } func TestExtends3(t *testing.T) { @@ -396,13 +334,25 @@ func TestExtends3(t *testing.T) { Uid: sender.Id, ToUid: receiver.Id, } + + session := testEngine.NewSession() + defer session.Close() + + // MSSQL deny insert identity column excep declare as below if testEngine.Dialect().DBType() == core.MSSQL { - _, err = testEngine.Exec("SET IDENTITY_INSERT message ON") + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT message ON") assert.NoError(t, err) } - _, err = testEngine.Insert(&msg) + _, err = session.Insert(&msg) assert.NoError(t, err) + if testEngine.Dialect().DBType() == core.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } + var mapper = testEngine.GetTableMapper().Obj2Table var quote = testEngine.Quote userTableName := quote(testEngine.TableName(mapper("MessageUser"), true)) @@ -410,7 +360,7 @@ func TestExtends3(t *testing.T) { msgTableName := quote(testEngine.TableName(mapper("Message"), true)) list := make([]MessageExtend3, 0) - err = testEngine.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). + err = session.Table(msgTableName).Join("LEFT", []string{userTableName, "sender"}, "`sender`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). Join("LEFT", []string{userTableName, "receiver"}, "`receiver`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("ToUid")+"`"). Join("LEFT", []string{typeTableName, "type"}, "`type`.`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). Find(&list) @@ -478,14 +428,23 @@ func TestExtends4(t *testing.T) { Content: "test", Uid: sender.Id, } + + session := testEngine.NewSession() + defer session.Close() + + // MSSQL deny insert identity column excep declare as below if testEngine.Dialect().DBType() == core.MSSQL { - _, err = testEngine.Exec("SET IDENTITY_INSERT message ON") + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("SET IDENTITY_INSERT message ON") assert.NoError(t, err) } - _, err = testEngine.Insert(&msg) - if err != nil { - t.Error(err) - panic(err) + _, err = session.Insert(&msg) + assert.NoError(t, err) + + if testEngine.Dialect().DBType() == core.MSSQL { + err = session.Commit() + assert.NoError(t, err) } var mapper = testEngine.GetTableMapper().Obj2Table @@ -495,7 +454,7 @@ func TestExtends4(t *testing.T) { msgTableName := quote(testEngine.TableName(mapper("Message"), true)) list := make([]MessageExtend4, 0) - err = testEngine.Table(msgTableName).Join("LEFT", userTableName, userTableName+".`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). + err = session.Table(msgTableName).Join("LEFT", userTableName, userTableName+".`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Uid")+"`"). Join("LEFT", typeTableName, typeTableName+".`"+mapper("Id")+"`="+msgTableName+".`"+mapper("Id")+"`"). Find(&list) if err != nil { @@ -527,3 +486,123 @@ func TestExtends4(t *testing.T) { panic(err) } } + +type Size struct { + ID int64 `xorm:"int(4) 'id' pk autoincr"` + Width float32 `json:"width" xorm:"float 'Width'"` + Height float32 `json:"height" xorm:"float 'Height'"` +} + +type Book struct { + ID int64 `xorm:"int(4) 'id' pk autoincr"` + SizeOpen *Size `xorm:"extends('Open')"` + SizeClosed *Size `xorm:"extends('Closed')"` + Size *Size `xorm:"extends('')"` +} + +func TestExtends5(t *testing.T) { + assert.NoError(t, prepareEngine()) + err := testEngine.DropTables(&Book{}, &Size{}) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(&Size{}, &Book{}) + if err != nil { + t.Error(err) + panic(err) + } + + var sc = Size{Width: 0.2, Height: 0.4} + var so = Size{Width: 0.2, Height: 0.8} + var s = Size{Width: 0.15, Height: 1.5} + var bk1 = Book{ + SizeOpen: &so, + SizeClosed: &sc, + Size: &s, + } + var bk2 = Book{ + SizeOpen: &so, + } + var bk3 = Book{ + SizeClosed: &sc, + Size: &s, + } + var bk4 = Book{} + var bk5 = Book{Size: &s} + _, err = testEngine.Insert(&sc, &so, &s, &bk1, &bk2, &bk3, &bk4, &bk5) + if err != nil { + t.Fatal(err) + } + + var books = map[int64]Book{ + bk1.ID: bk1, + bk2.ID: bk2, + bk3.ID: bk3, + bk4.ID: bk4, + bk5.ID: bk5, + } + + session := testEngine.NewSession() + defer session.Close() + + var mapper = testEngine.GetTableMapper().Obj2Table + var quote = testEngine.Quote + bookTableName := quote(testEngine.TableName(mapper("Book"), true)) + sizeTableName := quote(testEngine.TableName(mapper("Size"), true)) + + list := make([]Book, 0) + err = session. + Select(fmt.Sprintf( + "%s.%s, sc.%s AS %s, sc.%s AS %s, s.%s, s.%s", + quote(bookTableName), + quote("id"), + quote("Width"), + quote("ClosedWidth"), + quote("Height"), + quote("ClosedHeight"), + quote("Width"), + quote("Height"), + )). + Table(bookTableName). + Join( + "LEFT", + sizeTableName+" AS `sc`", + bookTableName+".`SizeClosed`=sc.`id`", + ). + Join( + "LEFT", + sizeTableName+" AS `s`", + bookTableName+".`Size`=s.`id`", + ). + Find(&list) + if err != nil { + t.Error(err) + panic(err) + } + + for _, book := range list { + if ok := assert.Equal(t, books[book.ID].SizeClosed.Width, book.SizeClosed.Width); !ok { + t.Error("Not bounded size closed") + panic("Not bounded size closed") + } + + if ok := assert.Equal(t, books[book.ID].SizeClosed.Height, book.SizeClosed.Height); !ok { + t.Error("Not bounded size closed") + panic("Not bounded size closed") + } + + if books[book.ID].Size != nil || book.Size != nil { + if ok := assert.Equal(t, books[book.ID].Size.Width, book.Size.Width); !ok { + t.Error("Not bounded size") + panic("Not bounded size") + } + + if ok := assert.Equal(t, books[book.ID].Size.Height, book.Size.Height); !ok { + t.Error("Not bounded size") + panic("Not bounded size") + } + } + } +} diff --git a/tag_id_test.go b/tag_id_test.go index a53fe6bd..f1c5a6bc 100644 --- a/tag_id_test.go +++ b/tag_id_test.go @@ -7,7 +7,7 @@ package xorm import ( "testing" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) diff --git a/tag_test.go b/tag_test.go index a1df6356..cfb16b3b 100644 --- a/tag_test.go +++ b/tag_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "github.com/go-xorm/core" "github.com/stretchr/testify/assert" + "xorm.io/core" ) type UserCU struct { diff --git a/tag_version_test.go b/tag_version_test.go index 570a6754..cd6dc935 100644 --- a/tag_version_test.go +++ b/tag_version_test.go @@ -85,7 +85,7 @@ func TestVersion1(t *testing.T) { } fmt.Println(newVer) if newVer.Ver != 2 { - err = errors.New("insert error") + err = errors.New("update error") t.Error(err) panic(err) } @@ -126,3 +126,117 @@ func TestVersion2(t *testing.T) { } } } + +type VersionUintS struct { + Id int64 + Name string + Ver uint `xorm:"version"` + Created time.Time `xorm:"created"` +} + +func TestVersion3(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + ver := &VersionUintS{Name: "sfsfdsfds"} + _, err = testEngine.Insert(ver) + if err != nil { + t.Error(err) + panic(err) + } + fmt.Println(ver) + if ver.Ver != 1 { + err = errors.New("insert error") + t.Error(err) + panic(err) + } + + newVer := new(VersionUintS) + has, err := testEngine.ID(ver.Id).Get(newVer) + if err != nil { + t.Error(err) + panic(err) + } + + if !has { + t.Error(errors.New(fmt.Sprintf("no version id is %v", ver.Id))) + panic(err) + } + fmt.Println(newVer) + if newVer.Ver != 1 { + err = errors.New("insert error") + t.Error(err) + panic(err) + } + + newVer.Name = "-------" + _, err = testEngine.ID(ver.Id).Update(newVer) + if err != nil { + t.Error(err) + panic(err) + } + if newVer.Ver != 2 { + err = errors.New("update should set version back to struct") + t.Error(err) + } + + newVer = new(VersionUintS) + has, err = testEngine.ID(ver.Id).Get(newVer) + if err != nil { + t.Error(err) + panic(err) + } + fmt.Println(newVer) + if newVer.Ver != 2 { + err = errors.New("update error") + t.Error(err) + panic(err) + } +} + +func TestVersion4(t *testing.T) { + assert.NoError(t, prepareEngine()) + + err := testEngine.DropTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + err = testEngine.CreateTables(new(VersionUintS)) + if err != nil { + t.Error(err) + panic(err) + } + + var vers = []VersionUintS{ + {Name: "sfsfdsfds"}, + {Name: "xxxxx"}, + } + _, err = testEngine.Insert(vers) + if err != nil { + t.Error(err) + panic(err) + } + + fmt.Println(vers) + + for _, v := range vers { + if v.Ver != 1 { + err := errors.New("version should be 1") + t.Error(err) + panic(err) + } + } +} diff --git a/test_mssql.sh b/test_mssql.sh index 6f9cf729..7f060cff 100755 --- a/test_mssql.sh +++ b/test_mssql.sh @@ -1 +1 @@ -go test -db=mssql -conn_str="server=192.168.1.58;user id=sa;password=123456;database=xorm_test" \ No newline at end of file +go test -db=mssql -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" \ No newline at end of file diff --git a/test_tidb.sh b/test_tidb.sh new file mode 100755 index 00000000..03d2d6cd --- /dev/null +++ b/test_tidb.sh @@ -0,0 +1 @@ +go test -db=mysql -conn_str="root:@tcp(localhost:4000)/xorm_test" -ignore_select_update=true \ No newline at end of file diff --git a/types.go b/types.go index 99d761c2..c76a5460 100644 --- a/types.go +++ b/types.go @@ -1,9 +1,13 @@ +// 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 ( "reflect" - "github.com/go-xorm/core" + "xorm.io/core" ) var ( diff --git a/types_test.go b/types_test.go index 20511407..274609b2 100644 --- a/types_test.go +++ b/types_test.go @@ -5,12 +5,11 @@ package xorm import ( - "encoding/json" "errors" "fmt" "testing" - "github.com/go-xorm/core" + "xorm.io/core" "github.com/stretchr/testify/assert" ) @@ -117,21 +116,21 @@ type ConvConfig struct { } func (s *ConvConfig) FromDB(data []byte) error { - return json.Unmarshal(data, s) + return DefaultJSONHandler.Unmarshal(data, s) } func (s *ConvConfig) ToDB() ([]byte, error) { - return json.Marshal(s) + return DefaultJSONHandler.Marshal(s) } type SliceType []*ConvConfig func (s *SliceType) FromDB(data []byte) error { - return json.Unmarshal(data, s) + return DefaultJSONHandler.Unmarshal(data, s) } func (s *SliceType) ToDB() ([]byte, error) { - return json.Marshal(s) + return DefaultJSONHandler.Marshal(s) } type ConvStruct struct { @@ -309,16 +308,24 @@ func TestCustomType2(t *testing.T) { _, err = testEngine.Exec("delete from " + testEngine.Quote(tableName)) assert.NoError(t, err) + session := testEngine.NewSession() + defer session.Close() + if testEngine.Dialect().DBType() == core.MSSQL { - return - /*_, err = engine.Exec("set IDENTITY_INSERT " + tableName + " on") - if err != nil { - t.Fatal(err) - }*/ + err = session.Begin() + assert.NoError(t, err) + _, err = session.Exec("set IDENTITY_INSERT " + tableName + " on") + assert.NoError(t, err) } - _, err = testEngine.Insert(&UserCus{1, "xlw", Registed}) + cnt, err := session.Insert(&UserCus{1, "xlw", Registed}) assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + + if testEngine.Dialect().DBType() == core.MSSQL { + err = session.Commit() + assert.NoError(t, err) + } user := UserCus{} exist, err := testEngine.ID(1).Get(&user) diff --git a/xorm.go b/xorm.go index 739de8d4..26d00d26 100644 --- a/xorm.go +++ b/xorm.go @@ -7,6 +7,7 @@ package xorm import ( + "context" "fmt" "os" "reflect" @@ -14,7 +15,7 @@ import ( "sync" "time" - "github.com/go-xorm/core" + "xorm.io/core" ) const ( @@ -85,14 +86,15 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { } engine := &Engine{ - db: db, - dialect: dialect, - Tables: make(map[reflect.Type]*core.Table), - mutex: &sync.RWMutex{}, - TagIdentifier: "xorm", - TZLocation: time.Local, - tagHandlers: defaultTagHandlers, - cachers: make(map[string]core.Cacher), + db: db, + dialect: dialect, + Tables: make(map[reflect.Type]*core.Table), + mutex: &sync.RWMutex{}, + TagIdentifier: "xorm", + TZLocation: time.Local, + tagHandlers: defaultTagHandlers, + cachers: make(map[string]core.Cacher), + defaultContext: context.Background(), } if uri.DbType == core.SQLITE { diff --git a/xorm_test.go b/xorm_test.go index 4e88dc40..c0302df3 100644 --- a/xorm_test.go +++ b/xorm_test.go @@ -1,15 +1,21 @@ +// Copyright 2018 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 ( + "database/sql" "flag" "fmt" + "log" "os" "strings" "testing" _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" - "github.com/go-xorm/core" + "xorm.io/core" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" _ "github.com/ziutek/mymysql/godrv" @@ -20,14 +26,15 @@ var ( dbType string connString string - db = flag.String("db", "sqlite3", "the tested database") - showSQL = flag.Bool("show_sql", true, "show generated SQLs") - ptrConnStr = flag.String("conn_str", "./test.db?cache=shared&mode=rwc", "test database connection string") - mapType = flag.String("map_type", "snake", "indicate the name mapping") - cache = flag.Bool("cache", false, "if enable cache") - cluster = flag.Bool("cluster", false, "if this is a cluster") - splitter = flag.String("splitter", ";", "the splitter on connstr for cluster") - schema = flag.String("schema", "", "specify the schema") + db = flag.String("db", "sqlite3", "the tested database") + showSQL = flag.Bool("show_sql", true, "show generated SQLs") + ptrConnStr = flag.String("conn_str", "./test.db?cache=shared&mode=rwc", "test database connection string") + mapType = flag.String("map_type", "snake", "indicate the name mapping") + cache = flag.Bool("cache", false, "if enable cache") + cluster = flag.Bool("cluster", false, "if this is a cluster") + splitter = flag.String("splitter", ";", "the splitter on connstr for cluster") + schema = flag.String("schema", "", "specify the schema") + ignoreSelectUpdate = flag.Bool("ignore_select_update", false, "ignore select update if implementation difference, only for tidb") ) func createEngine(dbType, connStr string) error { @@ -35,9 +42,59 @@ func createEngine(dbType, connStr string) error { var err error if !*cluster { + switch strings.ToLower(dbType) { + case core.MSSQL: + db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "master", -1)) + if err != nil { + return err + } + if _, err = db.Exec("If(db_id(N'xorm_test') IS NULL) BEGIN CREATE DATABASE xorm_test; END;"); err != nil { + return fmt.Errorf("db.Exec: %v", err) + } + db.Close() + *ignoreSelectUpdate = true + case core.POSTGRES: + db, err := sql.Open(dbType, connStr) + if err != nil { + return err + } + rows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = 'xorm_test'")) + if err != nil { + return fmt.Errorf("db.Query: %v", err) + } + defer rows.Close() + + if !rows.Next() { + if _, err = db.Exec("CREATE DATABASE xorm_test"); err != nil { + return fmt.Errorf("CREATE DATABASE: %v", err) + } + } + if *schema != "" { + if _, err = db.Exec("CREATE SCHEMA IF NOT EXISTS " + *schema); err != nil { + return fmt.Errorf("CREATE SCHEMA: %v", err) + } + } + db.Close() + *ignoreSelectUpdate = true + case core.MYSQL: + db, err := sql.Open(dbType, strings.Replace(connStr, "xorm_test", "mysql", -1)) + if err != nil { + return err + } + if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS xorm_test"); err != nil { + return fmt.Errorf("db.Exec: %v", err) + } + db.Close() + default: + *ignoreSelectUpdate = true + } + testEngine, err = NewEngine(dbType, connStr) } else { testEngine, err = NewEngineGroup(dbType, strings.Split(connStr, *splitter)) + if dbType != "mysql" && dbType != "mymysql" { + *ignoreSelectUpdate = true + } } if err != nil { return err @@ -95,7 +152,7 @@ func TestMain(m *testing.M) { } } else { if ptrConnStr == nil { - fmt.Println("you should indicate conn string") + log.Fatal("you should indicate conn string") return } connString = *ptrConnStr @@ -112,7 +169,7 @@ func TestMain(m *testing.M) { fmt.Println("testing", dbType, connString) if err := prepareEngine(); err != nil { - fmt.Println(err) + log.Fatal(err) return }