You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by sb...@apache.org on 2019/06/14 15:39:26 UTC
[arrow] branch master updated: ARROW-3671: [Go] implement
MonthInterval and DayTimeInterval
This is an automated email from the ASF dual-hosted git repository.
sbinet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/master by this push:
new 634c8d2 ARROW-3671: [Go] implement MonthInterval and DayTimeInterval
634c8d2 is described below
commit 634c8d26eb60d261fe5096a9835bbc386860dcab
Author: Sebastien Binet <bi...@cern.ch>
AuthorDate: Fri Jun 14 17:39:06 2019 +0200
ARROW-3671: [Go] implement MonthInterval and DayTimeInterval
Author: Sebastien Binet <bi...@cern.ch>
Closes #4562 from sbinet/issue-3671 and squashes the following commits:
9a7d04da6 <Sebastien Binet> ARROW-3671: implement MonthInterval and DayTimeInterval
---
go/arrow/array/array.go | 2 +-
go/arrow/array/compare.go | 12 ++
go/arrow/array/interval.go | 434 +++++++++++++++++++++++++++++++++++++++
go/arrow/array/interval_test.go | 276 +++++++++++++++++++++++++
go/arrow/datatype_fixedwidth.go | 71 +++++--
go/arrow/type_traits_interval.go | 125 +++++++++++
6 files changed, 901 insertions(+), 19 deletions(-)
diff --git a/go/arrow/array/array.go b/go/arrow/array/array.go
index 2f8be78..c13dd07 100644
--- a/go/arrow/array/array.go
+++ b/go/arrow/array/array.go
@@ -185,7 +185,7 @@ func init() {
arrow.TIMESTAMP: func(data *Data) Interface { return NewTimestampData(data) },
arrow.TIME32: func(data *Data) Interface { return NewTime32Data(data) },
arrow.TIME64: func(data *Data) Interface { return NewTime64Data(data) },
- arrow.INTERVAL: unsupportedArrayType,
+ arrow.INTERVAL: func(data *Data) Interface { return NewIntervalData(data) },
arrow.DECIMAL: unsupportedArrayType,
arrow.LIST: func(data *Data) Interface { return NewListData(data) },
arrow.STRUCT: func(data *Data) Interface { return NewStructData(data) },
diff --git a/go/arrow/array/compare.go b/go/arrow/array/compare.go
index da8f5ab..0ea0b61 100644
--- a/go/arrow/array/compare.go
+++ b/go/arrow/array/compare.go
@@ -152,6 +152,12 @@ func ArrayEqual(left, right Interface) bool {
case *Struct:
r := right.(*Struct)
return arrayEqualStruct(l, r)
+ case *MonthInterval:
+ r := right.(*MonthInterval)
+ return arrayEqualMonthInterval(l, r)
+ case *DayTimeInterval:
+ r := right.(*DayTimeInterval)
+ return arrayEqualDayTimeInterval(l, r)
default:
panic(errors.Errorf("arrow/array: unknown array type %T", l))
@@ -329,6 +335,12 @@ func arrayApproxEqual(left, right Interface, opt equalOption) bool {
case *Struct:
r := right.(*Struct)
return arrayApproxEqualStruct(l, r, opt)
+ case *MonthInterval:
+ r := right.(*MonthInterval)
+ return arrayEqualMonthInterval(l, r)
+ case *DayTimeInterval:
+ r := right.(*DayTimeInterval)
+ return arrayEqualDayTimeInterval(l, r)
default:
panic(errors.Errorf("arrow/array: unknown array type %T", l))
diff --git a/go/arrow/array/interval.go b/go/arrow/array/interval.go
new file mode 100644
index 0000000..21efd6e
--- /dev/null
+++ b/go/arrow/array/interval.go
@@ -0,0 +1,434 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package array // import "github.com/apache/arrow/go/arrow/array"
+
+import (
+ "fmt"
+ "strings"
+ "sync/atomic"
+
+ "github.com/apache/arrow/go/arrow"
+ "github.com/apache/arrow/go/arrow/internal/bitutil"
+ "github.com/apache/arrow/go/arrow/internal/debug"
+ "github.com/apache/arrow/go/arrow/memory"
+ "github.com/pkg/errors"
+)
+
+func NewIntervalData(data *Data) Interface {
+ switch data.dtype.(type) {
+ case *arrow.MonthIntervalType:
+ return NewMonthIntervalData(data)
+ case *arrow.DayTimeIntervalType:
+ return NewDayTimeIntervalData(data)
+ default:
+ panic(errors.Errorf("arrow/array: unknown interval data type %T", data.dtype))
+ }
+}
+
+// A type which represents an immutable sequence of arrow.MonthInterval values.
+type MonthInterval struct {
+ array
+ values []arrow.MonthInterval
+}
+
+func NewMonthIntervalData(data *Data) *MonthInterval {
+ a := &MonthInterval{}
+ a.refCount = 1
+ a.setData(data)
+ return a
+}
+
+func (a *MonthInterval) Value(i int) arrow.MonthInterval { return a.values[i] }
+func (a *MonthInterval) MonthIntervalValues() []arrow.MonthInterval { return a.values }
+
+func (a *MonthInterval) String() string {
+ o := new(strings.Builder)
+ o.WriteString("[")
+ for i, v := range a.values {
+ if i > 0 {
+ fmt.Fprintf(o, " ")
+ }
+ switch {
+ case a.IsNull(i):
+ o.WriteString("(null)")
+ default:
+ fmt.Fprintf(o, "%v", v)
+ }
+ }
+ o.WriteString("]")
+ return o.String()
+}
+
+func (a *MonthInterval) setData(data *Data) {
+ a.array.setData(data)
+ vals := data.buffers[1]
+ if vals != nil {
+ a.values = arrow.MonthIntervalTraits.CastFromBytes(vals.Bytes())
+ beg := a.array.data.offset
+ end := beg + a.array.data.length
+ a.values = a.values[beg:end]
+ }
+}
+
+func arrayEqualMonthInterval(left, right *MonthInterval) bool {
+ for i := 0; i < left.Len(); i++ {
+ if left.IsNull(i) {
+ continue
+ }
+ if left.Value(i) != right.Value(i) {
+ return false
+ }
+ }
+ return true
+}
+
+type MonthIntervalBuilder struct {
+ builder
+
+ data *memory.Buffer
+ rawData []arrow.MonthInterval
+}
+
+func NewMonthIntervalBuilder(mem memory.Allocator) *MonthIntervalBuilder {
+ return &MonthIntervalBuilder{builder: builder{refCount: 1, mem: mem}}
+}
+
+// Release decreases the reference count by 1.
+// When the reference count goes to zero, the memory is freed.
+func (b *MonthIntervalBuilder) Release() {
+ debug.Assert(atomic.LoadInt64(&b.refCount) > 0, "too many releases")
+
+ if atomic.AddInt64(&b.refCount, -1) == 0 {
+ if b.nullBitmap != nil {
+ b.nullBitmap.Release()
+ b.nullBitmap = nil
+ }
+ if b.data != nil {
+ b.data.Release()
+ b.data = nil
+ b.rawData = nil
+ }
+ }
+}
+
+func (b *MonthIntervalBuilder) Append(v arrow.MonthInterval) {
+ b.Reserve(1)
+ b.UnsafeAppend(v)
+}
+
+func (b *MonthIntervalBuilder) AppendNull() {
+ b.Reserve(1)
+ b.UnsafeAppendBoolToBitmap(false)
+}
+
+func (b *MonthIntervalBuilder) UnsafeAppend(v arrow.MonthInterval) {
+ bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+ b.rawData[b.length] = v
+ b.length++
+}
+
+func (b *MonthIntervalBuilder) UnsafeAppendBoolToBitmap(isValid bool) {
+ if isValid {
+ bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+ } else {
+ b.nulls++
+ }
+ b.length++
+}
+
+// AppendValues will append the values in the v slice. The valid slice determines which values
+// in v are valid (not null). The valid slice must either be empty or be equal in length to v. If empty,
+// all values in v are appended and considered valid.
+func (b *MonthIntervalBuilder) AppendValues(v []arrow.MonthInterval, valid []bool) {
+ if len(v) != len(valid) && len(valid) != 0 {
+ panic("len(v) != len(valid) && len(valid) != 0")
+ }
+
+ if len(v) == 0 {
+ return
+ }
+
+ b.Reserve(len(v))
+ arrow.MonthIntervalTraits.Copy(b.rawData[b.length:], v)
+ b.builder.unsafeAppendBoolsToBitmap(valid, len(v))
+}
+
+func (b *MonthIntervalBuilder) init(capacity int) {
+ b.builder.init(capacity)
+
+ b.data = memory.NewResizableBuffer(b.mem)
+ bytesN := arrow.MonthIntervalTraits.BytesRequired(capacity)
+ b.data.Resize(bytesN)
+ b.rawData = arrow.MonthIntervalTraits.CastFromBytes(b.data.Bytes())
+}
+
+// Reserve ensures there is enough space for appending n elements
+// by checking the capacity and calling Resize if necessary.
+func (b *MonthIntervalBuilder) Reserve(n int) {
+ b.builder.reserve(n, b.Resize)
+}
+
+// Resize adjusts the space allocated by b to n elements. If n is greater than b.Cap(),
+// additional memory will be allocated. If n is smaller, the allocated memory may reduced.
+func (b *MonthIntervalBuilder) Resize(n int) {
+ nBuilder := n
+ if n < minBuilderCapacity {
+ n = minBuilderCapacity
+ }
+
+ if b.capacity == 0 {
+ b.init(n)
+ } else {
+ b.builder.resize(nBuilder, b.init)
+ b.data.Resize(arrow.MonthIntervalTraits.BytesRequired(n))
+ b.rawData = arrow.MonthIntervalTraits.CastFromBytes(b.data.Bytes())
+ }
+}
+
+// NewArray creates a MonthInterval array from the memory buffers used by the builder and resets the MonthIntervalBuilder
+// so it can be used to build a new array.
+func (b *MonthIntervalBuilder) NewArray() Interface {
+ return b.NewMonthIntervalArray()
+}
+
+// NewMonthIntervalArray creates a MonthInterval array from the memory buffers used by the builder and resets the MonthIntervalBuilder
+// so it can be used to build a new array.
+func (b *MonthIntervalBuilder) NewMonthIntervalArray() (a *MonthInterval) {
+ data := b.newData()
+ a = NewMonthIntervalData(data)
+ data.Release()
+ return
+}
+
+func (b *MonthIntervalBuilder) newData() (data *Data) {
+ bytesRequired := arrow.MonthIntervalTraits.BytesRequired(b.length)
+ if bytesRequired > 0 && bytesRequired < b.data.Len() {
+ // trim buffers
+ b.data.Resize(bytesRequired)
+ }
+ data = NewData(arrow.FixedWidthTypes.MonthInterval, b.length, []*memory.Buffer{b.nullBitmap, b.data}, nil, b.nulls, 0)
+ b.reset()
+
+ if b.data != nil {
+ b.data.Release()
+ b.data = nil
+ b.rawData = nil
+ }
+
+ return
+}
+
+// A type which represents an immutable sequence of arrow.DayTimeInterval values.
+type DayTimeInterval struct {
+ array
+ values []arrow.DayTimeInterval
+}
+
+func NewDayTimeIntervalData(data *Data) *DayTimeInterval {
+ a := &DayTimeInterval{}
+ a.refCount = 1
+ a.setData(data)
+ return a
+}
+
+func (a *DayTimeInterval) Value(i int) arrow.DayTimeInterval { return a.values[i] }
+func (a *DayTimeInterval) DayTimeIntervalValues() []arrow.DayTimeInterval { return a.values }
+
+func (a *DayTimeInterval) String() string {
+ o := new(strings.Builder)
+ o.WriteString("[")
+ for i, v := range a.values {
+ if i > 0 {
+ fmt.Fprintf(o, " ")
+ }
+ switch {
+ case a.IsNull(i):
+ o.WriteString("(null)")
+ default:
+ fmt.Fprintf(o, "%v", v)
+ }
+ }
+ o.WriteString("]")
+ return o.String()
+}
+
+func (a *DayTimeInterval) setData(data *Data) {
+ a.array.setData(data)
+ vals := data.buffers[1]
+ if vals != nil {
+ a.values = arrow.DayTimeIntervalTraits.CastFromBytes(vals.Bytes())
+ beg := a.array.data.offset
+ end := beg + a.array.data.length
+ a.values = a.values[beg:end]
+ }
+}
+
+func arrayEqualDayTimeInterval(left, right *DayTimeInterval) bool {
+ for i := 0; i < left.Len(); i++ {
+ if left.IsNull(i) {
+ continue
+ }
+ if left.Value(i) != right.Value(i) {
+ return false
+ }
+ }
+ return true
+}
+
+type DayTimeIntervalBuilder struct {
+ builder
+
+ data *memory.Buffer
+ rawData []arrow.DayTimeInterval
+}
+
+func NewDayTimeIntervalBuilder(mem memory.Allocator) *DayTimeIntervalBuilder {
+ return &DayTimeIntervalBuilder{builder: builder{refCount: 1, mem: mem}}
+}
+
+// Release decreases the reference count by 1.
+// When the reference count goes to zero, the memory is freed.
+func (b *DayTimeIntervalBuilder) Release() {
+ debug.Assert(atomic.LoadInt64(&b.refCount) > 0, "too many releases")
+
+ if atomic.AddInt64(&b.refCount, -1) == 0 {
+ if b.nullBitmap != nil {
+ b.nullBitmap.Release()
+ b.nullBitmap = nil
+ }
+ if b.data != nil {
+ b.data.Release()
+ b.data = nil
+ b.rawData = nil
+ }
+ }
+}
+
+func (b *DayTimeIntervalBuilder) Append(v arrow.DayTimeInterval) {
+ b.Reserve(1)
+ b.UnsafeAppend(v)
+}
+
+func (b *DayTimeIntervalBuilder) AppendNull() {
+ b.Reserve(1)
+ b.UnsafeAppendBoolToBitmap(false)
+}
+
+func (b *DayTimeIntervalBuilder) UnsafeAppend(v arrow.DayTimeInterval) {
+ bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+ b.rawData[b.length] = v
+ b.length++
+}
+
+func (b *DayTimeIntervalBuilder) UnsafeAppendBoolToBitmap(isValid bool) {
+ if isValid {
+ bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+ } else {
+ b.nulls++
+ }
+ b.length++
+}
+
+// AppendValues will append the values in the v slice. The valid slice determines which values
+// in v are valid (not null). The valid slice must either be empty or be equal in length to v. If empty,
+// all values in v are appended and considered valid.
+func (b *DayTimeIntervalBuilder) AppendValues(v []arrow.DayTimeInterval, valid []bool) {
+ if len(v) != len(valid) && len(valid) != 0 {
+ panic("len(v) != len(valid) && len(valid) != 0")
+ }
+
+ if len(v) == 0 {
+ return
+ }
+
+ b.Reserve(len(v))
+ arrow.DayTimeIntervalTraits.Copy(b.rawData[b.length:], v)
+ b.builder.unsafeAppendBoolsToBitmap(valid, len(v))
+}
+
+func (b *DayTimeIntervalBuilder) init(capacity int) {
+ b.builder.init(capacity)
+
+ b.data = memory.NewResizableBuffer(b.mem)
+ bytesN := arrow.DayTimeIntervalTraits.BytesRequired(capacity)
+ b.data.Resize(bytesN)
+ b.rawData = arrow.DayTimeIntervalTraits.CastFromBytes(b.data.Bytes())
+}
+
+// Reserve ensures there is enough space for appending n elements
+// by checking the capacity and calling Resize if necessary.
+func (b *DayTimeIntervalBuilder) Reserve(n int) {
+ b.builder.reserve(n, b.Resize)
+}
+
+// Resize adjusts the space allocated by b to n elements. If n is greater than b.Cap(),
+// additional memory will be allocated. If n is smaller, the allocated memory may reduced.
+func (b *DayTimeIntervalBuilder) Resize(n int) {
+ nBuilder := n
+ if n < minBuilderCapacity {
+ n = minBuilderCapacity
+ }
+
+ if b.capacity == 0 {
+ b.init(n)
+ } else {
+ b.builder.resize(nBuilder, b.init)
+ b.data.Resize(arrow.DayTimeIntervalTraits.BytesRequired(n))
+ b.rawData = arrow.DayTimeIntervalTraits.CastFromBytes(b.data.Bytes())
+ }
+}
+
+// NewArray creates a DayTimeInterval array from the memory buffers used by the builder and resets the DayTimeIntervalBuilder
+// so it can be used to build a new array.
+func (b *DayTimeIntervalBuilder) NewArray() Interface {
+ return b.NewDayTimeIntervalArray()
+}
+
+// NewDayTimeIntervalArray creates a DayTimeInterval array from the memory buffers used by the builder and resets the DayTimeIntervalBuilder
+// so it can be used to build a new array.
+func (b *DayTimeIntervalBuilder) NewDayTimeIntervalArray() (a *DayTimeInterval) {
+ data := b.newData()
+ a = NewDayTimeIntervalData(data)
+ data.Release()
+ return
+}
+
+func (b *DayTimeIntervalBuilder) newData() (data *Data) {
+ bytesRequired := arrow.DayTimeIntervalTraits.BytesRequired(b.length)
+ if bytesRequired > 0 && bytesRequired < b.data.Len() {
+ // trim buffers
+ b.data.Resize(bytesRequired)
+ }
+ data = NewData(arrow.FixedWidthTypes.DayTimeInterval, b.length, []*memory.Buffer{b.nullBitmap, b.data}, nil, b.nulls, 0)
+ b.reset()
+
+ if b.data != nil {
+ b.data.Release()
+ b.data = nil
+ b.rawData = nil
+ }
+
+ return
+}
+
+var (
+ _ Interface = (*MonthInterval)(nil)
+ _ Interface = (*DayTimeInterval)(nil)
+
+ _ Builder = (*MonthIntervalBuilder)(nil)
+ _ Builder = (*DayTimeIntervalBuilder)(nil)
+)
diff --git a/go/arrow/array/interval_test.go b/go/arrow/array/interval_test.go
new file mode 100644
index 0000000..bac0bb4
--- /dev/null
+++ b/go/arrow/array/interval_test.go
@@ -0,0 +1,276 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package array_test
+
+import (
+ "testing"
+
+ "github.com/apache/arrow/go/arrow"
+ "github.com/apache/arrow/go/arrow/array"
+ "github.com/apache/arrow/go/arrow/memory"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMonthIntervalArray(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+ defer mem.AssertSize(t, 0)
+
+ var (
+ want = []arrow.MonthInterval{1, 2, 3, 4}
+ valids = []bool{true, true, false, true}
+ )
+
+ b := array.NewMonthIntervalBuilder(mem)
+ defer b.Release()
+
+ b.Retain()
+ b.Release()
+
+ b.AppendValues(want[:2], nil)
+ b.AppendNull()
+ b.Append(want[3])
+
+ if got, want := b.Len(), len(want); got != want {
+ t.Fatalf("invalid len: got=%d, want=%d", got, want)
+ }
+
+ if got, want := b.NullN(), 1; got != want {
+ t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+ }
+
+ arr := b.NewMonthIntervalArray()
+ defer arr.Release()
+
+ arr.Retain()
+ arr.Release()
+
+ if got, want := arr.Len(), len(want); got != want {
+ t.Fatalf("invalid len: got=%d, want=%d", got, want)
+ }
+
+ if got, want := arr.NullN(), 1; got != want {
+ t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+ }
+
+ for i := range want {
+ if arr.IsNull(i) != !valids[i] {
+ t.Fatalf("arr[%d]-validity: got=%v want=%v", i, !arr.IsNull(i), valids[i])
+ }
+ switch {
+ case arr.IsNull(i):
+ default:
+ got := arr.Value(i)
+ if got != want[i] {
+ t.Fatalf("arr[%d]: got=%q, want=%q", i, got, want[i])
+ }
+ }
+ }
+
+ sub := array.MakeFromData(arr.Data())
+ defer sub.Release()
+
+ if sub.DataType().ID() != arrow.INTERVAL {
+ t.Fatalf("invalid type: got=%q, want=interval", sub.DataType().Name())
+ }
+
+ if _, ok := sub.(*array.MonthInterval); !ok {
+ t.Fatalf("could not type-assert to array.MonthInterval")
+ }
+
+ if got, want := arr.String(), `[1 2 (null) 4]`; got != want {
+ t.Fatalf("got=%q, want=%q", got, want)
+ }
+ slice := array.NewSliceData(arr.Data(), 2, 4)
+ defer slice.Release()
+
+ sub1 := array.MakeFromData(slice)
+ defer sub1.Release()
+
+ v, ok := sub1.(*array.MonthInterval)
+ if !ok {
+ t.Fatalf("could not type-assert to array.MonthInterval")
+ }
+
+ if got, want := v.String(), `[(null) 4]`; got != want {
+ t.Fatalf("got=%q, want=%q", got, want)
+ }
+}
+
+func TestMonthIntervalBuilder_Empty(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+ defer mem.AssertSize(t, 0)
+
+ want := []arrow.MonthInterval{1, 2, 3, 4}
+
+ b := array.NewMonthIntervalBuilder(mem)
+ defer b.Release()
+
+ miValues := func(a *array.MonthInterval) []arrow.MonthInterval {
+ vs := make([]arrow.MonthInterval, a.Len())
+ for i := range vs {
+ vs[i] = a.Value(i)
+ }
+ return vs
+ }
+
+ b.AppendValues([]arrow.MonthInterval{}, nil)
+ arr := b.NewMonthIntervalArray()
+ assert.Zero(t, arr.Len())
+ arr.Release()
+
+ b.AppendValues(nil, nil)
+ arr = b.NewMonthIntervalArray()
+ assert.Zero(t, arr.Len())
+ arr.Release()
+
+ b.AppendValues([]arrow.MonthInterval{}, nil)
+ b.AppendValues(want, nil)
+ arr = b.NewMonthIntervalArray()
+ assert.Equal(t, want, miValues(arr))
+ arr.Release()
+
+ b.AppendValues(want, nil)
+ b.AppendValues([]arrow.MonthInterval{}, nil)
+ arr = b.NewMonthIntervalArray()
+ assert.Equal(t, want, miValues(arr))
+ arr.Release()
+}
+
+func TestDayTimeArray(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+ defer mem.AssertSize(t, 0)
+
+ var (
+ want = []arrow.DayTimeInterval{{1, 1}, {2, 2}, {3, 3}, {4, 4}}
+ valids = []bool{true, true, false, true}
+ )
+
+ b := array.NewDayTimeIntervalBuilder(mem)
+ defer b.Release()
+
+ b.Retain()
+ b.Release()
+
+ b.AppendValues(want[:2], nil)
+ b.AppendNull()
+ b.Append(want[3])
+
+ if got, want := b.Len(), len(want); got != want {
+ t.Fatalf("invalid len: got=%d, want=%d", got, want)
+ }
+
+ if got, want := b.NullN(), 1; got != want {
+ t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+ }
+
+ arr := b.NewDayTimeIntervalArray()
+ defer arr.Release()
+
+ arr.Retain()
+ arr.Release()
+
+ if got, want := arr.Len(), len(want); got != want {
+ t.Fatalf("invalid len: got=%d, want=%d", got, want)
+ }
+
+ if got, want := arr.NullN(), 1; got != want {
+ t.Fatalf("invalid nulls: got=%d, want=%d", got, want)
+ }
+
+ for i := range want {
+ if arr.IsNull(i) != !valids[i] {
+ t.Fatalf("arr[%d]-validity: got=%v want=%v", i, !arr.IsNull(i), valids[i])
+ }
+ switch {
+ case arr.IsNull(i):
+ default:
+ got := arr.Value(i)
+ if got != want[i] {
+ t.Fatalf("arr[%d]: got=%q, want=%q", i, got, want[i])
+ }
+ }
+ }
+
+ sub := array.MakeFromData(arr.Data())
+ defer sub.Release()
+
+ if sub.DataType().ID() != arrow.INTERVAL {
+ t.Fatalf("invalid type: got=%q, want=interval", sub.DataType().Name())
+ }
+
+ if _, ok := sub.(*array.DayTimeInterval); !ok {
+ t.Fatalf("could not type-assert to array.DayTimeInterval")
+ }
+
+ if got, want := arr.String(), `[{1 1} {2 2} (null) {4 4}]`; got != want {
+ t.Fatalf("got=%q, want=%q", got, want)
+ }
+ slice := array.NewSliceData(arr.Data(), 2, 4)
+ defer slice.Release()
+
+ sub1 := array.MakeFromData(slice)
+ defer sub1.Release()
+
+ v, ok := sub1.(*array.DayTimeInterval)
+ if !ok {
+ t.Fatalf("could not type-assert to array.DayInterval")
+ }
+
+ if got, want := v.String(), `[(null) {4 4}]`; got != want {
+ t.Fatalf("got=%q, want=%q", got, want)
+ }
+}
+
+func TestDayTimeIntervalBuilder_Empty(t *testing.T) {
+ mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+ defer mem.AssertSize(t, 0)
+
+ want := []arrow.DayTimeInterval{{1, 1}, {2, 2}, {3, 3}, {4, 4}}
+
+ b := array.NewDayTimeIntervalBuilder(mem)
+ defer b.Release()
+
+ dtValues := func(a *array.DayTimeInterval) []arrow.DayTimeInterval {
+ vs := make([]arrow.DayTimeInterval, a.Len())
+ for i := range vs {
+ vs[i] = a.Value(i)
+ }
+ return vs
+ }
+
+ b.AppendValues([]arrow.DayTimeInterval{}, nil)
+ arr := b.NewDayTimeIntervalArray()
+ assert.Zero(t, arr.Len())
+ arr.Release()
+
+ b.AppendValues(nil, nil)
+ arr = b.NewDayTimeIntervalArray()
+ assert.Zero(t, arr.Len())
+ arr.Release()
+
+ b.AppendValues([]arrow.DayTimeInterval{}, nil)
+ b.AppendValues(want, nil)
+ arr = b.NewDayTimeIntervalArray()
+ assert.Equal(t, want, dtValues(arr))
+ arr.Release()
+
+ b.AppendValues(want, nil)
+ b.AppendValues([]arrow.DayTimeInterval{}, nil)
+ arr = b.NewDayTimeIntervalArray()
+ assert.Equal(t, want, dtValues(arr))
+ arr.Release()
+}
diff --git a/go/arrow/datatype_fixedwidth.go b/go/arrow/datatype_fixedwidth.go
index fa7ed3f..14766fa 100644
--- a/go/arrow/datatype_fixedwidth.go
+++ b/go/arrow/datatype_fixedwidth.go
@@ -94,25 +94,29 @@ func (t *Time64Type) String() string { return "time64[" + t.Unit.String() + "]"
var (
FixedWidthTypes = struct {
- Boolean FixedWidthDataType
- Date32 FixedWidthDataType
- Date64 FixedWidthDataType
- Float16 FixedWidthDataType
- Time32s FixedWidthDataType
- Time32ms FixedWidthDataType
- Time64us FixedWidthDataType
- Time64ns FixedWidthDataType
- Timestamp FixedWidthDataType
+ Boolean FixedWidthDataType
+ Date32 FixedWidthDataType
+ Date64 FixedWidthDataType
+ DayTimeInterval FixedWidthDataType
+ Float16 FixedWidthDataType
+ MonthInterval FixedWidthDataType
+ Time32s FixedWidthDataType
+ Time32ms FixedWidthDataType
+ Time64us FixedWidthDataType
+ Time64ns FixedWidthDataType
+ Timestamp FixedWidthDataType
}{
- Boolean: &BooleanType{},
- Date32: &Date32Type{},
- Date64: &Date64Type{},
- Float16: &Float16Type{},
- Time32s: &Time32Type{Unit: Second},
- Time32ms: &Time32Type{Unit: Millisecond},
- Time64us: &Time64Type{Unit: Microsecond},
- Time64ns: &Time64Type{Unit: Nanosecond},
- Timestamp: &TimestampType{Unit: Nanosecond, TimeZone: "UTC"},
+ Boolean: &BooleanType{},
+ Date32: &Date32Type{},
+ Date64: &Date64Type{},
+ DayTimeInterval: &DayTimeIntervalType{},
+ Float16: &Float16Type{},
+ MonthInterval: &MonthIntervalType{},
+ Time32s: &Time32Type{Unit: Second},
+ Time32ms: &Time32Type{Unit: Millisecond},
+ Time64us: &Time64Type{Unit: Microsecond},
+ Time64ns: &Time64Type{Unit: Nanosecond},
+ Timestamp: &TimestampType{Unit: Nanosecond, TimeZone: "UTC"},
}
_ FixedWidthDataType = (*FixedSizeBinaryType)(nil)
@@ -126,3 +130,34 @@ func (t *Float16Type) String() string { return "float16" }
// BitWidth returns the number of bits required to store a single element of this data type in memory.
func (t *Float16Type) BitWidth() int { return 16 }
+
+// MonthInterval represents a number of months.
+type MonthInterval int32
+
+// MonthIntervalType is encoded as a 32-bit signed integer,
+// representing a number of months.
+type MonthIntervalType struct{}
+
+func (*MonthIntervalType) ID() Type { return INTERVAL }
+func (*MonthIntervalType) Name() string { return "month_interval" }
+func (*MonthIntervalType) String() string { return "month_interval" }
+
+// BitWidth returns the number of bits required to store a single element of this data type in memory.
+func (t *MonthIntervalType) BitWidth() int { return 32 }
+
+// DayTimeInterval represents a number of days and milliseconds (fraction of day).
+type DayTimeInterval struct {
+ Days int32
+ Milliseconds int32
+}
+
+// DayTimeIntervalType is encoded as a pair of 32-bit signed integer,
+// representing a number of days and milliseconds (fraction of day).
+type DayTimeIntervalType struct{}
+
+func (*DayTimeIntervalType) ID() Type { return INTERVAL }
+func (*DayTimeIntervalType) Name() string { return "day_time_interval" }
+func (*DayTimeIntervalType) String() string { return "day_time_interval" }
+
+// BitWidth returns the number of bits required to store a single element of this data type in memory.
+func (t *DayTimeIntervalType) BitWidth() int { return 64 }
diff --git a/go/arrow/type_traits_interval.go b/go/arrow/type_traits_interval.go
new file mode 100644
index 0000000..8ddaa51
--- /dev/null
+++ b/go/arrow/type_traits_interval.go
@@ -0,0 +1,125 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package arrow
+
+import (
+ "encoding/binary"
+ "reflect"
+ "unsafe"
+)
+
+var (
+ MonthIntervalTraits monthTraits
+ DayTimeIntervalTraits daytimeTraits
+)
+
+// MonthInterval traits
+
+const (
+ // MonthIntervalSizeBytes specifies the number of bytes required to store a single MonthInterval in memory
+ MonthIntervalSizeBytes = int(unsafe.Sizeof(MonthInterval(0)))
+)
+
+type monthTraits struct{}
+
+// BytesRequired returns the number of bytes required to store n elements in memory.
+func (monthTraits) BytesRequired(n int) int { return MonthIntervalSizeBytes * n }
+
+// PutValue
+func (monthTraits) PutValue(b []byte, v MonthInterval) {
+ binary.LittleEndian.PutUint32(b, uint32(v))
+}
+
+// CastFromBytes reinterprets the slice b to a slice of type MonthInterval.
+//
+// NOTE: len(b) must be a multiple of MonthIntervalSizeBytes.
+func (monthTraits) CastFromBytes(b []byte) []MonthInterval {
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+ var res []MonthInterval
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+ s.Data = h.Data
+ s.Len = h.Len / MonthIntervalSizeBytes
+ s.Cap = h.Cap / MonthIntervalSizeBytes
+
+ return res
+}
+
+// CastToBytes reinterprets the slice b to a slice of bytes.
+func (monthTraits) CastToBytes(b []MonthInterval) []byte {
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+ var res []byte
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+ s.Data = h.Data
+ s.Len = h.Len * MonthIntervalSizeBytes
+ s.Cap = h.Cap * MonthIntervalSizeBytes
+
+ return res
+}
+
+// Copy copies src to dst.
+func (monthTraits) Copy(dst, src []MonthInterval) { copy(dst, src) }
+
+// DayTimeInterval traits
+
+const (
+ // DayTimeIntervalSizeBytes specifies the number of bytes required to store a single DayTimeInterval in memory
+ DayTimeIntervalSizeBytes = int(unsafe.Sizeof(DayTimeInterval{}))
+)
+
+type daytimeTraits struct{}
+
+// BytesRequired returns the number of bytes required to store n elements in memory.
+func (daytimeTraits) BytesRequired(n int) int { return DayTimeIntervalSizeBytes * n }
+
+// PutValue
+func (daytimeTraits) PutValue(b []byte, v DayTimeInterval) {
+ binary.LittleEndian.PutUint32(b, uint32(v.Days))
+ binary.LittleEndian.PutUint32(b, uint32(v.Milliseconds))
+}
+
+// CastFromBytes reinterprets the slice b to a slice of type DayTimeInterval.
+//
+// NOTE: len(b) must be a multiple of DayTimeIntervalSizeBytes.
+func (daytimeTraits) CastFromBytes(b []byte) []DayTimeInterval {
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+ var res []DayTimeInterval
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+ s.Data = h.Data
+ s.Len = h.Len / DayTimeIntervalSizeBytes
+ s.Cap = h.Cap / DayTimeIntervalSizeBytes
+
+ return res
+}
+
+// CastToBytes reinterprets the slice b to a slice of bytes.
+func (daytimeTraits) CastToBytes(b []DayTimeInterval) []byte {
+ h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+ var res []byte
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+ s.Data = h.Data
+ s.Len = h.Len * DayTimeIntervalSizeBytes
+ s.Cap = h.Cap * DayTimeIntervalSizeBytes
+
+ return res
+}
+
+// Copy copies src to dst.
+func (daytimeTraits) Copy(dst, src []DayTimeInterval) { copy(dst, src) }