Skip to content

Commit 68a5616

Browse files
committed
feature: Add datatype for uuid.UUID (Google UUID)
This PR adds datatype for uuid.UUID along with supporting tests.
1 parent 6a271a4 commit 68a5616

File tree

4 files changed

+262
-0
lines changed

4 files changed

+262
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module gorm.io/datatypes
33
go 1.19
44

55
require (
6+
github.com/google/uuid v1.3.0
67
github.com/jinzhu/now v1.1.5
78
gorm.io/driver/mysql v1.5.6
89
gorm.io/driver/postgres v1.5.0

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2V
2222
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
2323
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
2424
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
25+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
2526
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
2627
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
2728
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=

uuid.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package datatypes
2+
3+
import (
4+
"database/sql/driver"
5+
6+
"github.com/google/uuid"
7+
"gorm.io/gorm"
8+
"gorm.io/gorm/schema"
9+
)
10+
11+
type UUID uuid.UUID
12+
13+
// GormDataType gorm common data type
14+
func (UUID) GormDataType() string {
15+
return "string"
16+
}
17+
18+
// GormDBDataType gorm db data type
19+
func (UUID) GormDBDataType(db *gorm.DB, field *schema.Field) string {
20+
switch db.Dialector.Name() {
21+
case "mysql":
22+
return "LONGTEXT"
23+
case "postgres":
24+
return "UUID"
25+
case "sqlserver":
26+
return "NVARCHAR"
27+
case "sqlite":
28+
return "TEXT"
29+
default:
30+
return ""
31+
}
32+
}
33+
34+
func (u *UUID) Scan(value interface{}) error {
35+
var result uuid.UUID
36+
if err := result.Scan(value); err != nil {
37+
return err
38+
}
39+
*u = UUID(result)
40+
return nil
41+
}
42+
43+
func (u UUID) Value() (driver.Value, error) {
44+
return uuid.UUID(u).Value()
45+
}
46+
47+
func (u UUID) String() string {
48+
return uuid.UUID(u).String()
49+
}
50+
51+
func (u UUID) Equals(other UUID) bool {
52+
return u.String() == other.String()
53+
}
54+
55+
func (u UUID) Length() int {
56+
return len(u.String())
57+
}
58+
59+
func (u UUID) IsNil() bool {
60+
return uuid.UUID(u) == uuid.Nil
61+
}
62+
63+
func (u UUID) IsEmpty() bool {
64+
return u.IsNil() || u.Length() == 0
65+
}
66+
67+
func (u *UUID) IsNilPtr() bool {
68+
return u == nil
69+
}
70+
71+
func (u *UUID) IsEmptyPtr() bool {
72+
return u.IsNilPtr() || u.IsEmpty()
73+
}

uuid_test.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package datatypes_test
2+
3+
import (
4+
"database/sql/driver"
5+
"testing"
6+
7+
"github.com/google/uuid"
8+
"gorm.io/datatypes"
9+
"gorm.io/gorm"
10+
. "gorm.io/gorm/utils/tests"
11+
)
12+
13+
var _ driver.Valuer = &datatypes.UUID{}
14+
15+
func TestUUID(t *testing.T) {
16+
if SupportedDriver("sqlite", "mysql", "postgres", "sqlserver") {
17+
type UserWithUUID struct {
18+
gorm.Model
19+
Name string
20+
UserUUID datatypes.UUID
21+
}
22+
23+
DB.Migrator().DropTable(&UserWithUUID{})
24+
if err := DB.Migrator().AutoMigrate(&UserWithUUID{}); err != nil {
25+
t.Errorf("failed to migrate, got error: %v", err)
26+
}
27+
28+
user1UUID, uuidGenErr := uuid.NewUUID()
29+
if uuidGenErr != nil {
30+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
31+
}
32+
user2UUID, uuidGenErr := uuid.NewUUID()
33+
if uuidGenErr != nil {
34+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
35+
}
36+
user3UUID, uuidGenErr := uuid.NewUUID()
37+
if uuidGenErr != nil {
38+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
39+
}
40+
user4UUID, uuidGenErr := uuid.NewUUID()
41+
if uuidGenErr != nil {
42+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
43+
}
44+
45+
users := []UserWithUUID{{
46+
Name: "uuid-1",
47+
UserUUID: datatypes.UUID(user1UUID),
48+
}, {
49+
Name: "uuid-2",
50+
UserUUID: datatypes.UUID(user2UUID),
51+
}, {
52+
Name: "uuid-3",
53+
UserUUID: datatypes.UUID(user3UUID),
54+
}, {
55+
Name: "uuid-4",
56+
UserUUID: datatypes.UUID(user4UUID),
57+
}}
58+
59+
if err := DB.Create(&users).Error; err != nil {
60+
t.Errorf("Failed to create users %v", err)
61+
}
62+
63+
for _, user := range users {
64+
result := UserWithUUID{}
65+
if err := DB.First(
66+
&result, "name = ? AND user_uuid = ?",
67+
user.Name,
68+
user.UserUUID,
69+
).Error; err != nil {
70+
t.Fatalf("failed to find user with uuid, got error: %v", err)
71+
}
72+
AssertEqual(t, !result.UserUUID.IsEmpty(), true)
73+
AssertEqual(t, user.UserUUID.Equals(result.UserUUID), true)
74+
valueUser, err := user.UserUUID.Value()
75+
if err != nil {
76+
t.Fatalf("failed to get user value, got error: %v", err)
77+
}
78+
valueResult, err := result.UserUUID.Value()
79+
if err != nil {
80+
t.Fatalf("failed to get result value, got error: %v", err)
81+
}
82+
AssertEqual(t, valueUser, valueResult)
83+
}
84+
85+
user1 := users[0]
86+
AssertEqual(t, user1.UserUUID.IsNil(), false)
87+
AssertEqual(t, user1.UserUUID.IsEmpty(), false)
88+
DB.Model(&user1).Updates(
89+
map[string]interface{}{"user_uuid": uuid.Nil},
90+
)
91+
AssertEqual(t, user1.UserUUID.IsNil(), true)
92+
AssertEqual(t, user1.UserUUID.IsEmpty(), true)
93+
94+
user2 := users[1]
95+
AssertEqual(t, user2.UserUUID.IsNil(), false)
96+
AssertEqual(t, user2.UserUUID.IsEmpty(), false)
97+
DB.Model(&user2).Updates(
98+
map[string]interface{}{"user_uuid": nil},
99+
)
100+
AssertEqual(t, user2.UserUUID.IsNil(), true)
101+
AssertEqual(t, user2.UserUUID.IsEmpty(), true)
102+
}
103+
}
104+
105+
func TestUUIDPtr(t *testing.T) {
106+
if SupportedDriver("sqlite", "mysql", "postgres", "sqlserver") {
107+
type UserWithUUIDPtr struct {
108+
gorm.Model
109+
Name string
110+
UserUUID *datatypes.UUID
111+
}
112+
113+
DB.Migrator().DropTable(&UserWithUUIDPtr{})
114+
if err := DB.Migrator().AutoMigrate(&UserWithUUIDPtr{}); err != nil {
115+
t.Errorf("failed to migrate, got error: %v", err)
116+
}
117+
118+
user1UUID, uuidGenErr := uuid.NewUUID()
119+
if uuidGenErr != nil {
120+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
121+
}
122+
user2UUID, uuidGenErr := uuid.NewUUID()
123+
if uuidGenErr != nil {
124+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
125+
}
126+
user3UUID, uuidGenErr := uuid.NewUUID()
127+
if uuidGenErr != nil {
128+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
129+
}
130+
user4UUID, uuidGenErr := uuid.NewUUID()
131+
if uuidGenErr != nil {
132+
t.Fatalf("failed to generate a new UUID, got error %v", uuidGenErr)
133+
}
134+
135+
uuid1 := datatypes.UUID(user1UUID)
136+
uuid2 := datatypes.UUID(user2UUID)
137+
uuid3 := datatypes.UUID(user3UUID)
138+
uuid4 := datatypes.UUID(user4UUID)
139+
140+
users := []UserWithUUIDPtr{{
141+
Name: "uuid-1",
142+
UserUUID: &uuid1,
143+
}, {
144+
Name: "uuid-2",
145+
UserUUID: &uuid2,
146+
}, {
147+
Name: "uuid-3",
148+
UserUUID: &uuid3,
149+
}, {
150+
Name: "uuid-4",
151+
UserUUID: &uuid4,
152+
}}
153+
154+
if err := DB.Create(&users).Error; err != nil {
155+
t.Errorf("Failed to create users %v", err)
156+
}
157+
158+
for _, user := range users {
159+
result := UserWithUUIDPtr{}
160+
if err := DB.First(
161+
&result, "name = ? AND user_uuid = ?",
162+
user.Name,
163+
*user.UserUUID,
164+
).Error; err != nil {
165+
t.Fatalf("failed to find user with uuid, got error: %v", err)
166+
}
167+
AssertEqual(t, !result.UserUUID.IsEmpty(), true)
168+
AssertEqual(t, user.UserUUID, result.UserUUID)
169+
valueUser, err := user.UserUUID.Value()
170+
if err != nil {
171+
t.Fatalf("failed to get user value, got error: %v", err)
172+
}
173+
valueResult, err := result.UserUUID.Value()
174+
if err != nil {
175+
t.Fatalf("failed to get result value, got error: %v", err)
176+
}
177+
AssertEqual(t, valueUser, valueResult)
178+
}
179+
180+
user1 := users[0]
181+
AssertEqual(t, user1.UserUUID.IsNilPtr(), false)
182+
AssertEqual(t, user1.UserUUID.IsEmptyPtr(), false)
183+
DB.Model(&user1).Updates(map[string]interface{}{"user_uuid": nil})
184+
AssertEqual(t, user1.UserUUID.IsNilPtr(), true)
185+
AssertEqual(t, user1.UserUUID.IsEmptyPtr(), true)
186+
}
187+
}

0 commit comments

Comments
 (0)