Merge pull request #3149 from liaoishere/feature/support-begintx
Support DB.BeginTx in go 1.8
This commit is contained in:
		
						commit
						d55f54a8ab
					
				| @ -1,7 +1,7 @@ | ||||
| language: go | ||||
| 
 | ||||
| go: | ||||
|   - "1.9.2" | ||||
|   - "1.9.7" | ||||
|   - "1.10.3" | ||||
| services: | ||||
|   - redis-server | ||||
| @ -44,8 +44,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: | ||||
| @ -59,4 +59,4 @@ script: | ||||
|   - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s | ||||
|   - golint ./... | ||||
| addons: | ||||
|   postgresql: "9.4" | ||||
|   postgresql: "9.6" | ||||
|  | ||||
| @ -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 { | ||||
|  | ||||
| @ -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 | ||||
| 	} | ||||
|  | ||||
| @ -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() | ||||
|  | ||||
| @ -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"} | ||||
| @ -2297,6 +2361,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 { | ||||
|  | ||||
							
								
								
									
										13
									
								
								orm/types.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user