Skip to content

Commit

Permalink
Allow decimal logical type of precision 1
Browse files Browse the repository at this point in the history
Fix scale type error message

closes #188
replaces / closes #196

Co-Authored-By: Henry Megarry <[email protected]>
  • Loading branch information
mooseburgr and henry-megarry committed Jan 13, 2022
1 parent ee02992 commit 22dfedf
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 2 deletions.
4 changes: 2 additions & 2 deletions logical_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,14 @@ func precisionAndScaleFromSchemaMap(schemaMap map[string]interface{}) (int, int,
return 0, 0, fmt.Errorf("cannot create decimal logical type with wrong precision type; expected: float64; received: %T", p1)
}
p3 := int(p2)
if p3 <= 1 {
if p3 < 1 {
return 0, 0, fmt.Errorf("cannot create decimal logical type when precision is less than one: %d", p3)
}
var s3 int // scale defaults to 0 if not set
if s1, ok := schemaMap["scale"]; ok {
s2, ok := s1.(float64)
if !ok {
return 0, 0, fmt.Errorf("cannot create decimal logical type with wrong precision type; expected: float64; received: %T", p1)
return 0, 0, fmt.Errorf("cannot create decimal logical type with wrong scale type; expected: float64; received: %T", s1)
}
s3 = int(s2)
if s3 < 0 {
Expand Down
52 changes: 52 additions & 0 deletions logical_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ package goavro

import (
"fmt"
"math"
"math/big"
"testing"
"time"
)

const (
precision = "precision"
scale = "scale"
)

func TestSchemaLogicalType(t *testing.T) {
testSchemaValid(t, `{"type": "long", "logicalType": "timestamp-millis"}`)
testSchemaInvalid(t, `{"type": "bytes", "logicalType": "decimal"}`, "precision")
Expand Down Expand Up @@ -163,6 +169,12 @@ func TestDecimalFixedLogicalTypeEncode(t *testing.T) {
// Encodes to 12 due to scale: 0
testBinaryEncodePass(t, schema0scale, big.NewRat(617, 50), []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c"))
testBinaryDecodePass(t, schema0scale, big.NewRat(12, 1), []byte("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c"))

schemaPrecision1 := `{"type": "fixed", "size": 4, "logicalType": "decimal", "precision": 1, "scale": 1}`
testBinaryCodecPass(t, schemaPrecision1, big.NewRat(163, 10), []byte("\x00\x00\x00\xa3"))
testBinaryCodecPass(t, schemaPrecision1, big.NewRat(-130, 4), []byte("\xff\xff\xfe\xbb"))
testBinaryCodecPass(t, schemaPrecision1, big.NewRat(25, 2), []byte("\x00\x00\x00\x7d"))
testBinaryEncodeFail(t, schemaPrecision1, big.NewRat(math.MaxInt, -1), "datum size ought to equal schema size")
}

func TestDecimalBytesLogicalTypeInRecordEncode(t *testing.T) {
Expand Down Expand Up @@ -258,3 +270,43 @@ func ExampleUnion_logicalType() {
fmt.Printf("%#v\n", out["long.timestamp-millis"].(time.Time).String())
// Output: "2006-01-02 15:04:05 +0000 UTC"
}

func TestPrecisionAndScaleFromSchemaMapValidation(t *testing.T) {
testCasesInvalid := []struct {
schemaMap map[string]interface{}
errMsg string
}{
{map[string]interface{}{}, "cannot create decimal logical type without precision"},
{map[string]interface{}{
precision: true,
}, "wrong precision type"},
{map[string]interface{}{
precision: float64(0),
}, "precision is less than one"},
{map[string]interface{}{
precision: float64(2),
scale: true,
}, "wrong scale type"},
{map[string]interface{}{
precision: float64(2),
scale: float64(-1),
}, "scale is less than zero"},
{map[string]interface{}{
precision: float64(2),
scale: float64(3),
}, "scale is larger than precision"},
}
for _, tc := range testCasesInvalid {
_, _, err := precisionAndScaleFromSchemaMap(tc.schemaMap)
ensureError(t, err, tc.errMsg)
}

// validation passes
p, s, err := precisionAndScaleFromSchemaMap(map[string]interface{}{
precision: float64(1),
scale: float64(1),
})
if p != 1 || s != 1 || err != nil {
t.Errorf("GOT: %v %v %v; WANT: 1 1 nil", p, s, err)
}
}

0 comments on commit 22dfedf

Please sign in to comment.