Skip to content

Commit eb1f407

Browse files
Adding support for json.RawMessage
1 parent 49eaeb0 commit eb1f407

File tree

5 files changed

+117
-41
lines changed

5 files changed

+117
-41
lines changed

go.mod

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@ module github.com/oracle-samples/gorm-oracle
22

33
go 1.24.4
44

5-
require gorm.io/gorm v1.30.0
5+
require gorm.io/gorm v1.31.0
66

7-
require github.com/godror/godror v0.49.0
7+
require github.com/godror/godror v0.49.3
88

99
require (
10+
filippo.io/edwards25519 v1.1.0 // indirect
1011
github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
1112
github.com/go-logfmt/logfmt v0.6.0 // indirect
13+
github.com/go-sql-driver/mysql v1.8.1 // indirect
1214
github.com/godror/knownpb v0.3.0 // indirect
15+
github.com/google/uuid v1.6.0 // indirect
1316
github.com/jinzhu/inflection v1.0.0 // indirect
1417
github.com/jinzhu/now v1.1.5 // indirect
15-
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
16-
golang.org/x/term v0.27.0 // indirect
17-
golang.org/x/text v0.25.0 // indirect
18-
google.golang.org/protobuf v1.36.6 // indirect
18+
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
19+
golang.org/x/sys v0.36.0 // indirect
20+
golang.org/x/term v0.35.0 // indirect
21+
golang.org/x/text v0.29.0 // indirect
22+
google.golang.org/protobuf v1.36.9 // indirect
23+
gorm.io/datatypes v1.2.6 // indirect
24+
gorm.io/driver/mysql v1.5.6 // indirect
1925
)

oracle/common.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ package oracle
4040

4141
import (
4242
"database/sql"
43+
"encoding/json"
4344
"fmt"
4445
"reflect"
4546
"strings"
@@ -175,6 +176,17 @@ func convertValue(val interface{}) interface{} {
175176
}
176177

177178
switch v := val.(type) {
179+
case json.RawMessage:
180+
if v == nil {
181+
return nil
182+
}
183+
return []byte(v)
184+
case *json.RawMessage:
185+
if v == nil {
186+
return nil
187+
}
188+
b := []byte(*v)
189+
return b
178190
case bool:
179191
if v {
180192
return 1
@@ -199,6 +211,17 @@ func convertFromOracleToField(value interface{}, field *schema.Field) interface{
199211
if isPtr {
200212
targetType = targetType.Elem()
201213
}
214+
if field.FieldType == reflect.TypeOf(json.RawMessage{}) {
215+
switch v := value.(type) {
216+
case []byte:
217+
return json.RawMessage(v) // from BLOB
218+
case *[]byte:
219+
if v == nil {
220+
return json.RawMessage(nil)
221+
}
222+
return json.RawMessage(*v)
223+
}
224+
}
202225
if isJSONField(field) {
203226
switch v := value.(type) {
204227
case string:
@@ -285,19 +308,21 @@ func convertFromOracleToField(value interface{}, field *schema.Field) interface{
285308
}
286309

287310
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
311+
_rawMsgT := reflect.TypeOf(json.RawMessage{})
312+
_gormJSON := reflect.TypeOf(datatypes.JSON{})
313+
if f == nil {
314+
return false
295315
}
296-
// Detect gorm.io/datatypes.JSON by reflected type
297-
if f.FieldType.Name() == "JSON" && f.FieldType.PkgPath() == "gorm.io/datatypes" {
298-
return true
316+
ft := f.FieldType
317+
return ft == _rawMsgT || ft == _gormJSON
318+
}
319+
320+
func isRawMessageField(f *schema.Field) bool {
321+
t := f.FieldType
322+
for t.Kind() == reflect.Ptr {
323+
t = t.Elem()
299324
}
300-
return false
325+
return t == reflect.TypeOf(json.RawMessage(nil))
301326
}
302327

303328
// Helper function to handle primitive type conversions

oracle/create.go

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -513,11 +513,25 @@ func buildBulkMergePLSQL(db *gorm.DB, createValues clause.Values, onConflictClau
513513
for _, column := range allColumns {
514514
if field := findFieldByDBName(schema, column); field != nil {
515515
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")
516+
if isRawMessageField(field) {
517+
// Column is a BLOB, return raw bytes; no JSON_SERIALIZE
518+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new([]byte)})
519+
plsqlBuilder.WriteString(fmt.Sprintf(
520+
" IF l_affected_records.COUNT > %d THEN :%d := l_affected_records(%d).",
521+
rowIdx, outParamIndex+1, rowIdx+1,
522+
))
523+
writeQuotedIdentifier(&plsqlBuilder, column)
524+
plsqlBuilder.WriteString("; END IF;\n")
525+
} else {
526+
// datatypes.JSON (text-based) -> serialize to CLOB
527+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new(string)})
528+
plsqlBuilder.WriteString(fmt.Sprintf(
529+
" IF l_affected_records.COUNT > %d THEN :%d := JSON_SERIALIZE(l_affected_records(%d).",
530+
rowIdx, outParamIndex+1, rowIdx+1,
531+
))
532+
writeQuotedIdentifier(&plsqlBuilder, column)
533+
plsqlBuilder.WriteString(" RETURNING CLOB); END IF;\n")
534+
}
521535
} else {
522536
stmt.Vars = append(stmt.Vars, sql.Out{Dest: createTypedDestination(field)})
523537
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_affected_records.COUNT > %d THEN :%d := l_affected_records(%d).", rowIdx, outParamIndex+1, rowIdx+1))
@@ -631,12 +645,21 @@ func buildBulkInsertOnlyPLSQL(db *gorm.DB, createValues clause.Values) {
631645

632646
if field := findFieldByDBName(schema, column); field != nil {
633647
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-
))
648+
if isRawMessageField(field) {
649+
// Column is a BLOB, return raw bytes; no JSON_SERIALIZE
650+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new([]byte)})
651+
plsqlBuilder.WriteString(fmt.Sprintf(
652+
" IF l_inserted_records.COUNT > %d THEN :%d := l_inserted_records(%d).%s; END IF;\n",
653+
rowIdx, outParamIndex+1, rowIdx+1, quotedColumn,
654+
))
655+
} else {
656+
// datatypes.JSON (text-based) -> serialize to CLOB
657+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new(string)})
658+
plsqlBuilder.WriteString(fmt.Sprintf(
659+
" IF l_inserted_records.COUNT > %d THEN :%d := JSON_SERIALIZE(l_inserted_records(%d).%s RETURNING CLOB); END IF;\n",
660+
rowIdx, outParamIndex+1, rowIdx+1, quotedColumn,
661+
))
662+
}
640663
} else {
641664
stmt.Vars = append(stmt.Vars, sql.Out{Dest: createTypedDestination(field)})
642665
plsqlBuilder.WriteString(fmt.Sprintf(

oracle/delete.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -292,13 +292,24 @@ func buildBulkDeletePLSQL(db *gorm.DB) {
292292
for _, column := range allColumns {
293293
if field := findFieldByDBName(schema, column); field != nil {
294294
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")
295+
if isRawMessageField(field) {
296+
// Column is a BLOB, return raw bytes; no JSON_SERIALIZE
297+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new([]byte)})
298+
plsqlBuilder.WriteString(fmt.Sprintf(
299+
" IF l_deleted_records.COUNT > %d THEN :%d := l_deleted_records(%d).",
300+
rowIdx, outParamIndex+1, rowIdx+1,
301+
))
302+
writeQuotedIdentifier(&plsqlBuilder, column)
303+
plsqlBuilder.WriteString("; END IF;\n")
304+
} else {
305+
// JSON -> text bind
306+
stmt.Vars = append(stmt.Vars, sql.Out{Dest: new(string)})
307+
plsqlBuilder.WriteString(fmt.Sprintf(" IF l_deleted_records.COUNT > %d THEN\n", rowIdx))
308+
plsqlBuilder.WriteString(fmt.Sprintf(" :%d := JSON_SERIALIZE(l_deleted_records(%d).", outParamIndex+1, rowIdx+1))
309+
writeQuotedIdentifier(&plsqlBuilder, column)
310+
plsqlBuilder.WriteString(" RETURNING CLOB);\n")
311+
plsqlBuilder.WriteString(" END IF;\n")
312+
}
302313
} else {
303314
// non-JSON as before
304315
dest := createTypedDestination(field)

oracle/update.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,13 @@ func buildUpdatePLSQL(db *gorm.DB) {
544544
if field != nil {
545545
var dest interface{}
546546
if isJSONField(field) {
547-
dest = new(string) // JSON comes back serialized as text
547+
if isRawMessageField(field) {
548+
// RawMessage -> BLOB -> []byte
549+
dest = new([]byte)
550+
} else {
551+
// datatypes.JSON -> text -> string (CLOB)
552+
dest = new(string)
553+
}
548554
} else {
549555
dest = createTypedDestination(field)
550556
}
@@ -564,11 +570,16 @@ func buildUpdatePLSQL(db *gorm.DB) {
564570
plsqlBuilder.WriteString(fmt.Sprintf(":%d := ", paramIndex))
565571

566572
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)")
573+
if isRawMessageField(field) {
574+
plsqlBuilder.WriteString(fmt.Sprintf("l_updated_records(%d).", rowIdx+1))
575+
writeQuotedIdentifier(&plsqlBuilder, column)
576+
} else {
577+
// serialize JSON so it binds as text
578+
plsqlBuilder.WriteString("JSON_SERIALIZE(")
579+
plsqlBuilder.WriteString(fmt.Sprintf("l_updated_records(%d).", rowIdx+1))
580+
writeQuotedIdentifier(&plsqlBuilder, column)
581+
plsqlBuilder.WriteString(" RETURNING CLOB)")
582+
}
572583
} else {
573584
plsqlBuilder.WriteString(fmt.Sprintf("l_updated_records(%d).", rowIdx+1))
574585
writeQuotedIdentifier(&plsqlBuilder, column)

0 commit comments

Comments
 (0)