diff --git a/README.md b/README.md index 06082556..ad399a72 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,13 @@ Drivers for Go's sql package which currently support database/sql includes: [More changelogs ...](https://github.com/lunny/xorm/blob/master/docs/Changelog.md) -# Installation +# Installation + +If you have [gopm](https://github.com/gpmgo/gopm) installed, + + gopm get github.com/lunny/xorm + +Or go get github.com/lunny/xorm diff --git a/README_CN.md b/README_CN.md index b90c8118..b2fe1f9b 100644 --- a/README_CN.md +++ b/README_CN.md @@ -50,6 +50,12 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 安装 +推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: + + gopm get github.com/lunny/xorm + +或者您也可以使用go工具进行安装: + go get github.com/lunny/xorm ## 文档 diff --git a/base_test.go b/base_test.go index 74535374..9f7a578a 100644 --- a/base_test.go +++ b/base_test.go @@ -677,7 +677,7 @@ func combineTransaction(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - //session.IsAutoRollback = false + user1 := Userinfo{Username: "xiaoxiao2", Departname: "dev", Alias: "lunny", Created: time.Now()} _, err = session.Insert(&user1) if err != nil { @@ -1706,22 +1706,64 @@ func testPrefixTableName(engine *Engine, t *testing.T) { } } +type CreatedUpdated struct { + Id int64 + Name string + Value float64 `xorm:"numeric"` + Created time.Time `xorm:"created"` + Created2 time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` +} + +func testCreatedUpdated(engine *Engine, t *testing.T) { + err := engine.Sync(&CreatedUpdated{}) + if err != nil { + t.Error(err) + panic(err) + } + + c := &CreatedUpdated{Name: "test"} + _, err = engine.Insert(c) + if err != nil { + t.Error(err) + panic(err) + } + + c2 := new(CreatedUpdated) + has, err := engine.Id(c.Id).Get(c2) + if err != nil { + t.Error(err) + panic(err) + } + + if !has { + panic(errors.New("no id")) + } + + c2.Value -= 1 + _, err = engine.Id(c2.Id).Update(c2) + if err != nil { + t.Error(err) + panic(err) + } +} + type ProcessorsStruct struct { Id int64 - - B4InsertFlag int - AfterInsertedFlag int - B4UpdateFlag int - AfterUpdatedFlag int - B4DeleteFlag int `xorm:"-"` - AfterDeletedFlag int `xorm:"-"` - B4InsertViaExt int - AfterInsertedViaExt int - B4UpdateViaExt int - AfterUpdatedViaExt int - B4DeleteViaExt int `xorm:"-"` - AfterDeletedViaExt int `xorm:"-"` + B4InsertFlag int + AfterInsertedFlag int + B4UpdateFlag int + AfterUpdatedFlag int + B4DeleteFlag int `xorm:"-"` + AfterDeletedFlag int `xorm:"-"` + + B4InsertViaExt int + AfterInsertedViaExt int + B4UpdateViaExt int + AfterUpdatedViaExt int + B4DeleteViaExt int `xorm:"-"` + AfterDeletedViaExt int `xorm:"-"` } func (p *ProcessorsStruct) BeforeInsert() { @@ -1754,13 +1796,14 @@ func testProcessors(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + tempEngine.ShowSQL = true err = tempEngine.DropTables(&ProcessorsStruct{}) if err != nil { t.Error(err) panic(err) } + p := &ProcessorsStruct{} err = tempEngine.CreateTables(&ProcessorsStruct{}) if err != nil { @@ -1768,9 +1811,6 @@ func testProcessors(engine *Engine, t *testing.T) { panic(err) } - p := &ProcessorsStruct{} - - // test insert processors b4InsertFunc := func(bean interface{}) { if v, ok := (bean).(*ProcessorsStruct); ok { v.B4InsertViaExt = 1 @@ -1786,28 +1826,44 @@ func testProcessors(engine *Engine, t *testing.T) { t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) } } - + _, err = tempEngine.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) if err != nil { t.Error(err) panic(err) } else { - if p.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p.AfterInsertedFlag == 0 { t.Error(errors.New("AfterInsertedFlag not set")) } - if p.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p.AfterInsertedViaExt == 0 { t.Error(errors.New("AfterInsertedViaExt not set")) } + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedViaExt == 0 { + t.Error(errors.New("AfterInsertedViaExt not set")) + } } - + p2 := &ProcessorsStruct{} _, err = tempEngine.Id(p.Id).Get(p2) if err != nil { t.Error(err) panic(err) } else { - if p2.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p2.AfterInsertedFlag != 0 { t.Error(errors.New("AfterInsertedFlag is set")) } - if p2.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p2.AfterInsertedViaExt != 0 { t.Error(errors.New("AfterInsertedViaExt is set")) } + if p2.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p2.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p2.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p2.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } } // -- @@ -1827,7 +1883,7 @@ func testProcessors(engine *Engine, t *testing.T) { t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) } } - + p = p2 // reset _, err = tempEngine.Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) @@ -1835,22 +1891,38 @@ func testProcessors(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p.B4UpdateFlag == 0 { t.Error(errors.New("B4UpdateFlag not set")) } - if p.AfterUpdatedFlag == 0 { t.Error(errors.New("AfterUpdatedFlag not set")) } - if p.B4UpdateViaExt == 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p.AfterUpdatedViaExt == 0 { t.Error(errors.New("AfterUpdatedViaExt not set")) } + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag == 0 { + t.Error(errors.New("AfterUpdatedFlag not set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt == 0 { + t.Error(errors.New("AfterUpdatedViaExt not set")) + } } - - p2 = &ProcessorsStruct{} + + p2 = &ProcessorsStruct{} _, err = tempEngine.Id(p.Id).Get(p2) if err != nil { t.Error(err) panic(err) } else { - if p2.B4UpdateFlag == 0 { t.Error(errors.New("B4UpdateFlag not set")) } - if p2.AfterUpdatedFlag != 0 { t.Error(errors.New("AfterUpdatedFlag is set")) } - if p2.B4UpdateViaExt == 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p2.AfterUpdatedViaExt != 0 { t.Error(errors.New("AfterUpdatedViaExt is set")) } + if p2.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p2.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set: " + string(p.AfterUpdatedFlag))) + } + if p2.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p2.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set: " + string(p.AfterUpdatedViaExt))) + } } // -- @@ -1870,22 +1942,30 @@ func testProcessors(engine *Engine, t *testing.T) { t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) } } - + p = p2 // reset _, err = tempEngine.Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) if err != nil { t.Error(err) panic(err) } else { - if p.B4DeleteFlag == 0 { t.Error(errors.New("B4DeleteFlag not set")) } - if p.AfterDeletedFlag == 0 { t.Error(errors.New("AfterDeletedFlag not set")) } - if p.B4DeleteViaExt == 0 { t.Error(errors.New("B4DeleteViaExt not set")) } - if p.AfterDeletedViaExt == 0 { t.Error(errors.New("AfterDeletedViaExt not set")) } + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) + } + if p.AfterDeletedFlag == 0 { + t.Error(errors.New("AfterDeletedFlag not set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt == 0 { + t.Error(errors.New("AfterDeletedViaExt not set")) + } } // -- // test insert multi - pslice := make([]*ProcessorsStruct, 0); + pslice := make([]*ProcessorsStruct, 0) pslice = append(pslice, &ProcessorsStruct{}) pslice = append(pslice, &ProcessorsStruct{}) cnt, err := tempEngine.Before(b4InsertFunc).After(afterInsertFunc).Insert(&pslice) @@ -1893,15 +1973,25 @@ func testProcessors(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if cnt != 2 { t.Error(errors.New("incorrect insert count")) } + if cnt != 2 { + t.Error(errors.New("incorrect insert count")) + } for _, elem := range pslice { - if elem.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if elem.AfterInsertedFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if elem.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if elem.AfterInsertedViaExt == 0 { t.Error(errors.New("AfterInsertedViaExt not set")) } + if elem.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if elem.AfterInsertedFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if elem.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if elem.AfterInsertedViaExt == 0 { + t.Error(errors.New("AfterInsertedViaExt not set")) + } } } - + for _, elem := range pslice { p = &ProcessorsStruct{} _, err = tempEngine.Id(elem.Id).Get(p) @@ -1909,10 +1999,18 @@ func testProcessors(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p2.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p2.AfterInsertedFlag != 0 { t.Error(errors.New("AfterInsertedFlag is set")) } - if p2.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p2.AfterInsertedViaExt != 0 { t.Error(errors.New("AfterInsertedViaExt is set")) } + if p2.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p2.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p2.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p2.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } } } // -- @@ -1924,7 +2022,7 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + tempEngine.ShowSQL = true err = tempEngine.DropTables(&ProcessorsStruct{}) if err != nil { @@ -1938,7 +2036,6 @@ func testProcessorsTx(engine *Engine, t *testing.T) { panic(err) } - // test insert processors with tx rollback session := tempEngine.NewSession() err = session.Begin() @@ -1946,7 +2043,7 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + p := &ProcessorsStruct{} b4InsertFunc := func(bean interface{}) { if v, ok := (bean).(*ProcessorsStruct); ok { @@ -1962,16 +2059,24 @@ func testProcessorsTx(engine *Engine, t *testing.T) { } else { t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) } - } + } _, err = session.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) if err != nil { t.Error(err) panic(err) } else { - if p.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p.AfterInsertedFlag != 0 { t.Error(errors.New("B4InsertFlag is set")) } - if p.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p.AfterInsertedViaExt != 0 { t.Error(errors.New("AfterInsertedViaExt is set")) } + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("B4InsertFlag is set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } } err = session.Rollback() @@ -1979,10 +2084,18 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p.AfterInsertedFlag != 0 { t.Error(errors.New("B4InsertFlag is set")) } - if p.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p.AfterInsertedViaExt != 0 { t.Error(errors.New("AfterInsertedViaExt is set")) } + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("B4InsertFlag is set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } } session.Close() p2 := &ProcessorsStruct{} @@ -1991,11 +2104,14 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p2.Id > 0 { t.Error(errors.New("tx got committed upon insert!?")) } + if p2.Id > 0 { + err = errors.New("tx got committed upon insert!?") + t.Error(err) + panic(err) + } } // -- - // test insert processors with tx commit session = tempEngine.NewSession() err = session.Begin() @@ -2003,17 +2119,25 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + p = &ProcessorsStruct{} _, err = session.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) if err != nil { t.Error(err) panic(err) } else { - if p.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p.AfterInsertedFlag != 0 { t.Error(errors.New("AfterInsertedFlag is set")) } - if p.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p.AfterInsertedViaExt != 0 { t.Error(errors.New("AfterInsertedViaExt is set")) } + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } } err = session.Commit() @@ -2021,10 +2145,18 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p.AfterInsertedFlag == 0 { t.Error(errors.New("AfterInsertedFlag not set")) } - if p.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p.AfterInsertedViaExt == 0 { t.Error(errors.New("AfterInsertedViaExt not set")) } + if p.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p.AfterInsertedFlag == 0 { + t.Error(errors.New("AfterInsertedFlag not set")) + } + if p.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p.AfterInsertedViaExt == 0 { + t.Error(errors.New("AfterInsertedViaExt not set")) + } } session.Close() p2 = &ProcessorsStruct{} @@ -2033,14 +2165,21 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p2.B4InsertFlag == 0 { t.Error(errors.New("B4InsertFlag not set")) } - if p2.AfterInsertedFlag != 0 { t.Error(errors.New("AfterInsertedFlag is set")) } - if p2.B4InsertViaExt == 0 { t.Error(errors.New("B4InsertViaExt not set")) } - if p2.AfterInsertedViaExt != 0 { t.Error(errors.New("AfterInsertedViaExt is set")) } + if p2.B4InsertFlag == 0 { + t.Error(errors.New("B4InsertFlag not set")) + } + if p2.AfterInsertedFlag != 0 { + t.Error(errors.New("AfterInsertedFlag is set")) + } + if p2.B4InsertViaExt == 0 { + t.Error(errors.New("B4InsertViaExt not set")) + } + if p2.AfterInsertedViaExt != 0 { + t.Error(errors.New("AfterInsertedViaExt is set")) + } } insertedId := p2.Id - // -- - + // -- // test update processors with tx rollback session = tempEngine.NewSession() @@ -2049,7 +2188,7 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + b4UpdateFunc := func(bean interface{}) { if v, ok := (bean).(*ProcessorsStruct); ok { v.B4UpdateViaExt = 1 @@ -2065,7 +2204,7 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) } } - + p = p2 // reset _, err = session.Id(insertedId).Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) @@ -2073,35 +2212,59 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p.B4UpdateFlag == 0 { t.Error(errors.New("B4UpdateFlag not set")) } - if p.AfterUpdatedFlag != 0 { t.Error(errors.New("AfterUpdatedFlag is set")) } - if p.B4UpdateViaExt == 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p.AfterUpdatedViaExt != 0 { t.Error(errors.New("AfterUpdatedViaExt is set")) } + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) + } } err = session.Rollback() if err != nil { t.Error(err) panic(err) } else { - if p.B4UpdateFlag == 0 { t.Error(errors.New("B4UpdateFlag not set")) } - if p.AfterUpdatedFlag != 0 { t.Error(errors.New("AfterUpdatedFlag is set")) } - if p.B4UpdateViaExt == 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p.AfterUpdatedViaExt != 0 { t.Error(errors.New("AfterUpdatedViaExt is set")) } + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) + } } - + session.Close() - p2 = &ProcessorsStruct{} + p2 = &ProcessorsStruct{} _, err = tempEngine.Id(insertedId).Get(p2) if err != nil { t.Error(err) panic(err) } else { - if p2.B4UpdateFlag != 0 { t.Error(errors.New("B4UpdateFlag is set")) } - if p2.AfterUpdatedFlag != 0 { t.Error(errors.New("AfterUpdatedFlag is set")) } - if p2.B4UpdateViaExt != 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p2.AfterUpdatedViaExt != 0 { t.Error(errors.New("AfterUpdatedViaExt is set")) } + if p2.B4UpdateFlag != 0 { + t.Error(errors.New("B4UpdateFlag is set")) + } + if p2.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p2.B4UpdateViaExt != 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p2.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) + } } - // -- + // -- // test update processors with tx commit session = tempEngine.NewSession() @@ -2110,28 +2273,44 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + p = &ProcessorsStruct{} - + _, err = session.Id(insertedId).Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) if err != nil { t.Error(err) panic(err) } else { - if p.B4UpdateFlag == 0 { t.Error(errors.New("B4UpdateFlag not set")) } - if p.AfterUpdatedFlag != 0 { t.Error(errors.New("AfterUpdatedFlag is set")) } - if p.B4UpdateViaExt == 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p.AfterUpdatedViaExt != 0 { t.Error(errors.New("AfterUpdatedViaExt is set")) } + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag != 0 { + t.Error(errors.New("AfterUpdatedFlag is set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt != 0 { + t.Error(errors.New("AfterUpdatedViaExt is set")) + } } err = session.Commit() if err != nil { t.Error(err) panic(err) } else { - if p.B4UpdateFlag == 0 { t.Error(errors.New("B4UpdateFlag not set")) } - if p.AfterUpdatedFlag == 0 { t.Error(errors.New("AfterUpdatedFlag not set")) } - if p.B4UpdateViaExt == 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p.AfterUpdatedViaExt == 0 { t.Error(errors.New("AfterUpdatedViaExt not set")) } + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag == 0 { + t.Error(errors.New("AfterUpdatedFlag not set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt == 0 { + t.Error(errors.New("AfterUpdatedViaExt not set")) + } } session.Close() p2 = &ProcessorsStruct{} @@ -2140,12 +2319,20 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p.B4UpdateFlag == 0 { t.Error(errors.New("B4UpdateFlag not set")) } - if p.AfterUpdatedFlag == 0 { t.Error(errors.New("AfterUpdatedFlag not set")) } - if p.B4UpdateViaExt == 0 { t.Error(errors.New("B4UpdateViaExt not set")) } - if p.AfterUpdatedViaExt == 0 { t.Error(errors.New("AfterUpdatedViaExt not set")) } + if p.B4UpdateFlag == 0 { + t.Error(errors.New("B4UpdateFlag not set")) + } + if p.AfterUpdatedFlag == 0 { + t.Error(errors.New("AfterUpdatedFlag not set")) + } + if p.B4UpdateViaExt == 0 { + t.Error(errors.New("B4UpdateViaExt not set")) + } + if p.AfterUpdatedViaExt == 0 { + t.Error(errors.New("AfterUpdatedViaExt not set")) + } } - // -- + // -- // test delete processors with tx rollback session = tempEngine.NewSession() @@ -2154,7 +2341,7 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + b4DeleteFunc := func(bean interface{}) { if v, ok := (bean).(*ProcessorsStruct); ok { v.B4DeleteViaExt = 1 @@ -2170,7 +2357,7 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) } } - + p = &ProcessorsStruct{} // reset _, err = session.Id(insertedId).Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) @@ -2178,35 +2365,59 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } else { - if p.B4DeleteFlag == 0 { t.Error(errors.New("B4DeleteFlag not set")) } - if p.AfterDeletedFlag != 0 { t.Error(errors.New("AfterDeletedFlag is set")) } - if p.B4DeleteViaExt == 0 { t.Error(errors.New("B4DeleteViaExt not set")) } - if p.AfterDeletedViaExt != 0 { t.Error(errors.New("AfterDeletedViaExt is set")) } + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) + } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) + } } err = session.Rollback() if err != nil { t.Error(err) panic(err) - } else { - if p.B4DeleteFlag == 0 { t.Error(errors.New("B4DeleteFlag not set")) } - if p.AfterDeletedFlag != 0 { t.Error(errors.New("AfterDeletedFlag is set")) } - if p.B4DeleteViaExt == 0 { t.Error(errors.New("B4DeleteViaExt not set")) } - if p.AfterDeletedViaExt != 0 { t.Error(errors.New("AfterDeletedViaExt is set")) } + } else { + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) + } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) + } } session.Close() - - p2 = &ProcessorsStruct{} + + p2 = &ProcessorsStruct{} _, err = tempEngine.Id(insertedId).Get(p2) if err != nil { t.Error(err) panic(err) } else { - if p2.B4DeleteFlag != 0 { t.Error(errors.New("B4DeleteFlag is set")) } - if p2.AfterDeletedFlag != 0 { t.Error(errors.New("AfterDeletedFlag is set")) } - if p2.B4DeleteViaExt != 0 { t.Error(errors.New("B4DeleteViaExt is set")) } - if p2.AfterDeletedViaExt != 0 { t.Error(errors.New("AfterDeletedViaExt is set")) } + if p2.B4DeleteFlag != 0 { + t.Error(errors.New("B4DeleteFlag is set")) + } + if p2.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p2.B4DeleteViaExt != 0 { + t.Error(errors.New("B4DeleteViaExt is set")) + } + if p2.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) + } } - // -- + // -- // test delete processors with tx commit session = tempEngine.NewSession() @@ -2215,31 +2426,47 @@ func testProcessorsTx(engine *Engine, t *testing.T) { t.Error(err) panic(err) } - + p = &ProcessorsStruct{} - + _, err = session.Id(insertedId).Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) if err != nil { t.Error(err) panic(err) } else { - if p.B4DeleteFlag == 0 { t.Error(errors.New("B4DeleteFlag not set")) } - if p.AfterDeletedFlag != 0 { t.Error(errors.New("AfterDeletedFlag is set")) } - if p.B4DeleteViaExt == 0 { t.Error(errors.New("B4DeleteViaExt not set")) } - if p.AfterDeletedViaExt != 0 { t.Error(errors.New("AfterDeletedViaExt is set")) } + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) + } + if p.AfterDeletedFlag != 0 { + t.Error(errors.New("AfterDeletedFlag is set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt != 0 { + t.Error(errors.New("AfterDeletedViaExt is set")) + } } err = session.Commit() if err != nil { t.Error(err) panic(err) } else { - if p.B4DeleteFlag == 0 { t.Error(errors.New("B4DeleteFlag not set")) } - if p.AfterDeletedFlag == 0 { t.Error(errors.New("AfterDeletedFlag not set")) } - if p.B4DeleteViaExt == 0 { t.Error(errors.New("B4DeleteViaExt not set")) } - if p.AfterDeletedViaExt == 0 { t.Error(errors.New("AfterDeletedViaExt not set")) } + if p.B4DeleteFlag == 0 { + t.Error(errors.New("B4DeleteFlag not set")) + } + if p.AfterDeletedFlag == 0 { + t.Error(errors.New("AfterDeletedFlag not set")) + } + if p.B4DeleteViaExt == 0 { + t.Error(errors.New("B4DeleteViaExt not set")) + } + if p.AfterDeletedViaExt == 0 { + t.Error(errors.New("AfterDeletedViaExt not set")) + } } session.Close() - // -- + // -- } func testAll(engine *Engine, t *testing.T) { @@ -2336,6 +2563,8 @@ func testAll2(engine *Engine, t *testing.T) { testTime(engine, t) fmt.Println("-------------- testPrefixTableName --------------") testPrefixTableName(engine, t) + //fmt.Println("-------------- testCreatedUpdated --------------") + //testCreatedUpdated(engine, t) fmt.Println("-------------- processors --------------") testProcessors(engine, t) fmt.Println("-------------- processors TX --------------") diff --git a/docs/Changelog.md b/docs/Changelog.md index b221c4a9..bc737e68 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,5 +1,6 @@ ## Changelog +* **v0.2.3** : Improved documents; Optimistic Locking support; Timestamp with time zone support; Mapper change to tableMapper and columnMapper & added PrefixMapper & SuffixMapper support custom table or column name's prefix and suffix;Insert now return affected, err instead of id, err; Added UseBool & Distinct; * **v0.2.2** : Postgres drivers now support lib/pq; Added method Iterate for record by record to handler;Added SetMaxConns(go1.2+) support; some bugs fixed. * **v0.2.1** : Added database reverse tool, now support generate go & c++ codes, see [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); some bug fixed. * **v0.2.0** : Added Cache supported, select is speeder up 3~5x; Added SameMapper for same name between struct and table; Added Sync method for auto added tables, columns, indexes; diff --git a/docs/ChangelogCN.md b/docs/ChangelogCN.md index f7fbdc61..19f6022d 100644 --- a/docs/ChangelogCN.md +++ b/docs/ChangelogCN.md @@ -1,5 +1,6 @@ ## 更新日志 +* **v0.2.3** : 改善了文档;提供了乐观锁支持;添加了带时区时间字段支持;Mapper现在分成表名Mapper和字段名Mapper,同时实现了表或字段的自定义前缀后缀;Insert方法的返回值含义从id, err更改为 affected, err,请大家注意;添加了UseBool 和 Distinct函数。 * **v0.2.2** : Postgres驱动新增了对lib/pq的支持;新增了逐条遍历方法Iterate;新增了SetMaxConns(go1.2+)支持,修复了bug若干; * **v0.2.1** : 新增数据库反转工具,当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); 修复了一些bug. * **v0.2.0** : 新增 [缓存](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#120)支持,查询速度提升3-5倍; 新增数据库表和Struct同名的映射方式; 新增Sync同步表结构; diff --git a/session.go b/session.go index 6fe16e4b..0a0104a8 100644 --- a/session.go +++ b/session.go @@ -1307,6 +1307,30 @@ func rows2maps(rows *sql.Rows) (resultsSlice []map[string][]byte, err error) { return resultsSlice, nil } +func (session *Session) query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { + for _, filter := range session.Engine.Filters { + sql = filter.Do(sql, session) + } + + session.Engine.LogSQL(sql) + session.Engine.LogSQL(paramStr) + + if session.IsAutoCommit { + return query(session.Db, sql, paramStr...) + } + return txQuery(session.Tx, sql, paramStr...) +} + +func txQuery(tx *sql.Tx, sql string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { + rows, err := tx.Query(sql, params...) + if err != nil { + return nil, err + } + defer rows.Close() + + return rows2maps(rows) +} + func query(db *sql.DB, sql string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { s, err := db.Prepare(sql) if err != nil { @@ -1322,17 +1346,6 @@ func query(db *sql.DB, sql string, params ...interface{}) (resultsSlice []map[st return rows2maps(rows) } -func (session *Session) query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { - for _, filter := range session.Engine.Filters { - sql = filter.Do(sql, session) - } - - session.Engine.LogSQL(sql) - session.Engine.LogSQL(paramStr) - - return query(session.Db, sql, paramStr...) -} - // Exec a raw sql and return records as []map[string][]byte func (session *Session) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { err = session.newDb() diff --git a/statement.go b/statement.go index 1dc06dc6..90837a3b 100644 --- a/statement.go +++ b/statement.go @@ -116,6 +116,122 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { return statement } +/*func (statement *Statement) genFields(bean interface{}) map[string]interface{} { + results := make(map[string]interface{}) + table := statement.Engine.autoMap(bean) + for _, col := range table.Columns { + fieldValue := col.ValueOf(bean) + fieldType := reflect.TypeOf(fieldValue.Interface()) + var val interface{} + switch fieldType.Kind() { + case reflect.Bool: + if allUseBool { + val = fieldValue.Interface() + } else if _, ok := boolColumnMap[col.Name]; ok { + val = fieldValue.Interface() + } else { + // if a bool in a struct, it will not be as a condition because it default is false, + // please use Where() instead + continue + } + case reflect.String: + if fieldValue.String() == "" { + continue + } + // for MyString, should convert to string or panic + if fieldType.String() != reflect.String.String() { + val = fieldValue.String() + } else { + val = fieldValue.Interface() + } + case reflect.Int8, reflect.Int16, reflect.Int, reflect.Int32, reflect.Int64: + if fieldValue.Int() == 0 { + continue + } + val = fieldValue.Interface() + case reflect.Float32, reflect.Float64: + if fieldValue.Float() == 0.0 { + continue + } + val = fieldValue.Interface() + case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64: + if fieldValue.Uint() == 0 { + continue + } + val = fieldValue.Interface() + case reflect.Struct: + if fieldType == reflect.TypeOf(time.Now()) { + t := fieldValue.Interface().(time.Time) + if t.IsZero() || !fieldValue.IsValid() { + continue + } + var str string + if col.SQLType.Name == Time { + s := t.UTC().Format("2006-01-02 15:04:05") + val = s[11:19] + } else if col.SQLType.Name == Date { + str = t.Format("2006-01-02") + val = str + } else { + val = t + } + } else { + engine.autoMapType(fieldValue.Type()) + if table, ok := engine.Tables[fieldValue.Type()]; ok { + pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumn().FieldName) + if pkField.Int() != 0 { + val = pkField.Interface() + } else { + continue + } + } else { + val = fieldValue.Interface() + } + } + case reflect.Array, reflect.Slice, reflect.Map: + if fieldValue == reflect.Zero(fieldType) { + continue + } + if fieldValue.IsNil() || !fieldValue.IsValid() { + continue + } + + if col.SQLType.IsText() { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + engine.LogSQL(err) + continue + } + val = string(bytes) + } else if col.SQLType.IsBlob() { + var bytes []byte + var err error + if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) && + fieldType.Elem().Kind() == reflect.Uint8 { + if fieldValue.Len() > 0 { + val = fieldValue.Bytes() + } else { + continue + } + } else { + bytes, err = json.Marshal(fieldValue.Interface()) + if err != nil { + engine.LogSQL(err) + continue + } + val = bytes + } + } else { + continue + } + default: + val = fieldValue.Interface() + } + results[col.Name] = val + } + return results +}*/ + // Auto generating conditions according a struct func buildConditions(engine *Engine, table *Table, bean interface{}, includeVersion bool, allUseBool bool, boolColumnMap map[string]bool) ([]string, []interface{}) { colNames := make([]string, 0) diff --git a/xorm/install.sh b/xorm/install.sh deleted file mode 100755 index e8455d2a..00000000 --- a/xorm/install.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -if [ ! -f install.sh ]; then -echo 'install must be run within its container folder' 1>&2 -exit 1 -fi - -CURDIR=`pwd` -NEWPATH="$GOPATH/src/github.com/lunny/xorm/${PWD##*/}" -if [ ! -d "$NEWPATH" ]; then -ln -s $CURDIR $NEWPATH -fi - -gofmt -w $CURDIR - -cd $NEWPATH -go install ${PWD##*/} -cd $CURDIR - -echo 'finished' diff --git a/xorm/shell.go b/xorm/shell.go new file mode 100644 index 00000000..7f4cb200 --- /dev/null +++ b/xorm/shell.go @@ -0,0 +1,121 @@ +package main + +import ( + "fmt" + "github.com/lunny/xorm" + "strings" +) + +var CmdShell = &Command{ + UsageLine: "shell driverName datasourceName", + Short: "a general shell to operate all kinds of database", + Long: ` +general database's shell for sqlite3, mysql, postgres. + + driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres + datasourceName Database connection uri, for detail infomation please visit driver's project page +`, +} + +func init() { + CmdShell.Run = runShell + CmdShell.Flags = map[string]bool{} +} + +var engine *xorm.Engine + +func runShell(cmd *Command, args []string) { + if len(args) != 2 { + fmt.Println("params error, please see xorm help shell") + return + } + + var err error + engine, err = xorm.NewEngine(args[0], args[1]) + if err != nil { + fmt.Println(err) + return + } + + var scmd string + fmt.Print("xorm$ ") + for { + var input string + _, err := fmt.Scan(&input) + if err != nil { + fmt.Println(err) + continue + } + if strings.ToLower(input) == "exit" { + fmt.Println("bye") + return + } + if !strings.HasSuffix(input, ";") { + scmd = scmd + " " + input + continue + } + scmd = scmd + " " + input + lcmd := strings.TrimSpace(strings.ToLower(scmd)) + if strings.HasPrefix(lcmd, "select") { + res, err := engine.Query(scmd + "\n") + if err != nil { + fmt.Println(err) + } else { + if len(res) <= 0 { + fmt.Println("no records") + } else { + columns := make(map[string]int) + for k, _ := range res[0] { + columns[k] = len(k) + } + + for _, m := range res { + for k, s := range m { + l := len(string(s)) + if l > columns[k] { + columns[k] = l + } + } + } + + var maxlen = 0 + for _, l := range columns { + maxlen = maxlen + l + 3 + } + maxlen = maxlen + 1 + + fmt.Println(strings.Repeat("-", maxlen)) + fmt.Print("|") + slice := make([]string, 0) + for k, l := range columns { + fmt.Print(" " + k + " ") + fmt.Print(strings.Repeat(" ", l-len(k))) + fmt.Print("|") + slice = append(slice, k) + } + fmt.Print("\n") + for _, r := range res { + fmt.Print("|") + for _, k := range slice { + fmt.Print(" " + string(r[k]) + " ") + fmt.Print(strings.Repeat(" ", columns[k]-len(string(r[k])))) + fmt.Print("|") + } + fmt.Print("\n") + } + fmt.Println(strings.Repeat("-", maxlen)) + //fmt.Println(res) + } + } + } else { + cnt, err := engine.Exec(scmd) + if err != nil { + fmt.Println(err) + } else { + fmt.Printf("%d records changed.\n", cnt) + } + } + scmd = "" + fmt.Print("xorm$ ") + } +} diff --git a/xorm/xorm.go b/xorm/xorm.go index 78583a6a..e19933d5 100644 --- a/xorm/xorm.go +++ b/xorm/xorm.go @@ -22,6 +22,7 @@ const go11tag = true // The order here is the order in which they are printed by 'gopm help'. var commands = []*Command{ CmdReverse, + CmdShell, } func init() {