Skip to content

Commit bedddf4

Browse files
authored
fix: [#858] facades.DB().Insert() can not operate nil columns as expected (#1347)
* fix: [#858] facades.DB().Insert() can not operate nil columns as expected * optimize
1 parent 23dba56 commit bedddf4

File tree

4 files changed

+143
-8
lines changed

4 files changed

+143
-8
lines changed

database/db/query.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,16 +1107,23 @@ func (r *Query) buildInsert(data []map[string]any) (sql string, args []any, err
11071107
builder = builder.PlaceholderFormat(placeholderFormat)
11081108
}
11091109

1110-
first := data[0]
1111-
cols := make([]string, 0, len(first))
1112-
for col := range first {
1110+
// Collect all unique columns from all maps to avoid missing columns
1111+
colSet := make(map[string]bool)
1112+
for _, row := range data {
1113+
for col := range row {
1114+
colSet[col] = true
1115+
}
1116+
}
1117+
1118+
cols := make([]string, 0, len(colSet))
1119+
for col := range colSet {
11131120
cols = append(cols, col)
11141121
}
11151122
sort.Strings(cols)
11161123
builder = builder.Columns(cols...)
11171124

11181125
for _, row := range data {
1119-
vals := make([]any, 0, len(first))
1126+
vals := make([]any, 0, len(cols))
11201127
for _, col := range cols {
11211128
vals = append(vals, row[col])
11221129
}

database/db/utils_test.go

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"gorm.io/gorm"
99

1010
"github.com/goravel/framework/support/carbon"
11+
"github.com/goravel/framework/support/convert"
1112
)
1213

1314
type Body struct {
@@ -20,11 +21,25 @@ type Body struct {
2021
leg int `db:"leg"`
2122
}
2223

24+
type House struct {
25+
Address string `db:"address"`
26+
Size *int `db:"size"`
27+
}
28+
29+
type Job struct {
30+
Title string `db:"title"`
31+
Salary *float64 `db:"salary"`
32+
}
33+
2334
type User struct {
24-
ID int `db:"id"`
25-
Name string `db:"-"`
26-
Email string
35+
ID int `db:"id"`
36+
Name string `db:"-"`
37+
Email string
38+
Avatar *string
39+
Alias *int
2740
Body
41+
House *House
42+
Job Job
2843
TestSoftDeletes
2944
TestTimestamps
3045
}
@@ -124,6 +139,110 @@ func TestConvertToSliceMap(t *testing.T) {
124139
},
125140
want: []map[string]any{{"weight": "100kg", "Age": 25}, {"weight": "90kg", "Age": 20}},
126141
},
142+
{
143+
name: "user with nested struct pointer",
144+
data: User{
145+
ID: 1,
146+
Name: "John",
147+
Email: "john@example.com",
148+
Body: Body{Weight: "100kg", Head: &head, DateTime: *dateTime},
149+
House: &House{Address: "123 Main St", Size: nil},
150+
Job: Job{Title: "Engineer", Salary: nil},
151+
TestSoftDeletes: TestSoftDeletes{DeletedAt: deletedAt},
152+
TestTimestamps: TestTimestamps{CreatedAt: dateTime, UpdatedAt: dateTime},
153+
},
154+
want: []map[string]any{
155+
{
156+
"id": 1, "email": "john@example.com", "weight": "100kg", "head": &head, "date_time": *dateTime,
157+
"house": &House{Address: "123 Main St", Size: nil},
158+
"job": Job{Title: "Engineer", Salary: nil},
159+
"created_at": dateTime, "updated_at": dateTime, "deleted_at": deletedAt,
160+
},
161+
},
162+
},
163+
{
164+
name: "user with nil nested struct pointer",
165+
data: User{
166+
ID: 1,
167+
Email: "john@example.com",
168+
Body: Body{Weight: "100kg", DateTime: *dateTime},
169+
House: nil,
170+
Job: Job{Title: "Engineer"},
171+
TestTimestamps: TestTimestamps{CreatedAt: dateTime, UpdatedAt: dateTime},
172+
},
173+
want: []map[string]any{
174+
{
175+
"id": 1, "email": "john@example.com", "weight": "100kg", "date_time": *dateTime,
176+
"job": Job{Title: "Engineer"},
177+
"created_at": dateTime, "updated_at": dateTime,
178+
},
179+
},
180+
},
181+
{
182+
name: "user with pointer fields",
183+
data: func() User {
184+
return User{
185+
ID: 1,
186+
Email: "john@example.com",
187+
Avatar: convert.Pointer("avatar.jpg"),
188+
Alias: convert.Pointer(42),
189+
Body: Body{Weight: "100kg", Head: &head, DateTime: *dateTime},
190+
House: &House{Address: "123 Main St", Size: convert.Pointer(100)},
191+
Job: Job{Title: "Engineer", Salary: convert.Pointer(50000.0)},
192+
TestTimestamps: TestTimestamps{CreatedAt: dateTime, UpdatedAt: dateTime},
193+
}
194+
}(),
195+
want: []map[string]any{
196+
{
197+
"id": 1, "email": "john@example.com", "avatar": convert.Pointer("avatar.jpg"),
198+
"alias": convert.Pointer(42), "weight": "100kg", "head": &head, "date_time": *dateTime,
199+
"house": &House{Address: "123 Main St", Size: convert.Pointer(100)},
200+
"job": Job{Title: "Engineer", Salary: convert.Pointer(50000.0)},
201+
"created_at": dateTime, "updated_at": dateTime,
202+
},
203+
},
204+
},
205+
{
206+
name: "user slice with mixed nested structs",
207+
data: []User{
208+
{
209+
ID: 1,
210+
Email: "john@example.com",
211+
Body: Body{Length: 10, Weight: "100kg", Head: &head, DateTime: *dateTime},
212+
House: &House{Address: "123 Main St"},
213+
Job: Job{Title: "Engineer"},
214+
TestTimestamps: TestTimestamps{CreatedAt: dateTime, UpdatedAt: dateTime},
215+
},
216+
{
217+
ID: 2,
218+
Email: "jane@example.com",
219+
Body: Body{Weight: "90kg", DateTime: *dateTime},
220+
House: nil,
221+
Job: Job{Title: "Designer"},
222+
TestTimestamps: TestTimestamps{CreatedAt: dateTime, UpdatedAt: dateTime},
223+
},
224+
},
225+
want: []map[string]any{
226+
{
227+
"id": 1, "email": "john@example.com", "length": 10, "weight": "100kg", "head": &head, "date_time": *dateTime,
228+
"house": &House{Address: "123 Main St"},
229+
"job": Job{Title: "Engineer"},
230+
"created_at": dateTime, "updated_at": dateTime,
231+
},
232+
{
233+
"id": 2, "email": "jane@example.com", "weight": "90kg", "date_time": *dateTime,
234+
"job": Job{Title: "Designer"},
235+
"created_at": dateTime, "updated_at": dateTime,
236+
},
237+
},
238+
},
239+
{
240+
name: "user with all zero values",
241+
data: User{},
242+
want: []map[string]any{
243+
{},
244+
},
245+
},
127246
}
128247

129248
for _, tt := range tests {

support/str/str_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ func (s *StringTestSuite) TestSnake() {
555555
s.Equal("a", Of("A").Snake().String())
556556
s.Equal("foo123_bar", Of("foo123Bar").Snake().String())
557557
s.Equal("123foo", Of("123foo").Snake().String())
558+
s.Equal("id", Of("ID").Snake().String())
558559
}
559560

560561
func (s *StringTestSuite) TestSplit() {

tests/db_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,8 @@ func (s *DBTestSuite) TestInsert_First_Get() {
541541
},
542542
},
543543
{
544-
Name: "multiple structs2",
544+
Name: "multiple structs2",
545+
Weight: convert.Pointer(1),
545546
},
546547
})
547548
s.NoError(err)
@@ -551,8 +552,15 @@ func (s *DBTestSuite) TestInsert_First_Get() {
551552
err = query.DB().Table("products").Where("name", []string{"multiple structs1", "multiple structs2"}).Where("deleted_at", nil).Get(&products)
552553
s.NoError(err)
553554
s.Equal(2, len(products))
555+
s.True(products[0].ID > 0)
554556
s.Equal("multiple structs1", products[0].Name)
557+
s.NotEmpty(products[0].CreatedAt)
558+
s.NotEmpty(products[0].UpdatedAt)
559+
s.True(products[1].ID > 0)
555560
s.Equal("multiple structs2", products[1].Name)
561+
s.Equal(1, *products[1].Weight)
562+
s.Empty(products[1].CreatedAt)
563+
s.Empty(products[1].UpdatedAt)
556564
})
557565

558566
s.Run("single map", func() {

0 commit comments

Comments
 (0)