Skip to content

Commit f28e464

Browse files
committed
decimal: fix default values
Unlike timestamp types which accepts native values, decimal types must be big.Rat, which means defaults need to be converted. This fixes: #202
1 parent d517b19 commit f28e464

File tree

4 files changed

+43
-15
lines changed

4 files changed

+43
-15
lines changed

binary_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func testBinaryDecodePass(t *testing.T, schema string, datum interface{}, encode
8888
t.Helper()
8989
codec, err := NewCodec(schema)
9090
if err != nil {
91-
t.Fatal(err)
91+
t.Fatalf("unable to create codec: %s", err)
9292
}
9393

9494
value, remaining, err := codec.NativeFromBinary(encoded)

logical_type_test.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ func TestDecimalBytesLogicalTypeEncode(t *testing.T) {
151151
d, _ := new(big.Int).SetString("100000000000000000000000000000000000000", 10)
152152
largeRat := new(big.Rat).SetFrac(n, d)
153153
testBinaryCodecPass(t, largeDecimalSchema, largeRat, []byte("\x40\x1b\x4b\x68\x19\x26\x11\xfa\xea\x20\x8f\xca\x21\x62\x7b\xe9\xda\xee\x32\x19\x83\x83\x95\x5d\xe8\x13\x1f\x4b\xf1\xc7\x1c\x71\xc7"))
154-
155154
}
156155

157156
func TestDecimalFixedLogicalTypeEncode(t *testing.T) {
@@ -178,6 +177,12 @@ func TestDecimalBytesLogicalTypeInRecordEncode(t *testing.T) {
178177
testBinaryCodecPass(t, schema, map[string]interface{}{"mydecimal": big.NewRat(617, 50)}, []byte("\x04\x04\xd2"))
179178
}
180179

180+
func TestDecimalBytesLogicalTypeInRecordDecodeWithDefault(t *testing.T) {
181+
schema := `{"type": "record", "name": "myrecord", "fields" : [
182+
{"name": "mydecimal", "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2, "default":"\u0000"}]}`
183+
testBinaryCodecPass(t, schema, map[string]interface{}{"mydecimal": big.NewRat(617, 50)}, []byte("\x04\x04\xd2"))
184+
}
185+
181186
func TestValidatedStringLogicalTypeInRecordEncode(t *testing.T) {
182187
schema := `{
183188
"type": "record",

record.go

+21-7
Original file line numberDiff line numberDiff line change
@@ -66,43 +66,43 @@ func makeRecordCodec(st map[string]*Codec, enclosingNamespace string, schemaMap
6666
case "boolean":
6767
v, ok := defaultValue.(bool)
6868
if !ok {
69-
return nil, fmt.Errorf("Record %q field %q: default value ought to encode using field schema: %s", c.typeName, fieldName, err)
69+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a bool type, got: %T", c.typeName, fieldName, defaultValue)
7070
}
7171
defaultValue = v
7272
case "bytes":
7373
v, ok := defaultValue.(string)
7474
if !ok {
75-
return nil, fmt.Errorf("Record %q field %q: default value ought to encode using field schema: %s", c.typeName, fieldName, err)
75+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a string type got: %T", c.typeName, fieldName, defaultValue)
7676
}
7777
defaultValue = []byte(v)
7878
case "double":
7979
v, ok := defaultValue.(float64)
8080
if !ok {
81-
return nil, fmt.Errorf("Record %q field %q: default value ought to encode using field schema: %s", c.typeName, fieldName, err)
81+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a double type got: %T", c.typeName, fieldName, defaultValue)
8282
}
8383
defaultValue = v
8484
case "float":
8585
v, ok := defaultValue.(float64)
8686
if !ok {
87-
return nil, fmt.Errorf("Record %q field %q: default value ought to encode using field schema: %s", c.typeName, fieldName, err)
87+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a float type got: %T", c.typeName, fieldName, defaultValue)
8888
}
8989
defaultValue = float32(v)
9090
case "int":
9191
v, ok := defaultValue.(float64)
9292
if !ok {
93-
return nil, fmt.Errorf("Record %q field %q: default value ought to encode using field schema: %s", c.typeName, fieldName, err)
93+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a number type got: %T", c.typeName, fieldName, defaultValue)
9494
}
9595
defaultValue = int32(v)
9696
case "long":
9797
v, ok := defaultValue.(float64)
9898
if !ok {
99-
return nil, fmt.Errorf("Record %q field %q: default value ought to encode using field schema: %s", c.typeName, fieldName, err)
99+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a number type got: %T", c.typeName, fieldName, defaultValue)
100100
}
101101
defaultValue = int64(v)
102102
case "string":
103103
v, ok := defaultValue.(string)
104104
if !ok {
105-
return nil, fmt.Errorf("Record %q field %q: default value ought to encode using field schema: %s", c.typeName, fieldName, err)
105+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a string type got: %T", c.typeName, fieldName, defaultValue)
106106
}
107107
defaultValue = v
108108
case "union":
@@ -118,6 +118,20 @@ func makeRecordCodec(st map[string]*Codec, enclosingNamespace string, schemaMap
118118
defaultValue = Union(fieldCodec.schemaOriginal, defaultValue)
119119
default:
120120
debug("fieldName: %q; type: %q; defaultValue: %T(%#v)\n", fieldName, c.typeName, defaultValue, defaultValue)
121+
122+
// Support defaults for logical types
123+
if logicalType, ok := fieldSchemaMap["logicalType"]; ok {
124+
if logicalType == "decimal" {
125+
v, ok := defaultValue.(string)
126+
if !ok {
127+
return nil, fmt.Errorf("Record %q field %q: default value ought to have a string type got: %T", c.typeName, fieldName, defaultValue)
128+
}
129+
defaultValue, _, err = fieldCodec.nativeFromBinary([]byte(v))
130+
if err != nil {
131+
return nil, fmt.Errorf("Record %q field %q: default value ought to decode from textual: %w", c.typeName, fieldName, err)
132+
}
133+
}
134+
}
121135
}
122136

123137
// attempt to encode default value using codec

record_test.go

+15-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package goavro
1212
import (
1313
"bytes"
1414
"fmt"
15+
"math/big"
1516
"testing"
1617
)
1718

@@ -389,7 +390,7 @@ func TestRecordFieldDefaultValue(t *testing.T) {
389390
testSchemaValid(t, `{"type":"record","name":"r1","fields":[{"name":"f1","type":"string","default":"foo"}]}`)
390391
testSchemaInvalid(t,
391392
`{"type":"record","name":"r1","fields":[{"name":"f1","type":"int","default":"foo"}]}`,
392-
"default value ought to encode using field schema")
393+
"default value ought to have a number type")
393394
}
394395

395396
func TestRecordFieldUnionDefaultValue(t *testing.T) {
@@ -618,7 +619,7 @@ func TestRecordFieldFixedDefaultValue(t *testing.T) {
618619

619620
func TestRecordFieldDefaultValueTypes(t *testing.T) {
620621
t.Run("success", func(t *testing.T) {
621-
codec, err := NewCodec(`{"type": "record", "name": "r1", "fields":[{"name": "someBoolean", "type": "boolean", "default": true},{"name": "someBytes", "type": "bytes", "default": "0"},{"name": "someDouble", "type": "double", "default": 0},{"name": "someFloat", "type": "float", "default": 0},{"name": "someInt", "type": "int", "default": 0},{"name": "someLong", "type": "long", "default": 0},{"name": "someString", "type": "string", "default": "0"}]}`)
622+
codec, err := NewCodec(`{"type": "record", "name": "r1", "fields":[{"name": "someBoolean", "type": "boolean", "default": true},{"name": "someBytes", "type": "bytes", "default": "0"},{"name": "someDouble", "type": "double", "default": 0},{"name": "someFloat", "type": "float", "default": 0},{"name": "someInt", "type": "int", "default": 0},{"name": "someLong", "type": "long", "default": 0},{"name": "someString", "type": "string", "default": "0"}, {"name":"someTimestamp", "type":"long", "logicalType":"timestamp-millis","default":0}, {"name": "someDecimal", "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2, "default":"\u0000"}]}`)
622623
ensureError(t, err)
623624

624625
r1, _, err := codec.NativeFromTextual([]byte("{}"))
@@ -660,24 +661,32 @@ func TestRecordFieldDefaultValueTypes(t *testing.T) {
660661
if _, ok := someString.(string); !ok {
661662
t.Errorf("GOT: %T; WANT: string", someString)
662663
}
664+
someTimestamp := r1m["someTimestamp"]
665+
if _, ok := someTimestamp.(float64); !ok {
666+
t.Errorf("GOT: %T; WANT: float64", someTimestamp)
667+
}
668+
someDecimal := r1m["someDecimal"]
669+
if _, ok := someDecimal.(*big.Rat); !ok {
670+
t.Errorf("GOT: %T; WANT: *big.Rat", someDecimal)
671+
}
663672
})
664673

665674
t.Run("provided default is wrong type", func(t *testing.T) {
666675
t.Run("long", func(t *testing.T) {
667676
_, err := NewCodec(`{"type": "record", "name": "r1", "fields":[{"name": "someLong", "type": "long", "default": "0"},{"name": "someInt", "type": "int", "default": 0},{"name": "someFloat", "type": "float", "default": 0},{"name": "someDouble", "type": "double", "default": 0}]}`)
668-
ensureError(t, err, "field schema")
677+
ensureError(t, err, "default value ought to have a number type")
669678
})
670679
t.Run("int", func(t *testing.T) {
671680
_, err := NewCodec(`{"type": "record", "name": "r1", "fields":[{"name": "someLong", "type": "long", "default": 0},{"name": "someInt", "type": "int", "default": "0"},{"name": "someFloat", "type": "float", "default": 0},{"name": "someDouble", "type": "double", "default": 0}]}`)
672-
ensureError(t, err, "field schema")
681+
ensureError(t, err, "default value ought to have a number type")
673682
})
674683
t.Run("float", func(t *testing.T) {
675684
_, err := NewCodec(`{"type": "record", "name": "r1", "fields":[{"name": "someLong", "type": "long", "default": 0},{"name": "someInt", "type": "int", "default": 0},{"name": "someFloat", "type": "float", "default": "0"},{"name": "someDouble", "type": "double", "default": 0}]}`)
676-
ensureError(t, err, "field schema")
685+
ensureError(t, err, "default value ought to have a float type")
677686
})
678687
t.Run("double", func(t *testing.T) {
679688
_, err := NewCodec(`{"type": "record", "name": "r1", "fields":[{"name": "someLong", "type": "long", "default": 0},{"name": "someInt", "type": "int", "default": 0},{"name": "someFloat", "type": "float", "default": 0},{"name": "someDouble", "type": "double", "default": "0"}]}`)
680-
ensureError(t, err, "field schema")
689+
ensureError(t, err, "default value ought to have a double type")
681690
})
682691
})
683692

0 commit comments

Comments
 (0)