From 0b133c82021273b023efdf894a48a0afb88dcadc Mon Sep 17 00:00:00 2001 From: Michal Golinski Date: Wed, 20 Oct 2021 08:45:50 +0200 Subject: [PATCH] fix lack of error passing for nested preload --- callback_query_preload.go | 6 ++- preload_test.go | 104 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/callback_query_preload.go b/callback_query_preload.go index a936180ad..b34535af6 100644 --- a/callback_query_preload.go +++ b/callback_query_preload.go @@ -73,12 +73,14 @@ func preloadCallback(scope *Scope) { scope.Err(errors.New("unsupported relation")) } - preloadedMap[preloadKey] = true + if currentScope.DB().Error == nil { + preloadedMap[preloadKey] = true + } break } if !preloadedMap[preloadKey] { - scope.Err(fmt.Errorf("can't preload field %s for %s", preloadField, currentScope.GetModelStruct().ModelType)) + scope.Err(fmt.Errorf("can't preload field %s for %s: %s", preloadField, currentScope.GetModelStruct().ModelType, currentScope.DB().Error)) return } } diff --git a/preload_test.go b/preload_test.go index dd29fb5e3..c97dec98f 100644 --- a/preload_test.go +++ b/preload_test.go @@ -3,8 +3,10 @@ package gorm_test import ( "database/sql" "encoding/json" + "errors" "os" "reflect" + "strings" "testing" "github.com/jinzhu/gorm" @@ -192,6 +194,108 @@ func TestNestedPreload1(t *testing.T) { } } +type SQLCommonMock struct { + db *sql.DB + errorQuery string +} + +func (s *SQLCommonMock) Exec(query string, args ...interface{}) (sql.Result, error) { + return s.db.Exec(query, args...) +} + +func (s *SQLCommonMock) Prepare(query string) (*sql.Stmt, error) { + return s.db.Prepare(query) +} + +func (s *SQLCommonMock) Query(query string, args ...interface{}) (*sql.Rows, error) { + if query == s.errorQuery { + return nil, errors.New("faked database error") + } + return s.db.Query(query, args...) +} + +func (s *SQLCommonMock) QueryRow(query string, args ...interface{}) *sql.Row { + return s.db.QueryRow(query, args...) +} + +func TestNestedPreload1FirstLevelError(t *testing.T) { + var err error + sqlDB := DB.DB() + + // will not preload Level2 due to faked database error + sqlMock := &SQLCommonMock{db: sqlDB, errorQuery: `SELECT * FROM "level2" WHERE ("level3_id" IN (?))`} + localDB, err := gorm.Open(DB.Dialect().GetName(), sqlMock) + if err != nil { + t.Error(err) + } + + type ( + Level1 struct { + ID uint + Value string + Level2ID uint + } + Level2 struct { + ID uint + Level1 Level1 + Level3ID uint + } + Level3 struct { + ID uint + Name string + Level2 Level2 + } + ) + localDB.DropTableIfExists(&Level3{}) + localDB.DropTableIfExists(&Level2{}) + localDB.DropTableIfExists(&Level1{}) + if err := localDB.AutoMigrate(&Level3{}, &Level2{}, &Level1{}).Error; err != nil { + t.Error(err) + } + + savedToDb := Level3{Level2: Level2{Level1: Level1{Value: "value"}}} + if err = localDB.Create(&savedToDb).Error; err != nil { + t.Error(err) + } + + want := savedToDb + want.Level2 = Level2{} + + var got Level3 + if err = localDB.Preload("Level2").Find(&got).Error; errors.Is(err, nil) { + t.Error("expecting error on preload failue") + } + + if err != nil && !strings.Contains(err.Error(), "faked database error") { + t.Error(err) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) + } + + // will not preload Level1 due to faked database error + sqlMock = &SQLCommonMock{db: sqlDB, errorQuery: `SELECT * FROM "level1" WHERE ("level2_id" IN (?))`} + localDB, err = gorm.Open(DB.Dialect().GetName(), sqlMock) + if err != nil { + t.Error(err) + } + + want = savedToDb + want.Level2.Level1 = Level1{} + if err = localDB.Preload("Level2.Level1").Find(&got).Error; errors.Is(err, nil) { + t.Error("expecting error on nested preload failue") + } + + if err != nil && !strings.Contains(err.Error(), "faked database error") { + t.Error(err) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("got %s; want %s", toJSONString(got), toJSONString(want)) + } +} + func TestNestedPreload2(t *testing.T) { type ( Level1 struct {