From 0dff77170730b4f7b8403f0e18f4710044813251 Mon Sep 17 00:00:00 2001 From: Penghui Liao Date: Fri, 4 May 2018 16:15:50 +0800 Subject: [PATCH 1/7] fix unquoted identifier that may be misleading in postgres Signed-off-by: Penghui Liao --- orm/db.go | 2 ++ orm/models_test.go | 4 +--- orm/orm_test.go | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/orm/db.go b/orm/db.go index 6b749425..87d08df6 100644 --- a/orm/db.go +++ b/orm/db.go @@ -536,6 +536,8 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a updates := make([]string, len(names)) var conflitValue interface{} for i, v := range names { + // identifier in database may not be case-sensitive, so quote it + v = fmt.Sprintf("%s%s%s", Q, v, Q) marks[i] = "?" valueStr := argsMap[strings.ToLower(v)] if v == args0 { diff --git a/orm/models_test.go b/orm/models_test.go index d6c2b581..d60be27a 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -477,9 +477,7 @@ go test -v github.com/astaxie/beego/orm #### TiDB export ORM_DRIVER=tidb export ORM_SOURCE='memory://test/test' -go test -v github.com/astaxie/beego/orm - -`) +go test -v github.com/astaxie/beego/orm`) os.Exit(2) } diff --git a/orm/orm_test.go b/orm/orm_test.go index f1f2d85e..ce6fc6d4 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -2297,6 +2297,11 @@ func TestInsertOrUpdate(t *testing.T) { throwFailNow(t, AssertIs(user2.Status, test.Status)) throwFailNow(t, AssertIs(user2.Password, strings.TrimSpace(test.Password))) } + + //postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values + if IsPostgres { + return + } //test3 + _, err = dORM.InsertOrUpdate(&user2, "user_name", "status=status+1") if err != nil { From 443c77b3035c8860ae3b2380153271055ae336bd Mon Sep 17 00:00:00 2001 From: Penghui Liao Date: Fri, 4 May 2018 16:38:03 +0800 Subject: [PATCH 2/7] support DB.BeginTx of golang 1.8 Signed-off-by: Penghui Liao --- orm/{orm.go => orm_go18.go} | 9 ++++- orm/orm_log.go | 8 +++++ orm/orm_test.go | 70 +++++++++++++++++++++++++++++++++++-- orm/types.go | 13 +++++++ 4 files changed, 96 insertions(+), 4 deletions(-) rename orm/{orm.go => orm_go18.go} (98%) diff --git a/orm/orm.go b/orm/orm_go18.go similarity index 98% rename from orm/orm.go rename to orm/orm_go18.go index fcf82590..e59589aa 100644 --- a/orm/orm.go +++ b/orm/orm_go18.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +// +build go1.8 + // Package orm provide ORM for MySQL/PostgreSQL/sqlite // Simple Usage // @@ -52,6 +54,7 @@ package orm import ( + "context" "database/sql" "errors" "fmt" @@ -458,11 +461,15 @@ func (o *orm) Using(name string) error { // begin transaction func (o *orm) Begin() error { + return o.BeginTx(context.Background(), nil) +} + +func (o *orm) BeginTx(ctx context.Context, opts *sql.TxOptions) error { if o.isTx { return ErrTxHasBegan } var tx *sql.Tx - tx, err := o.db.(txer).Begin() + tx, err := o.db.(txer).BeginTx(ctx, opts) if err != nil { return err } diff --git a/orm/orm_log.go b/orm/orm_log.go index 26c73f9e..979dbbc6 100644 --- a/orm/orm_log.go +++ b/orm/orm_log.go @@ -15,6 +15,7 @@ package orm import ( + "context" "database/sql" "fmt" "io" @@ -150,6 +151,13 @@ func (d *dbQueryLog) Begin() (*sql.Tx, error) { return tx, err } +func (d *dbQueryLog) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) { + a := time.Now() + tx, err := d.db.(txer).BeginTx(ctx, opts) + debugLogQueies(d.alias, "db.BeginTx", "START TRANSACTION", a, err) + return tx, err +} + func (d *dbQueryLog) Commit() error { a := time.Now() err := d.db.(txEnder).Commit() diff --git a/orm/orm_test.go b/orm/orm_test.go index ce6fc6d4..ceb814ff 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -12,10 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +// +build go1.8 + package orm import ( "bytes" + "context" "database/sql" "fmt" "io/ioutil" @@ -452,9 +455,9 @@ func TestNullDataTypes(t *testing.T) { throwFail(t, AssertIs(*d.Float32Ptr, float32Ptr)) throwFail(t, AssertIs(*d.Float64Ptr, float64Ptr)) throwFail(t, AssertIs(*d.DecimalPtr, decimalPtr)) - throwFail(t, AssertIs((*d.TimePtr).Format(testTime), timePtr.Format(testTime))) - throwFail(t, AssertIs((*d.DatePtr).Format(testDate), datePtr.Format(testDate))) - throwFail(t, AssertIs((*d.DateTimePtr).Format(testDateTime), dateTimePtr.Format(testDateTime))) + throwFail(t, AssertIs((*d.TimePtr).UTC().Format(testTime), timePtr.UTC().Format(testTime))) + throwFail(t, AssertIs((*d.DatePtr).UTC().Format(testDate), datePtr.UTC().Format(testDate))) + throwFail(t, AssertIs((*d.DateTimePtr).UTC().Format(testDateTime), dateTimePtr.UTC().Format(testDateTime))) } func TestDataCustomTypes(t *testing.T) { @@ -1990,6 +1993,66 @@ func TestTransaction(t *testing.T) { } +func TestTransactionIsolationLevel(t *testing.T) { + // this test worked when database support transaction isolation level + if IsSqlite { + return + } + + o1 := NewOrm() + o2 := NewOrm() + + // start two transaction with isolation level repeatable read + err := o1.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead}) + throwFail(t, err) + err = o2.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead}) + throwFail(t, err) + + // o1 insert tag + var tag Tag + tag.Name = "test-transaction" + id, err := o1.Insert(&tag) + throwFail(t, err) + throwFail(t, AssertIs(id > 0, true)) + + // o2 query tag table, no result + num, err := o2.QueryTable("tag").Filter("name", "test-transaction").Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 0)) + + // o1 commit + o1.Commit() + + // o2 query tag table, still no result + num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 0)) + + // o2 commit and query tag table, get the result + o2.Commit() + num, err = o2.QueryTable("tag").Filter("name", "test-transaction").Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + + num, err = o1.QueryTable("tag").Filter("name", "test-transaction").Delete() + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) +} + +func TestBeginTxWithContextCanceled(t *testing.T) { + o := NewOrm() + ctx, cancel := context.WithCancel(context.Background()) + o.BeginTx(ctx, nil) + id, err := o.Insert(&Tag{Name: "test-context"}) + throwFail(t, err) + throwFail(t, AssertIs(id > 0, true)) + + // cancel the context before commit to make it error + cancel() + err = o.Commit() + throwFail(t, AssertIs(err, context.Canceled)) +} + func TestReadOrCreate(t *testing.T) { u := &User{ UserName: "Kyle", @@ -2260,6 +2323,7 @@ func TestIgnoreCaseTag(t *testing.T) { throwFail(t, AssertIs(info.fields.GetByName("Name02").column, "Name")) throwFail(t, AssertIs(info.fields.GetByName("Name03").column, "name")) } + func TestInsertOrUpdate(t *testing.T) { RegisterModel(new(User)) user := User{UserName: "unique_username133", Status: 1, Password: "o"} diff --git a/orm/types.go b/orm/types.go index e3373096..2fdc98c7 100644 --- a/orm/types.go +++ b/orm/types.go @@ -15,6 +15,7 @@ package orm import ( + "context" "database/sql" "reflect" "time" @@ -106,6 +107,17 @@ type Ormer interface { // ... // err = o.Rollback() Begin() error + // begin transaction with provided context and option + // the provided context is used until the transaction is committed or rolled back. + // if the context is canceled, the transaction will be rolled back. + // the provided TxOptions is optional and may be nil if defaults should be used. + // if a non-default isolation level is used that the driver doesn't support, an error will be returned. + // for example: + // o := NewOrm() + // err := o.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead}) + // ... + // err = o.Rollback() + BeginTx(ctx context.Context, opts *sql.TxOptions) error // commit transaction Commit() error // rollback transaction @@ -401,6 +413,7 @@ type dbQuerier interface { // transaction beginner type txer interface { Begin() (*sql.Tx, error) + BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) } // transaction ending From 6bdd152d91a50bbd46bcfff1ac1f807d70096f60 Mon Sep 17 00:00:00 2001 From: Penghui Liao Date: Fri, 4 May 2018 17:31:01 +0800 Subject: [PATCH 3/7] upgrade postgres in travis Signed-off-by: Penghui Liao --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c0e923d0..6b7353f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,8 +43,8 @@ before_script: - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi" - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi" - - sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi" - - sh -c "if [ $(go version) == *1.[5-9]* ]; then go tool vet .; fi" + - sh -c "go get github.com/golang/lint/golint; golint ./...;" + - sh -c "go tool vet ." - mkdir -p res/var - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d after_script: @@ -58,4 +58,4 @@ script: - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s - golint ./... addons: - postgresql: "9.4" + postgresql: "9.6" From 1a3f1d66c1bed1f737efce486a278aabdd7ab153 Mon Sep 17 00:00:00 2001 From: Penghui Liao Date: Fri, 20 Jul 2018 15:29:56 +0800 Subject: [PATCH 4/7] rename orm_go18.go to orm.go Signed-off-by: Penghui Liao --- orm/{orm_go18.go => orm.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename orm/{orm_go18.go => orm.go} (100%) diff --git a/orm/orm_go18.go b/orm/orm.go similarity index 100% rename from orm/orm_go18.go rename to orm/orm.go From 30bbc81a2e784862854af5b6577d9347def028ad Mon Sep 17 00:00:00 2001 From: Penghui Liao Date: Fri, 20 Jul 2018 16:51:36 +0800 Subject: [PATCH 5/7] fix test case that calls All() Signed-off-by: Penghui Liao --- orm/orm_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orm/orm_test.go b/orm/orm_test.go index ceb814ff..a27a5fcb 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -1011,13 +1011,13 @@ func TestAll(t *testing.T) { qs = dORM.QueryTable("user") num, err = qs.Filter("user_name", "nothing").All(&users) - throwFailNow(t, err) + throwFail(t, AssertIs(err, ErrNoRows)) throwFailNow(t, AssertIs(num, 0)) var users3 []*User qs = dORM.QueryTable("user") num, err = qs.Filter("user_name", "nothing").All(&users3) - throwFailNow(t, err) + throwFail(t, AssertIs(err, ErrNoRows)) throwFailNow(t, AssertIs(num, 0)) throwFailNow(t, AssertIs(users3 == nil, false)) } From b8868d6d2d8bfa8986e637106dfedbe4b8e8885b Mon Sep 17 00:00:00 2001 From: Penghui Liao Date: Fri, 20 Jul 2018 17:07:17 +0800 Subject: [PATCH 6/7] remove unnecessary conversion Signed-off-by: Penghui Liao --- config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config_test.go b/config_test.go index c1973f7b..379fe1c6 100644 --- a/config_test.go +++ b/config_test.go @@ -75,7 +75,7 @@ func TestAssignConfig_02(t *testing.T) { jcf := &config.JSONConfig{} bs, _ = json.Marshal(configMap) - ac, _ := jcf.ParseData([]byte(bs)) + ac, _ := jcf.ParseData(bs) for _, i := range []interface{}{_BConfig, &_BConfig.Listen, &_BConfig.WebConfig, &_BConfig.Log, &_BConfig.WebConfig.Session} { assignSingleConfig(i, ac) From feb0e67fd73200efc10f7e0fa6bd27d5a9b47ef1 Mon Sep 17 00:00:00 2001 From: Penghui Liao Date: Mon, 23 Jul 2018 11:27:19 +0800 Subject: [PATCH 7/7] upgrade go version from 1.9.2 to 1.9.7 in test env. upgrade to avoid bug: https://github.com/golang/go/issues/22976 Signed-off-by: Penghui Liao --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fd1194c5..cf389896 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - "1.9.2" + - "1.9.7" - "1.10.3" services: - redis-server