Merge branch 'master' into master
This commit is contained in:
commit
7ce3f70a81
@ -31,7 +31,7 @@ func beforeCreateCallback(scope *Scope) {
|
|||||||
// updateTimeStampForCreateCallback will set `CreatedAt`, `UpdatedAt` when creating
|
// updateTimeStampForCreateCallback will set `CreatedAt`, `UpdatedAt` when creating
|
||||||
func updateTimeStampForCreateCallback(scope *Scope) {
|
func updateTimeStampForCreateCallback(scope *Scope) {
|
||||||
if !scope.HasError() {
|
if !scope.HasError() {
|
||||||
now := NowFunc()
|
now := scope.db.nowFunc()
|
||||||
|
|
||||||
if createdAtField, ok := scope.FieldByName("CreatedAt"); ok {
|
if createdAtField, ok := scope.FieldByName("CreatedAt"); ok {
|
||||||
if createdAtField.IsBlank {
|
if createdAtField.IsBlank {
|
||||||
@ -50,7 +50,7 @@ func updateTimeStampForCreateCallback(scope *Scope) {
|
|||||||
// createCallback the callback used to insert data into database
|
// createCallback the callback used to insert data into database
|
||||||
func createCallback(scope *Scope) {
|
func createCallback(scope *Scope) {
|
||||||
if !scope.HasError() {
|
if !scope.HasError() {
|
||||||
defer scope.trace(NowFunc())
|
defer scope.trace(scope.db.nowFunc())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
columns, placeholders []string
|
columns, placeholders []string
|
||||||
|
@ -40,7 +40,7 @@ func deleteCallback(scope *Scope) {
|
|||||||
"UPDATE %v SET %v=%v%v%v",
|
"UPDATE %v SET %v=%v%v%v",
|
||||||
scope.QuotedTableName(),
|
scope.QuotedTableName(),
|
||||||
scope.Quote(deletedAtField.DBName),
|
scope.Quote(deletedAtField.DBName),
|
||||||
scope.AddToVars(NowFunc()),
|
scope.AddToVars(scope.db.nowFunc()),
|
||||||
addExtraSpaceIfExist(scope.CombinedConditionSql()),
|
addExtraSpaceIfExist(scope.CombinedConditionSql()),
|
||||||
addExtraSpaceIfExist(extraOption),
|
addExtraSpaceIfExist(extraOption),
|
||||||
)).Exec()
|
)).Exec()
|
||||||
|
@ -24,7 +24,7 @@ func queryCallback(scope *Scope) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer scope.trace(NowFunc())
|
defer scope.trace(scope.db.nowFunc())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
isSlice, isPtr bool
|
isSlice, isPtr bool
|
||||||
|
@ -50,7 +50,7 @@ func beforeUpdateCallback(scope *Scope) {
|
|||||||
// updateTimeStampForUpdateCallback will set `UpdatedAt` when updating
|
// updateTimeStampForUpdateCallback will set `UpdatedAt` when updating
|
||||||
func updateTimeStampForUpdateCallback(scope *Scope) {
|
func updateTimeStampForUpdateCallback(scope *Scope) {
|
||||||
if _, ok := scope.Get("gorm:update_column"); !ok {
|
if _, ok := scope.Get("gorm:update_column"); !ok {
|
||||||
scope.SetColumn("UpdatedAt", NowFunc())
|
scope.SetColumn("UpdatedAt", scope.db.nowFunc())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,46 @@ func TestCreateWithExistingTimestamp(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateWithNowFuncOverride(t *testing.T) {
|
||||||
|
user1 := User{Name: "CreateUserTimestampOverride"}
|
||||||
|
|
||||||
|
timeA := now.MustParse("2016-01-01")
|
||||||
|
|
||||||
|
// do DB.New() because we don't want this test to affect other tests
|
||||||
|
db1 := DB.New()
|
||||||
|
// set the override to use static timeA
|
||||||
|
db1.SetNowFuncOverride(func() time.Time {
|
||||||
|
return timeA
|
||||||
|
})
|
||||||
|
// call .New again to check the override is carried over as well during clone
|
||||||
|
db1 = db1.New()
|
||||||
|
|
||||||
|
db1.Save(&user1)
|
||||||
|
|
||||||
|
if user1.CreatedAt.UTC().Format(time.RFC3339) != timeA.UTC().Format(time.RFC3339) {
|
||||||
|
t.Errorf("CreatedAt be using the nowFuncOverride")
|
||||||
|
}
|
||||||
|
if user1.UpdatedAt.UTC().Format(time.RFC3339) != timeA.UTC().Format(time.RFC3339) {
|
||||||
|
t.Errorf("UpdatedAt be using the nowFuncOverride")
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create another user with a fresh DB.Now() that doesn't have the nowFuncOverride set
|
||||||
|
// to make sure that setting it only affected the above instance
|
||||||
|
|
||||||
|
user2 := User{Name: "CreateUserTimestampOverrideNoMore"}
|
||||||
|
|
||||||
|
db2 := DB.New()
|
||||||
|
|
||||||
|
db2.Save(&user2)
|
||||||
|
|
||||||
|
if user2.CreatedAt.UTC().Format(time.RFC3339) == timeA.UTC().Format(time.RFC3339) {
|
||||||
|
t.Errorf("CreatedAt no longer be using the nowFuncOverride")
|
||||||
|
}
|
||||||
|
if user2.UpdatedAt.UTC().Format(time.RFC3339) == timeA.UTC().Format(time.RFC3339) {
|
||||||
|
t.Errorf("UpdatedAt no longer be using the nowFuncOverride")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type AutoIncrementUser struct {
|
type AutoIncrementUser struct {
|
||||||
User
|
User
|
||||||
Sequence uint `gorm:"AUTO_INCREMENT"`
|
Sequence uint `gorm:"AUTO_INCREMENT"`
|
||||||
|
10
go.mod
10
go.mod
@ -1,11 +1,13 @@
|
|||||||
module github.com/jinzhu/gorm
|
module github.com/jinzhu/gorm
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02
|
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3
|
||||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5
|
||||||
github.com/go-sql-driver/mysql v1.4.1
|
github.com/go-sql-driver/mysql v1.4.1
|
||||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a
|
github.com/jinzhu/inflection v1.0.0
|
||||||
github.com/jinzhu/now v1.0.0
|
github.com/jinzhu/now v1.0.1
|
||||||
github.com/lib/pq v1.1.0
|
github.com/lib/pq v1.1.1
|
||||||
github.com/mattn/go-sqlite3 v1.10.0
|
github.com/mattn/go-sqlite3 v1.10.0
|
||||||
)
|
)
|
||||||
|
23
go.sum
23
go.sum
@ -11,8 +11,8 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
|
|||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk=
|
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
@ -32,6 +32,7 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
|||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/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/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
@ -40,17 +41,17 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
|
|||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns=
|
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||||
github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
|
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
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/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/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/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/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
|
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
@ -58,7 +59,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
|
|||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/openzipkin/zipkin-go v0.1.6 h1:yXiysv1CSK7Q5yjGy1710zZGnsbMUIjluWBxtLXHPBo=
|
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@ -112,16 +112,13 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
google.golang.org/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.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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
|
||||||
google.golang.org/appengine v1.5.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-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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
|
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package gorm
|
package gorm
|
||||||
|
|
||||||
import "database/sql"
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
// SQLCommon is the minimal database connection functionality gorm requires. Implemented by *sql.DB.
|
// SQLCommon is the minimal database connection functionality gorm requires. Implemented by *sql.DB.
|
||||||
type SQLCommon interface {
|
type SQLCommon interface {
|
||||||
@ -12,6 +15,7 @@ type SQLCommon interface {
|
|||||||
|
|
||||||
type sqlDb interface {
|
type sqlDb interface {
|
||||||
Begin() (*sql.Tx, error)
|
Begin() (*sql.Tx, error)
|
||||||
|
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type sqlTx interface {
|
type sqlTx interface {
|
||||||
|
49
main.go
49
main.go
@ -1,6 +1,7 @@
|
|||||||
package gorm
|
package gorm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -30,6 +31,9 @@ type DB struct {
|
|||||||
callbacks *Callback
|
callbacks *Callback
|
||||||
dialect Dialect
|
dialect Dialect
|
||||||
singularTable bool
|
singularTable bool
|
||||||
|
|
||||||
|
// function to be used to override the creating of a new timestamp
|
||||||
|
nowFuncOverride func() time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type logModeValue int
|
type logModeValue int
|
||||||
@ -157,6 +161,22 @@ func (s *DB) LogMode(enable bool) *DB {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNowFuncOverride set the function to be used when creating a new timestamp
|
||||||
|
func (s *DB) SetNowFuncOverride(nowFuncOverride func() time.Time) *DB {
|
||||||
|
s.nowFuncOverride = nowFuncOverride
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a new timestamp, using the provided nowFuncOverride on the DB instance if set,
|
||||||
|
// otherwise defaults to the global NowFunc()
|
||||||
|
func (s *DB) nowFunc() time.Time {
|
||||||
|
if s.nowFuncOverride != nil {
|
||||||
|
return s.nowFuncOverride()
|
||||||
|
}
|
||||||
|
|
||||||
|
return NowFunc()
|
||||||
|
}
|
||||||
|
|
||||||
// BlockGlobalUpdate if true, generates an error on update/delete without where clause.
|
// BlockGlobalUpdate if true, generates an error on update/delete without where clause.
|
||||||
// This is to prevent eventual error with empty objects updates/deletions
|
// This is to prevent eventual error with empty objects updates/deletions
|
||||||
func (s *DB) BlockGlobalUpdate(enable bool) *DB {
|
func (s *DB) BlockGlobalUpdate(enable bool) *DB {
|
||||||
@ -446,7 +466,7 @@ func (s *DB) Save(value interface{}) *DB {
|
|||||||
if !scope.PrimaryKeyZero() {
|
if !scope.PrimaryKeyZero() {
|
||||||
newDB := scope.callCallbacks(s.parent.callbacks.updates).db
|
newDB := scope.callCallbacks(s.parent.callbacks.updates).db
|
||||||
if newDB.Error == nil && newDB.RowsAffected == 0 {
|
if newDB.Error == nil && newDB.RowsAffected == 0 {
|
||||||
return s.New().FirstOrCreate(value)
|
return s.New().Table(scope.TableName()).FirstOrCreate(value)
|
||||||
}
|
}
|
||||||
return newDB
|
return newDB
|
||||||
}
|
}
|
||||||
@ -503,11 +523,16 @@ func (s *DB) Debug() *DB {
|
|||||||
return s.clone().LogMode(true)
|
return s.clone().LogMode(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin begin a transaction
|
// Begin begins a transaction
|
||||||
func (s *DB) Begin() *DB {
|
func (s *DB) Begin() *DB {
|
||||||
|
return s.BeginTx(context.Background(), &sql.TxOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginTX begins a transaction with options
|
||||||
|
func (s *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) *DB {
|
||||||
c := s.clone()
|
c := s.clone()
|
||||||
if db, ok := c.db.(sqlDb); ok && db != nil {
|
if db, ok := c.db.(sqlDb); ok && db != nil {
|
||||||
tx, err := db.Begin()
|
tx, err := db.BeginTx(ctx, opts)
|
||||||
c.db = interface{}(tx).(SQLCommon)
|
c.db = interface{}(tx).(SQLCommon)
|
||||||
|
|
||||||
c.dialect.SetDB(c.db)
|
c.dialect.SetDB(c.db)
|
||||||
@ -542,6 +567,23 @@ func (s *DB) Rollback() *DB {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RollbackUnlessCommitted rollback a transaction if it has not yet been
|
||||||
|
// committed.
|
||||||
|
func (s *DB) RollbackUnlessCommitted() *DB {
|
||||||
|
var emptySQLTx *sql.Tx
|
||||||
|
if db, ok := s.db.(sqlTx); ok && db != nil && db != emptySQLTx {
|
||||||
|
err := db.Rollback()
|
||||||
|
// Ignore the error indicating that the transaction has already
|
||||||
|
// been committed.
|
||||||
|
if err != sql.ErrTxDone {
|
||||||
|
s.AddError(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.AddError(ErrInvalidTransaction)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// NewRecord check if value's primary key is blank
|
// NewRecord check if value's primary key is blank
|
||||||
func (s *DB) NewRecord(value interface{}) bool {
|
func (s *DB) NewRecord(value interface{}) bool {
|
||||||
return s.NewScope(value).PrimaryKeyZero()
|
return s.NewScope(value).PrimaryKeyZero()
|
||||||
@ -777,6 +819,7 @@ func (s *DB) clone() *DB {
|
|||||||
Error: s.Error,
|
Error: s.Error,
|
||||||
blockGlobalUpdate: s.blockGlobalUpdate,
|
blockGlobalUpdate: s.blockGlobalUpdate,
|
||||||
dialect: newDialect(s.dialect.GetName(), s.db),
|
dialect: newDialect(s.dialect.GetName(), s.db),
|
||||||
|
nowFuncOverride: s.nowFuncOverride,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.values.Range(func(k, v interface{}) bool {
|
s.values.Range(func(k, v interface{}) bool {
|
||||||
|
89
main_test.go
89
main_test.go
@ -1,6 +1,11 @@
|
|||||||
package gorm_test
|
package gorm_test
|
||||||
|
|
||||||
|
// Run tests
|
||||||
|
// $ docker-compose up
|
||||||
|
// $ ./test_all.sh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -177,6 +182,15 @@ func TestSetTable(t *testing.T) {
|
|||||||
t.Errorf("Query from specified table")
|
t.Errorf("Query from specified table")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var user User
|
||||||
|
DB.Table("deleted_users").First(&user, "name = ?", "DeletedUser")
|
||||||
|
|
||||||
|
user.Age = 20
|
||||||
|
DB.Table("deleted_users").Save(&user)
|
||||||
|
if DB.Table("deleted_users").First(&user, "name = ? AND age = ?", "DeletedUser", 20).RecordNotFound() {
|
||||||
|
t.Errorf("Failed to found updated user")
|
||||||
|
}
|
||||||
|
|
||||||
DB.Save(getPreparedUser("normal_user", "reset_table"))
|
DB.Save(getPreparedUser("normal_user", "reset_table"))
|
||||||
DB.Table("deleted_users").Save(getPreparedUser("deleted_user", "reset_table"))
|
DB.Table("deleted_users").Save(getPreparedUser("deleted_user", "reset_table"))
|
||||||
var user1, user2, user3 User
|
var user1, user2, user3 User
|
||||||
@ -419,6 +433,40 @@ func TestTransaction(t *testing.T) {
|
|||||||
if err := DB.First(&User{}, "name = ?", "transcation-2").Error; err != nil {
|
if err := DB.First(&User{}, "name = ?", "transcation-2").Error; err != nil {
|
||||||
t.Errorf("Should be able to find committed record")
|
t.Errorf("Should be able to find committed record")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tx3 := DB.Begin()
|
||||||
|
u3 := User{Name: "transcation-3"}
|
||||||
|
if err := tx3.Save(&u3).Error; err != nil {
|
||||||
|
t.Errorf("No error should raise")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx3.First(&User{}, "name = ?", "transcation-3").Error; err != nil {
|
||||||
|
t.Errorf("Should find saved record")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx3.RollbackUnlessCommitted()
|
||||||
|
|
||||||
|
if err := tx.First(&User{}, "name = ?", "transcation").Error; err == nil {
|
||||||
|
t.Errorf("Should not find record after rollback")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx4 := DB.Begin()
|
||||||
|
u4 := User{Name: "transcation-4"}
|
||||||
|
if err := tx4.Save(&u4).Error; err != nil {
|
||||||
|
t.Errorf("No error should raise")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx4.First(&User{}, "name = ?", "transcation-4").Error; err != nil {
|
||||||
|
t.Errorf("Should find saved record")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx4.Commit()
|
||||||
|
|
||||||
|
tx4.RollbackUnlessCommitted()
|
||||||
|
|
||||||
|
if err := DB.First(&User{}, "name = ?", "transcation-4").Error; err != nil {
|
||||||
|
t.Errorf("Should be able to find committed record")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTransaction_NoErrorOnRollbackAfterCommit(t *testing.T) {
|
func TestTransaction_NoErrorOnRollbackAfterCommit(t *testing.T) {
|
||||||
@ -437,6 +485,40 @@ func TestTransaction_NoErrorOnRollbackAfterCommit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransactionReadonly(t *testing.T) {
|
||||||
|
dialect := os.Getenv("GORM_DIALECT")
|
||||||
|
if dialect == "" {
|
||||||
|
dialect = "sqlite"
|
||||||
|
}
|
||||||
|
switch dialect {
|
||||||
|
case "mssql", "sqlite":
|
||||||
|
t.Skipf("%s does not support readonly transactions\n", dialect)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := DB.Begin()
|
||||||
|
u := User{Name: "transcation"}
|
||||||
|
if err := tx.Save(&u).Error; err != nil {
|
||||||
|
t.Errorf("No error should raise")
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
|
tx = DB.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true})
|
||||||
|
if err := tx.First(&User{}, "name = ?", "transcation").Error; err != nil {
|
||||||
|
t.Errorf("Should find saved record")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sqlTx, ok := tx.CommonDB().(*sql.Tx); !ok || sqlTx == nil {
|
||||||
|
t.Errorf("Should return the underlying sql.Tx")
|
||||||
|
}
|
||||||
|
|
||||||
|
u = User{Name: "transcation-2"}
|
||||||
|
if err := tx.Save(&u).Error; err == nil {
|
||||||
|
t.Errorf("Error should have been raised in a readonly transaction")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Rollback()
|
||||||
|
}
|
||||||
|
|
||||||
func TestRow(t *testing.T) {
|
func TestRow(t *testing.T) {
|
||||||
user1 := User{Name: "RowUser1", Age: 1, Birthday: parseTime("2000-1-1")}
|
user1 := User{Name: "RowUser1", Age: 1, Birthday: parseTime("2000-1-1")}
|
||||||
user2 := User{Name: "RowUser2", Age: 10, Birthday: parseTime("2010-1-1")}
|
user2 := User{Name: "RowUser2", Age: 10, Birthday: parseTime("2010-1-1")}
|
||||||
@ -1211,12 +1293,11 @@ func TestWhereUpdates(t *testing.T) {
|
|||||||
OwnerEntity OwnerEntity `gorm:"polymorphic:Owner"`
|
OwnerEntity OwnerEntity `gorm:"polymorphic:Owner"`
|
||||||
}
|
}
|
||||||
|
|
||||||
db := DB.Debug()
|
DB.DropTable(&SomeEntity{})
|
||||||
db.DropTable(&SomeEntity{})
|
DB.AutoMigrate(&SomeEntity{})
|
||||||
db.AutoMigrate(&SomeEntity{})
|
|
||||||
|
|
||||||
a := SomeEntity{Name: "test"}
|
a := SomeEntity{Name: "test"}
|
||||||
db.Model(&a).Where(a).Updates(SomeEntity{Name: "test2"})
|
DB.Model(&a).Where(a).Updates(SomeEntity{Name: "test2"})
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkGorm(b *testing.B) {
|
func BenchmarkGorm(b *testing.B) {
|
||||||
|
@ -202,7 +202,7 @@ func (scope *Scope) GetModelStruct() *ModelStruct {
|
|||||||
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
|
modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := field.TagSettingsGet("DEFAULT"); ok {
|
if _, ok := field.TagSettingsGet("DEFAULT"); ok && !field.IsPrimaryKey {
|
||||||
field.HasDefaultValue = true
|
field.HasDefaultValue = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
scope.go
6
scope.go
@ -358,7 +358,7 @@ func (scope *Scope) Raw(sql string) *Scope {
|
|||||||
|
|
||||||
// Exec perform generated SQL
|
// Exec perform generated SQL
|
||||||
func (scope *Scope) Exec() *Scope {
|
func (scope *Scope) Exec() *Scope {
|
||||||
defer scope.trace(NowFunc())
|
defer scope.trace(scope.db.nowFunc())
|
||||||
|
|
||||||
if !scope.HasError() {
|
if !scope.HasError() {
|
||||||
if result, err := scope.SQLDB().Exec(scope.SQL, scope.SQLVars...); scope.Err(err) == nil {
|
if result, err := scope.SQLDB().Exec(scope.SQL, scope.SQLVars...); scope.Err(err) == nil {
|
||||||
@ -935,7 +935,7 @@ func (scope *Scope) updatedAttrsWithValues(value interface{}) (results map[strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (scope *Scope) row() *sql.Row {
|
func (scope *Scope) row() *sql.Row {
|
||||||
defer scope.trace(NowFunc())
|
defer scope.trace(scope.db.nowFunc())
|
||||||
|
|
||||||
result := &RowQueryResult{}
|
result := &RowQueryResult{}
|
||||||
scope.InstanceSet("row_query_result", result)
|
scope.InstanceSet("row_query_result", result)
|
||||||
@ -945,7 +945,7 @@ func (scope *Scope) row() *sql.Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (scope *Scope) rows() (*sql.Rows, error) {
|
func (scope *Scope) rows() (*sql.Rows, error) {
|
||||||
defer scope.trace(NowFunc())
|
defer scope.trace(scope.db.nowFunc())
|
||||||
|
|
||||||
result := &RowsQueryResult{}
|
result := &RowsQueryResult{}
|
||||||
scope.InstanceSet("row_query_result", result)
|
scope.InstanceSet("row_query_result", result)
|
||||||
|
24
wercker.yml
24
wercker.yml
@ -9,6 +9,13 @@ services:
|
|||||||
MYSQL_USER: gorm
|
MYSQL_USER: gorm
|
||||||
MYSQL_PASSWORD: gorm
|
MYSQL_PASSWORD: gorm
|
||||||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||||||
|
- name: mysql
|
||||||
|
id: mysql:latest
|
||||||
|
env:
|
||||||
|
MYSQL_DATABASE: gorm
|
||||||
|
MYSQL_USER: gorm
|
||||||
|
MYSQL_PASSWORD: gorm
|
||||||
|
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||||||
- name: mysql57
|
- name: mysql57
|
||||||
id: mysql:5.7
|
id: mysql:5.7
|
||||||
env:
|
env:
|
||||||
@ -23,13 +30,6 @@ services:
|
|||||||
MYSQL_USER: gorm
|
MYSQL_USER: gorm
|
||||||
MYSQL_PASSWORD: gorm
|
MYSQL_PASSWORD: gorm
|
||||||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
||||||
- name: mysql55
|
|
||||||
id: mysql:5.5
|
|
||||||
env:
|
|
||||||
MYSQL_DATABASE: gorm
|
|
||||||
MYSQL_USER: gorm
|
|
||||||
MYSQL_PASSWORD: gorm
|
|
||||||
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
|
|
||||||
- name: postgres
|
- name: postgres
|
||||||
id: postgres:latest
|
id: postgres:latest
|
||||||
env:
|
env:
|
||||||
@ -102,6 +102,11 @@ build:
|
|||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mariadb:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mariadb:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
||||||
|
|
||||||
|
- script:
|
||||||
|
name: test mysql
|
||||||
|
code: |
|
||||||
|
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test mysql5.7
|
name: test mysql5.7
|
||||||
code: |
|
code: |
|
||||||
@ -112,11 +117,6 @@ build:
|
|||||||
code: |
|
code: |
|
||||||
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql56:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql56:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
||||||
|
|
||||||
- script:
|
|
||||||
name: test mysql5.5
|
|
||||||
code: |
|
|
||||||
GORM_DIALECT=mysql GORM_DSN="gorm:gorm@tcp(mysql55:3306)/gorm?charset=utf8&parseTime=True" go test -race ./...
|
|
||||||
|
|
||||||
- script:
|
- script:
|
||||||
name: test postgres
|
name: test postgres
|
||||||
code: |
|
code: |
|
||||||
|
Loading…
x
Reference in New Issue
Block a user