-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathgenerated_test.go
More file actions
127 lines (115 loc) · 4.23 KB
/
Copy pathgenerated_test.go
File metadata and controls
127 lines (115 loc) · 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package sqlite
import (
"strings"
"testing"
"gorm.io/gorm/schema"
)
func TestDataTypeOfGeneratedColumn(t *testing.T) {
dialector := Dialector{}
tests := []struct {
name string
field *schema.Field
want string
}{
{
name: "computed column renders a STORED generated column",
field: &schema.Field{DataType: schema.Float, TagSettings: map[string]string{"GENERATED": "price * quantity"}},
want: "real GENERATED ALWAYS AS (price * quantity) STORED",
},
{
name: "computed expression keeps commas",
field: &schema.Field{DataType: schema.String, TagSettings: map[string]string{"GENERATED": "coalesce(first_name, last_name)"}},
want: "text GENERATED ALWAYS AS (coalesce(first_name, last_name)) STORED",
},
{
// `identity` is reserved for identity columns, which SQLite renders
// through its native AUTOINCREMENT rather than a computed column.
name: "identity keyword is not treated as a computed column",
field: &schema.Field{DataType: schema.Int, AutoIncrement: true, TagSettings: map[string]string{"GENERATED": "identity"}},
want: "integer PRIMARY KEY AUTOINCREMENT",
},
{
name: "identity with an explicit mode is also reserved",
field: &schema.Field{DataType: schema.Int, AutoIncrement: true, TagSettings: map[string]string{"GENERATED": "identity always"}},
want: "integer PRIMARY KEY AUTOINCREMENT",
},
{
name: "a bare generated tag is ignored",
field: &schema.Field{DataType: schema.Float, TagSettings: map[string]string{"GENERATED": "GENERATED"}},
want: "real",
},
{
name: "a lowercase generated expression is not mistaken for a bare tag",
field: &schema.Field{DataType: schema.Float, TagSettings: map[string]string{"GENERATED": "generated"}},
want: "real GENERATED ALWAYS AS (generated) STORED",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := dialector.DataTypeOf(tt.field); got != tt.want {
t.Errorf("DataTypeOf() = %q, want %q", got, tt.want)
}
})
}
}
type GeneratedProduct struct {
ID uint `gorm:"primaryKey"`
Name string
Price float64
Quantity int
Total float64 `gorm:"->;generated:price * quantity"`
}
func (GeneratedProduct) TableName() string { return "generated_products" }
func TestGeneratedColumnEndToEnd(t *testing.T) {
db := openTestDB(t, "")
if err := db.AutoMigrate(&GeneratedProduct{}); err != nil {
t.Fatalf("AutoMigrate: %v", err)
}
ddl := tableDDL(t, db, "generated_products")
if !strings.Contains(ddl, "GENERATED ALWAYS AS (price * quantity) STORED") {
t.Fatalf("DDL missing generated clause: %s", ddl)
}
if err := db.Create(&GeneratedProduct{ID: 1, Name: "x", Price: 2.5, Quantity: 4}).Error; err != nil {
t.Fatalf("insert: %v", err)
}
var got GeneratedProduct
if err := db.First(&got, 1).Error; err != nil {
t.Fatal(err)
}
if got.Total != 10 {
t.Errorf("Total = %v, want 10", got.Total)
}
// repeated AutoMigrate must be idempotent, not rebuild or re-alter
if err := db.AutoMigrate(&GeneratedProduct{}); err != nil {
t.Fatalf("second AutoMigrate: %v", err)
}
if again := tableDDL(t, db, "generated_products"); again != ddl {
t.Errorf("DDL changed after second AutoMigrate:\n before: %s\n after: %s", ddl, again)
}
}
// A table rebuild copies data with INSERT INTO ... SELECT, which must exclude
// generated columns (SQLite forbids writing them); the rebuilt table keeps the
// generated column working.
func TestGeneratedColumnSurvivesRecreateTable(t *testing.T) {
db := openTestDB(t, "")
if err := db.AutoMigrate(&GeneratedProduct{}); err != nil {
t.Fatal(err)
}
if err := db.Create(&GeneratedProduct{ID: 1, Name: "x", Price: 3, Quantity: 3}).Error; err != nil {
t.Fatal(err)
}
if err := db.Migrator().DropColumn(&GeneratedProduct{}, "name"); err != nil {
t.Fatalf("DropColumn on a table with a generated column: %v", err)
}
ddl := tableDDL(t, db, "generated_products")
if !strings.Contains(ddl, "GENERATED ALWAYS AS (price * quantity) STORED") {
t.Errorf("generated clause lost after rebuild: %s", ddl)
}
var total float64
if err := db.Raw("SELECT `total` FROM `generated_products` WHERE `id` = 1").Scan(&total).Error; err != nil {
t.Fatal(err)
}
if total != 9 {
t.Errorf("Total after rebuild = %v, want 9 (data copied, expression recomputed)", total)
}
}