Skip to content

Commit 1d79345

Browse files
Merge pull request #84 from oracle-samples/78-fix-upsert-clobs
Fix Issue #78
2 parents 606787c + d45a78c commit 1d79345

File tree

3 files changed

+95
-3
lines changed

3 files changed

+95
-3
lines changed

oracle/common.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import (
5353
)
5454

5555
// Helper function to get Oracle array type for a field
56-
func getOracleArrayType(field *schema.Field) string {
56+
func getOracleArrayType(field *schema.Field, values []any) string {
5757
switch field.DataType {
5858
case schema.Bool:
5959
return "TABLE OF NUMBER(1)"
@@ -64,6 +64,14 @@ func getOracleArrayType(field *schema.Field) string {
6464
case schema.String:
6565
if field.Size > 0 && field.Size <= 4000 {
6666
return fmt.Sprintf("TABLE OF VARCHAR2(%d)", field.Size)
67+
} else {
68+
for _, value := range values {
69+
if strValue, ok := value.(string); ok {
70+
if len(strValue) > 4000 {
71+
return "TABLE OF CLOB"
72+
}
73+
}
74+
}
6775
}
6876
return "TABLE OF VARCHAR2(4000)"
6977
case schema.Time:

oracle/create.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ func buildBulkMergePLSQL(db *gorm.DB, createValues clause.Values, onConflictClau
296296
for i, column := range createValues.Columns {
297297
var arrayType string
298298
if field := findFieldByDBName(schema, column.Name); field != nil {
299-
arrayType = getOracleArrayType(field)
299+
arrayType = getOracleArrayType(field, pluck(createValues.Values, i))
300300
} else {
301301
arrayType = "TABLE OF VARCHAR2(4000)"
302302
}
@@ -579,7 +579,7 @@ func buildBulkInsertOnlyPLSQL(db *gorm.DB, createValues clause.Values) {
579579
for i, column := range createValues.Columns {
580580
var arrayType string
581581
if field := findFieldByDBName(schema, column.Name); field != nil {
582-
arrayType = getOracleArrayType(field)
582+
arrayType = getOracleArrayType(field, pluck(createValues.Values, i))
583583
} else {
584584
arrayType = "TABLE OF VARCHAR2(4000)"
585585
}
@@ -1007,3 +1007,12 @@ func sanitizeCreateValuesForBulkArrays(stmt *gorm.Statement, cv *clause.Values)
10071007
}
10081008
}
10091009
}
1010+
1011+
// pluck extracts the values at index col from a slice of arrays []T.
1012+
func pluck[T any, N int](data [][]T, col int) []T {
1013+
out := make([]T, len(data))
1014+
for i := range data {
1015+
out[i] = data[i][col]
1016+
}
1017+
return out
1018+
}

tests/create_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
"strings"
4646
"testing"
4747

48+
"github.com/google/uuid"
4849
. "github.com/oracle-samples/gorm-oracle/tests/utils"
4950

5051
"time"
@@ -846,3 +847,77 @@ func TestCreateFromMapWithTable(t *testing.T) {
846847
t.Errorf("failed to create data from map with table, @id != id")
847848
}
848849
}
850+
851+
// Issue #78: PL/SQL failure when upserting CLOBs
852+
func TestCreateAndUpdateLargeString(t *testing.T) {
853+
DB.Migrator().DropTable(FolderData{}, FolderProperty{})
854+
855+
if err := DB.Exec(`
856+
CREATE TABLE "folder_data" (
857+
"folder_id" VARCHAR2(4000),
858+
"folder_nm" VARCHAR2(4000),
859+
PRIMARY KEY ("folder_id"))
860+
`).Error; err != nil {
861+
t.Errorf("Failed to create table: %v", err)
862+
}
863+
864+
if err := DB.Exec(`
865+
CREATE TABLE "folder_properties" (
866+
"seq" NUMBER(20) GENERATED BY DEFAULT AS IDENTITY,
867+
"folder_id" VARCHAR2(4000),
868+
"key" VARCHAR2(4000),
869+
"value" CLOB,
870+
PRIMARY KEY ("folder_id","key"),
871+
CONSTRAINT "fk_folder_data_properties" FOREIGN KEY ("folder_id") REFERENCES "folder_data"("folder_id"),
872+
CONSTRAINT "uni_folder_properties_key" UNIQUE ("key"))
873+
`).Error; err != nil {
874+
t.Errorf("Failed to create table. Got: %v", err)
875+
}
876+
877+
id := uuid.New().String()
878+
879+
folder := &FolderData{
880+
ID: id,
881+
Name: "My Folder",
882+
Properties: []FolderProperty{
883+
{
884+
ID: id,
885+
Key: "prop1",
886+
Value: strings.Repeat("A", 5000),
887+
},
888+
{
889+
ID: id,
890+
Key: "prop2",
891+
Value: strings.Repeat("B", 5000),
892+
},
893+
},
894+
}
895+
896+
if err := DB.Create(&folder).Error; err != nil {
897+
t.Errorf("Failed to insert record. Got: %v", err)
898+
}
899+
900+
createdFolder := &FolderData{}
901+
if err := DB.Model(&FolderData{}).Preload("Properties").First(&createdFolder).Error; err != nil {
902+
t.Errorf("Failed to load record. Got: %v", err)
903+
}
904+
905+
for i, p := range folder.Properties {
906+
tests.AssertObjEqual(t, p, createdFolder.Properties[i], "Seq", "ID", "Key", "Value")
907+
}
908+
909+
clobContent := strings.Repeat("C", 4020)
910+
folder.Properties[1].Value = clobContent
911+
if err := DB.Save(&folder).Error; err != nil {
912+
t.Errorf("Failed to update record. Got: %v", err)
913+
}
914+
915+
updatedFolder := &FolderData{}
916+
if err := DB.Model(&FolderData{}).Preload("Properties").First(&updatedFolder).Error; err != nil {
917+
t.Errorf("Failed to load record. Got: %v", err)
918+
}
919+
920+
for i, p := range folder.Properties {
921+
tests.AssertObjEqual(t, p, updatedFolder.Properties[i], "Seq", "ID", "Key", "Value")
922+
}
923+
}

0 commit comments

Comments
 (0)