From 2ad174c0782132ee46b460793eb306503546ed8d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 30 Jul 2019 15:31:23 +0800 Subject: [PATCH 01/20] update drone (#1374) --- .drone.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index df9d405b..be978458 100644 --- a/.drone.yml +++ b/.drone.yml @@ -37,10 +37,9 @@ services: matrix: GO_VERSION: - - 1.8 - - 1.9 - 1.10 - 1.11 + - 1.12 pipeline: init_postgres: From 9681be3e4d1f13e2b4ba2bec4d312131eb11e87c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 30 Jul 2019 16:35:36 +0800 Subject: [PATCH 02/20] fix bugs (#1375) --- session_find.go | 2 ++ session_get.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/session_find.go b/session_find.go index d3fc0d30..6b8aa469 100644 --- a/session_find.go +++ b/session_find.go @@ -63,6 +63,8 @@ func (session *Session) FindAndCount(rowsSlicePtr interface{}, condiBean ...inte } func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{}) error { + defer session.resetStatement() + if session.statement.lastError != nil { return session.statement.lastError } diff --git a/session_get.go b/session_get.go index ad2627f4..8901357e 100644 --- a/session_get.go +++ b/session_get.go @@ -24,6 +24,8 @@ func (session *Session) Get(bean interface{}) (bool, error) { } func (session *Session) get(bean interface{}) (bool, error) { + defer session.resetStatement() + if session.statement.lastError != nil { return false, session.statement.lastError } @@ -75,6 +77,8 @@ func (session *Session) get(bean interface{}) (bool, error) { if context != nil { res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args)) if res != nil { + session.engine.logger.Debug("hit context cache", sqlStr) + structValue := reflect.Indirect(reflect.ValueOf(bean)) structValue.Set(reflect.Indirect(reflect.ValueOf(res))) session.lastSQL = "" From ce79b1cf28a4a457a730212fc1bec616a8db4db9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 5 Aug 2019 17:05:42 +0800 Subject: [PATCH 03/20] fix get customize type bug (#1382) --- go.sum | 21 +++++++++++++++------ session_get.go | 2 +- session_get_test.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/go.sum b/go.sum index f00ff971..370dcd0b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ 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= @@ -13,7 +12,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 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= @@ -36,6 +34,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU 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/protobuf v1.3.1/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= @@ -100,8 +99,9 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK 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/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= @@ -114,29 +114,38 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r 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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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/sync v0.0.0-20190423024810-112230192c58/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/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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-20180917221912-90fa682c2a6e/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= +golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 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/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw= +google.golang.org/appengine v1.6.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= @@ -155,5 +164,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh 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= +xorm.io/core v0.7.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM= +xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI= diff --git a/session_get.go b/session_get.go index 8901357e..cc0a2019 100644 --- a/session_get.go +++ b/session_get.go @@ -256,7 +256,7 @@ func (session *Session) nocacheGet(beanKind reflect.Kind, table *core.Table, bea 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) + err = rows.Scan(bean) default: err = rows.Scan(bean) } diff --git a/session_get_test.go b/session_get_test.go index 3af4395f..f93068c8 100644 --- a/session_get_test.go +++ b/session_get_test.go @@ -584,3 +584,37 @@ func TestGetNullVar(t *testing.T) { assert.True(t, has) assert.EqualValues(t, 0, age10) } + +func TestCustomTypes(t *testing.T) { + type MyInt int + type MyString string + + type TestCustomizeStruct struct { + Id int64 + Name MyString + Age MyInt + } + + assert.NoError(t, prepareEngine()) + assertSync(t, new(TestCustomizeStruct)) + + var s = TestCustomizeStruct{ + Name: "test", + Age: 32, + } + _, err := testEngine.Insert(&s) + assert.NoError(t, err) + assert.EqualValues(t, 1, s.Id) + + var name MyString + has, err := testEngine.Table(new(TestCustomizeStruct)).ID(s.Id).Cols("name").Get(&name) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "test", name) + + var age MyInt + has, err = testEngine.Table(new(TestCustomizeStruct)).ID(s.Id).Select("age").Get(&age) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, 32, age) +} From b78ac8ce0a8c507fb5045363ca9b0c4dcebb5723 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 14 Aug 2019 23:46:40 +0800 Subject: [PATCH 04/20] Use drone new format (#1388) * use drone new format --- .drone.yml | 517 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 412 insertions(+), 105 deletions(-) diff --git a/.drone.yml b/.drone.yml index be978458..94ba92e6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,124 +1,431 @@ +--- +kind: pipeline +name: matrix-1 + +platform: + os: linux + arch: amd64 + +clone: + disable: true + workspace: base: /go path: src/github.com/go-xorm/xorm -clone: - git: - image: plugins/git:next +steps: +- name: git + pull: default + image: plugins/git:next + settings: depth: 50 tags: true +- name: init_postgres + pull: default + image: postgres:9.5 + commands: + - "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n" + - "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n" + +- name: build + pull: default + image: golang:1.10 + commands: + - go get -t -d -v ./... + - go get -u xorm.io/core + - go get -u xorm.io/builder + - go build -v + when: + event: + - push + - pull_request + +- name: test-sqlite + pull: default + image: golang:1.10 + commands: + - 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" + when: + event: + - push + - pull_request + +- name: test-mysql + pull: default + image: golang:1.10 + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mysql-utf8mb4 + pull: default + image: golang:1.10 + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mymysql + pull: default + image: golang:1.10 + commands: + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic" + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres + pull: default + image: golang:1.10 + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres-schema + pull: default + image: golang:1.10 + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/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 coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt + when: + event: + - push + - pull_request + services: - mysql: - image: mysql:5.7 - environment: - - MYSQL_DATABASE=xorm_test - - MYSQL_ALLOW_EMPTY_PASSWORD=yes - when: - event: [ push, tag, pull_request ] +- name: mysql + pull: default + image: mysql:5.7 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: xorm_test + when: + event: + - push + - tag + - pull_request - pgsql: - image: postgres:9.5 - environment: - - POSTGRES_USER=postgres - - POSTGRES_DB=xorm_test - when: - event: [ push, tag, pull_request ] +- name: pgsql + pull: default + image: postgres:9.5 + environment: + POSTGRES_DB: xorm_test + POSTGRES_USER: postgres + when: + event: + - push + - tag + - pull_request - #mssql: - # image: microsoft/mssql-server-linux:2017-CU11 - # environment: - # - ACCEPT_EULA=Y - # - SA_PASSWORD=yourStrong(!)Password - # - MSSQL_PID=Developer - # commands: - # - echo 'CREATE DATABASE xorm_test' > create.sql - # - /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P yourStrong(!)Password -i "create.sql" +--- +kind: pipeline +name: matrix-2 -matrix: - GO_VERSION: - - 1.10 - - 1.11 - - 1.12 +platform: + os: linux + arch: amd64 -pipeline: - init_postgres: - image: postgres:9.5 - commands: - # wait for postgres service to become available - - | - until psql -U postgres -d xorm_test -h pgsql \ - -c "SELECT 1;" >/dev/null 2>&1; do sleep 1; done - # query the database - - | - psql -U postgres -d xorm_test -h pgsql \ - -c "create schema xorm;" +clone: + disable: true - build: - image: golang:${GO_VERSION} - commands: - - go get -t -d -v ./... - - go get -u xorm.io/core - - go get -u xorm.io/builder - - go build -v - when: - event: [ push, pull_request ] +workspace: + base: /go + path: src/github.com/go-xorm/xorm - test-sqlite: - image: golang:${GO_VERSION} - commands: - - 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 - when: - event: [ push, pull_request ] +steps: +- name: git + pull: default + image: plugins/git:next + settings: + depth: 50 + tags: true - test-mysql: - image: golang:${GO_VERSION} - commands: - - go test -v -race -db="mysql" -conn_str="root:@tcp(mysql)/xorm_test" -coverprofile=coverage2-1.txt -covermode=atomic - - go test -v -race -db="mysql" -conn_str="root:@tcp(mysql)/xorm_test" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic - when: - event: [ push, pull_request ] +- name: init_postgres + pull: default + image: postgres:9.5 + commands: + - "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n" + - "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n" - test-mysql-utf8mb4: - image: golang:${GO_VERSION} - commands: - - go test -v -race -db="mysql" -conn_str="root:@tcp(mysql)/xorm_test?charset=utf8mb4" -coverprofile=coverage2.1-1.txt -covermode=atomic - - go test -v -race -db="mysql" -conn_str="root:@tcp(mysql)/xorm_test?charset=utf8mb4" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic - when: - event: [ push, pull_request ] +- name: build + pull: default + image: golang:1.11 + commands: + - go get -t -d -v ./... + - go get -u xorm.io/core + - go get -u xorm.io/builder + - GO111MODULE=off go build -v + when: + event: + - push + - pull_request - test-mymysql: - image: golang:${GO_VERSION} - commands: - - go test -v -race -db="mymysql" -conn_str="tcp:mysql:3306*xorm_test/root/" -coverprofile=coverage3-1.txt -covermode=atomic - - go test -v -race -db="mymysql" -conn_str="tcp:mysql:3306*xorm_test/root/" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic - when: - event: [ push, pull_request ] +- name: build-gomod + pull: default + image: golang:1.11 + environment: + GOPROXY: "https://goproxy.cn" + commands: + - GO111MODULE=on go build -v + when: + event: + - push + - pull_request - test-postgres: - image: golang:${GO_VERSION} - commands: - - go test -v -race -db="postgres" -conn_str="postgres://postgres:@pgsql/xorm_test?sslmode=disable" -coverprofile=coverage4-1.txt -covermode=atomic - - go test -v -race -db="postgres" -conn_str="postgres://postgres:@pgsql/xorm_test?sslmode=disable" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic - when: - event: [ push, pull_request ] - - test-postgres-schema: - image: golang:${GO_VERSION} - commands: - - go test -v -race -db="postgres" -conn_str="postgres://postgres:@pgsql/xorm_test?sslmode=disable" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic - - go test -v -race -db="postgres" -conn_str="postgres://postgres:@pgsql/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 coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt - when: - event: [ push, pull_request ] +- name: test-sqlite + pull: default + image: golang:1.11 + commands: + - 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" + when: + event: + - push + - pull_request - #coverage: - # image: robertstettner/drone-codecov - # secrets: [ codecov_token ] - # files: - # - coverage.txt - # when: - # event: [ push, pull_request ] - # branch: [ master ] \ No newline at end of file +- name: test-mysql + pull: default + image: golang:1.11 + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mysql-utf8mb4 + pull: default + image: golang:1.11 + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mymysql + pull: default + image: golang:1.11 + commands: + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic" + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres + pull: default + image: golang:1.11 + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres-schema + pull: default + image: golang:1.11 + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/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 coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt + when: + event: + - push + - pull_request + +services: +- name: mysql + pull: default + image: mysql:5.7 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: xorm_test + when: + event: + - push + - tag + - pull_request + +- name: pgsql + pull: default + image: postgres:9.5 + environment: + POSTGRES_DB: xorm_test + POSTGRES_USER: postgres + when: + event: + - push + - tag + - pull_request + +--- +kind: pipeline +name: matrix-3 + +platform: + os: linux + arch: amd64 + +clone: + disable: true + +workspace: + base: /go + path: src/github.com/go-xorm/xorm + +steps: +- name: git + pull: default + image: plugins/git:next + settings: + depth: 50 + tags: true + +- name: init_postgres + pull: default + image: postgres:9.5 + commands: + - "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n" + - "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n" + +- name: build + pull: default + image: golang:1.12 + commands: + - go get -t -d -v ./... + - go get -u xorm.io/core + - go get -u xorm.io/builder + - GO111MODULE=off go build -v + when: + event: + - push + - pull_request + +- name: build-gomod + pull: default + image: golang:1.12 + environment: + GOPROXY: "https://goproxy.cn" + commands: + - GO111MODULE=on go build -v + when: + event: + - push + - pull_request + +- name: test-sqlite + pull: default + image: golang:1.12 + commands: + - 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" + when: + event: + - push + - pull_request + +- name: test-mysql + pull: default + image: golang:1.12 + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mysql-utf8mb4 + pull: default + image: golang:1.12 + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mymysql + pull: default + image: golang:1.12 + commands: + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic" + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres + pull: default + image: golang:1.12 + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres-schema + pull: default + image: golang:1.12 + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/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 coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt + when: + event: + - push + - pull_request + +services: +- name: mysql + pull: default + image: mysql:5.7 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: xorm_test + when: + event: + - push + - tag + - pull_request + +- name: pgsql + pull: default + image: postgres:9.5 + environment: + POSTGRES_DB: xorm_test + POSTGRES_USER: postgres + when: + event: + - push + - tag + - pull_request \ No newline at end of file From 17592d96b35bfc831c1161d86840df73f0b14bef Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 22 Aug 2019 23:40:23 +0800 Subject: [PATCH 05/20] Add insert select where support (#1401) --- session_insert.go | 116 +++++++++++++++++++++++++++++++++-------- session_insert_test.go | 59 +++++++++++++++++++++ 2 files changed, 153 insertions(+), 22 deletions(-) diff --git a/session_insert.go b/session_insert.go index 71356566..24b32831 100644 --- a/session_insert.go +++ b/session_insert.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "xorm.io/builder" "xorm.io/core" ) @@ -345,7 +346,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { for _, v := range exprColumns { // remove the expr columns for i, colName := range colNames { - if colName == v.colName { + if colName == strings.Trim(v.colName, "`") { colNames = append(colNames[:i], colNames[i+1:]...) args = append(args[:i], args[i+1:]...) } @@ -371,12 +372,30 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { 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)%s VALUES (%v)", - session.engine.Quote(tableName), - quoteColumns(colNames, session.engine.Quote, ","), - output, - colPlaces) + if session.statement.cond.IsValid() { + condSQL, condArgs, err := builder.ToSQL(session.statement.cond) + if err != nil { + return 0, err + } + + sqlStr = fmt.Sprintf("INSERT INTO %s (%v)%s SELECT %v FROM %v WHERE %v", + session.engine.Quote(tableName), + quoteColumns(colNames, session.engine.Quote, ","), + output, + colPlaces, + session.engine.Quote(tableName), + condSQL, + ) + args = append(args, condArgs...) + } else { + sqlStr = fmt.Sprintf("INSERT INTO %s (%v)%s VALUES (%v)", + session.engine.Quote(tableName), + 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)) @@ -663,6 +682,11 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err return 0, ErrParamsType } + tableName := session.statement.TableName() + if len(tableName) <= 0 { + return 0, ErrTableNotFound + } + var columns = make([]string, 0, len(m)) for k := range m { columns = append(columns, k) @@ -670,19 +694,40 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err 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]) } + // insert expr columns, override if exists + exprColumns := session.statement.getExpr() + for _, col := range exprColumns { + columns = append(columns, strings.Trim(col.colName, "`")) + qm = qm + col.expr + "," + } + + qm = qm[:len(qm)-1] + + var sql string + + if session.statement.cond.IsValid() { + condSQL, condArgs, err := builder.ToSQL(session.statement.cond) + if err != nil { + return 0, err + } + sql = fmt.Sprintf("INSERT INTO %s (`%s`) SELECT %s FROM %s WHERE %s", + session.engine.Quote(tableName), + strings.Join(columns, "`,`"), + qm, + session.engine.Quote(tableName), + condSQL, + ) + args = append(args, condArgs...) + } else { + sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) + } + if err := session.cacheInsert(tableName); err != nil { return 0, err } @@ -703,26 +748,53 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { return 0, ErrParamsType } + tableName := session.statement.TableName() + if len(tableName) <= 0 { + return 0, ErrTableNotFound + } + 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]) } + qm := strings.Repeat("?,", len(columns)) + + // insert expr columns, override if exists + exprColumns := session.statement.getExpr() + for _, col := range exprColumns { + columns = append(columns, strings.Trim(col.colName, "`")) + qm = qm + col.expr + "," + } + + qm = qm[:len(qm)-1] + + var sql string + + if session.statement.cond.IsValid() { + qm = "(" + qm[:len(qm)-1] + ")" + condSQL, condArgs, err := builder.ToSQL(session.statement.cond) + if err != nil { + return 0, err + } + sql = fmt.Sprintf("INSERT INTO %s (`%s`) SELECT %s FROM %s WHERE %s", + session.engine.Quote(tableName), + strings.Join(columns, "`,`"), + qm, + session.engine.Quote(tableName), + condSQL, + ) + args = append(args, condArgs...) + } else { + sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) + } + if err := session.cacheInsert(tableName); err != nil { return 0, err } diff --git a/session_insert_test.go b/session_insert_test.go index 8e7ffa99..daf08e7f 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -834,3 +834,62 @@ func TestInsertMap(t *testing.T) { assert.EqualValues(t, 10, ims[3].Height) assert.EqualValues(t, "lunny", ims[3].Name) } + +/*INSERT INTO `issue` (`repo_id`, `poster_id`, ... ,`name`, `content`, ... ,`index`) +SELECT $1, $2, ..., $14, $15, ..., MAX(`index`) + 1 FROM `issue` WHERE `repo_id` = $1; +*/ +func TestInsertWhere(t *testing.T) { + type InsertWhere struct { + Id int64 + Index int `xorm:"unique(s) notnull"` + RepoId int64 `xorm:"unique(s)"` + Width uint32 + Height uint32 + Name string + } + + assert.NoError(t, prepareEngine()) + assertSync(t, new(InsertWhere)) + + var i = InsertWhere{ + RepoId: 1, + Width: 10, + Height: 20, + Name: "trest", + } + + inserted, err := testEngine.SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). + Where("repo_id=?", 1). + Insert(&i) + assert.NoError(t, err) + assert.EqualValues(t, 1, inserted) + assert.EqualValues(t, 1, i.Id) + + var j InsertWhere + has, err := testEngine.ID(i.Id).Get(&j) + assert.NoError(t, err) + assert.True(t, has) + i.Index = 1 + assert.EqualValues(t, i, j) + + inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). + Insert(map[string]interface{}{ + "repo_id": 1, + "width": 20, + "height": 40, + "name": "trest2", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, inserted) + + var j2 InsertWhere + has, err = testEngine.ID(2).Get(&j2) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, 1, j2.RepoId) + assert.EqualValues(t, 20, j2.Width) + assert.EqualValues(t, 40, j2.Height) + assert.EqualValues(t, "trest2", j2.Name) + assert.EqualValues(t, 2, j2.Index) +} From 76ca740a628bb30068e039b6141250689ed80549 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 4 Sep 2019 12:33:44 +0800 Subject: [PATCH 06/20] Add mssql ci test (#1410) * add mssql ci test * fix drone test --- .drone.yml | 140 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 10 deletions(-) diff --git a/.drone.yml b/.drone.yml index 94ba92e6..6001ec59 100644 --- a/.drone.yml +++ b/.drone.yml @@ -103,7 +103,18 @@ steps: commands: - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic" - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/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 coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt + when: + event: + - push + - pull_request + +- name: test-mssql + pull: default + image: golang:1.10 + commands: + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" + - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-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 > coverage.txt when: event: - push @@ -134,6 +145,19 @@ services: - tag - pull_request +- name: mssql + pull: default + image: microsoft/mssql-server-linux:latest + environment: + ACCEPT_EULA: Y + SA_PASSWORD: yourStrong(!)Password + MSSQL_PID: Developer + when: + event: + - push + - tag + - pull_request + --- kind: pipeline name: matrix-2 @@ -167,11 +191,13 @@ steps: - name: build pull: default image: golang:1.11 + environment: + GO111MODULE: "off" commands: - go get -t -d -v ./... - go get -u xorm.io/core - go get -u xorm.io/builder - - GO111MODULE=off go build -v + - go build -v when: event: - push @@ -181,9 +207,10 @@ steps: pull: default image: golang:1.11 environment: + GO111MODULE: "on" GOPROXY: "https://goproxy.cn" commands: - - GO111MODULE=on go build -v + - go build -v when: event: - push @@ -192,8 +219,10 @@ steps: - name: test-sqlite pull: default image: golang:1.11 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - - 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" when: @@ -204,6 +233,9 @@ steps: - name: test-mysql pull: default image: golang:1.11 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic" - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic" @@ -215,6 +247,9 @@ steps: - name: test-mysql-utf8mb4 pull: default image: golang:1.11 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic" - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic" @@ -226,6 +261,9 @@ steps: - name: test-mymysql pull: default image: golang:1.11 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic" - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic" @@ -237,6 +275,9 @@ steps: - name: test-postgres pull: default image: golang:1.11 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic" - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic" @@ -248,10 +289,28 @@ steps: - name: test-postgres-schema pull: default image: golang:1.11 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic" - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/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 coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt + when: + event: + - push + - pull_request + +- name: test-mssql + pull: default + image: golang:1.11 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" + - go get github.com/wadey/gocovmerge + - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-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 > coverage.txt when: event: - push @@ -282,6 +341,19 @@ services: - tag - pull_request +- name: mssql + pull: default + image: microsoft/mssql-server-linux:latest + environment: + ACCEPT_EULA: Y + SA_PASSWORD: yourStrong(!)Password + MSSQL_PID: Developer + when: + event: + - push + - tag + - pull_request + --- kind: pipeline name: matrix-3 @@ -315,11 +387,13 @@ steps: - name: build pull: default image: golang:1.12 + environment: + GO111MODULE: "off" commands: - go get -t -d -v ./... - go get -u xorm.io/core - go get -u xorm.io/builder - - GO111MODULE=off go build -v + - go build -v when: event: - push @@ -329,9 +403,10 @@ steps: pull: default image: golang:1.12 environment: - GOPROXY: "https://goproxy.cn" + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - - GO111MODULE=on go build -v + - go build -v when: event: - push @@ -340,8 +415,10 @@ steps: - name: test-sqlite pull: default image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - - 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" when: @@ -352,6 +429,9 @@ steps: - name: test-mysql pull: default image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic" - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic" @@ -363,6 +443,9 @@ steps: - name: test-mysql-utf8mb4 pull: default image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic" - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic" @@ -374,6 +457,9 @@ steps: - name: test-mymysql pull: default image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic" - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic" @@ -385,6 +471,9 @@ steps: - name: test-postgres pull: default image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic" - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic" @@ -396,10 +485,28 @@ steps: - name: test-postgres-schema pull: default image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" commands: - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic" - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/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 coverage2.1-1.txt coverage2.1-2.txt coverage3-1.txt coverage3-2.txt coverage4-1.txt coverage4-2.txt coverage5-1.txt coverage5-2.txt > coverage.txt + when: + event: + - push + - pull_request + +- name: test-mssql + pull: default + image: golang:1.12 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" + - go get -u github.com/wadey/gocovmerge + - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-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 > coverage.txt when: event: - push @@ -424,6 +531,19 @@ services: environment: POSTGRES_DB: xorm_test POSTGRES_USER: postgres + when: + event: + - push + - tag + - pull_request + +- name: mssql + pull: default + image: microsoft/mssql-server-linux:latest + environment: + ACCEPT_EULA: Y + SA_PASSWORD: yourStrong(!)Password + MSSQL_PID: Developer when: event: - push From 61f1d19281ae587182b34497b1cdc2845ec6e90c Mon Sep 17 00:00:00 2001 From: yudppp Date: Thu, 5 Sep 2019 17:14:27 +0900 Subject: [PATCH 07/20] Remove unuse get cols code (#1413) --- dialect_postgres.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dialect_postgres.go b/dialect_postgres.go index e1c377a0..3df682e8 100644 --- a/dialect_postgres.go +++ b/dialect_postgres.go @@ -952,7 +952,7 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { args := []interface{}{tableName} - s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix , + s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey, CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey FROM pg_attribute f @@ -987,14 +987,14 @@ WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.att col.Indexes = make(map[string]int) var colName, isNullable, dataType string - var maxLenStr, colDefault, numPrecision, numRadix *string + var maxLenStr, colDefault *string var isPK, isUnique bool - err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix, &isPK, &isUnique) + err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &isPK, &isUnique) if err != nil { return nil, nil, err } - // fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, numPrecision, numRadix, isPK, isUnique) + // fmt.Println(args, colName, isNullable, dataType, maxLenStr, colDefault, isPK, isUnique) var maxLen int if maxLenStr != nil { maxLen, err = strconv.Atoi(*maxLenStr) From 57a7e4421e878f5c3da8cb68b5e2c9cc646bbf2d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 6 Sep 2019 22:15:04 +0800 Subject: [PATCH 08/20] Use strings.Builder instead of builder.StringBuilder (#1417) * use strings.Builder instead of builder.StringBuilder * fix dependency * fix dependency --- engine.go | 4 ++-- go.mod | 4 +++- go.sum | 4 ++-- statement.go | 10 +++++----- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/engine.go b/engine.go index ebcab91b..f04c702e 100644 --- a/engine.go +++ b/engine.go @@ -190,14 +190,14 @@ func (engine *Engine) Quote(value string) string { return value } - buf := builder.StringBuilder{} + buf := strings.Builder{} engine.QuoteTo(&buf, value) return buf.String() } // QuoteTo quotes string and writes into the buffer -func (engine *Engine) QuoteTo(buf *builder.StringBuilder, value string) { +func (engine *Engine) QuoteTo(buf *strings.Builder, value string) { if buf == nil { return } diff --git a/go.mod b/go.mod index 9a30e797..a3e78cae 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,7 @@ module github.com/go-xorm/xorm +go 1.11 + require ( github.com/cockroachdb/apd v1.1.0 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 @@ -14,6 +16,6 @@ require ( 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/builder v0.3.6-0.20190906062455-b937eb46ecfb xorm.io/core v0.7.0 ) diff --git a/go.sum b/go.sum index 370dcd0b..0f2baf17 100644 --- a/go.sum +++ b/go.sum @@ -162,7 +162,7 @@ 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/builder v0.3.6-0.20190906062455-b937eb46ecfb h1:2idZcp79ldX5qLeQ6WKCdS7aEFNOMvQc9wrtt5hSRwM= +xorm.io/builder v0.3.6-0.20190906062455-b937eb46ecfb/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= xorm.io/core v0.7.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM= xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI= diff --git a/statement.go b/statement.go index 585378a8..6cdbad7d 100644 --- a/statement.go +++ b/statement.go @@ -695,7 +695,7 @@ func (statement *Statement) OrderBy(order string) *Statement { // Desc generate `ORDER BY xx DESC` func (statement *Statement) Desc(colNames ...string) *Statement { - var buf builder.StringBuilder + var buf strings.Builder if len(statement.OrderStr) > 0 { fmt.Fprint(&buf, statement.OrderStr, ", ") } @@ -707,7 +707,7 @@ func (statement *Statement) Desc(colNames ...string) *Statement { // Asc provide asc order by query condition, the input parameters are columns. func (statement *Statement) Asc(colNames ...string) *Statement { - var buf builder.StringBuilder + var buf strings.Builder if len(statement.OrderStr) > 0 { fmt.Fprint(&buf, statement.OrderStr, ", ") } @@ -736,7 +736,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { // Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { - var buf builder.StringBuilder + var buf strings.Builder if len(statement.JoinStr) > 0 { fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) } else { @@ -801,7 +801,7 @@ func (statement *Statement) genColumnStr() string { return "" } - var buf builder.StringBuilder + var buf strings.Builder columns := statement.RefTable.Columns() for _, col := range columns { @@ -1118,7 +1118,7 @@ func (statement *Statement) genSelectSQL(columnStr, condSQL string, needLimit, n } } - var buf builder.StringBuilder + var buf strings.Builder fmt.Fprintf(&buf, "SELECT %v%v%v%v%v", distinct, top, columnStr, fromStr, whereStr) if len(mssqlCondi) > 0 { if len(whereStr) > 0 { From d4419ac49600aa4f376f6f3d500c1c26212b7665 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 22 Sep 2019 23:13:55 +0800 Subject: [PATCH 09/20] fix tests (#1429) --- dialect_postgres_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/dialect_postgres_test.go b/dialect_postgres_test.go index ebc079e1..f2afdefc 100644 --- a/dialect_postgres_test.go +++ b/dialect_postgres_test.go @@ -4,9 +4,8 @@ import ( "reflect" "testing" - "xorm.io/core" - "github.com/jackc/pgx/stdlib" "github.com/stretchr/testify/assert" + "xorm.io/core" ) func TestParsePostgres(t *testing.T) { @@ -72,10 +71,7 @@ func TestParsePgx(t *testing.T) { } // Register DriverConfig - drvierConfig := stdlib.DriverConfig{} - stdlib.RegisterDriverConfig(&drvierConfig) - uri, err = driver.Parse("pgx", - drvierConfig.ConnectionString(test.in)) + uri, err = driver.Parse("pgx", 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) { From 201839c092cb95992776e84f03c1c5b93137b380 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 23 Sep 2019 00:13:52 +0800 Subject: [PATCH 10/20] fix go mod (#1427) --- go.mod | 5 ++--- go.sum | 14 +++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index a3e78cae..25131024 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,12 @@ require ( 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/gofrs/uuid v3.2.0+incompatible // indirect 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/jackc/pgx v3.6.0+incompatible 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 diff --git a/go.sum b/go.sum index 0f2baf17..f539c0c3 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG 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/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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= @@ -48,18 +50,13 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ 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/jackc/pgx v3.6.0+incompatible h1:bJeo4JdVbDAW8KB2m8XkFeo8CPipREoG37BwEoKGz+Q= +github.com/jackc/pgx v3.6.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= @@ -84,8 +81,6 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 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= @@ -133,6 +128,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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= From 6d1191376545c321b4abfafac909fe3e08b7f95f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 23 Sep 2019 23:34:26 +0800 Subject: [PATCH 11/20] Add support subquery on SetExpr (#1428) * add support subquery on SetExpr * fix tests --- engine.go | 2 +- go.mod | 2 +- go.sum | 4 +- interface.go | 2 +- session_cols.go | 45 +------ session_insert.go | 263 +++++++++++++++++++++++++---------------- session_insert_test.go | 15 +++ session_update.go | 51 +++++--- statement.go | 42 ++----- statement_args.go | 68 +++++++++++ statement_columnmap.go | 35 ++++++ statement_exprparam.go | 100 ++++++++++++++++ statement_quote.go | 19 +++ 13 files changed, 450 insertions(+), 198 deletions(-) create mode 100644 statement_args.go create mode 100644 statement_columnmap.go create mode 100644 statement_exprparam.go create mode 100644 statement_quote.go diff --git a/engine.go b/engine.go index f04c702e..649fd1e3 100644 --- a/engine.go +++ b/engine.go @@ -729,7 +729,7 @@ func (engine *Engine) Decr(column string, arg ...interface{}) *Session { } // SetExpr provides a update string like "column = {expression}" -func (engine *Engine) SetExpr(column string, expression string) *Session { +func (engine *Engine) SetExpr(column string, expression interface{}) *Session { session := engine.NewSession() session.isAutoClose = true return session.SetExpr(column, expression) diff --git a/go.mod b/go.mod index 25131024..eb0d71ba 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,6 @@ require ( 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.6-0.20190906062455-b937eb46ecfb + xorm.io/builder v0.3.6 xorm.io/core v0.7.0 ) diff --git a/go.sum b/go.sum index f539c0c3..15f16fbc 100644 --- a/go.sum +++ b/go.sum @@ -158,7 +158,7 @@ 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.6-0.20190906062455-b937eb46ecfb h1:2idZcp79ldX5qLeQ6WKCdS7aEFNOMvQc9wrtt5hSRwM= -xorm.io/builder v0.3.6-0.20190906062455-b937eb46ecfb/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= +xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8= +xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= xorm.io/core v0.7.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM= xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI= diff --git a/interface.go b/interface.go index 0928f66a..a564db12 100644 --- a/interface.go +++ b/interface.go @@ -54,7 +54,7 @@ type Interface interface { QueryInterface(sqlOrArgs ...interface{}) ([]map[string]interface{}, error) QueryString(sqlOrArgs ...interface{}) ([]map[string]string, error) Rows(bean interface{}) (*Rows, error) - SetExpr(string, string) *Session + SetExpr(string, interface{}) *Session SQL(interface{}, ...interface{}) *Session Sum(bean interface{}, colName string) (float64, error) SumInt(bean interface{}, colName string) (int64, error) diff --git a/session_cols.go b/session_cols.go index dc3befcf..1558074f 100644 --- a/session_cols.go +++ b/session_cols.go @@ -12,49 +12,6 @@ import ( "xorm.io/core" ) -type incrParam struct { - colName string - arg interface{} -} - -type decrParam struct { - colName string - arg interface{} -} - -type exprParam struct { - colName string - expr string -} - -type columnMap []string - -func (m columnMap) contain(colName string) bool { - if len(m) == 0 { - return false - } - - n := len(colName) - for _, mk := range m { - if len(mk) != n { - continue - } - if strings.EqualFold(mk, colName) { - return true - } - } - - return false -} - -func (m *columnMap) add(colName string) bool { - if m.contain(colName) { - return false - } - *m = append(*m, colName) - return true -} - func setColumnInt(bean interface{}, col *core.Column, t int64) { v, err := col.ValueOf(bean) if err != nil { @@ -132,7 +89,7 @@ func (session *Session) Decr(column string, arg ...interface{}) *Session { } // SetExpr provides a query string like "column = {expression}" -func (session *Session) SetExpr(column string, expression string) *Session { +func (session *Session) SetExpr(column string, expression interface{}) *Session { session.statement.SetExpr(column, expression) return session } diff --git a/session_insert.go b/session_insert.go index 24b32831..44cae7c4 100644 --- a/session_insert.go +++ b/session_insert.go @@ -340,74 +340,96 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { if err != nil { return 0, err } - // insert expr columns, override if exists - exprColumns := session.statement.getExpr() - exprColVals := make([]string, 0, len(exprColumns)) - for _, v := range exprColumns { - // remove the expr columns - for i, colName := range colNames { - if colName == strings.Trim(v.colName, "`") { - colNames = append(colNames[:i], colNames[i+1:]...) - args = append(args[:i], args[i+1:]...) - } - } - // append expr column to the end - colNames = append(colNames, v.colName) - exprColVals = append(exprColVals, v.expr) + exprs := session.statement.exprColumns + colPlaces := strings.Repeat("?, ", len(colNames)) + if exprs.Len() <= 0 && len(colPlaces) > 0 { + colPlaces = colPlaces[0 : len(colPlaces)-2] } - colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns)) - if len(exprColVals) > 0 { - colPlaces = colPlaces + strings.Join(exprColVals, ", ") - } else { - if len(colPlaces) > 0 { - colPlaces = colPlaces[0 : len(colPlaces)-2] - } - } - - 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 { + var buf = builder.NewWriter() + if _, err := buf.WriteString(fmt.Sprintf("INSERT INTO %s", session.engine.Quote(tableName))); err != nil { + return 0, err + } + + if len(colPlaces) <= 0 { + if session.engine.dialect.DBType() == core.MYSQL { + if _, err := buf.WriteString(" VALUES ()"); err != nil { + return 0, err + } + } else { + if _, err := buf.WriteString(fmt.Sprintf("%s DEFAULT VALUES", output)); err != nil { + return 0, err + } + } + } else { + if _, err := buf.WriteString(" ("); err != nil { + return 0, err + } + + if err := writeStrings(buf, append(colNames, exprs.colNames...), "`", "`"); err != nil { + return 0, err + } + if session.statement.cond.IsValid() { - condSQL, condArgs, err := builder.ToSQL(session.statement.cond) - if err != nil { + if _, err := buf.WriteString(fmt.Sprintf(")%s SELECT ", output)); err != nil { return 0, err } - sqlStr = fmt.Sprintf("INSERT INTO %s (%v)%s SELECT %v FROM %v WHERE %v", - session.engine.Quote(tableName), - quoteColumns(colNames, session.engine.Quote, ","), - output, - colPlaces, - session.engine.Quote(tableName), - condSQL, - ) - args = append(args, condArgs...) + if err := writeArgs(buf, args); err != nil { + return 0, err + } + + if len(exprs.args) > 0 { + if _, err := buf.WriteString(","); err != nil { + return 0, err + } + } + if err := exprs.writeArgs(buf); err != nil { + return 0, err + } + + if _, err := buf.WriteString(fmt.Sprintf(" FROM %v WHERE ", session.engine.Quote(tableName))); err != nil { + return 0, err + } + + if err := session.statement.cond.WriteTo(buf); err != nil { + return 0, err + } } else { - sqlStr = fmt.Sprintf("INSERT INTO %s (%v)%s VALUES (%v)", - session.engine.Quote(tableName), - quoteColumns(colNames, session.engine.Quote, ","), + buf.Append(args...) + + if _, err := buf.WriteString(fmt.Sprintf(")%s VALUES (%v", 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%s DEFAULT VALUES", session.engine.Quote(tableName), output) + colPlaces)); err != nil { + return 0, err + } + + if err := exprs.writeArgs(buf); err != nil { + return 0, err + } + + if _, err := buf.WriteString(")"); err != nil { + return 0, err + } } } if len(table.AutoIncrement) > 0 && session.engine.dialect.DBType() == core.POSTGRES { - sqlStr = sqlStr + " RETURNING " + session.engine.Quote(table.AutoIncrement) + if _, err := buf.WriteString(" RETURNING " + session.engine.Quote(table.AutoIncrement)); err != nil { + return 0, err + } } + sqlStr := buf.String() + args = buf.Args() + handleAfterInsertProcessorFunc := func(bean interface{}) { if session.isAutoCommit { for _, closure := range session.afterClosures { @@ -611,9 +633,11 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac continue } - if _, ok := session.statement.incrColumns[col.Name]; ok { + if session.statement.incrColumns.isColExist(col.Name) { continue - } else if _, ok := session.statement.decrColumns[col.Name]; ok { + } else if session.statement.decrColumns.isColExist(col.Name) { + continue + } else if session.statement.exprColumns.isColExist(col.Name) { continue } @@ -688,46 +712,66 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err } var columns = make([]string, 0, len(m)) + exprs := session.statement.exprColumns for k := range m { - columns = append(columns, k) + if !exprs.isColExist(k) { + columns = append(columns, k) + } } sort.Strings(columns) - qm := strings.Repeat("?,", len(columns)) - var args = make([]interface{}, 0, len(m)) for _, colName := range columns { args = append(args, m[colName]) } - // insert expr columns, override if exists - exprColumns := session.statement.getExpr() - for _, col := range exprColumns { - columns = append(columns, strings.Trim(col.colName, "`")) - qm = qm + col.expr + "," - } - - qm = qm[:len(qm)-1] - - var sql string - + w := builder.NewWriter() if session.statement.cond.IsValid() { - condSQL, condArgs, err := builder.ToSQL(session.statement.cond) - if err != nil { + if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (", session.engine.Quote(tableName))); err != nil { + return 0, err + } + + if err := writeStrings(w, append(columns, exprs.colNames...), "`", "`"); err != nil { + return 0, err + } + + if _, err := w.WriteString(") SELECT "); err != nil { + return 0, err + } + + if err := writeArgs(w, args); err != nil { + return 0, err + } + + if len(exprs.args) > 0 { + if _, err := w.WriteString(","); err != nil { + return 0, err + } + if err := exprs.writeArgs(w); err != nil { + return 0, err + } + } + + if _, err := w.WriteString(fmt.Sprintf(" FROM %s WHERE ", session.engine.Quote(tableName))); err != nil { + return 0, err + } + + if err := session.statement.cond.WriteTo(w); err != nil { return 0, err } - sql = fmt.Sprintf("INSERT INTO %s (`%s`) SELECT %s FROM %s WHERE %s", - session.engine.Quote(tableName), - strings.Join(columns, "`,`"), - qm, - session.engine.Quote(tableName), - condSQL, - ) - args = append(args, condArgs...) } else { - sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) + qm := strings.Repeat("?,", len(columns)) + qm = qm[:len(qm)-1] + + if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm)); err != nil { + return 0, err + } + w.Append(args...) } + sql := w.String() + args = w.Args() + if err := session.cacheInsert(tableName); err != nil { return 0, err } @@ -754,8 +798,11 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { } var columns = make([]string, 0, len(m)) + exprs := session.statement.exprColumns for k := range m { - columns = append(columns, k) + if !exprs.isColExist(k) { + columns = append(columns, k) + } } sort.Strings(columns) @@ -764,37 +811,53 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { args = append(args, m[colName]) } - qm := strings.Repeat("?,", len(columns)) - - // insert expr columns, override if exists - exprColumns := session.statement.getExpr() - for _, col := range exprColumns { - columns = append(columns, strings.Trim(col.colName, "`")) - qm = qm + col.expr + "," - } - - qm = qm[:len(qm)-1] - - var sql string - + w := builder.NewWriter() if session.statement.cond.IsValid() { - qm = "(" + qm[:len(qm)-1] + ")" - condSQL, condArgs, err := builder.ToSQL(session.statement.cond) - if err != nil { + if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (", session.engine.Quote(tableName))); err != nil { + return 0, err + } + + if err := writeStrings(w, append(columns, exprs.colNames...), "`", "`"); err != nil { + return 0, err + } + + if _, err := w.WriteString(") SELECT "); err != nil { + return 0, err + } + + if err := writeArgs(w, args); err != nil { + return 0, err + } + + if len(exprs.args) > 0 { + if _, err := w.WriteString(","); err != nil { + return 0, err + } + if err := exprs.writeArgs(w); err != nil { + return 0, err + } + } + + if _, err := w.WriteString(fmt.Sprintf(" FROM %s WHERE ", session.engine.Quote(tableName))); err != nil { + return 0, err + } + + if err := session.statement.cond.WriteTo(w); err != nil { return 0, err } - sql = fmt.Sprintf("INSERT INTO %s (`%s`) SELECT %s FROM %s WHERE %s", - session.engine.Quote(tableName), - strings.Join(columns, "`,`"), - qm, - session.engine.Quote(tableName), - condSQL, - ) - args = append(args, condArgs...) } else { - sql = fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm) + qm := strings.Repeat("?,", len(columns)) + qm = qm[:len(qm)-1] + + if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm)); err != nil { + return 0, err + } + w.Append(args...) } + sql := w.String() + args = w.Args() + if err := session.cacheInsert(tableName); err != nil { return 0, err } diff --git a/session_insert_test.go b/session_insert_test.go index daf08e7f..f9c99071 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -892,4 +892,19 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, 40, j2.Height) assert.EqualValues(t, "trest2", j2.Name) assert.EqualValues(t, 2, j2.Index) + + inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). + Insert(map[string]string{ + "name": "trest3", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, inserted) + + var j3 InsertWhere + has, err = testEngine.ID(3).Get(&j3) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "trest3", j3.Name) + assert.EqualValues(t, 3, j3.Index) } diff --git a/session_update.go b/session_update.go index 85b0bb0b..402470e5 100644 --- a/session_update.go +++ b/session_update.go @@ -223,21 +223,31 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } // 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) + incColumns := session.statement.incrColumns + for i, colName := range incColumns.colNames { + colNames = append(colNames, session.engine.Quote(colName)+" = "+session.engine.Quote(colName)+" + ?") + args = append(args, incColumns.args[i]) } // 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) + decColumns := session.statement.decrColumns + for i, colName := range decColumns.colNames { + colNames = append(colNames, session.engine.Quote(colName)+" = "+session.engine.Quote(colName)+" - ?") + args = append(args, decColumns.args[i]) } // 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) + exprColumns := session.statement.exprColumns + for i, colName := range exprColumns.colNames { + switch tp := exprColumns.args[i].(type) { + case string: + colNames = append(colNames, session.engine.Quote(colName)+" = "+tp) + case *builder.Builder: + subQuery, subArgs, err := builder.ToSQL(tp) + if err != nil { + return 0, err + } + colNames = append(colNames, session.engine.Quote(colName)+" = "+subQuery) + args = append(args, subArgs...) + } } if err = session.statement.processIDParam(); err != nil { @@ -468,14 +478,17 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac continue } - if len(session.statement.columnMap) > 0 { - if !session.statement.columnMap.contain(col.Name) { - continue - } else if _, ok := session.statement.incrColumns[col.Name]; ok { - continue - } else if _, ok := session.statement.decrColumns[col.Name]; ok { - continue - } + // if only update specify columns + if len(session.statement.columnMap) > 0 && !session.statement.columnMap.contain(col.Name) { + continue + } + + if session.statement.incrColumns.isColExist(col.Name) { + continue + } else if session.statement.decrColumns.isColExist(col.Name) { + continue + } else if session.statement.exprColumns.isColExist(col.Name) { + continue } // !evalphobia! set fieldValue as nil when column is nullable and zero-value diff --git a/statement.go b/statement.go index 6cdbad7d..3cc0831e 100644 --- a/statement.go +++ b/statement.go @@ -52,9 +52,9 @@ type Statement struct { omitColumnMap columnMap mustColumnMap map[string]bool nullableMap map[string]bool - incrColumns map[string]incrParam - decrColumns map[string]decrParam - exprColumns map[string]exprParam + incrColumns exprParams + decrColumns exprParams + exprColumns exprParams cond builder.Cond bufferSize int context ContextCache @@ -94,9 +94,9 @@ func (statement *Statement) Init() { statement.nullableMap = make(map[string]bool) statement.checkVersion = true statement.unscoped = false - statement.incrColumns = make(map[string]incrParam) - statement.decrColumns = make(map[string]decrParam) - statement.exprColumns = make(map[string]exprParam) + statement.incrColumns = exprParams{} + statement.decrColumns = exprParams{} + statement.exprColumns = exprParams{} statement.cond = builder.NewCond() statement.bufferSize = 0 statement.context = nil @@ -534,48 +534,30 @@ func (statement *Statement) ID(id interface{}) *Statement { // Incr Generate "Update ... Set column = column + arg" statement func (statement *Statement) Incr(column string, arg ...interface{}) *Statement { - k := strings.ToLower(column) if len(arg) > 0 { - statement.incrColumns[k] = incrParam{column, arg[0]} + statement.incrColumns.addParam(column, arg[0]) } else { - statement.incrColumns[k] = incrParam{column, 1} + statement.incrColumns.addParam(column, 1) } return statement } // Decr Generate "Update ... Set column = column - arg" statement func (statement *Statement) Decr(column string, arg ...interface{}) *Statement { - k := strings.ToLower(column) if len(arg) > 0 { - statement.decrColumns[k] = decrParam{column, arg[0]} + statement.decrColumns.addParam(column, arg[0]) } else { - statement.decrColumns[k] = decrParam{column, 1} + statement.decrColumns.addParam(column, 1) } return statement } // SetExpr Generate "Update ... Set column = {expression}" statement -func (statement *Statement) SetExpr(column string, expression string) *Statement { - k := strings.ToLower(column) - statement.exprColumns[k] = exprParam{column, expression} +func (statement *Statement) SetExpr(column string, expression interface{}) *Statement { + statement.exprColumns.addParam(column, expression) return statement } -// Generate "Update ... Set column = column + arg" statement -func (statement *Statement) getInc() map[string]incrParam { - return statement.incrColumns -} - -// Generate "Update ... Set column = column - arg" statement -func (statement *Statement) getDec() map[string]decrParam { - return statement.decrColumns -} - -// Generate "Update ... Set column = {expression}" statement -func (statement *Statement) getExpr() map[string]exprParam { - return statement.exprColumns -} - func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { newColumns := make([]string, 0) quotes := append(strings.Split(statement.Engine.Quote(""), ""), "`") diff --git a/statement_args.go b/statement_args.go new file mode 100644 index 00000000..c6168db1 --- /dev/null +++ b/statement_args.go @@ -0,0 +1,68 @@ +// 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 ( + "fmt" + + "xorm.io/builder" +) + +func writeArg(w *builder.BytesWriter, arg interface{}) error { + switch argv := arg.(type) { + case string: + if _, err := w.WriteString("'" + argv + "'"); err != nil { + return err + } + case *builder.Builder: + if err := argv.WriteTo(w); err != nil { + return err + } + default: + if _, err := w.WriteString(fmt.Sprintf("%v", argv)); err != nil { + return err + } + } + return nil +} + +func writeArgs(w *builder.BytesWriter, args []interface{}) error { + for i, arg := range args { + if err := writeArg(w, arg); err != nil { + return err + } + + if i+1 != len(args) { + if _, err := w.WriteString(","); err != nil { + return err + } + } + } + return nil +} + +func writeStrings(w *builder.BytesWriter, cols []string, leftQuote, rightQuote string) error { + for i, colName := range cols { + if len(leftQuote) > 0 && colName[0] != '`' { + if _, err := w.WriteString(leftQuote); err != nil { + return err + } + } + if _, err := w.WriteString(colName); err != nil { + return err + } + if len(rightQuote) > 0 && colName[len(colName)-1] != '`' { + if _, err := w.WriteString(rightQuote); err != nil { + return err + } + } + if i+1 != len(cols) { + if _, err := w.WriteString(","); err != nil { + return err + } + } + } + return nil +} diff --git a/statement_columnmap.go b/statement_columnmap.go new file mode 100644 index 00000000..b6523b1e --- /dev/null +++ b/statement_columnmap.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 "strings" + +type columnMap []string + +func (m columnMap) contain(colName string) bool { + if len(m) == 0 { + return false + } + + n := len(colName) + for _, mk := range m { + if len(mk) != n { + continue + } + if strings.EqualFold(mk, colName) { + return true + } + } + + return false +} + +func (m *columnMap) add(colName string) bool { + if m.contain(colName) { + return false + } + *m = append(*m, colName) + return true +} diff --git a/statement_exprparam.go b/statement_exprparam.go new file mode 100644 index 00000000..a72f0aea --- /dev/null +++ b/statement_exprparam.go @@ -0,0 +1,100 @@ +// 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 ( + "fmt" + "strings" + + "xorm.io/builder" +) + +type ErrUnsupportedExprType struct { + tp string +} + +func (err ErrUnsupportedExprType) Error() string { + return fmt.Sprintf("Unsupported expression type: %v", err.tp) +} + +type exprParam struct { + colName string + arg interface{} +} + +type exprParams struct { + colNames []string + args []interface{} +} + +func (exprs *exprParams) Len() int { + return len(exprs.colNames) +} + +func (exprs *exprParams) addParam(colName string, arg interface{}) { + exprs.colNames = append(exprs.colNames, colName) + exprs.args = append(exprs.args, arg) +} + +func (exprs *exprParams) isColExist(colName string) bool { + for _, name := range exprs.colNames { + if strings.EqualFold(trimQuote(name), trimQuote(colName)) { + return true + } + } + return false +} + +func (exprs *exprParams) getByName(colName string) (exprParam, bool) { + for i, name := range exprs.colNames { + if strings.EqualFold(name, colName) { + return exprParam{name, exprs.args[i]}, true + } + } + return exprParam{}, false +} + +func (exprs *exprParams) writeArgs(w *builder.BytesWriter) error { + for _, expr := range exprs.args { + switch arg := expr.(type) { + case *builder.Builder: + if err := arg.WriteTo(w); err != nil { + return err + } + default: + if _, err := w.WriteString(fmt.Sprintf("%v", arg)); err != nil { + return err + } + } + } + return nil +} + +func (exprs *exprParams) writeNameArgs(w *builder.BytesWriter) error { + for i, colName := range exprs.colNames { + if _, err := w.WriteString(colName); err != nil { + return err + } + if _, err := w.WriteString("="); err != nil { + return err + } + + switch arg := exprs.args[i].(type) { + case *builder.Builder: + if err := arg.WriteTo(w); err != nil { + return err + } + default: + w.Append(exprs.args[i]) + } + + if i+1 != len(exprs.colNames) { + if _, err := w.WriteString(","); err != nil { + return err + } + } + } + return nil +} diff --git a/statement_quote.go b/statement_quote.go new file mode 100644 index 00000000..e22e0d14 --- /dev/null +++ b/statement_quote.go @@ -0,0 +1,19 @@ +// 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 + +func trimQuote(s string) string { + if len(s) == 0 { + return s + } + + if s[0] == '`' { + s = s[1:] + } + if len(s) > 0 && s[len(s)-1] == '`' { + return s[:len(s)-1] + } + return s +} From 2fbb9cd8ded8786605a271f3681e25c5270820b7 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Sep 2019 12:58:25 +0800 Subject: [PATCH 12/20] fix setexpr missing big quotes (#1431) * fix setexpr missing big quotes * fix tests * fix tests --- session_cols_test.go | 35 +++++++++++++++++++++++++++++++---- session_update.go | 2 +- statement_args.go | 6 ++++++ statement_exprparam.go | 12 ++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/session_cols_test.go b/session_cols_test.go index 5f5954c7..96cb1620 100644 --- a/session_cols_test.go +++ b/session_cols_test.go @@ -7,21 +7,38 @@ package xorm import ( "testing" - "xorm.io/core" "github.com/stretchr/testify/assert" + "xorm.io/builder" + "xorm.io/core" ) func TestSetExpr(t *testing.T) { assert.NoError(t, prepareEngine()) + type UserExprIssue struct { + Id int64 + Title string + } + + assert.NoError(t, testEngine.Sync2(new(UserExprIssue))) + + var issue = UserExprIssue{ + Title: "my issue", + } + cnt, err := testEngine.Insert(&issue) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) + assert.EqualValues(t, 1, issue.Id) + type UserExpr struct { - Id int64 - Show bool + Id int64 + IssueId int64 `xorm:"index"` + Show bool } assert.NoError(t, testEngine.Sync2(new(UserExpr))) - cnt, err := testEngine.Insert(&UserExpr{ + cnt, err = testEngine.Insert(&UserExpr{ Show: true, }) assert.NoError(t, err) @@ -34,6 +51,16 @@ func TestSetExpr(t *testing.T) { cnt, err = testEngine.SetExpr("show", not+" `show`").ID(1).Update(new(UserExpr)) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) + + tableName := testEngine.TableName(new(UserExprIssue), true) + cnt, err = testEngine.SetExpr("issue_id", + builder.Select("id"). + From(tableName). + Where(builder.Eq{"id": issue.Id})). + ID(1). + Update(new(UserExpr)) + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) } func TestCols(t *testing.T) { diff --git a/session_update.go b/session_update.go index 402470e5..c5c65a45 100644 --- a/session_update.go +++ b/session_update.go @@ -245,7 +245,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 if err != nil { return 0, err } - colNames = append(colNames, session.engine.Quote(colName)+" = "+subQuery) + colNames = append(colNames, session.engine.Quote(colName)+" = ("+subQuery+")") args = append(args, subArgs...) } } diff --git a/statement_args.go b/statement_args.go index c6168db1..5353ae1a 100644 --- a/statement_args.go +++ b/statement_args.go @@ -17,9 +17,15 @@ func writeArg(w *builder.BytesWriter, arg interface{}) error { return err } case *builder.Builder: + if _, err := w.WriteString("("); err != nil { + return err + } if err := argv.WriteTo(w); err != nil { return err } + if _, err := w.WriteString(")"); err != nil { + return err + } default: if _, err := w.WriteString(fmt.Sprintf("%v", argv)); err != nil { return err diff --git a/statement_exprparam.go b/statement_exprparam.go index a72f0aea..0cddca02 100644 --- a/statement_exprparam.go +++ b/statement_exprparam.go @@ -60,9 +60,15 @@ func (exprs *exprParams) writeArgs(w *builder.BytesWriter) error { for _, expr := range exprs.args { switch arg := expr.(type) { case *builder.Builder: + if _, err := w.WriteString("("); err != nil { + return err + } if err := arg.WriteTo(w); err != nil { return err } + if _, err := w.WriteString(")"); err != nil { + return err + } default: if _, err := w.WriteString(fmt.Sprintf("%v", arg)); err != nil { return err @@ -83,9 +89,15 @@ func (exprs *exprParams) writeNameArgs(w *builder.BytesWriter) error { switch arg := exprs.args[i].(type) { case *builder.Builder: + if _, err := w.WriteString("("); err != nil { + return err + } if err := arg.WriteTo(w); err != nil { return err } + if _, err := w.WriteString("("); err != nil { + return err + } default: w.Append(exprs.args[i]) } From 59ed80ce1a6748146cf91b032addb3b22c4a3f6d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 24 Sep 2019 16:05:35 +0800 Subject: [PATCH 13/20] fix insert where with bool bug on mssql (#1432) --- session_insert.go | 6 +++--- session_insert_test.go | 1 + statement_args.go | 29 ++++++++++++++++++++++++++--- test_mssql.sh | 2 +- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/session_insert.go b/session_insert.go index 44cae7c4..de645290 100644 --- a/session_insert.go +++ b/session_insert.go @@ -382,7 +382,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { return 0, err } - if err := writeArgs(buf, args); err != nil { + if err := session.statement.writeArgs(buf, args); err != nil { return 0, err } @@ -739,7 +739,7 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err return 0, err } - if err := writeArgs(w, args); err != nil { + if err := session.statement.writeArgs(w, args); err != nil { return 0, err } @@ -825,7 +825,7 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) { return 0, err } - if err := writeArgs(w, args); err != nil { + if err := session.statement.writeArgs(w, args); err != nil { return 0, err } diff --git a/session_insert_test.go b/session_insert_test.go index f9c99071..cc3e663b 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -846,6 +846,7 @@ func TestInsertWhere(t *testing.T) { Width uint32 Height uint32 Name string + IsTrue bool } assert.NoError(t, prepareEngine()) diff --git a/statement_args.go b/statement_args.go index 5353ae1a..4ce336f4 100644 --- a/statement_args.go +++ b/statement_args.go @@ -8,14 +8,37 @@ import ( "fmt" "xorm.io/builder" + "xorm.io/core" ) -func writeArg(w *builder.BytesWriter, arg interface{}) error { +func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) error { switch argv := arg.(type) { case string: if _, err := w.WriteString("'" + argv + "'"); err != nil { return err } + case bool: + if statement.Engine.dialect.DBType() == core.MSSQL { + if argv { + if _, err := w.WriteString("1"); err != nil { + return err + } + } else { + if _, err := w.WriteString("0"); err != nil { + return err + } + } + } else { + if argv { + if _, err := w.WriteString("true"); err != nil { + return err + } + } else { + if _, err := w.WriteString("false"); err != nil { + return err + } + } + } case *builder.Builder: if _, err := w.WriteString("("); err != nil { return err @@ -34,9 +57,9 @@ func writeArg(w *builder.BytesWriter, arg interface{}) error { return nil } -func writeArgs(w *builder.BytesWriter, args []interface{}) error { +func (statement *Statement) writeArgs(w *builder.BytesWriter, args []interface{}) error { for i, arg := range args { - if err := writeArg(w, arg); err != nil { + if err := statement.writeArg(w, arg); err != nil { return err } diff --git a/test_mssql.sh b/test_mssql.sh index 7f060cff..e26e1641 100755 --- a/test_mssql.sh +++ b/test_mssql.sh @@ -1 +1 @@ -go test -db=mssql -conn_str="server=localhost;user id=sa;password=yourStrong(!)Password;database=xorm_test" \ No newline at end of file +go test -db=mssql -conn_str="server=localhost;user id=sa;password=MwantsaSecurePassword1;database=xorm_test" \ No newline at end of file From 691f6e76980b54665fbfdc0afc2ca557e9edc26a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 25 Sep 2019 16:42:24 +0800 Subject: [PATCH 14/20] fix bug when insert multiple slices with customize table name (#1433) * fix bug when insert multiple slices with customize table name * fix tests on mssql * fix tests --- dialect_mssql.go | 3 +++ session_insert.go | 8 ++++++-- session_insert_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/dialect_mssql.go b/dialect_mssql.go index 61061cb2..ce4dd00c 100644 --- a/dialect_mssql.go +++ b/dialect_mssql.go @@ -254,6 +254,9 @@ func (db *mssql) SqlType(c *core.Column) string { case core.TinyInt: res = core.TinyInt c.Length = 0 + case core.BigInt: + res = core.BigInt + c.Length = 0 default: res = t } diff --git a/session_insert.go b/session_insert.go index de645290..1e19ce7a 100644 --- a/session_insert.go +++ b/session_insert.go @@ -25,6 +25,12 @@ func (session *Session) Insert(beans ...interface{}) (int64, error) { defer session.Close() } + session.autoResetStatement = false + defer func() { + session.autoResetStatement = true + session.resetStatement() + }() + for _, bean := range beans { switch bean.(type) { case map[string]interface{}: @@ -35,7 +41,6 @@ func (session *Session) Insert(beans ...interface{}) (int64, error) { 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 { @@ -51,7 +56,6 @@ func (session *Session) Insert(beans ...interface{}) (int64, error) { 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 { diff --git a/session_insert_test.go b/session_insert_test.go index cc3e663b..88879ef6 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -909,3 +909,42 @@ func TestInsertWhere(t *testing.T) { assert.EqualValues(t, "trest3", j3.Name) assert.EqualValues(t, 3, j3.Index) } + +type NightlyRate struct { + ID int64 `xorm:"'id' not null pk BIGINT(20)" json:"id"` +} + +func (NightlyRate) TableName() string { + return "prd_nightly_rate" +} + +func TestMultipleInsertTableName(t *testing.T) { + assert.NoError(t, prepareEngine()) + + tableName := `prd_nightly_rate_16` + assert.NoError(t, testEngine.Table(tableName).Sync2(new(NightlyRate))) + + trans := testEngine.NewSession() + defer trans.Close() + err := trans.Begin() + assert.NoError(t, err) + + rtArr := []interface{}{ + []*NightlyRate{ + {ID: 1}, + {ID: 2}, + }, + []*NightlyRate{ + {ID: 3}, + {ID: 4}, + }, + []*NightlyRate{ + {ID: 5}, + }, + } + + _, err = trans.Table(tableName).Insert(rtArr...) + assert.NoError(t, err) + + assert.NoError(t, trans.Commit()) +} From 71947cf034b623db7571f79ba0a6c2a932c173bb Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 26 Sep 2019 01:29:02 +0800 Subject: [PATCH 15/20] Fix bug on insert where (#1436) * fix bug on insert where * fix bug * fix lint --- session_insert_test.go | 9 +++++++ statement_args.go | 53 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/session_insert_test.go b/session_insert_test.go index 88879ef6..d040c9e9 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -908,6 +908,15 @@ func TestInsertWhere(t *testing.T) { assert.True(t, has) assert.EqualValues(t, "trest3", j3.Name) assert.EqualValues(t, 3, j3.Index) + + inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). + Insert(map[string]interface{}{ + "repo_id": 1, + "name": "10';delete * from insert_where; --", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, inserted) } type NightlyRate struct { diff --git a/statement_args.go b/statement_args.go index 4ce336f4..23496443 100644 --- a/statement_args.go +++ b/statement_args.go @@ -6,17 +6,60 @@ package xorm import ( "fmt" + "reflect" + "strings" + "time" "xorm.io/builder" "xorm.io/core" ) +func quoteNeeded(a interface{}) bool { + switch a.(type) { + case int, int8, int16, int32, int64: + return false + case uint, uint8, uint16, uint32, uint64: + return false + case float32, float64: + return false + case bool: + return false + case string: + return true + case time.Time, *time.Time: + return true + case builder.Builder, *builder.Builder: + return false + } + + t := reflect.TypeOf(a) + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return false + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return false + case reflect.Float32, reflect.Float64: + return false + case reflect.Bool: + return false + case reflect.String: + return true + } + + return true +} + +func convertArg(arg interface{}) string { + if quoteNeeded(arg) { + argv := fmt.Sprintf("%v", arg) + return "'" + strings.Replace(argv, "'", "''", -1) + "'" + } + + return fmt.Sprintf("%v", arg) +} + func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) error { switch argv := arg.(type) { - case string: - if _, err := w.WriteString("'" + argv + "'"); err != nil { - return err - } case bool: if statement.Engine.dialect.DBType() == core.MSSQL { if argv { @@ -50,7 +93,7 @@ func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) er return err } default: - if _, err := w.WriteString(fmt.Sprintf("%v", argv)); err != nil { + if _, err := w.WriteString(convertArg(arg)); err != nil { return err } } From 3f2ca6e74128546a8444f5354b45eae0287edfa6 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 26 Sep 2019 11:01:55 +0800 Subject: [PATCH 16/20] add go1.13 tests on drone (#1416) --- .drone.yml | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/.drone.yml b/.drone.yml index 6001ec59..bd682e5f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -537,6 +537,202 @@ services: - tag - pull_request +- name: mssql + pull: default + image: microsoft/mssql-server-linux:latest + environment: + ACCEPT_EULA: Y + SA_PASSWORD: yourStrong(!)Password + MSSQL_PID: Developer + when: + event: + - push + - tag + - pull_request + +--- +kind: pipeline +name: go1.13 + +platform: + os: linux + arch: amd64 + +clone: + disable: true + +workspace: + base: /go + path: src/github.com/go-xorm/xorm + +steps: +- name: git + pull: default + image: plugins/git:next + settings: + depth: 50 + tags: true + +- name: init_postgres + pull: default + image: postgres:9.5 + commands: + - "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n" + - "psql -U postgres -d xorm_test -h pgsql \\\n -c \"create schema xorm;\"\n" + +- name: build + pull: default + image: golang:1.13 + environment: + GO111MODULE: "off" + commands: + - go get -t -d -v ./... + - go get -u xorm.io/core + - go get -u xorm.io/builder + - go build -v + when: + event: + - push + - pull_request + +- name: build-gomod + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - go build -v + when: + event: + - push + - pull_request + +- name: test-sqlite + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "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" + when: + event: + - push + - pull_request + +- name: test-mysql + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mysql-utf8mb4 + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic" + - "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mymysql + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic" + - "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-postgres-schema + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic" + - "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic" + when: + event: + - push + - pull_request + +- name: test-mssql + pull: default + image: golang:1.13 + environment: + GO111MODULE: "on" + GOPROXY: "https://goproxy.cn" + commands: + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic" + - "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic" + - go get -u github.com/wadey/gocovmerge + - gocovmerge coverage1-1.txt coverage1-2.txt coverage2-1.txt coverage2-2.txt coverage2.1-1.txt coverage2.1-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 > coverage.txt + when: + event: + - push + - pull_request + +services: +- name: mysql + pull: default + image: mysql:5.7 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: xorm_test + when: + event: + - push + - tag + - pull_request + +- name: pgsql + pull: default + image: postgres:9.5 + environment: + POSTGRES_DB: xorm_test + POSTGRES_USER: postgres + when: + event: + - push + - tag + - pull_request + - name: mssql pull: default image: microsoft/mssql-server-linux:latest From d76126b2bd044fb7656fc6369103e14d308544a3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 27 Sep 2019 23:05:23 +0800 Subject: [PATCH 17/20] add tests (#1439) --- session_insert_test.go | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/session_insert_test.go b/session_insert_test.go index d040c9e9..3dcc87d0 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -957,3 +957,46 @@ func TestMultipleInsertTableName(t *testing.T) { assert.NoError(t, trans.Commit()) } + +func TestInsertMultiWithOmit(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type TestMultiOmit struct { + Id int64 `xorm:"int(11) pk"` + Name string `xorm:"varchar(255)"` + Omitted string `xorm:"varchar(255) 'omitted'"` + } + + assert.NoError(t, testEngine.Sync2(new(TestMultiOmit))) + + l := []interface{}{ + TestMultiOmit{Id: 1, Name: "1", Omitted: "1"}, + TestMultiOmit{Id: 2, Name: "1", Omitted: "2"}, + TestMultiOmit{Id: 3, Name: "1", Omitted: "3"}, + } + + check := func() { + var ls []TestMultiOmit + err := testEngine.Find(&ls) + assert.NoError(t, err) + assert.EqualValues(t, 3, len(ls)) + + for e := range ls { + assert.EqualValues(t, "", ls[e].Omitted) + } + } + + num, err := testEngine.Omit("omitted").Insert(l...) + assert.NoError(t, err) + assert.EqualValues(t, 3, num) + check() + + num, err = testEngine.Delete(TestMultiOmit{Name: "1"}) + assert.NoError(t, err) + assert.EqualValues(t, 3, num) + + num, err = testEngine.Omit("omitted").Insert(l) + assert.NoError(t, err) + assert.EqualValues(t, 3, num) + check() +} From 9872e133cfb296bd2ce62f4694f735db5a505e61 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 28 Sep 2019 10:55:12 +0800 Subject: [PATCH 18/20] upgrade core (#1440) --- go.mod | 4 ++-- go.sum | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index eb0d71ba..c30d6df5 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,8 @@ require ( github.com/mattn/go-sqlite3 v1.10.0 github.com/pkg/errors v0.8.1 // indirect github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect - github.com/stretchr/testify v1.3.0 + github.com/stretchr/testify v1.4.0 github.com/ziutek/mymysql v1.5.4 xorm.io/builder v0.3.6 - xorm.io/core v0.7.0 + xorm.io/core v0.7.1 ) diff --git a/go.sum b/go.sum index 15f16fbc..9ca37897 100644 --- a/go.sum +++ b/go.sum @@ -89,6 +89,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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= @@ -162,3 +163,5 @@ xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8= xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= xorm.io/core v0.7.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM= xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI= +xorm.io/core v0.7.1 h1:I6x6Q6dYb67aDEoYFWr2t8UcKIYjJPyCHS+aXuj5V0Y= +xorm.io/core v0.7.1/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= From d6963b7d423db56a4a07b8e0737af98317028bb0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 28 Sep 2019 18:02:19 +0800 Subject: [PATCH 19/20] fix arg conversion (#1441) * fix arg conversion * fix bugs * fix bug on postgres * use traditional positional parameters on insert into select * remove unnecessary tests --- go.mod | 2 +- go.sum | 5 +++++ session_insert_test.go | 24 ++++++++++++++++++++++++ statement_args.go | 38 ++++++++++++++++++++++++++++++++++---- statement_exprparam.go | 7 ++++++- 5 files changed, 70 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c30d6df5..1ab39831 100644 --- a/go.mod +++ b/go.mod @@ -16,5 +16,5 @@ require ( github.com/stretchr/testify v1.4.0 github.com/ziutek/mymysql v1.5.4 xorm.io/builder v0.3.6 - xorm.io/core v0.7.1 + xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb ) diff --git a/go.sum b/go.sum index 9ca37897..cf637a8e 100644 --- a/go.sum +++ b/go.sum @@ -89,12 +89,14 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -155,6 +157,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 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= @@ -165,3 +168,5 @@ xorm.io/core v0.7.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM= xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI= xorm.io/core v0.7.1 h1:I6x6Q6dYb67aDEoYFWr2t8UcKIYjJPyCHS+aXuj5V0Y= xorm.io/core v0.7.1/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= +xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo= +xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= diff --git a/session_insert_test.go b/session_insert_test.go index 3dcc87d0..2785401d 100644 --- a/session_insert_test.go +++ b/session_insert_test.go @@ -896,6 +896,7 @@ func TestInsertWhere(t *testing.T) { inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). + SetExpr("repo_id", "1"). Insert(map[string]string{ "name": "trest3", }) @@ -917,6 +918,29 @@ func TestInsertWhere(t *testing.T) { }) assert.NoError(t, err) assert.EqualValues(t, 1, inserted) + + var j4 InsertWhere + has, err = testEngine.ID(4).Get(&j4) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "10';delete * from insert_where; --", j4.Name) + assert.EqualValues(t, 4, j4.Index) + + inserted, err = testEngine.Table(new(InsertWhere)).Where("repo_id=?", 1). + SetExpr("`index`", "coalesce(MAX(`index`),0)+1"). + Insert(map[string]interface{}{ + "repo_id": 1, + "name": "10\\';delete * from insert_where; --", + }) + assert.NoError(t, err) + assert.EqualValues(t, 1, inserted) + + var j5 InsertWhere + has, err = testEngine.ID(5).Get(&j5) + assert.NoError(t, err) + assert.True(t, has) + assert.EqualValues(t, "10\\';delete * from insert_where; --", j5.Name) + assert.EqualValues(t, 5, j5.Index) } type NightlyRate struct { diff --git a/statement_args.go b/statement_args.go index 23496443..310f24d6 100644 --- a/statement_args.go +++ b/statement_args.go @@ -49,15 +49,34 @@ func quoteNeeded(a interface{}) bool { return true } -func convertArg(arg interface{}) string { +func convertStringSingleQuote(arg string) string { + return "'" + strings.Replace(arg, "'", "''", -1) + "'" +} + +func convertString(arg string) string { + var buf strings.Builder + buf.WriteRune('\'') + for _, c := range arg { + if c == '\\' || c == '\'' { + buf.WriteRune('\\') + } + buf.WriteRune(c) + } + buf.WriteRune('\'') + return buf.String() +} + +func convertArg(arg interface{}, convertFunc func(string) string) string { if quoteNeeded(arg) { argv := fmt.Sprintf("%v", arg) - return "'" + strings.Replace(argv, "'", "''", -1) + "'" + return convertFunc(argv) } return fmt.Sprintf("%v", arg) } +const insertSelectPlaceHolder = true + func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) error { switch argv := arg.(type) { case bool: @@ -93,8 +112,19 @@ func (statement *Statement) writeArg(w *builder.BytesWriter, arg interface{}) er return err } default: - if _, err := w.WriteString(convertArg(arg)); err != nil { - return err + if insertSelectPlaceHolder { + if err := w.WriteByte('?'); err != nil { + return err + } + w.Append(arg) + } else { + var convertFunc = convertStringSingleQuote + if statement.Engine.dialect.DBType() == core.MYSQL { + convertFunc = convertString + } + if _, err := w.WriteString(convertArg(arg, convertFunc)); err != nil { + return err + } } } return nil diff --git a/statement_exprparam.go b/statement_exprparam.go index 0cddca02..4da4f1ea 100644 --- a/statement_exprparam.go +++ b/statement_exprparam.go @@ -57,7 +57,7 @@ func (exprs *exprParams) getByName(colName string) (exprParam, bool) { } func (exprs *exprParams) writeArgs(w *builder.BytesWriter) error { - for _, expr := range exprs.args { + for i, expr := range exprs.args { switch arg := expr.(type) { case *builder.Builder: if _, err := w.WriteString("("); err != nil { @@ -74,6 +74,11 @@ func (exprs *exprParams) writeArgs(w *builder.BytesWriter) error { return err } } + if i != len(exprs.args)-1 { + if _, err := w.WriteString(","); err != nil { + return err + } + } } return nil } From a5702e52b51afdace44bfc286a353a3afb0ee3c6 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 29 Sep 2019 12:31:06 +0800 Subject: [PATCH 20/20] Fix default value parse bugs (#1437) * fix default value * fix default value tags * fix postgres default * fix default on postgres * fix default on postgres * fix mssql default --- dialect_mssql.go | 19 +-- dialect_mysql.go | 16 +-- dialect_postgres.go | 32 +++-- dialect_sqlite3.go | 33 ++++- dialect_sqlite3_test.go | 35 +++++ engine.go | 11 +- helpers_test.go | 19 --- tag.go | 1 + tag_test.go | 284 ++++++++++++++++++++++++++++++---------- test_mssql.sh | 2 +- 10 files changed, 321 insertions(+), 131 deletions(-) create mode 100644 dialect_sqlite3_test.go diff --git a/dialect_mssql.go b/dialect_mssql.go index ce4dd00c..524d05a4 100644 --- a/dialect_mssql.go +++ b/dialect_mssql.go @@ -338,6 +338,7 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) { func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { args := []interface{}{} s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable, + "default_is_null" = (CASE WHEN c.text is null THEN 1 ELSE 0 END), replace(replace(isnull(c.text,''),'(',''),')','') as vdefault, ISNULL(i.is_primary_key, 0) from sys.columns a @@ -361,8 +362,8 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column for rows.Next() { var name, ctype, vdefault string var maxLen, precision, scale int - var nullable, isPK bool - err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault, &isPK) + var nullable, isPK, defaultIsNull bool + err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &defaultIsNull, &vdefault, &isPK) if err != nil { return nil, nil, err } @@ -371,7 +372,10 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column col.Indexes = make(map[string]int) col.Name = strings.Trim(name, "` ") col.Nullable = nullable - col.Default = vdefault + col.DefaultIsEmpty = defaultIsNull + if !defaultIsNull { + col.Default = vdefault + } col.IsPrimaryKey = isPK ct := strings.ToUpper(ctype) if ct == "DECIMAL" { @@ -395,15 +399,6 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column } } - if col.SQLType.IsText() || col.SQLType.IsTime() { - if col.Default != "" { - col.Default = "'" + col.Default + "'" - } else { - if col.DefaultIsEmpty { - col.Default = "''" - } - } - } cols[col.Name] = col colSeq = append(colSeq, col.Name) } diff --git a/dialect_mysql.go b/dialect_mysql.go index a108b81f..cf1dbb6f 100644 --- a/dialect_mysql.go +++ b/dialect_mysql.go @@ -345,9 +345,9 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column if colDefault != nil { col.Default = *colDefault - if col.Default == "" { - col.DefaultIsEmpty = true - } + col.DefaultIsEmpty = false + } else { + col.DefaultIsEmpty = true } cts := strings.Split(colType, "(") @@ -411,13 +411,11 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column col.IsAutoIncrement = true } - if col.SQLType.IsText() || col.SQLType.IsTime() { - if col.Default != "" { + if !col.DefaultIsEmpty { + if col.SQLType.IsText() { + col.Default = "'" + col.Default + "'" + } else if col.SQLType.IsTime() && col.Default != "CURRENT_TIMESTAMP" { col.Default = "'" + col.Default + "'" - } else { - if col.DefaultIsEmpty { - col.Default = "''" - } } } cols[col.Name] = col diff --git a/dialect_postgres.go b/dialect_postgres.go index 3df682e8..ccef3086 100644 --- a/dialect_postgres.go +++ b/dialect_postgres.go @@ -1005,16 +1005,18 @@ WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.att col.Name = strings.Trim(colName, `" `) - if colDefault != nil || isPK { - if isPK { - col.IsPrimaryKey = true - } else { - col.Default = *colDefault + if colDefault != nil { + col.Default = *colDefault + col.DefaultIsEmpty = false + if strings.HasPrefix(col.Default, "nextval(") { + col.IsAutoIncrement = true } + } else { + col.DefaultIsEmpty = true } - if colDefault != nil && strings.HasPrefix(*colDefault, "nextval(") { - col.IsAutoIncrement = true + if isPK { + col.IsPrimaryKey = true } col.Nullable = (isNullable == "YES") @@ -1043,12 +1045,16 @@ WHERE c.relkind = 'r'::char AND c.relname = $1%s AND f.attnum > 0 ORDER BY f.att col.Length = maxLen - if col.SQLType.IsText() || col.SQLType.IsTime() { - if col.Default != "" { - col.Default = "'" + col.Default + "'" - } else { - if col.DefaultIsEmpty { - col.Default = "''" + if !col.DefaultIsEmpty { + if col.SQLType.IsText() { + if strings.HasSuffix(col.Default, "::character varying") { + col.Default = strings.TrimRight(col.Default, "::character varying") + } else if !strings.HasPrefix(col.Default, "'") { + col.Default = "'" + col.Default + "'" + } + } else if col.SQLType.IsTime() { + if strings.HasSuffix(col.Default, "::timestamp without time zone") { + col.Default = strings.TrimRight(col.Default, "::timestamp without time zone") } } } diff --git a/dialect_sqlite3.go b/dialect_sqlite3.go index 60f07295..d1852e9b 100644 --- a/dialect_sqlite3.go +++ b/dialect_sqlite3.go @@ -270,6 +270,34 @@ func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) { return false, nil } +// splitColStr splits a sqlite col strings as fields +func splitColStr(colStr string) []string { + colStr = strings.TrimSpace(colStr) + var results = make([]string, 0, 10) + var lastIdx int + var hasC, hasQuote bool + for i, c := range colStr { + if c == ' ' && !hasQuote { + if hasC { + results = append(results, colStr[lastIdx:i]) + hasC = false + } + } else { + if c == '\'' { + hasQuote = !hasQuote + } + if !hasC { + lastIdx = i + } + hasC = true + if i == len(colStr)-1 { + results = append(results, colStr[lastIdx:i+1]) + } + } + } + return results +} + func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { args := []interface{}{tableName} s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?" @@ -315,7 +343,7 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu continue } - fields := strings.Fields(strings.TrimSpace(colStr)) + fields := splitColStr(colStr) col := new(core.Column) col.Indexes = make(map[string]int) col.Nullable = true @@ -344,9 +372,6 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu col.DefaultIsEmpty = false } } - if !col.SQLType.IsNumeric() && !col.DefaultIsEmpty { - col.Default = "'" + col.Default + "'" - } cols[col.Name] = col colSeq = append(colSeq, col.Name) } diff --git a/dialect_sqlite3_test.go b/dialect_sqlite3_test.go new file mode 100644 index 00000000..a2036159 --- /dev/null +++ b/dialect_sqlite3_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 ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSplitColStr(t *testing.T) { + var kases = []struct { + colStr string + fields []string + }{ + { + colStr: "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL", + fields: []string{ + "`id`", "INTEGER", "PRIMARY", "KEY", "AUTOINCREMENT", "NOT", "NULL", + }, + }, + { + colStr: "`created` DATETIME DEFAULT '2006-01-02 15:04:05' NULL", + fields: []string{ + "`created`", "DATETIME", "DEFAULT", "'2006-01-02 15:04:05'", "NULL", + }, + }, + } + + for _, kase := range kases { + assert.EqualValues(t, kase.fields, splitColStr(kase.colStr)) + } +} diff --git a/engine.go b/engine.go index 649fd1e3..96100fce 100644 --- a/engine.go +++ b/engine.go @@ -907,8 +907,15 @@ func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { fieldType := fieldValue.Type() if ormTagStr != "" { - col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, - IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]int)} + col = &core.Column{ + FieldName: t.Field(i).Name, + Nullable: true, + IsPrimaryKey: false, + IsAutoIncrement: false, + MapType: core.TWOSIDES, + Indexes: make(map[string]int), + DefaultIsEmpty: true, + } tags := splitTag(ormTagStr) if len(tags) > 0 { diff --git a/helpers_test.go b/helpers_test.go index 7e317126..caf7b9f0 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -10,25 +10,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSplitTag(t *testing.T) { - var cases = []struct { - tag string - tags []string - }{ - {"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}}, - {"TEXT", []string{"TEXT"}}, - {"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}}, - {"json binary", []string{"json", "binary"}}, - } - - for _, kase := range cases { - tags := splitTag(kase.tag) - if !sliceEq(tags, kase.tags) { - t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags) - } - } -} - func TestEraseAny(t *testing.T) { raw := "SELECT * FROM `table`.[table_name]" assert.EqualValues(t, raw, eraseAny(raw)) diff --git a/tag.go b/tag.go index 6feb581a..ec8d5cf0 100644 --- a/tag.go +++ b/tag.go @@ -125,6 +125,7 @@ func DefaultTagHandler(ctx *tagContext) error { ctx.col.Default = ctx.nextTag ctx.ignoreNext = true } + ctx.col.DefaultIsEmpty = false return nil } diff --git a/tag_test.go b/tag_test.go index cfb16b3b..891c6ffc 100644 --- a/tag_test.go +++ b/tag_test.go @@ -5,7 +5,6 @@ package xorm import ( - "errors" "fmt" "strings" "testing" @@ -27,58 +26,27 @@ func TestCreatedAndUpdated(t *testing.T) { u := new(UserCU) err := testEngine.DropTables(u) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) err = testEngine.CreateTables(u) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) u.Name = "sss" cnt, err := testEngine.Insert(u) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) u.Name = "xxx" cnt, err = testEngine.ID(u.Id).Update(u) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("update not returned 1") - t.Error(err) - panic(err) - return - } + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) u.Id = 0 u.Created = time.Now().Add(-time.Hour * 24 * 365) u.Updated = u.Created - fmt.Println(u) cnt, err = testEngine.NoAutoTime().Insert(u) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } + assert.NoError(t, err) + assert.EqualValues(t, 1, cnt) } type StrangeName struct { @@ -90,25 +58,17 @@ func TestStrangeName(t *testing.T) { assert.NoError(t, prepareEngine()) err := testEngine.DropTables(new(StrangeName)) - if err != nil { - t.Error(err) - } + assert.NoError(t, err) err = testEngine.CreateTables(new(StrangeName)) - if err != nil { - t.Error(err) - } + assert.NoError(t, err) _, err = testEngine.Insert(&StrangeName{Name: "sfsfdsfds"}) - if err != nil { - t.Error(err) - } + assert.NoError(t, err) beans := make([]StrangeName, 0) err = testEngine.Find(&beans) - if err != nil { - t.Error(err) - } + assert.NoError(t, err) } func TestCreatedUpdated(t *testing.T) { @@ -179,29 +139,17 @@ func TestLowerCase(t *testing.T) { assert.NoError(t, prepareEngine()) err := testEngine.Sync2(&Lowercase{}) - _, err = testEngine.Where("(id) > 0").Delete(&Lowercase{}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) + _, err = testEngine.Where("id > 0").Delete(&Lowercase{}) + assert.NoError(t, err) + _, err = testEngine.Insert(&Lowercase{ended: 1}) - if err != nil { - t.Error(err) - panic(err) - } + assert.NoError(t, err) ls := make([]Lowercase, 0) err = testEngine.Find(&ls) - if err != nil { - t.Error(err) - panic(err) - } - - if len(ls) != 1 { - err = errors.New("should be 1") - t.Error(err) - panic(err) - } + assert.NoError(t, err) + assert.EqualValues(t, 1, len(ls)) } func TestAutoIncrTag(t *testing.T) { @@ -297,6 +245,24 @@ func TestTagDefault(t *testing.T) { assertSync(t, new(DefaultStruct)) + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("age") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.EqualValues(t, "10", defaultVal) + cnt, err := testEngine.Omit("age").Insert(&DefaultStruct{ Name: "test", Age: 20, @@ -312,6 +278,163 @@ func TestTagDefault(t *testing.T) { assert.EqualValues(t, "test", s.Name) } +func TestTagDefault2(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct2 struct { + Id int64 + Name string + } + + assertSync(t, new(DefaultStruct2)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct2") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("name") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.False(t, isDefaultExist, fmt.Sprintf("default value is --%v--", defaultVal)) + assert.EqualValues(t, "", defaultVal) +} + +func TestTagDefault3(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct3 struct { + Id int64 + Name string `xorm:"default('myname')"` + } + + assertSync(t, new(DefaultStruct3)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct3") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("name") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.EqualValues(t, "'myname'", defaultVal) +} + +func TestTagDefault4(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct4 struct { + Id int64 + Created time.Time `xorm:"default(CURRENT_TIMESTAMP)"` + } + + assertSync(t, new(DefaultStruct4)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct4") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("created") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.True(t, "CURRENT_TIMESTAMP" == defaultVal || + "now()" == defaultVal || + "getdate" == defaultVal, defaultVal) +} + +func TestTagDefault5(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct5 struct { + Id int64 + Created time.Time `xorm:"default('2006-01-02 15:04:05')"` + } + + assertSync(t, new(DefaultStruct5)) + table := testEngine.TableInfo(new(DefaultStruct5)) + createdCol := table.GetColumn("created") + assert.NotNil(t, createdCol) + assert.EqualValues(t, "'2006-01-02 15:04:05'", createdCol.Default) + assert.False(t, createdCol.DefaultIsEmpty) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct5") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("created") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + assert.EqualValues(t, "'2006-01-02 15:04:05'", defaultVal) +} + +func TestTagDefault6(t *testing.T) { + assert.NoError(t, prepareEngine()) + + type DefaultStruct6 struct { + Id int64 + IsMan bool `xorm:"default(true)"` + } + + assertSync(t, new(DefaultStruct6)) + + tables, err := testEngine.DBMetas() + assert.NoError(t, err) + + var defaultVal string + var isDefaultExist bool + tableName := testEngine.GetColumnMapper().Obj2Table("DefaultStruct6") + for _, table := range tables { + if table.Name == tableName { + col := table.GetColumn("is_man") + assert.NotNil(t, col) + defaultVal = col.Default + isDefaultExist = !col.DefaultIsEmpty + break + } + } + assert.True(t, isDefaultExist) + if defaultVal == "1" { + defaultVal = "true" + } else if defaultVal == "0" { + defaultVal = "false" + } + assert.EqualValues(t, "true", defaultVal) +} + func TestTagsDirection(t *testing.T) { assert.NoError(t, prepareEngine()) @@ -407,3 +530,22 @@ func TestTagTime(t *testing.T) { assert.EqualValues(t, s.Created.UTC().Format("2006-01-02 15:04:05"), strings.Replace(strings.Replace(tm, "T", " ", -1), "Z", "", -1)) } + +func TestSplitTag(t *testing.T) { + var cases = []struct { + tag string + tags []string + }{ + {"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}}, + {"TEXT", []string{"TEXT"}}, + {"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}}, + {"json binary", []string{"json", "binary"}}, + } + + for _, kase := range cases { + tags := splitTag(kase.tag) + if !sliceEq(tags, kase.tags) { + t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags) + } + } +} diff --git a/test_mssql.sh b/test_mssql.sh index e26e1641..7f060cff 100755 --- a/test_mssql.sh +++ b/test_mssql.sh @@ -1 +1 @@ -go test -db=mssql -conn_str="server=localhost;user id=sa;password=MwantsaSecurePassword1;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