Skip to content

Commit 40899c0

Browse files
committed
Add support for serializer
1 parent 153b92e commit 40899c0

File tree

3 files changed

+131
-10
lines changed

3 files changed

+131
-10
lines changed

oracle/common.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func getOracleArrayType(field *schema.Field, values []any) string {
7979
case schema.Bytes:
8080
return "TABLE OF BLOB"
8181
default:
82-
return "TABLE OF VARCHAR2(4000)" // Safe default
82+
return "TABLE OF " + strings.ToUpper(string(field.DataType))
8383
}
8484
}
8585

@@ -110,13 +110,52 @@ func findFieldByDBName(schema *schema.Schema, dbName string) *schema.Field {
110110
return nil
111111
}
112112

113+
// Extra data types to determine the destination type for OUT parameters
114+
// when using a serializer
115+
const (
116+
Timestamp schema.DataType = "timestamp"
117+
TimestampWithTimeZone schema.DataType = "timestamp with time zone"
118+
)
119+
113120
// Create typed destination for OUT parameters
114121
func createTypedDestination(f *schema.Field) interface{} {
115122
if f == nil {
116123
var s string
117124
return &s
118125
}
119126

127+
// If the field has a serializer, the field type may not be directly related to the column type in the database.
128+
// In this case, determine the destination type using the field's data type, which is the column type in the database.
129+
// use the data type as the destination type,
130+
// because
131+
// If the type is declared in the tag,
132+
if f.Serializer != nil {
133+
dt := strings.ToLower(string(f.DataType))
134+
switch schema.DataType(dt) {
135+
case schema.Bool:
136+
return new(bool)
137+
case schema.Uint:
138+
return new(uint64)
139+
case schema.Int:
140+
return new(int64)
141+
case schema.Float:
142+
return new(float64)
143+
case schema.String:
144+
return new(string)
145+
case Timestamp:
146+
fallthrough
147+
case TimestampWithTimeZone:
148+
fallthrough
149+
case schema.Time:
150+
return new(time.Time)
151+
case schema.Bytes:
152+
return new([]byte)
153+
default:
154+
// Fallback
155+
return new(string)
156+
}
157+
}
158+
120159
ft := f.FieldType
121160
for ft.Kind() == reflect.Ptr {
122161
ft = ft.Elem()
@@ -218,6 +257,13 @@ func convertFromOracleToField(value interface{}, field *schema.Field) interface{
218257
return nil
219258
}
220259

260+
// Deserialize data into objects when a serializer is used
261+
if field.Serializer != nil {
262+
serializerField := field.NewValuePool.Get().(sql.Scanner)
263+
serializerField.Scan(value)
264+
return serializerField
265+
}
266+
221267
targetType := field.FieldType
222268
var converted any
223269

oracle/update.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ func checkMissingWhereConditions(db *gorm.DB) {
168168
}
169169
// Has non-soft-delete equality condition, this is valid
170170
hasMeaningfulConditions = true
171-
break
172171
case clause.IN:
173172
// Has IN condition with values, this is valid
174173
if len(e.Values) > 0 {
@@ -187,11 +186,9 @@ func checkMissingWhereConditions(db *gorm.DB) {
187186
}
188187
// Has non-soft-delete expression condition, consider it valid
189188
hasMeaningfulConditions = true
190-
break
191189
case clause.AndConditions, clause.OrConditions:
192190
// Complex conditions are likely valid (but we could be more thorough here)
193191
hasMeaningfulConditions = true
194-
break
195192
case clause.Where:
196193
// Handle nested WHERE clauses - recursively check their expressions
197194
if len(e.Exprs) > 0 {
@@ -208,7 +205,6 @@ func checkMissingWhereConditions(db *gorm.DB) {
208205
default:
209206
// Unknown condition types - assume they're meaningful for safety
210207
hasMeaningfulConditions = true
211-
break
212208
}
213209

214210
// If we found meaningful conditions, we can stop checking

tests/serializer_test.go

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ type SerializerStruct struct {
6060
Roles3 *Roles `gorm:"serializer:json;not null"`
6161
Contracts map[string]interface{} `gorm:"serializer:json"`
6262
JobInfo Job `gorm:"type:bytes;serializer:gob"`
63-
CreatedTime int64 `gorm:"serializer:unixtime;type:timestamp"` // store time in db, use int as field type
64-
UpdatedTime *int64 `gorm:"serializer:unixtime;type:timestamp"` // store time in db, use int as field type
63+
CreatedTime int64 `gorm:"serializer:unixtime;type:timestamp with time zone"` // store time in db, use int as field type
64+
UpdatedTime *int64 `gorm:"serializer:unixtime;type:timestamp with time zone"` // store time in db, use int as field type
6565
CustomSerializerString string `gorm:"serializer:custom"`
6666
EncryptedString EncryptedString
6767
}
@@ -122,13 +122,16 @@ func (c *CustomSerializer) Value(ctx context.Context, field *schema.Field, dst r
122122
}
123123

124124
func TestSerializer(t *testing.T) {
125-
schema.RegisterSerializer("custom", NewCustomSerializer("hello"))
125+
if _, ok := schema.GetSerializer("custom"); !ok {
126+
schema.RegisterSerializer("custom", NewCustomSerializer("hello"))
127+
}
126128
DB.Migrator().DropTable(adaptorSerializerModel(&SerializerStruct{}))
127129
if err := DB.Migrator().AutoMigrate(adaptorSerializerModel(&SerializerStruct{})); err != nil {
128130
t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err)
129131
}
130132

131133
createdAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
134+
fmt.Printf("======= createdAt1 = %v\n", createdAt.Unix())
132135
updatedAt := createdAt.Unix()
133136

134137
data := SerializerStruct{
@@ -168,8 +171,82 @@ func TestSerializer(t *testing.T) {
168171
}
169172
}
170173

174+
// Issue 48: https://github.com/oracle-samples/gorm-oracle/issues/48
175+
func TestSerializerBulkInsert(t *testing.T) {
176+
if _, ok := schema.GetSerializer("custom"); !ok {
177+
schema.RegisterSerializer("custom", NewCustomSerializer("hello"))
178+
}
179+
DB.Migrator().DropTable(adaptorSerializerModel(&SerializerStruct{}))
180+
if err := DB.Migrator().AutoMigrate(adaptorSerializerModel(&SerializerStruct{})); err != nil {
181+
t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err)
182+
}
183+
184+
createdAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
185+
updatedAt := createdAt.Unix()
186+
187+
data := []SerializerStruct{
188+
{
189+
Name: []byte("jinzhu"),
190+
Roles: []string{"r1", "r2"},
191+
Roles3: &Roles{},
192+
Contracts: map[string]interface{}{"name": "jinzhu", "age": 10},
193+
EncryptedString: EncryptedString("pass"),
194+
CreatedTime: createdAt.Unix(),
195+
UpdatedTime: &updatedAt,
196+
JobInfo: Job{
197+
Title: "programmer",
198+
Number: 9920,
199+
Location: "Kenmawr",
200+
IsIntern: false,
201+
},
202+
CustomSerializerString: "world",
203+
},
204+
{
205+
Name: []byte("john"),
206+
Roles: []string{"l1", "l2"},
207+
Roles3: &Roles{},
208+
Contracts: map[string]interface{}{"name": "john", "age": 20},
209+
EncryptedString: EncryptedString("pass"),
210+
CreatedTime: createdAt.Unix(),
211+
UpdatedTime: &updatedAt,
212+
JobInfo: Job{
213+
Title: "manager",
214+
Number: 7710,
215+
Location: "Redwood City",
216+
IsIntern: false,
217+
},
218+
CustomSerializerString: "foo",
219+
},
220+
}
221+
222+
if err := DB.Create(&data).Error; err != nil {
223+
t.Fatalf("failed to create data, got error %v", err)
224+
}
225+
226+
var result []SerializerStruct
227+
if err := DB.Find(&result).Error; err != nil {
228+
t.Fatalf("failed to query data, got error %v", err)
229+
}
230+
231+
tests.AssertEqual(t, result, data)
232+
233+
// Update all the "roles" columns to "n1"
234+
if err := DB.Model(&SerializerStruct{}).Where("\"roles\" IS NOT NULL").Update("roles", []string{"n1"}).Error; err != nil {
235+
t.Fatalf("failed to update data's roles, got error %v", err)
236+
}
237+
238+
var count int64
239+
if err := DB.Model(&SerializerStruct{}).Where("\"roles\" = ?", "n1").Count(&count).Error; err != nil {
240+
t.Fatalf("failed to query data, got error %v", err)
241+
}
242+
243+
tests.AssertEqual(t, count, 2)
244+
}
245+
171246
func TestSerializerZeroValue(t *testing.T) {
172-
schema.RegisterSerializer("custom", NewCustomSerializer("hello"))
247+
if _, ok := schema.GetSerializer("custom"); !ok {
248+
schema.RegisterSerializer("custom", NewCustomSerializer("hello"))
249+
}
173250
DB.Migrator().DropTable(adaptorSerializerModel(&SerializerStruct{}))
174251
if err := DB.Migrator().AutoMigrate(adaptorSerializerModel(&SerializerStruct{})); err != nil {
175252
t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err)
@@ -200,7 +277,9 @@ func TestSerializerZeroValue(t *testing.T) {
200277
}
201278

202279
func TestSerializerAssignFirstOrCreate(t *testing.T) {
203-
schema.RegisterSerializer("custom", NewCustomSerializer("hello"))
280+
if _, ok := schema.GetSerializer("custom"); !ok {
281+
schema.RegisterSerializer("custom", NewCustomSerializer("hello"))
282+
}
204283
DB.Migrator().DropTable(adaptorSerializerModel(&SerializerStruct{}))
205284
if err := DB.Migrator().AutoMigrate(adaptorSerializerModel(&SerializerStruct{})); err != nil {
206285
t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err)

0 commit comments

Comments
 (0)