Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions oracle/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ func validateCreateData(stmt *gorm.Statement) error {

// Build PL/SQL block for bulk INSERT/MERGE with RETURNING
func buildBulkInsertPLSQL(db *gorm.DB, createValues clause.Values) {
sanitizeCreateValuesForBulkArrays(db.Statement, &createValues)

stmt := db.Statement
schema := stmt.Schema

Expand Down Expand Up @@ -238,6 +240,8 @@ func buildBulkInsertPLSQL(db *gorm.DB, createValues clause.Values) {

// Build PL/SQL block for bulk MERGE with RETURNING (OnConflict case)
func buildBulkMergePLSQL(db *gorm.DB, createValues clause.Values, onConflictClause clause.Clause) {
sanitizeCreateValuesForBulkArrays(db.Statement, &createValues)

stmt := db.Statement
schema := stmt.Schema

Expand Down Expand Up @@ -409,6 +413,25 @@ func buildBulkMergePLSQL(db *gorm.DB, createValues clause.Values, onConflictClau
}
}
plsqlBuilder.WriteString("\n")
} else {
onCols := map[string]struct{}{}
for _, c := range conflictColumns {
onCols[strings.ToUpper(c.Name)] = struct{}{}
}

// Picking the first non-ON column from the INSERT/MERGE columns
var noopCol string
for _, c := range createValues.Columns {
if _, inOn := onCols[strings.ToUpper(c.Name)]; !inOn {
noopCol = c.Name
break
}
}
plsqlBuilder.WriteString(" WHEN MATCHED THEN UPDATE SET t.")
writeQuotedIdentifier(&plsqlBuilder, noopCol)
plsqlBuilder.WriteString(" = t.")
writeQuotedIdentifier(&plsqlBuilder, noopCol)
plsqlBuilder.WriteString("\n")
}

// WHEN NOT MATCHED THEN INSERT (unless DoNothing for inserts)
Expand Down Expand Up @@ -923,3 +946,29 @@ func handleLastInsertId(db *gorm.DB, result sql.Result) {
}
}
}
func sanitizeCreateValuesForBulkArrays(stmt *gorm.Statement, cv *clause.Values) {
for r := range cv.Values {
for c, col := range cv.Columns {
v := cv.Values[r][c]
switch v.(type) {
case clause.Expr:
if f := findFieldByDBName(stmt.Schema, col.Name); f != nil {
switch f.DataType {
case schema.Int, schema.Uint:
cv.Values[r][c] = sql.NullInt64{} // NULL
case schema.Float:
cv.Values[r][c] = sql.NullFloat64{}
case schema.String:
cv.Values[r][c] = sql.NullString{}
case schema.Time:
cv.Values[r][c] = sql.NullTime{}
default:
cv.Values[r][c] = nil
}
} else {
cv.Values[r][c] = nil
}
}
}
}
}
51 changes: 22 additions & 29 deletions tests/associations_has_many_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,35 +47,34 @@ import (
)

func TestHasManyAssociation(t *testing.T) {
t.Skip()
user := *GetUser("hasmany", Config{Pets: 2})

if err := DB.Create(&user).Error; err != nil {
t.Fatalf("errors happened when create: %v", err)
}

CheckUser(t, user, user)
CheckUserSkipUpdatedAt(t, user, user)

// Find
var user2 User
DB.Find(&user2, "id = ?", user.ID)
DB.Find(&user2, "\"id\" = ?", user.ID)
DB.Model(&user2).Association("Pets").Find(&user2.Pets)
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

var pets []Pet
DB.Model(&user).Where("name = ?", user.Pets[0].Name).Association("Pets").Find(&pets)
DB.Model(&user).Where("\"name\" = ?", user.Pets[0].Name).Association("Pets").Find(&pets)

if len(pets) != 1 {
t.Fatalf("should only find one pets, but got %v", len(pets))
}

CheckPet(t, pets[0], *user.Pets[0])

if count := DB.Model(&user).Where("name = ?", user.Pets[1].Name).Association("Pets").Count(); count != 1 {
if count := DB.Model(&user).Where("\"name\" = ?", user.Pets[1].Name).Association("Pets").Count(); count != 1 {
t.Fatalf("should only find one pets, but got %v", count)
}

if count := DB.Model(&user).Where("name = ?", "not found").Association("Pets").Count(); count != 0 {
if count := DB.Model(&user).Where("\"name\" = ?", "not found").Association("Pets").Count(); count != 0 {
t.Fatalf("should only find no pet with invalid conditions, but got %v", count)
}

Expand All @@ -94,7 +93,7 @@ func TestHasManyAssociation(t *testing.T) {
}

user.Pets = append(user.Pets, &pet)
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Pets", 3, "AfterAppend")

Expand All @@ -113,7 +112,7 @@ func TestHasManyAssociation(t *testing.T) {
user.Pets = append(user.Pets, &pet)
}

CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Pets", 5, "AfterAppendSlice")

Expand All @@ -129,7 +128,7 @@ func TestHasManyAssociation(t *testing.T) {
}

user.Pets = []*Pet{&pet2}
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user2, "Pets", 1, "AfterReplace")

Expand Down Expand Up @@ -160,20 +159,19 @@ func TestHasManyAssociation(t *testing.T) {
}

func TestSingleTableHasManyAssociation(t *testing.T) {
t.Skip()
user := *GetUser("hasmany", Config{Team: 2})

if err := DB.Create(&user).Error; err != nil {
t.Fatalf("errors happened when create: %v", err)
}

CheckUser(t, user, user)
CheckUserSkipUpdatedAt(t, user, user)

// Find
var user2 User
DB.Find(&user2, "id = ?", user.ID)
DB.Find(&user2, "\"id\" = ?", user.ID)
DB.Model(&user2).Association("Team").Find(&user2.Team)
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

// Count
AssertAssociationCount(t, user, "Team", 2, "")
Expand All @@ -190,7 +188,7 @@ func TestSingleTableHasManyAssociation(t *testing.T) {
}

user.Team = append(user.Team, team)
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Team", 3, "AfterAppend")

Expand All @@ -209,7 +207,7 @@ func TestSingleTableHasManyAssociation(t *testing.T) {
user.Team = append(user.Team, team)
}

CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Team", 5, "AfterAppendSlice")

Expand All @@ -225,7 +223,7 @@ func TestSingleTableHasManyAssociation(t *testing.T) {
}

user.Team = []User{team2}
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user2, "Team", 1, "AfterReplace")

Expand Down Expand Up @@ -256,7 +254,6 @@ func TestSingleTableHasManyAssociation(t *testing.T) {
}

func TestHasManyAssociationForSlice(t *testing.T) {
t.Skip()
users := []User{
*GetUser("slice-hasmany-1", Config{Pets: 2}),
*GetUser("slice-hasmany-2", Config{Pets: 0}),
Expand Down Expand Up @@ -311,7 +308,6 @@ func TestHasManyAssociationForSlice(t *testing.T) {
}

func TestSingleTableHasManyAssociationForSlice(t *testing.T) {
t.Skip()
users := []User{
*GetUser("slice-hasmany-1", Config{Team: 2}),
*GetUser("slice-hasmany-2", Config{Team: 0}),
Expand Down Expand Up @@ -368,20 +364,19 @@ func TestSingleTableHasManyAssociationForSlice(t *testing.T) {
}

func TestPolymorphicHasManyAssociation(t *testing.T) {
t.Skip()
user := *GetUser("hasmany", Config{Toys: 2})

if err := DB.Create(&user).Error; err != nil {
t.Fatalf("errors happened when create: %v", err)
}

CheckUser(t, user, user)
CheckUserSkipUpdatedAt(t, user, user)

// Find
var user2 User
DB.Find(&user2, "id = ?", user.ID)
DB.Find(&user2, "\"id\" = ?", user.ID)
DB.Model(&user2).Association("Toys").Find(&user2.Toys)
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

// Count
AssertAssociationCount(t, user, "Toys", 2, "")
Expand All @@ -398,7 +393,7 @@ func TestPolymorphicHasManyAssociation(t *testing.T) {
}

user.Toys = append(user.Toys, toy)
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Toys", 3, "AfterAppend")

Expand All @@ -417,7 +412,7 @@ func TestPolymorphicHasManyAssociation(t *testing.T) {
user.Toys = append(user.Toys, toy)
}

CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Toys", 5, "AfterAppendSlice")

Expand All @@ -433,7 +428,7 @@ func TestPolymorphicHasManyAssociation(t *testing.T) {
}

user.Toys = []Toy{toy2}
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user2, "Toys", 1, "AfterReplace")

Expand Down Expand Up @@ -464,7 +459,6 @@ func TestPolymorphicHasManyAssociation(t *testing.T) {
}

func TestPolymorphicHasManyAssociationForSlice(t *testing.T) {
t.Skip()
users := []User{
*GetUser("slice-hasmany-1", Config{Toys: 2}),
*GetUser("slice-hasmany-2", Config{Toys: 0, Tools: 2}),
Expand Down Expand Up @@ -601,8 +595,7 @@ func TestHasManyAssociationUnscoped(t *testing.T) {
}

func TestHasManyAssociationReplaceWithNonValidValue(t *testing.T) {
t.Skip()
user := User{Name: "jinzhu", Languages: []Language{{Name: "EN"}}}
user := User{Name: "jinzhu", Languages: []Language{{Code: "EN", Name: "EN"}}}

if err := DB.Create(&user).Error; err != nil {
t.Fatalf("errors happened when create: %v", err)
Expand Down
12 changes: 5 additions & 7 deletions tests/associations_many2many_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,21 +235,20 @@ func TestMany2ManyAssociationForSlice(t *testing.T) {
}

func TestSingleTableMany2ManyAssociation(t *testing.T) {
t.Skip()
user := *GetUser("many2many", Config{Friends: 2})

if err := DB.Create(&user).Error; err != nil {
t.Fatalf("errors happened when create: %v", err)
}

CheckUser(t, user, user)
CheckUserSkipUpdatedAt(t, user, user)

// Find
var user2 User
DB.Find(&user2, "\"id\" = ?", user.ID)
DB.Model(&user2).Association("Friends").Find(&user2.Friends)

CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

// Count
AssertAssociationCount(t, user, "Friends", 2, "")
Expand All @@ -262,7 +261,7 @@ func TestSingleTableMany2ManyAssociation(t *testing.T) {
}

user.Friends = append(user.Friends, &friend)
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Friends", 3, "AfterAppend")

Expand All @@ -274,7 +273,7 @@ func TestSingleTableMany2ManyAssociation(t *testing.T) {

user.Friends = append(user.Friends, friends...)

CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user, "Friends", 5, "AfterAppendSlice")

Expand All @@ -286,7 +285,7 @@ func TestSingleTableMany2ManyAssociation(t *testing.T) {
}

user.Friends = []*User{&friend2}
CheckUser(t, user2, user)
CheckUserSkipUpdatedAt(t, user2, user)

AssertAssociationCount(t, user2, "Friends", 1, "AfterReplace")

Expand Down Expand Up @@ -317,7 +316,6 @@ func TestSingleTableMany2ManyAssociation(t *testing.T) {
}

func TestSingleTableMany2ManyAssociationForSlice(t *testing.T) {
t.Skip()
users := []User{
*GetUser("slice-many2many-1", Config{Team: 2}),
*GetUser("slice-many2many-2", Config{Team: 0}),
Expand Down
2 changes: 1 addition & 1 deletion tests/multi_primary_keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ func TestManyToManyWithCustomizedForeignKeys2(t *testing.T) {

func TestCompositePrimaryKeysAssociations(t *testing.T) {
t.Skip()

type Label struct {
BookID *uint `gorm:"primarykey"`
Name string `gorm:"primarykey"`
Expand Down
24 changes: 12 additions & 12 deletions tests/passed-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ TestBelongsToAssociation
TestBelongsToAssociationForSlice
TestBelongsToDefaultValue
TestBelongsToAssociationUnscoped
#TestHasManyAssociation
#TestSingleTableHasManyAssociation
#TestHasManyAssociationForSlice
#TestSingleTableHasManyAssociationForSlice
#TestPolymorphicHasManyAssociation
#TestPolymorphicHasManyAssociationForSlice
TestHasManyAssociation
TestSingleTableHasManyAssociation
TestHasManyAssociationForSlice
TestSingleTableHasManyAssociationForSlice
TestPolymorphicHasManyAssociation
TestPolymorphicHasManyAssociationForSlice
TestHasManyAssociationUnscoped
#TestHasManyAssociationReplaceWithNonValidValue
TestHasManyAssociationReplaceWithNonValidValue
TestHasOneAssociation
TestHasOneAssociationWithSelect
TestHasOneAssociationForSlice
Expand All @@ -19,8 +19,8 @@ TestHasOneAssociationReplaceWithNonValidValue
TestMany2ManyAssociation
TestMany2ManyOmitAssociations
TestMany2ManyAssociationForSlice
#TestSingleTableMany2ManyAssociation
#TestSingleTableMany2ManyAssociationForSlice
TestSingleTableMany2ManyAssociation
TestSingleTableMany2ManyAssociationForSlice
TestDuplicateMany2ManyAssociation
TestConcurrentMany2ManyAssociation
TestMany2ManyDuplicateBelongsToAssociation
Expand Down Expand Up @@ -211,8 +211,8 @@ TestManyToManyPreloadWithMultiPrimaryKeys
TestManyToManyPreloadForNestedPointer
TestNestedManyToManyPreload
TestNestedManyToManyPreload2
#TestNestedManyToManyPreload3
#TestNestedManyToManyPreload3ForStruct
TestNestedManyToManyPreload3
TestNestedManyToManyPreload3ForStruct
TestNestedManyToManyPreload4
TestManyToManyPreloadForPointer
TestNilPointerSlice
Expand Down Expand Up @@ -338,7 +338,7 @@ TestTransactionWithRawSQL
TestUpdateBelongsTo
#TestUpdateHasManyAssociations
TestUpdateHasOne
#TestUpdateMany2ManyAssociations
TestUpdateMany2ManyAssociations
TestUpdate
TestUpdates
TestUpdateColumn
Expand Down
Loading
Loading