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 16:22:19 UTC

[arrow] branch master updated: ARROW-5592: [Go] implement Duration array

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 462cbe8  ARROW-5592: [Go] implement Duration array
462cbe8 is described below

commit 462cbe8715df8b1ad3a3d2c2418de9433d4b00d3
Author: Sebastien Binet <bi...@cern.ch>
AuthorDate: Fri Jun 14 18:21:59 2019 +0200

    ARROW-5592: [Go] implement Duration array
    
    Author: Sebastien Binet <bi...@cern.ch>
    
    Closes #4563 from sbinet/issue-5592 and squashes the following commits:
    
    de7bbd32e <Sebastien Binet> ARROW-5592:  implement Duration array
---
 go/arrow/array/array.go                        |   2 +-
 go/arrow/array/array_test.go                   |   2 +-
 go/arrow/array/compare.go                      |   6 +
 go/arrow/array/numeric.gen.go                  |  57 ++
 go/arrow/array/numericbuilder.gen.go           | 138 +++++
 go/arrow/array/numericbuilder.gen_test.go      | 815 +++++++++++++++++++++++++
 go/arrow/array/numericbuilder.gen_test.go.tmpl |  43 ++
 go/arrow/datatype_fixedwidth.go                |  77 ++-
 go/arrow/numeric.tmpldata                      |  12 +
 go/arrow/type_traits_numeric.gen.go            |  49 ++
 10 files changed, 1171 insertions(+), 30 deletions(-)

diff --git a/go/arrow/array/array.go b/go/arrow/array/array.go
index c13dd07..1912f3e 100644
--- a/go/arrow/array/array.go
+++ b/go/arrow/array/array.go
@@ -194,7 +194,7 @@ func init() {
 		arrow.MAP:               unsupportedArrayType,
 		arrow.EXTENSION:         unsupportedArrayType,
 		arrow.FIXED_SIZE_LIST:   func(data *Data) Interface { return NewFixedSizeListData(data) },
-		arrow.DURATION:          unsupportedArrayType,
+		arrow.DURATION:          func(data *Data) Interface { return NewDurationData(data) },
 
 		// invalid data types to fill out array size 2⁵-1
 		31: invalidDataType,
diff --git a/go/arrow/array/array_test.go b/go/arrow/array/array_test.go
index 884bb8d..724f3b4 100644
--- a/go/arrow/array/array_test.go
+++ b/go/arrow/array/array_test.go
@@ -80,13 +80,13 @@ func TestMakeFromData(t *testing.T) {
 			array.NewData(&testDataType{arrow.INT64}, 0, make([]*memory.Buffer, 4), nil, 0, 0),
 			array.NewData(&testDataType{arrow.INT64}, 0, make([]*memory.Buffer, 4), nil, 0, 0),
 		}},
+		{name: "duration", d: &testDataType{arrow.DURATION}},
 
 		// unsupported types
 		{name: "union", d: &testDataType{arrow.UNION}, expPanic: true, expError: "unsupported data type: UNION"},
 		{name: "dictionary", d: &testDataType{arrow.DICTIONARY}, expPanic: true, expError: "unsupported data type: DICTIONARY"},
 		{name: "map", d: &testDataType{arrow.Type(27)}, expPanic: true, expError: "unsupported data type: MAP"},
 		{name: "extension", d: &testDataType{arrow.Type(28)}, expPanic: true, expError: "unsupported data type: EXTENSION"},
-		{name: "duration", d: &testDataType{arrow.Type(30)}, expPanic: true, expError: "unsupported data type: DURATION"},
 
 		// invalid types
 		{name: "invalid(-1)", d: &testDataType{arrow.Type(-1)}, expPanic: true, expError: "invalid data type: Type(-1)"},
diff --git a/go/arrow/array/compare.go b/go/arrow/array/compare.go
index 0ea0b61..c6665c9 100644
--- a/go/arrow/array/compare.go
+++ b/go/arrow/array/compare.go
@@ -158,6 +158,9 @@ func ArrayEqual(left, right Interface) bool {
 	case *DayTimeInterval:
 		r := right.(*DayTimeInterval)
 		return arrayEqualDayTimeInterval(l, r)
+	case *Duration:
+		r := right.(*Duration)
+		return arrayEqualDuration(l, r)
 
 	default:
 		panic(errors.Errorf("arrow/array: unknown array type %T", l))
@@ -341,6 +344,9 @@ func arrayApproxEqual(left, right Interface, opt equalOption) bool {
 	case *DayTimeInterval:
 		r := right.(*DayTimeInterval)
 		return arrayEqualDayTimeInterval(l, r)
+	case *Duration:
+		r := right.(*Duration)
+		return arrayEqualDuration(l, r)
 
 	default:
 		panic(errors.Errorf("arrow/array: unknown array type %T", l))
diff --git a/go/arrow/array/numeric.gen.go b/go/arrow/array/numeric.gen.go
index d72d7d0..21c4e4b 100644
--- a/go/arrow/array/numeric.gen.go
+++ b/go/arrow/array/numeric.gen.go
@@ -879,3 +879,60 @@ func arrayEqualDate64(left, right *Date64) bool {
 	}
 	return true
 }
+
+// A type which represents an immutable sequence of arrow.Duration values.
+type Duration struct {
+	array
+	values []arrow.Duration
+}
+
+func NewDurationData(data *Data) *Duration {
+	a := &Duration{}
+	a.refCount = 1
+	a.setData(data)
+	return a
+}
+
+func (a *Duration) Value(i int) arrow.Duration       { return a.values[i] }
+func (a *Duration) DurationValues() []arrow.Duration { return a.values }
+
+func (a *Duration) 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 *Duration) setData(data *Data) {
+	a.array.setData(data)
+	vals := data.buffers[1]
+	if vals != nil {
+		a.values = arrow.DurationTraits.CastFromBytes(vals.Bytes())
+		beg := a.array.data.offset
+		end := beg + a.array.data.length
+		a.values = a.values[beg:end]
+	}
+}
+
+func arrayEqualDuration(left, right *Duration) bool {
+	for i := 0; i < left.Len(); i++ {
+		if left.IsNull(i) {
+			continue
+		}
+		if left.Value(i) != right.Value(i) {
+			return false
+		}
+	}
+	return true
+}
diff --git a/go/arrow/array/numericbuilder.gen.go b/go/arrow/array/numericbuilder.gen.go
index 3c9421b..b113908 100644
--- a/go/arrow/array/numericbuilder.gen.go
+++ b/go/arrow/array/numericbuilder.gen.go
@@ -2070,6 +2070,143 @@ func (b *Date64Builder) newData() (data *Data) {
 	return
 }
 
+type DurationBuilder struct {
+	builder
+
+	dtype   *arrow.DurationType
+	data    *memory.Buffer
+	rawData []arrow.Duration
+}
+
+func NewDurationBuilder(mem memory.Allocator, dtype *arrow.DurationType) *DurationBuilder {
+	return &DurationBuilder{builder: builder{refCount: 1, mem: mem}, dtype: dtype}
+}
+
+// Release decreases the reference count by 1.
+// When the reference count goes to zero, the memory is freed.
+func (b *DurationBuilder) 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 *DurationBuilder) Append(v arrow.Duration) {
+	b.Reserve(1)
+	b.UnsafeAppend(v)
+}
+
+func (b *DurationBuilder) AppendNull() {
+	b.Reserve(1)
+	b.UnsafeAppendBoolToBitmap(false)
+}
+
+func (b *DurationBuilder) UnsafeAppend(v arrow.Duration) {
+	bitutil.SetBit(b.nullBitmap.Bytes(), b.length)
+	b.rawData[b.length] = v
+	b.length++
+}
+
+func (b *DurationBuilder) 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 *DurationBuilder) AppendValues(v []arrow.Duration, 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.DurationTraits.Copy(b.rawData[b.length:], v)
+	b.builder.unsafeAppendBoolsToBitmap(valid, len(v))
+}
+
+func (b *DurationBuilder) init(capacity int) {
+	b.builder.init(capacity)
+
+	b.data = memory.NewResizableBuffer(b.mem)
+	bytesN := arrow.DurationTraits.BytesRequired(capacity)
+	b.data.Resize(bytesN)
+	b.rawData = arrow.DurationTraits.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 *DurationBuilder) 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 *DurationBuilder) 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.DurationTraits.BytesRequired(n))
+		b.rawData = arrow.DurationTraits.CastFromBytes(b.data.Bytes())
+	}
+}
+
+// NewArray creates a Duration array from the memory buffers used by the builder and resets the DurationBuilder
+// so it can be used to build a new array.
+func (b *DurationBuilder) NewArray() Interface {
+	return b.NewDurationArray()
+}
+
+// NewDurationArray creates a Duration array from the memory buffers used by the builder and resets the DurationBuilder
+// so it can be used to build a new array.
+func (b *DurationBuilder) NewDurationArray() (a *Duration) {
+	data := b.newData()
+	a = NewDurationData(data)
+	data.Release()
+	return
+}
+
+func (b *DurationBuilder) newData() (data *Data) {
+	bytesRequired := arrow.DurationTraits.BytesRequired(b.length)
+	if bytesRequired > 0 && bytesRequired < b.data.Len() {
+		// trim buffers
+		b.data.Resize(bytesRequired)
+	}
+	data = NewData(b.dtype, 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 (
 	_ Builder = (*Int64Builder)(nil)
 	_ Builder = (*Uint64Builder)(nil)
@@ -2086,4 +2223,5 @@ var (
 	_ Builder = (*Time64Builder)(nil)
 	_ Builder = (*Date32Builder)(nil)
 	_ Builder = (*Date64Builder)(nil)
+	_ Builder = (*DurationBuilder)(nil)
 )
diff --git a/go/arrow/array/numericbuilder.gen_test.go b/go/arrow/array/numericbuilder.gen_test.go
index 099629e..d5f6aaa 100644
--- a/go/arrow/array/numericbuilder.gen_test.go
+++ b/go/arrow/array/numericbuilder.gen_test.go
@@ -34,6 +34,9 @@ func TestNewInt64Builder(t *testing.T) {
 	ab := array.NewInt64Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -74,6 +77,46 @@ func TestNewInt64Builder(t *testing.T) {
 	assert.Len(t, a.Int64Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []int64{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewInt64Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Int64); !ok {
+		t.Fatalf("could not type-assert to array.Int64")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Int64)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Int64")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestInt64Builder_AppendValues(t *testing.T) {
@@ -157,6 +200,9 @@ func TestNewUint64Builder(t *testing.T) {
 	ab := array.NewUint64Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -197,6 +243,46 @@ func TestNewUint64Builder(t *testing.T) {
 	assert.Len(t, a.Uint64Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []uint64{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewUint64Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Uint64); !ok {
+		t.Fatalf("could not type-assert to array.Uint64")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Uint64)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Uint64")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestUint64Builder_AppendValues(t *testing.T) {
@@ -280,6 +366,9 @@ func TestNewFloat64Builder(t *testing.T) {
 	ab := array.NewFloat64Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -320,6 +409,46 @@ func TestNewFloat64Builder(t *testing.T) {
 	assert.Len(t, a.Float64Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []float64{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewFloat64Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Float64); !ok {
+		t.Fatalf("could not type-assert to array.Float64")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Float64)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Float64")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestFloat64Builder_AppendValues(t *testing.T) {
@@ -403,6 +532,9 @@ func TestNewInt32Builder(t *testing.T) {
 	ab := array.NewInt32Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -443,6 +575,46 @@ func TestNewInt32Builder(t *testing.T) {
 	assert.Len(t, a.Int32Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []int32{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewInt32Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Int32); !ok {
+		t.Fatalf("could not type-assert to array.Int32")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Int32)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Int32")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestInt32Builder_AppendValues(t *testing.T) {
@@ -526,6 +698,9 @@ func TestNewUint32Builder(t *testing.T) {
 	ab := array.NewUint32Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -566,6 +741,46 @@ func TestNewUint32Builder(t *testing.T) {
 	assert.Len(t, a.Uint32Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []uint32{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewUint32Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Uint32); !ok {
+		t.Fatalf("could not type-assert to array.Uint32")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Uint32)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Uint32")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestUint32Builder_AppendValues(t *testing.T) {
@@ -649,6 +864,9 @@ func TestNewFloat32Builder(t *testing.T) {
 	ab := array.NewFloat32Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -689,6 +907,46 @@ func TestNewFloat32Builder(t *testing.T) {
 	assert.Len(t, a.Float32Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []float32{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewFloat32Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Float32); !ok {
+		t.Fatalf("could not type-assert to array.Float32")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Float32)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Float32")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestFloat32Builder_AppendValues(t *testing.T) {
@@ -772,6 +1030,9 @@ func TestNewInt16Builder(t *testing.T) {
 	ab := array.NewInt16Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -812,6 +1073,46 @@ func TestNewInt16Builder(t *testing.T) {
 	assert.Len(t, a.Int16Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []int16{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewInt16Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Int16); !ok {
+		t.Fatalf("could not type-assert to array.Int16")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Int16)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Int16")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestInt16Builder_AppendValues(t *testing.T) {
@@ -895,6 +1196,9 @@ func TestNewUint16Builder(t *testing.T) {
 	ab := array.NewUint16Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -935,6 +1239,46 @@ func TestNewUint16Builder(t *testing.T) {
 	assert.Len(t, a.Uint16Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []uint16{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewUint16Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Uint16); !ok {
+		t.Fatalf("could not type-assert to array.Uint16")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Uint16)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Uint16")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestUint16Builder_AppendValues(t *testing.T) {
@@ -1018,6 +1362,9 @@ func TestNewInt8Builder(t *testing.T) {
 	ab := array.NewInt8Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -1058,6 +1405,46 @@ func TestNewInt8Builder(t *testing.T) {
 	assert.Len(t, a.Int8Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []int8{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewInt8Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Int8); !ok {
+		t.Fatalf("could not type-assert to array.Int8")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Int8)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Int8")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestInt8Builder_AppendValues(t *testing.T) {
@@ -1141,6 +1528,9 @@ func TestNewUint8Builder(t *testing.T) {
 	ab := array.NewUint8Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -1181,6 +1571,46 @@ func TestNewUint8Builder(t *testing.T) {
 	assert.Len(t, a.Uint8Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []uint8{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewUint8Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Uint8); !ok {
+		t.Fatalf("could not type-assert to array.Uint8")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Uint8)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Uint8")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestUint8Builder_AppendValues(t *testing.T) {
@@ -1265,6 +1695,9 @@ func TestNewTimestampBuilder(t *testing.T) {
 	ab := array.NewTimestampBuilder(mem, dtype)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -1305,6 +1738,46 @@ func TestNewTimestampBuilder(t *testing.T) {
 	assert.Len(t, a.TimestampValues(), 2)
 
 	a.Release()
+
+	var (
+		want   = []arrow.Timestamp{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewTimestampArray()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Timestamp); !ok {
+		t.Fatalf("could not type-assert to array.Timestamp")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Timestamp)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Timestamp")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestTimestampBuilder_AppendValues(t *testing.T) {
@@ -1392,6 +1865,9 @@ func TestNewTime32Builder(t *testing.T) {
 	ab := array.NewTime32Builder(mem, dtype)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -1432,6 +1908,46 @@ func TestNewTime32Builder(t *testing.T) {
 	assert.Len(t, a.Time32Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []arrow.Time32{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewTime32Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Time32); !ok {
+		t.Fatalf("could not type-assert to array.Time32")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Time32)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Time32")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestTime32Builder_AppendValues(t *testing.T) {
@@ -1519,6 +2035,9 @@ func TestNewTime64Builder(t *testing.T) {
 	ab := array.NewTime64Builder(mem, dtype)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -1559,6 +2078,46 @@ func TestNewTime64Builder(t *testing.T) {
 	assert.Len(t, a.Time64Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []arrow.Time64{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewTime64Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Time64); !ok {
+		t.Fatalf("could not type-assert to array.Time64")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Time64)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Time64")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestTime64Builder_AppendValues(t *testing.T) {
@@ -1645,6 +2204,9 @@ func TestNewDate32Builder(t *testing.T) {
 	ab := array.NewDate32Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -1685,6 +2247,46 @@ func TestNewDate32Builder(t *testing.T) {
 	assert.Len(t, a.Date32Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []arrow.Date32{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewDate32Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Date32); !ok {
+		t.Fatalf("could not type-assert to array.Date32")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Date32)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Date32")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestDate32Builder_AppendValues(t *testing.T) {
@@ -1768,6 +2370,9 @@ func TestNewDate64Builder(t *testing.T) {
 	ab := array.NewDate64Builder(mem)
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -1808,6 +2413,46 @@ func TestNewDate64Builder(t *testing.T) {
 	assert.Len(t, a.Date64Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []arrow.Date64{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewDate64Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Date64); !ok {
+		t.Fatalf("could not type-assert to array.Date64")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Date64)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Date64")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func TestDate64Builder_AppendValues(t *testing.T) {
@@ -1883,3 +2528,173 @@ func TestDate64Builder_Resize(t *testing.T) {
 	ab.Resize(32)
 	assert.Equal(t, 5, ab.Len())
 }
+
+func TestNewDurationBuilder(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	dtype := &arrow.DurationType{Unit: arrow.Second}
+	ab := array.NewDurationBuilder(mem, dtype)
+	defer ab.Release()
+
+	ab.Retain()
+	ab.Release()
+
+	ab.Append(1)
+	ab.Append(2)
+	ab.Append(3)
+	ab.AppendNull()
+	ab.Append(5)
+	ab.Append(6)
+	ab.AppendNull()
+	ab.Append(8)
+	ab.Append(9)
+	ab.Append(10)
+
+	// check state of builder before NewDurationArray
+	assert.Equal(t, 10, ab.Len(), "unexpected Len()")
+	assert.Equal(t, 2, ab.NullN(), "unexpected NullN()")
+
+	a := ab.NewDurationArray()
+
+	// check state of builder after NewDurationArray
+	assert.Zero(t, ab.Len(), "unexpected ArrayBuilder.Len(), NewDurationArray did not reset state")
+	assert.Zero(t, ab.Cap(), "unexpected ArrayBuilder.Cap(), NewDurationArray did not reset state")
+	assert.Zero(t, ab.NullN(), "unexpected ArrayBuilder.NullN(), NewDurationArray did not reset state")
+
+	// check state of array
+	assert.Equal(t, 2, a.NullN(), "unexpected null count")
+	assert.Equal(t, []arrow.Duration{1, 2, 3, 0, 5, 6, 0, 8, 9, 10}, a.DurationValues(), "unexpected DurationValues")
+	assert.Equal(t, []byte{0xb7}, a.NullBitmapBytes()[:1]) // 4 bytes due to minBuilderCapacity
+	assert.Len(t, a.DurationValues(), 10, "unexpected length of DurationValues")
+
+	a.Release()
+
+	ab.Append(7)
+	ab.Append(8)
+
+	a = ab.NewDurationArray()
+
+	assert.Equal(t, 0, a.NullN())
+	assert.Equal(t, []arrow.Duration{7, 8}, a.DurationValues())
+	assert.Len(t, a.DurationValues(), 2)
+
+	a.Release()
+
+	var (
+		want   = []arrow.Duration{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.NewDurationArray()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.Duration); !ok {
+		t.Fatalf("could not type-assert to array.Duration")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.Duration)
+	if !ok {
+		t.Fatalf("could not type-assert to array.Duration")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
+}
+
+func TestDurationBuilder_AppendValues(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	dtype := &arrow.DurationType{Unit: arrow.Second}
+	ab := array.NewDurationBuilder(mem, dtype)
+	defer ab.Release()
+
+	exp := []arrow.Duration{0, 1, 2, 3}
+	ab.AppendValues(exp, nil)
+	a := ab.NewDurationArray()
+	assert.Equal(t, exp, a.DurationValues())
+
+	a.Release()
+}
+
+func TestDurationBuilder_Empty(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	dtype := &arrow.DurationType{Unit: arrow.Second}
+	ab := array.NewDurationBuilder(mem, dtype)
+	defer ab.Release()
+
+	exp := []arrow.Duration{0, 1, 2, 3}
+
+	ab.AppendValues([]arrow.Duration{}, nil)
+	a := ab.NewDurationArray()
+	assert.Zero(t, a.Len())
+	a.Release()
+
+	ab.AppendValues(nil, nil)
+	a = ab.NewDurationArray()
+	assert.Zero(t, a.Len())
+	a.Release()
+
+	ab.AppendValues([]arrow.Duration{}, nil)
+	ab.AppendValues(exp, nil)
+	a = ab.NewDurationArray()
+	assert.Equal(t, exp, a.DurationValues())
+	a.Release()
+
+	ab.AppendValues(exp, nil)
+	ab.AppendValues([]arrow.Duration{}, nil)
+	a = ab.NewDurationArray()
+	assert.Equal(t, exp, a.DurationValues())
+	a.Release()
+}
+
+func TestDurationBuilder_Resize(t *testing.T) {
+	mem := memory.NewCheckedAllocator(memory.NewGoAllocator())
+	defer mem.AssertSize(t, 0)
+
+	dtype := &arrow.DurationType{Unit: arrow.Second}
+	ab := array.NewDurationBuilder(mem, dtype)
+	defer ab.Release()
+
+	assert.Equal(t, 0, ab.Cap())
+	assert.Equal(t, 0, ab.Len())
+
+	ab.Reserve(63)
+	assert.Equal(t, 64, ab.Cap())
+	assert.Equal(t, 0, ab.Len())
+
+	for i := 0; i < 63; i++ {
+		ab.Append(0)
+	}
+	assert.Equal(t, 64, ab.Cap())
+	assert.Equal(t, 63, ab.Len())
+
+	ab.Resize(5)
+	assert.Equal(t, 5, ab.Len())
+
+	ab.Resize(32)
+	assert.Equal(t, 5, ab.Len())
+}
diff --git a/go/arrow/array/numericbuilder.gen_test.go.tmpl b/go/arrow/array/numericbuilder.gen_test.go.tmpl
index ea74536..e4f7810 100644
--- a/go/arrow/array/numericbuilder.gen_test.go.tmpl
+++ b/go/arrow/array/numericbuilder.gen_test.go.tmpl
@@ -38,6 +38,9 @@ func TestNew{{.Name}}Builder(t *testing.T) {
 {{end -}}
 	defer ab.Release()
 
+	ab.Retain()
+	ab.Release()
+
 	ab.Append(1)
 	ab.Append(2)
 	ab.Append(3)
@@ -78,6 +81,46 @@ func TestNew{{.Name}}Builder(t *testing.T) {
 	assert.Len(t, a.{{.Name}}Values(), 2)
 
 	a.Release()
+
+	var (
+		want   = []{{or .QualifiedType .Type}}{1, 2, 3, 4}
+		valids = []bool{true, true, false, true}
+	)
+
+	ab.AppendValues(want, valids)
+	a = ab.New{{.Name}}Array()
+
+	sub := array.MakeFromData(a.Data())
+	defer sub.Release()
+
+	if got, want := sub.DataType().ID(), a.DataType().ID(); got != want {
+		t.Fatalf("invalid type: got=%q, want=%q", got, want)
+	}
+
+	if _, ok := sub.(*array.{{.Name}}); !ok {
+		t.Fatalf("could not type-assert to array.{{.Name}}")
+	}
+
+	if got, want := a.String(), `[1 2 (null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	slice := array.NewSliceData(a.Data(), 2, 4)
+	defer slice.Release()
+
+	sub1 := array.MakeFromData(slice)
+	defer sub1.Release()
+
+	v, ok := sub1.(*array.{{.Name}})
+	if !ok {
+		t.Fatalf("could not type-assert to array.{{.Name}}")
+	}
+
+	if got, want := v.String(), `[(null) 4]`; got != want {
+		t.Fatalf("got=%q, want=%q", got, want)
+	}
+
+	a.Release()
 }
 
 func Test{{.Name}}Builder_AppendValues(t *testing.T) {
diff --git a/go/arrow/datatype_fixedwidth.go b/go/arrow/datatype_fixedwidth.go
index 14766fa..8dc9c81 100644
--- a/go/arrow/datatype_fixedwidth.go
+++ b/go/arrow/datatype_fixedwidth.go
@@ -46,6 +46,7 @@ type (
 	TimeUnit  int
 	Date32    int32
 	Date64    int64
+	Duration  int64
 )
 
 const (
@@ -92,36 +93,18 @@ func (*Time64Type) Name() string     { return "time64" }
 func (*Time64Type) BitWidth() int    { return 64 }
 func (t *Time64Type) String() string { return "time64[" + t.Unit.String() + "]" }
 
-var (
-	FixedWidthTypes = struct {
-		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{},
-		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"},
-	}
+// DurationType is encoded as a 64-bit signed integer, representing an amount
+// of elapsed time without any relation to a calendar artifact.
+type DurationType struct {
+	Unit TimeUnit
+}
 
-	_ FixedWidthDataType = (*FixedSizeBinaryType)(nil)
-)
+func (*DurationType) ID() Type         { return DURATION }
+func (*DurationType) Name() string     { return "duration" }
+func (*DurationType) BitWidth() int    { return 64 }
+func (t *DurationType) String() string { return "duration[" + t.Unit.String() + "]" }
 
+// Float16Type represents a floating point value encoded with a 16-bit precision.
 type Float16Type struct{}
 
 func (t *Float16Type) ID() Type       { return FLOAT16 }
@@ -161,3 +144,41 @@ 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 }
+
+var (
+	FixedWidthTypes = struct {
+		Boolean         FixedWidthDataType
+		Date32          FixedWidthDataType
+		Date64          FixedWidthDataType
+		DayTimeInterval FixedWidthDataType
+		Duration_s      FixedWidthDataType
+		Duration_ms     FixedWidthDataType
+		Duration_us     FixedWidthDataType
+		Duration_ns     FixedWidthDataType
+		Float16         FixedWidthDataType
+		MonthInterval   FixedWidthDataType
+		Time32s         FixedWidthDataType
+		Time32ms        FixedWidthDataType
+		Time64us        FixedWidthDataType
+		Time64ns        FixedWidthDataType
+		Timestamp       FixedWidthDataType
+	}{
+		Boolean:         &BooleanType{},
+		Date32:          &Date32Type{},
+		Date64:          &Date64Type{},
+		DayTimeInterval: &DayTimeIntervalType{},
+		Duration_s:      &DurationType{Unit: Second},
+		Duration_ms:     &DurationType{Unit: Millisecond},
+		Duration_us:     &DurationType{Unit: Microsecond},
+		Duration_ns:     &DurationType{Unit: Nanosecond},
+		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)
+)
diff --git a/go/arrow/numeric.tmpldata b/go/arrow/numeric.tmpldata
index 45452ab..127a5a1 100644
--- a/go/arrow/numeric.tmpldata
+++ b/go/arrow/numeric.tmpldata
@@ -125,5 +125,17 @@
     "InternalType": "int64",
     "Default": "0",
     "Size": "8"
+  },
+  {
+    "Name": "Duration",
+    "name": "duration",
+    "Type": "Duration",
+    "QualifiedType": "arrow.Duration",
+    "InternalType": "int64",
+    "Default": "0",
+    "Size": "8",
+    "Opt": {
+      "Parametric": true
+    }
   }
 ]
diff --git a/go/arrow/type_traits_numeric.gen.go b/go/arrow/type_traits_numeric.gen.go
index 14fafbc..c8c063a 100644
--- a/go/arrow/type_traits_numeric.gen.go
+++ b/go/arrow/type_traits_numeric.gen.go
@@ -40,6 +40,7 @@ var (
 	Time64Traits    time64Traits
 	Date32Traits    date32Traits
 	Date64Traits    date64Traits
+	DurationTraits  durationTraits
 )
 
 // Int64 traits
@@ -761,3 +762,51 @@ func (date64Traits) CastToBytes(b []Date64) []byte {
 
 // Copy copies src to dst.
 func (date64Traits) Copy(dst, src []Date64) { copy(dst, src) }
+
+// Duration traits
+
+const (
+	// DurationSizeBytes specifies the number of bytes required to store a single Duration in memory
+	DurationSizeBytes = int(unsafe.Sizeof(Duration(0)))
+)
+
+type durationTraits struct{}
+
+// BytesRequired returns the number of bytes required to store n elements in memory.
+func (durationTraits) BytesRequired(n int) int { return DurationSizeBytes * n }
+
+// PutValue
+func (durationTraits) PutValue(b []byte, v Duration) {
+	binary.LittleEndian.PutUint64(b, uint64(v))
+}
+
+// CastFromBytes reinterprets the slice b to a slice of type Duration.
+//
+// NOTE: len(b) must be a multiple of DurationSizeBytes.
+func (durationTraits) CastFromBytes(b []byte) []Duration {
+	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+	var res []Duration
+	s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	s.Data = h.Data
+	s.Len = h.Len / DurationSizeBytes
+	s.Cap = h.Cap / DurationSizeBytes
+
+	return res
+}
+
+// CastToBytes reinterprets the slice b to a slice of bytes.
+func (durationTraits) CastToBytes(b []Duration) []byte {
+	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+
+	var res []byte
+	s := (*reflect.SliceHeader)(unsafe.Pointer(&res))
+	s.Data = h.Data
+	s.Len = h.Len * DurationSizeBytes
+	s.Cap = h.Cap * DurationSizeBytes
+
+	return res
+}
+
+// Copy copies src to dst.
+func (durationTraits) Copy(dst, src []Duration) { copy(dst, src) }