Skip to content

Commit 49eaeb0

Browse files
Fixing issue regarding JSON columns in bulk returning clause
1 parent 8a144c5 commit 49eaeb0

File tree

4 files changed

+98
-31
lines changed

4 files changed

+98
-31
lines changed

oracle/common.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
"strings"
4646
"time"
4747

48+
"gorm.io/datatypes"
4849
"gorm.io/gorm"
4950
"gorm.io/gorm/schema"
5051
)
@@ -198,7 +199,14 @@ func convertFromOracleToField(value interface{}, field *schema.Field) interface{
198199
if isPtr {
199200
targetType = targetType.Elem()
200201
}
201-
202+
if isJSONField(field) {
203+
switch v := value.(type) {
204+
case string:
205+
return datatypes.JSON([]byte(v))
206+
case []byte:
207+
return datatypes.JSON(v)
208+
}
209+
}
202210
var converted interface{}
203211

204212
switch targetType {
@@ -276,6 +284,22 @@ func convertFromOracleToField(value interface{}, field *schema.Field) interface{
276284
return converted
277285
}
278286

287+
func isJSONField(f *schema.Field) bool {
288+
// Schema says it's JSON
289+
if strings.EqualFold(string(f.DataType), "json") {
290+
return true
291+
}
292+
// Some drivers/taggers carry type hints
293+
if strings.Contains(strings.ToUpper(f.Tag.Get("TYPE")), "JSON") {
294+
return true
295+
}
296+
// Detect gorm.io/datatypes.JSON by reflected type
297+
if f.FieldType.Name() == "JSON" && f.FieldType.PkgPath() == "gorm.io/datatypes" {
298+
return true
299+
}
300+
return false
301+
}
302+
279303
// Helper function to handle primitive type conversions
280304
func convertPrimitiveType(value interface{}, targetType reflect.Type) interface{} {
281305
switch targetType.Kind() {

oracle/create.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -507,15 +507,23 @@ func buildBulkMergePLSQL(db *gorm.DB, createValues clause.Values, onConflictClau
507507
}
508508
plsqlBuilder.WriteString("\n BULK COLLECT INTO l_affected_records;\n")
509509

510-
// Add OUT parameter population
510+
// Add OUT parameter population (JSON serialized to CLOB)
511511
outParamIndex := len(stmt.Vars)
512512
for rowIdx := 0; rowIdx < len(createValues.Values); rowIdx++ {
513513
for _, column := range allColumns {
514514
if field := findFieldByDBName(schema, column); field != nil {
515-
stmt.Vars = append(stmt.Vars, sql.Out{Dest: createTypedDestination(field)})
516-
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_affected_records.COUNT > %d THEN :%d := l_affected_records(%d).", rowIdx, outParamIndex+1, rowIdx+1))
517-
writeQuotedIdentifier(&plsqlBuilder, column)
518-
plsqlBuilder.WriteString("; END IF;\n")
515+
if isJSONField(field) {
516+
// JSON -> text bind
517+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new(string)})
518+
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_affected_records.COUNT > %d THEN :%d := JSON_SERIALIZE(l_affected_records(%d).", rowIdx, outParamIndex+1, rowIdx+1))
519+
writeQuotedIdentifier(&plsqlBuilder, column)
520+
plsqlBuilder.WriteString(" RETURNING CLOB); END IF;\n")
521+
} else {
522+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: createTypedDestination(field)})
523+
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_affected_records.COUNT > %d THEN :%d := l_affected_records(%d).", rowIdx, outParamIndex+1, rowIdx+1))
524+
writeQuotedIdentifier(&plsqlBuilder, column)
525+
plsqlBuilder.WriteString("; END IF;\n")
526+
}
519527
outParamIndex++
520528
}
521529
}
@@ -613,7 +621,7 @@ func buildBulkInsertOnlyPLSQL(db *gorm.DB, createValues clause.Values) {
613621
}
614622
plsqlBuilder.WriteString("\n BULK COLLECT INTO l_inserted_records;\n")
615623

616-
// Add OUT parameter population
624+
// Add OUT parameter population (JSON serialized to CLOB)
617625
outParamIndex := len(stmt.Vars)
618626
for rowIdx := 0; rowIdx < len(createValues.Values); rowIdx++ {
619627
for _, column := range allColumns {
@@ -622,9 +630,20 @@ func buildBulkInsertOnlyPLSQL(db *gorm.DB, createValues clause.Values) {
622630
quotedColumn := columnBuilder.String()
623631

624632
if field := findFieldByDBName(schema, column); field != nil {
625-
stmt.Vars = append(stmt.Vars, sql.Out{Dest: createTypedDestination(field)})
626-
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_inserted_records.COUNT > %d THEN :%d := l_inserted_records(%d).%s; END IF;\n",
627-
rowIdx, outParamIndex+1, rowIdx+1, quotedColumn))
633+
if isJSONField(field) {
634+
// JSON -> text bind
635+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new(string)})
636+
plsqlBuilder.WriteString(fmt.Sprintf(
637+
" IF l_inserted_records.COUNT > %d THEN :%d := JSON_SERIALIZE(l_inserted_records(%d).%s RETURNING CLOB); END IF;\n",
638+
rowIdx, outParamIndex+1, rowIdx+1, quotedColumn,
639+
))
640+
} else {
641+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: createTypedDestination(field)})
642+
plsqlBuilder.WriteString(fmt.Sprintf(
643+
" IF l_inserted_records.COUNT > %d THEN :%d := l_inserted_records(%d).%s; END IF;\n",
644+
rowIdx, outParamIndex+1, rowIdx+1, quotedColumn,
645+
))
646+
}
628647
outParamIndex++
629648
}
630649
}

oracle/delete.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -283,27 +283,37 @@ func buildBulkDeletePLSQL(db *gorm.DB) {
283283
}
284284
plsqlBuilder.WriteString("\n BULK COLLECT INTO l_deleted_records;\n")
285285

286-
// Create OUT parameters for each field and each row that will be deleted
286+
// Create OUT parameters for each field and each row that will be deleted (JSON-safe)
287287
outParamIndex := len(stmt.Vars)
288-
//TODO make it configurable
289-
estimatedRows := 100 // Estimate maximum rows to delete
288+
// keep your current fixed cap (same as other callbacks)
289+
estimatedRows := 100
290290

291291
for rowIdx := 0; rowIdx < estimatedRows; rowIdx++ {
292292
for _, column := range allColumns {
293-
field := findFieldByDBName(schema, column)
294-
if field != nil {
295-
dest := createTypedDestination(field)
296-
stmt.Vars = append(stmt.Vars, sql.Out{Dest: dest})
297-
298-
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_deleted_records.COUNT > %d THEN\n", rowIdx))
299-
plsqlBuilder.WriteString(fmt.Sprintf(" :%d := l_deleted_records(%d).", outParamIndex+1, rowIdx+1))
300-
writeQuotedIdentifier(&plsqlBuilder, column)
301-
plsqlBuilder.WriteString(";\n")
302-
plsqlBuilder.WriteString(" END IF;\n")
293+
if field := findFieldByDBName(schema, column); field != nil {
294+
if isJSONField(field) {
295+
// JSON -> text bind
296+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new(string)})
297+
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_deleted_records.COUNT > %d THEN\n", rowIdx))
298+
plsqlBuilder.WriteString(fmt.Sprintf(" :%d := JSON_SERIALIZE(l_deleted_records(%d).", outParamIndex+1, rowIdx+1))
299+
writeQuotedIdentifier(&plsqlBuilder, column)
300+
plsqlBuilder.WriteString(" RETURNING CLOB);\n")
301+
plsqlBuilder.WriteString(" END IF;\n")
302+
} else {
303+
// non-JSON as before
304+
dest := createTypedDestination(field)
305+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: dest})
306+
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_deleted_records.COUNT > %d THEN\n", rowIdx))
307+
plsqlBuilder.WriteString(fmt.Sprintf(" :%d := l_deleted_records(%d).", outParamIndex+1, rowIdx+1))
308+
writeQuotedIdentifier(&plsqlBuilder, column)
309+
plsqlBuilder.WriteString(";\n")
310+
plsqlBuilder.WriteString(" END IF;\n")
311+
}
303312
outParamIndex++
304313
}
305314
}
306315
}
316+
307317
plsqlBuilder.WriteString("END;")
308318

309319
stmt.SQL.Reset()

oracle/update.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,12 @@ func buildUpdatePLSQL(db *gorm.DB) {
542542
for _, column := range allColumns {
543543
field := findFieldByDBName(schema, column)
544544
if field != nil {
545-
dest := createTypedDestination(field)
545+
var dest interface{}
546+
if isJSONField(field) {
547+
dest = new(string) // JSON comes back serialized as text
548+
} else {
549+
dest = createTypedDestination(field)
550+
}
546551
stmt.Vars = append(stmt.Vars, sql.Out{Dest: dest})
547552
}
548553
}
@@ -553,18 +558,27 @@ func buildUpdatePLSQL(db *gorm.DB) {
553558
for colIdx, column := range allColumns {
554559
field := findFieldByDBName(schema, column)
555560
if field != nil {
556-
// Calculate the correct parameter index (1-based for Oracle)
557561
paramIndex := outParamStartIndex + (rowIdx * len(allColumns)) + colIdx + 1
558562

559-
// Add the assignment to PL/SQL with correct parameter reference
560-
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_updated_records.COUNT > %d THEN\n", rowIdx))
561-
plsqlBuilder.WriteString(fmt.Sprintf(" :%d := l_updated_records(%d).", paramIndex, rowIdx+1))
562-
writeQuotedIdentifier(&plsqlBuilder, column)
563-
plsqlBuilder.WriteString(";\n")
564-
plsqlBuilder.WriteString(" END IF;\n")
563+
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_updated_records.COUNT > %d THEN ", rowIdx))
564+
plsqlBuilder.WriteString(fmt.Sprintf(":%d := ", paramIndex))
565+
566+
if isJSONField(field) {
567+
// serialize JSON so it binds as text
568+
plsqlBuilder.WriteString("JSON_SERIALIZE(")
569+
plsqlBuilder.WriteString(fmt.Sprintf("l_updated_records(%d).", rowIdx+1))
570+
writeQuotedIdentifier(&plsqlBuilder, column)
571+
plsqlBuilder.WriteString(" RETURNING CLOB)")
572+
} else {
573+
plsqlBuilder.WriteString(fmt.Sprintf("l_updated_records(%d).", rowIdx+1))
574+
writeQuotedIdentifier(&plsqlBuilder, column)
575+
}
576+
577+
plsqlBuilder.WriteString("; END IF;\n")
565578
}
566579
}
567580
}
581+
568582
plsqlBuilder.WriteString("END;")
569583

570584
stmt.SQL.Reset()

0 commit comments

Comments
 (0)