Skip to content

Commit d65f8e1

Browse files
authored
Fix nested preload with duplicate ptr belongs to (#365)
1 parent 3b36085 commit d65f8e1

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed

cursor.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,14 @@ func scanMulti(cur Cursor, keyField string, keyType reflect.Type, cols map[any][
9292
key, found := doc.Value(keyField)
9393
mustTrue(found, "rel: key field not found")
9494

95+
needCopy := false
9596
for _, col := range cols[key] {
96-
col.Append(doc)
97+
if needCopy {
98+
col.Append(doc.Copy())
99+
} else {
100+
col.Append(doc)
101+
needCopy = true
102+
}
97103
}
98104

99105
// create new doc for next scan

document.go

+7
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,13 @@ func (d Document) NewDocument() *Document {
266266
return newZeroDocument(d.rt)
267267
}
268268

269+
// Copy returns copy of this document
270+
func (d Document) Copy() *Document {
271+
rv := reflect.New(d.rt)
272+
rv.Elem().Set(d.rv)
273+
return NewDocument(rv)
274+
}
275+
269276
// Append is alias for Assign for compatibility with internal slice interface
270277
func (d *Document) Append(o *Document) {
271278
d.Assign(o)

repository_test.go

+88
Original file line numberDiff line numberDiff line change
@@ -4025,6 +4025,94 @@ func TestRepository_Preload_scanErrors(t *testing.T) {
40254025
cur.AssertExpectations(t)
40264026
}
40274027

4028+
type ScheduledQuestion struct {
4029+
ID int
4030+
QuestionID int
4031+
Question *Question
4032+
}
4033+
4034+
type Question struct {
4035+
ID int
4036+
Answers []Answers
4037+
}
4038+
4039+
type Answers struct {
4040+
ID int
4041+
QuestionID int
4042+
}
4043+
4044+
func TestRepository_Preload_nestedWithDuplicatePtrBelongsTo(t *testing.T) {
4045+
var (
4046+
adapter = &testAdapter{}
4047+
repo = New(adapter)
4048+
scheduledQuestions = []ScheduledQuestion{}
4049+
cur = &testCursor{}
4050+
)
4051+
4052+
{
4053+
adapter.On("Query", From("scheduled_questions")).Return(cur, nil).Once()
4054+
cur.On("Close").Return(nil).Once()
4055+
cur.On("Fields").Return([]string{"id", "question_id"}, nil).Once()
4056+
cur.On("Next").Return(true).Times(2)
4057+
cur.MockScan(1, 1).Once()
4058+
cur.MockScan(2, 1).Once()
4059+
cur.On("Next").Return(false).Once()
4060+
4061+
assert.Nil(t, repo.FindAll(context.TODO(), &scheduledQuestions))
4062+
}
4063+
4064+
{
4065+
adapter.On("Query", From("questions").Where(In("id", 1))).Return(cur, nil).Once()
4066+
cur.On("Close").Return(nil).Once()
4067+
cur.On("Fields").Return([]string{"id"}, nil).Once()
4068+
cur.On("Next").Return(true).Times(1)
4069+
cur.MockScan(1).Once()
4070+
cur.On("Next").Return(false).Once()
4071+
4072+
assert.Nil(t, repo.Preload(context.TODO(), &scheduledQuestions, "question"))
4073+
}
4074+
4075+
{
4076+
adapter.On("Query", From("answers").Where(In("question_id", 1))).Return(cur, nil).Once()
4077+
cur.On("Close").Return(nil).Once()
4078+
cur.On("Fields").Return([]string{"id", "question_id"}, nil).Once()
4079+
cur.On("Next").Return(true).Times(1)
4080+
cur.MockScan(1, 1).Once()
4081+
cur.On("Next").Return(false).Once()
4082+
4083+
assert.Nil(t, repo.Preload(context.TODO(), &scheduledQuestions, "question.answers"))
4084+
}
4085+
4086+
assert.Equal(t, []ScheduledQuestion{
4087+
{
4088+
ID: 1,
4089+
QuestionID: 1,
4090+
Question: &Question{
4091+
ID: 1,
4092+
Answers: []Answers{
4093+
{
4094+
ID: 1,
4095+
QuestionID: 1,
4096+
},
4097+
},
4098+
},
4099+
},
4100+
{
4101+
ID: 2,
4102+
QuestionID: 1,
4103+
Question: &Question{
4104+
ID: 1,
4105+
Answers: []Answers{
4106+
{
4107+
ID: 1,
4108+
QuestionID: 1,
4109+
},
4110+
},
4111+
},
4112+
},
4113+
}, scheduledQuestions)
4114+
}
4115+
40284116
func TestRepository_MustPreload(t *testing.T) {
40294117
var (
40304118
adapter = &testAdapter{}

0 commit comments

Comments
 (0)