Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions arrow/datatype_fixedwidth.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
"time"

"github.com/apache/arrow-go/v18/arrow/decimal"
"github.com/apache/arrow-go/v18/arrow/internal/debug"
"github.com/apache/arrow-go/v18/internal/json"
)

Expand Down Expand Up @@ -567,22 +566,32 @@ func NarrowestDecimalType(prec, scale int32) (DecimalType, error) {
}

func NewDecimalType(id Type, prec, scale int32) (DecimalType, error) {
var (
dtype DecimalType
maxPrecision int32
)
switch id {
case DECIMAL32:
debug.Assert(prec <= int32(decimal.MaxPrecision[decimal.Decimal32]()), "invalid precision for decimal32")
return &Decimal32Type{Precision: prec, Scale: scale}, nil
dtype = &Decimal32Type{Precision: prec, Scale: scale}
maxPrecision = int32(decimal.MaxPrecision[decimal.Decimal32]())
case DECIMAL64:
debug.Assert(prec <= int32(decimal.MaxPrecision[decimal.Decimal64]()), "invalid precision for decimal64")
return &Decimal64Type{Precision: prec, Scale: scale}, nil
dtype = &Decimal64Type{Precision: prec, Scale: scale}
maxPrecision = int32(decimal.MaxPrecision[decimal.Decimal64]())
case DECIMAL128:
debug.Assert(prec <= int32(decimal.MaxPrecision[decimal.Decimal128]()), "invalid precision for decimal128")
return &Decimal128Type{Precision: prec, Scale: scale}, nil
dtype = &Decimal128Type{Precision: prec, Scale: scale}
maxPrecision = int32(decimal.MaxPrecision[decimal.Decimal128]())
case DECIMAL256:
debug.Assert(prec <= int32(decimal.MaxPrecision[decimal.Decimal256]()), "invalid precision for decimal256")
return &Decimal256Type{Precision: prec, Scale: scale}, nil
dtype = &Decimal256Type{Precision: prec, Scale: scale}
maxPrecision = int32(decimal.MaxPrecision[decimal.Decimal256]())
default:
return nil, fmt.Errorf("%w: must use one of the DECIMAL IDs to create a DecimalType", ErrInvalid)
}

if prec <= 0 || prec > maxPrecision {
return nil, fmt.Errorf("%w: precision for %s must be between 1 and %d, got %d",
ErrInvalid, id, maxPrecision, prec)
}
return dtype, nil
}

// Decimal32Type represents a fixed-size 32-bit decimal type.
Expand Down
29 changes: 29 additions & 0 deletions arrow/datatype_fixedwidth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,32 @@ func TestNarrowestDecimalType(t *testing.T) {
assert.Error(t, err)
assert.ErrorIs(t, err, arrow.ErrInvalid)
}

func TestNewDecimalTypeValidatesPrecision(t *testing.T) {
tests := []struct {
id arrow.Type
maxPrecision int32
}{
{arrow.DECIMAL32, 9},
{arrow.DECIMAL64, 18},
{arrow.DECIMAL128, 38},
{arrow.DECIMAL256, 76},
}

for _, tt := range tests {
t.Run(tt.id.String(), func(t *testing.T) {
for _, precision := range []int32{1, tt.maxPrecision} {
typ, err := arrow.NewDecimalType(tt.id, precision, 2)
require.NoError(t, err)
assert.Equal(t, precision, typ.GetPrecision())
assert.Equal(t, int32(2), typ.GetScale())
}

for _, precision := range []int32{-1, 0, tt.maxPrecision + 1} {
typ, err := arrow.NewDecimalType(tt.id, precision, 2)
assert.Nil(t, typ)
assert.ErrorIs(t, err, arrow.ErrInvalid)
}
})
}
}