add support for WithVersion option to updates (#126)
This commit is contained in:
11 files changed
+323
-50
No files matched your search
@@ -49,9 +49,13 @@ type StoreTestUser struct {
|
||||
// name is the optional friendly name used to
|
||||
// access the Scope via an API
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
PhoneNumber string `protobuf:"bytes,6,opt,name=phone_number,json=phoneNumber,proto3" json:"phone_number,omitempty"`
|
||||
Email string `protobuf:"bytes,7,opt,name=email,proto3" json:"email,omitempty"`
|
||||
Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"`
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
PhoneNumber string `protobuf:"bytes,6,opt,name=phone_number,json=phoneNumber,proto3" json:"phone_number,omitempty" gorm:"default:null"`
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Email string `protobuf:"bytes,7,opt,name=email,proto3" json:"email,omitempty" gorm:"default:null"`
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
Version uint32 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"`
|
||||
}
|
||||
|
||||
func (x *StoreTestUser) Reset() {
|
||||
@@ -135,6 +139,13 @@ func (x *StoreTestUser) GetEmail() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *StoreTestUser) GetVersion() uint32 {
|
||||
if x != nil {
|
||||
return x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// TestCar for gorm test car model
|
||||
type StoreTestCar struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -356,7 +367,7 @@ var file_controller_storage_db_db_test_v1_db_test_proto_rawDesc = []byte{
|
||||
0x76, 0x31, 0x1a, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
||||
0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x02, 0x0a, 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x65, 0x73,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0xbd, 0x02, 0x0a, 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x65, 0x73,
|
||||
0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f,
|
||||
0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e,
|
||||
@@ -374,47 +385,48 @@ var file_controller_storage_db_db_test_v1_db_test_proto_rawDesc = []byte{
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
|
||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d,
|
||||
0x62, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x91, 0x02, 0x0a, 0x0c, 0x53, 0x74,
|
||||
0x6f, 0x72, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72,
|
||||
0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f,
|
||||
0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65,
|
||||
0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
|
||||
0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49,
|
||||
0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x06,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d,
|
||||
0x70, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6d, 0x70, 0x67, 0x22, 0x9c, 0x02,
|
||||
0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x6e, 0x74, 0x61,
|
||||
0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69,
|
||||
0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
|
||||
0x69, 0x6f, 0x6e, 0x22, 0x91, 0x02, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x65, 0x73,
|
||||
0x74, 0x43, 0x61, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x02, 0x69, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
|
||||
0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
|
||||
0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||
0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b,
|
||||
0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
|
||||
0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
|
||||
0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70,
|
||||
0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07,
|
||||
0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75,
|
||||
0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x63, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x61, 0x72, 0x49, 0x64, 0x42, 0x3d, 0x5a, 0x3b,
|
||||
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69,
|
||||
0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x62, 0x2f, 0x64, 0x62, 0x5f, 0x74,
|
||||
0x65, 0x73, 0x74, 0x3b, 0x64, 0x62, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b,
|
||||
0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x70, 0x67, 0x18, 0x07, 0x20, 0x01,
|
||||
0x28, 0x05, 0x52, 0x03, 0x6d, 0x70, 0x67, 0x22, 0x9c, 0x02, 0x0a, 0x0f, 0x53, 0x74, 0x6f, 0x72,
|
||||
0x65, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x4b, 0x0a, 0x0b, 0x63,
|
||||
0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74,
|
||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72,
|
||||
0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61,
|
||||
0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61,
|
||||
0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||
0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12,
|
||||
0x15, 0x0a, 0x06, 0x63, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x05, 0x63, 0x61, 0x72, 0x49, 0x64, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x77,
|
||||
0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, 0x77, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x61, 0x6c, 0x2f, 0x64, 0x62, 0x2f, 0x64, 0x62, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x3b, 0x64, 0x62,
|
||||
0x5f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/golang-sql/civil"
|
||||
_ "github.com/jackc/pgx/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDomain_PublicId(t *testing.T) {
|
||||
@@ -316,3 +317,66 @@ set update_time = null;
|
||||
nextUpdated := civil.DateTimeOf(nextUpdateTime)
|
||||
assert.True(nextUpdated.After(updated))
|
||||
}
|
||||
|
||||
func TestDomain_wt_version(t *testing.T) {
|
||||
const (
|
||||
createTable = `
|
||||
create table if not exists test_table_wt_version(
|
||||
public_id bigint generated always as identity primary key,
|
||||
name text,
|
||||
version wt_version
|
||||
);
|
||||
`
|
||||
addTrigger = `
|
||||
create trigger
|
||||
update_version_column
|
||||
after update on test_table_wt_version
|
||||
for each row execute procedure update_version_column();
|
||||
`
|
||||
insert = `
|
||||
insert into test_table_wt_version (name)
|
||||
values ($1)
|
||||
returning public_id;
|
||||
`
|
||||
update = `update test_table_wt_version set name = $1 where public_id = $2;`
|
||||
|
||||
updateVersion = `update test_table_wt_version set version = $1 where public_id = $2;`
|
||||
|
||||
search = `select version from test_table_wt_version where public_id = $1`
|
||||
)
|
||||
cleanup, conn, _ := TestSetup(t, "postgres")
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
defer func() {
|
||||
assert.NoError(conn.Close())
|
||||
assert.NoError(cleanup())
|
||||
}()
|
||||
|
||||
db := conn.DB()
|
||||
_, err := db.Exec(createTable)
|
||||
require.NoError(err)
|
||||
|
||||
_, err = db.Exec(addTrigger)
|
||||
require.NoError(err)
|
||||
|
||||
var id int
|
||||
err = db.QueryRow(insert, "alice").Scan(&id)
|
||||
require.NoError(err)
|
||||
require.NotEmpty(id)
|
||||
|
||||
var v int
|
||||
err = db.QueryRow(search, id).Scan(&v)
|
||||
require.NoError(err)
|
||||
assert.Equal(1, v)
|
||||
|
||||
_, err = db.Exec(update, "bob", id)
|
||||
require.NoError(err)
|
||||
err = db.QueryRow(search, id).Scan(&v)
|
||||
require.NoError(err)
|
||||
assert.Equal(2, v)
|
||||
|
||||
_, err = db.Exec(updateVersion, 22, id)
|
||||
require.NoError(err)
|
||||
err = db.QueryRow(search, id).Scan(&v)
|
||||
require.NoError(err)
|
||||
assert.Equal(3, v)
|
||||
}
|
||||
@@ -12,10 +12,12 @@ begin;
|
||||
|
||||
drop domain wt_timestamp;
|
||||
drop domain wt_public_id;
|
||||
drop domain wt_version;
|
||||
|
||||
drop function default_create_time;
|
||||
drop function immutable_create_time_func;
|
||||
drop function update_time_column;
|
||||
drop function update_version_column;
|
||||
|
||||
commit;
|
||||
|
||||
@@ -39,7 +41,6 @@ create domain wt_timestamp as
|
||||
comment on domain wt_timestamp is
|
||||
'Standard timestamp for all create_time and update_time columns';
|
||||
|
||||
|
||||
create or replace function
|
||||
update_time_column()
|
||||
returns trigger
|
||||
@@ -95,6 +96,39 @@ comment on function
|
||||
is
|
||||
'function used in before insert triggers to set create_time column to now';
|
||||
|
||||
|
||||
create domain wt_version as bigint
|
||||
default 1
|
||||
check(
|
||||
value > 0
|
||||
);
|
||||
comment on domain wt_version is
|
||||
'standard column for row version';
|
||||
|
||||
-- update_version_column() will increment the version column whenever row data
|
||||
-- is updated and should only be used in an update after trigger. This function
|
||||
-- will overwrite any explicit updates to the version column.
|
||||
create or replace function
|
||||
update_version_column()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
if pg_trigger_depth() = 1 then
|
||||
if row(new.*) is distinct from row(old.*) then
|
||||
execute format('update %I set version = $1 where public_id = $2', tg_relid::regclass) using old.version+1, new.public_id;
|
||||
new.version = old.version + 1;
|
||||
return new;
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
comment on function
|
||||
update_version_column()
|
||||
is
|
||||
'function used in after update triggers to properly set version columns';
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
@@ -250,7 +284,8 @@ create table if not exists db_test_user (
|
||||
public_id text not null unique,
|
||||
name text unique,
|
||||
phone_number text,
|
||||
email text
|
||||
email text,
|
||||
version wt_version
|
||||
);
|
||||
|
||||
create trigger
|
||||
@@ -271,6 +306,11 @@ before
|
||||
insert on db_test_user
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
update_version_column
|
||||
after update on db_test_user
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create table if not exists db_test_car (
|
||||
id bigint generated always as identity primary key,
|
||||
create_time wt_timestamp,
|
||||
|
||||
@@ -2,9 +2,11 @@ begin;
|
||||
|
||||
drop domain wt_timestamp;
|
||||
drop domain wt_public_id;
|
||||
drop domain wt_version;
|
||||
|
||||
drop function default_create_time;
|
||||
drop function immutable_create_time_func;
|
||||
drop function update_time_column;
|
||||
drop function update_version_column;
|
||||
|
||||
commit;
|
||||
@@ -13,7 +13,6 @@ create domain wt_timestamp as
|
||||
comment on domain wt_timestamp is
|
||||
'Standard timestamp for all create_time and update_time columns';
|
||||
|
||||
|
||||
create or replace function
|
||||
update_time_column()
|
||||
returns trigger
|
||||
@@ -69,4 +68,37 @@ comment on function
|
||||
is
|
||||
'function used in before insert triggers to set create_time column to now';
|
||||
|
||||
|
||||
create domain wt_version as bigint
|
||||
default 1
|
||||
check(
|
||||
value > 0
|
||||
);
|
||||
comment on domain wt_version is
|
||||
'standard column for row version';
|
||||
|
||||
-- update_version_column() will increment the version column whenever row data
|
||||
-- is updated and should only be used in an update after trigger. This function
|
||||
-- will overwrite any explicit updates to the version column.
|
||||
create or replace function
|
||||
update_version_column()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
if pg_trigger_depth() = 1 then
|
||||
if row(new.*) is distinct from row(old.*) then
|
||||
execute format('update %I set version = $1 where public_id = $2', tg_relid::regclass) using old.version+1, new.public_id;
|
||||
new.version = old.version + 1;
|
||||
return new;
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
comment on function
|
||||
update_version_column()
|
||||
is
|
||||
'function used in after update triggers to properly set version columns';
|
||||
|
||||
commit;
|
||||
@@ -10,7 +10,8 @@ create table if not exists db_test_user (
|
||||
public_id text not null unique,
|
||||
name text unique,
|
||||
phone_number text,
|
||||
email text
|
||||
email text,
|
||||
version wt_version
|
||||
);
|
||||
|
||||
create trigger
|
||||
@@ -31,6 +32,11 @@ before
|
||||
insert on db_test_user
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
update_version_column
|
||||
after update on db_test_user
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create table if not exists db_test_car (
|
||||
id bigint generated always as identity primary key,
|
||||
create_time wt_timestamp,
|
||||
|
||||
@@ -28,6 +28,8 @@ type Options struct {
|
||||
WithFieldMaskPaths []string
|
||||
// WithNullPaths must be accessible from other packages.
|
||||
WithNullPaths []string
|
||||
// WithVersion must be accessible from other packages
|
||||
WithVersion int
|
||||
}
|
||||
|
||||
type oplogOpts struct {
|
||||
@@ -46,6 +48,7 @@ func getDefaultOptions() Options {
|
||||
WithFieldMaskPaths: []string{},
|
||||
WithNullPaths: []string{},
|
||||
WithLimit: 0,
|
||||
WithVersion: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,3 +92,10 @@ func WithLimit(limit int) Option {
|
||||
o.WithLimit = limit
|
||||
}
|
||||
}
|
||||
|
||||
// WithVersion provides an option version number for update operations.
|
||||
func WithVersion(version int) Option {
|
||||
return func(o *Options) {
|
||||
o.WithVersion = version
|
||||
}
|
||||
}
|
||||
@@ -94,4 +94,16 @@ func Test_getOpts(t *testing.T) {
|
||||
testOpts.WithLimit = 1
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
t.Run("WithVersion", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// test default of 0
|
||||
opts := GetOpts()
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.WithVersion = 0
|
||||
assert.Equal(opts, testOpts)
|
||||
opts = GetOpts(WithVersion(2))
|
||||
testOpts = getDefaultOptions()
|
||||
testOpts.WithVersion = 2
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
}
|
||||
@@ -227,7 +227,11 @@ func (rw *Db) Create(ctx context.Context, i interface{}, opt ...Option) error {
|
||||
// is responsible for the transaction life cycle of the writer and if an
|
||||
// error is returned the caller must decide what to do with the transaction,
|
||||
// which almost always should be to rollback. Update returns the number of
|
||||
// rows updated. Supported options: WithOplog.
|
||||
// rows updated. Supported options: WithOplog and WithVersion. If WithVersion
|
||||
// is used, then the update will include the version number in the update where
|
||||
// clause, which basically makes the update use optimistic locking and the
|
||||
// update will only succeed if the existing rows version matches the WithVersion
|
||||
// option.
|
||||
func (rw *Db) Update(ctx context.Context, i interface{}, fieldMaskPaths []string, setToNullPaths []string, opt ...Option) (int, error) {
|
||||
if rw.underlying == nil {
|
||||
return NoRowsAffected, fmt.Errorf("update: missing underlying db %w", ErrNilParameter)
|
||||
@@ -289,7 +293,16 @@ func (rw *Db) Update(ctx context.Context, i interface{}, fieldMaskPaths []string
|
||||
return NoRowsAffected, fmt.Errorf("update: unable to get ticket: %w", err)
|
||||
}
|
||||
}
|
||||
underlying := rw.underlying.Model(i).Updates(updateFields)
|
||||
var underlying *gorm.DB
|
||||
switch {
|
||||
case opts.WithVersion > 0:
|
||||
if _, ok := scope.FieldByName("version"); !ok {
|
||||
return NoRowsAffected, fmt.Errorf("update: %s does not have a version field", scope.TableName())
|
||||
}
|
||||
underlying = rw.underlying.Model(i).Where("version = ?", opts.WithVersion).Updates(updateFields)
|
||||
default:
|
||||
underlying = rw.underlying.Model(i).Updates(updateFields)
|
||||
}
|
||||
if underlying.Error != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return NoRowsAffected, fmt.Errorf("update: failed %w", ErrRecordNotFound)
|
||||
|
||||
@@ -53,6 +53,7 @@ func TestDb_Update(t *testing.T) {
|
||||
wantName string
|
||||
wantEmail string
|
||||
wantPhoneNumber string
|
||||
wantVersion int
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
@@ -74,6 +75,46 @@ func TestDb_Update(t *testing.T) {
|
||||
wantEmail: "",
|
||||
wantPhoneNumber: "updated" + id,
|
||||
},
|
||||
{
|
||||
name: "simple-with-bad-version",
|
||||
args: args{
|
||||
i: &db_test.TestUser{
|
||||
StoreTestUser: &db_test.StoreTestUser{
|
||||
Name: "simple-with-bad-version" + id,
|
||||
Email: "updated" + id,
|
||||
PhoneNumber: "updated" + id,
|
||||
},
|
||||
},
|
||||
fieldMaskPaths: []string{"Name", "PhoneNumber"},
|
||||
setToNullPaths: []string{"Email"},
|
||||
opt: []Option{WithVersion(22)},
|
||||
},
|
||||
want: 0,
|
||||
wantErr: false,
|
||||
wantErrMsg: "",
|
||||
},
|
||||
{
|
||||
name: "simple-with-version",
|
||||
args: args{
|
||||
i: &db_test.TestUser{
|
||||
StoreTestUser: &db_test.StoreTestUser{
|
||||
Name: "simple-with-version" + id,
|
||||
Email: "updated" + id,
|
||||
PhoneNumber: "updated" + id,
|
||||
},
|
||||
},
|
||||
fieldMaskPaths: []string{"Name", "PhoneNumber"},
|
||||
setToNullPaths: []string{"Email"},
|
||||
opt: []Option{WithVersion(1)},
|
||||
},
|
||||
want: 1,
|
||||
wantErr: false,
|
||||
wantErrMsg: "",
|
||||
wantName: "simple-with-version" + id,
|
||||
wantEmail: "",
|
||||
wantPhoneNumber: "updated" + id,
|
||||
wantVersion: 2,
|
||||
},
|
||||
{
|
||||
name: "multiple-null",
|
||||
args: args{
|
||||
@@ -204,7 +245,9 @@ func TestDb_Update(t *testing.T) {
|
||||
}
|
||||
assert.NoError(err)
|
||||
assert.Equal(tt.want, rowsUpdated)
|
||||
|
||||
if tt.want == 0 {
|
||||
return
|
||||
}
|
||||
foundUser, err := db_test.NewTestUser()
|
||||
assert.NoError(err)
|
||||
foundUser.PublicId = tt.args.i.PublicId
|
||||
@@ -225,8 +268,21 @@ func TestDb_Update(t *testing.T) {
|
||||
assert.NotEqual(now, foundUser.CreateTime)
|
||||
assert.NotEqual(now, foundUser.UpdateTime)
|
||||
assert.NotEqual(publicId, foundUser.PublicId)
|
||||
assert.Equal(u.Version+1, foundUser.Version)
|
||||
})
|
||||
}
|
||||
t.Run("no-version-field", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
w := Db{underlying: db}
|
||||
id, err := uuid.GenerateUUID()
|
||||
assert.NoError(err)
|
||||
car := testCar(t, db, "foo-"+id, id, int32(100))
|
||||
|
||||
car.Name = "friendly-" + id
|
||||
rowsUpdated, err := w.Update(context.Background(), car, []string{"Name"}, nil, WithVersion(1))
|
||||
assert.Error(err)
|
||||
assert.Equal(0, rowsUpdated)
|
||||
})
|
||||
t.Run("valid-WithOplog", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
w := Db{underlying: db}
|
||||
@@ -1201,6 +1257,26 @@ func testUser(t *testing.T, conn *gorm.DB, name, email, phoneNumber string) *db_
|
||||
}
|
||||
return u
|
||||
}
|
||||
func testCar(t *testing.T, conn *gorm.DB, name, model string, mpg int32) *db_test.TestCar {
|
||||
t.Helper()
|
||||
require := require.New(t)
|
||||
|
||||
publicId, err := base62.Random(20)
|
||||
require.NoError(err)
|
||||
c := &db_test.TestCar{
|
||||
StoreTestCar: &db_test.StoreTestCar{
|
||||
PublicId: publicId,
|
||||
Name: name,
|
||||
Model: model,
|
||||
Mpg: mpg,
|
||||
},
|
||||
}
|
||||
if conn != nil {
|
||||
err = conn.Create(c).Error
|
||||
require.NoError(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func testId(t *testing.T) string {
|
||||
t.Helper()
|
||||
|
||||
@@ -29,8 +29,14 @@ message StoreTestUser {
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string name = 5;
|
||||
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string phone_number = 6;
|
||||
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
string email = 7;
|
||||
|
||||
// @inject_tag: `gorm:"default:null"`
|
||||
uint32 version = 8;
|
||||
}
|
||||
// TestCar for gorm test car model
|
||||
message StoreTestCar {
|
||||
|
||||
Reference in new issue
Block a user