This is an automated email from the ASF dual-hosted git repository.
zeroshade 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 6655c86 ARROW-8452: [Go] support proper nested nullable flags
6655c86 is described below
commit 6655c86760b90bf1df953ffbd72b360462f20688
Author: Matthew Topol <[email protected]>
AuthorDate: Tue Oct 12 11:30:32 2021 -0400
ARROW-8452: [Go] support proper nested nullable flags
Closes #11314 from zeroshade/arrow-8452-nested-nullable
Lead-authored-by: Matthew Topol <[email protected]>
Co-authored-by: Matt Topol <[email protected]>
Signed-off-by: Matthew Topol <[email protected]>
---
go/arrow/cdata/cdata.go | 2 +
go/arrow/cdata/cdata_exports.go | 6 +--
go/arrow/cdata/cdata_fulltest.c | 3 +-
go/arrow/cdata/cdata_test.go | 15 +++---
go/arrow/cdata/cdata_test_framework.go | 15 ++++--
go/arrow/compare.go | 10 +++-
go/arrow/datatype_nested.go | 83 ++++++++++++++++++++++++++++++----
go/arrow/datatype_nested_test.go | 4 +-
go/arrow/example_test.go | 5 +-
go/arrow/internal/arrjson/arrjson.go | 10 ++--
go/arrow/ipc/cmd/arrow-ls/main_test.go | 8 ++--
go/arrow/ipc/metadata.go | 12 +++--
12 files changed, 133 insertions(+), 40 deletions(-)
diff --git a/go/arrow/cdata/cdata.go b/go/arrow/cdata/cdata.go
index 2f108e2..a8d1a5d 100644
--- a/go/arrow/cdata/cdata.go
+++ b/go/arrow/cdata/cdata.go
@@ -224,6 +224,7 @@ func importSchema(schema *CArrowSchema) (ret arrow.Field,
err error) {
switch f[1] {
case 'l': // list
dt = arrow.ListOf(childFields[0].Type)
+ dt.(*arrow.ListType).NullableElem =
childFields[0].Nullable
case 'w': // fixed size list is w:# where # is the list size.
listSize, err := strconv.Atoi(strings.Split(f, ":")[1])
if err != nil {
@@ -231,6 +232,7 @@ func importSchema(schema *CArrowSchema) (ret arrow.Field,
err error) {
}
dt = arrow.FixedSizeListOf(int32(listSize),
childFields[0].Type)
+ dt.(*arrow.FixedSizeListType).NullableElem =
childFields[0].Nullable
case 's': // struct
dt = arrow.StructOf(childFields...)
case 'm': // map type is basically a list of structs.
diff --git a/go/arrow/cdata/cdata_exports.go b/go/arrow/cdata/cdata_exports.go
index 0143725..8565952 100644
--- a/go/arrow/cdata/cdata_exports.go
+++ b/go/arrow/cdata/cdata_exports.go
@@ -233,7 +233,7 @@ func (exp *schemaExporter) export(field arrow.Field) {
switch dt := field.Type.(type) {
case *arrow.ListType:
exp.children = make([]schemaExporter, 1)
- exp.children[0].export(arrow.Field{Name: "item", Type:
dt.Elem(), Nullable: field.Nullable})
+ exp.children[0].export(dt.ElemField())
case *arrow.StructType:
exp.children = make([]schemaExporter, len(dt.Fields()))
for i, f := range dt.Fields() {
@@ -241,10 +241,10 @@ func (exp *schemaExporter) export(field arrow.Field) {
}
case *arrow.MapType:
exp.children = make([]schemaExporter, 1)
- exp.children[0].export(arrow.Field{Name: "keyvalue", Type:
dt.ValueType(), Nullable: field.Nullable})
+ exp.children[0].export(dt.ValueField())
case *arrow.FixedSizeListType:
exp.children = make([]schemaExporter, 1)
- exp.children[0].export(arrow.Field{Name: "item", Type:
dt.Elem(), Nullable: field.Nullable})
+ exp.children[0].export(dt.ElemField())
}
exp.exportMeta(&field.Metadata)
diff --git a/go/arrow/cdata/cdata_fulltest.c b/go/arrow/cdata/cdata_fulltest.c
index 5c4ca49..06ea477 100644
--- a/go/arrow/cdata/cdata_fulltest.c
+++ b/go/arrow/cdata/cdata_fulltest.c
@@ -151,7 +151,7 @@ void free_malloced_schemas(struct ArrowSchema** schemas) {
free(schemas);
}
-struct ArrowSchema** test_lists(const char** fmts, const char** names, const
int n) {
+struct ArrowSchema** test_lists(const char** fmts, const char** names, const
int* nullflags, const int n) {
struct ArrowSchema** schemas = malloc(sizeof(struct ArrowSchema*)*n);
for (int i = 0; i < n; ++i) {
schemas[i] = malloc(sizeof(struct ArrowSchema));
@@ -168,6 +168,7 @@ struct ArrowSchema** test_lists(const char** fmts, const
char** names, const int
if (i != 0) {
schemas[i-1]->n_children = 1;
schemas[i-1]->children = &schemas[i];
+ schemas[i]->flags = nullflags[i-1];
}
}
return schemas;
diff --git a/go/arrow/cdata/cdata_test.go b/go/arrow/cdata/cdata_test.go
index d29bf04..47cd334 100644
--- a/go/arrow/cdata/cdata_test.go
+++ b/go/arrow/cdata/cdata_test.go
@@ -175,18 +175,19 @@ func TestImportTemporalSchema(t *testing.T) {
func TestListSchemas(t *testing.T) {
tests := []struct {
- typ arrow.DataType
- fmts []string
- names []string
+ typ arrow.DataType
+ fmts []string
+ names []string
+ isnull []bool
}{
- {arrow.ListOf(arrow.PrimitiveTypes.Int8), []string{"+l", "c"},
[]string{"", "item"}},
- {arrow.FixedSizeListOf(2, arrow.PrimitiveTypes.Int64),
[]string{"+w:2", "l"}, []string{"", "item"}},
- {arrow.ListOf(arrow.ListOf(arrow.PrimitiveTypes.Int32)),
[]string{"+l", "+l", "i"}, []string{"", "item", "item"}},
+ {arrow.ListOf(arrow.PrimitiveTypes.Int8), []string{"+l", "c"},
[]string{"", "item"}, []bool{true}},
+ {arrow.FixedSizeListOfNonNullable(2,
arrow.PrimitiveTypes.Int64), []string{"+w:2", "l"}, []string{"", "item"},
[]bool{false}},
+
{arrow.ListOfNonNullable(arrow.ListOf(arrow.PrimitiveTypes.Int32)),
[]string{"+l", "+l", "i"}, []string{"", "item", "item"}, []bool{false, true}},
}
for _, tt := range tests {
t.Run(tt.typ.Name(), func(t *testing.T) {
- sc := testNested(tt.fmts, tt.names)
+ sc := testNested(tt.fmts, tt.names, tt.isnull)
defer freeMallocedSchemas(sc)
top := (*[1]*CArrowSchema)(unsafe.Pointer(sc))[0]
diff --git a/go/arrow/cdata/cdata_test_framework.go
b/go/arrow/cdata/cdata_test_framework.go
index 68dbeb0..048d950 100644
--- a/go/arrow/cdata/cdata_test_framework.go
+++ b/go/arrow/cdata/cdata_test_framework.go
@@ -44,7 +44,7 @@ package cdata
// int test1_is_released();
// void test_primitive(struct ArrowSchema* schema, const char* fmt);
// void free_malloced_schemas(struct ArrowSchema**);
-// struct ArrowSchema** test_lists(const char** fmts, const char** names,
const int n);
+// struct ArrowSchema** test_lists(const char** fmts, const char** names,
const int* nullflags, const int n);
// struct ArrowSchema** test_struct(const char** fmts, const char** names,
int64_t* flags, const int n);
// struct ArrowSchema** test_map(const char** fmts, const char** names,
int64_t* flags, const int n);
// struct ArrowSchema** test_schema(const char** fmts, const char** names,
int64_t* flags, const int n);
@@ -117,19 +117,28 @@ func freeMallocedSchemas(schemas **CArrowSchema) {
C.free_malloced_schemas(schemas)
}
-func testNested(fmts, names []string) **CArrowSchema {
+func testNested(fmts, names []string, isnull []bool) **CArrowSchema {
if len(fmts) != len(names) {
panic("testing nested lists must have same size fmts and names")
}
cfmts := make([]*C.char, len(fmts))
cnames := make([]*C.char, len(names))
+ nulls := make([]C.int, len(isnull))
for i := range fmts {
cfmts[i] = C.CString(fmts[i])
cnames[i] = C.CString(names[i])
}
- return C.test_lists((**C.char)(unsafe.Pointer(&cfmts[0])),
(**C.char)(unsafe.Pointer(&cnames[0])), C.int(len(fmts)))
+ for i, v := range isnull {
+ if v {
+ nulls[i] = C.ARROW_FLAG_NULLABLE
+ } else {
+ nulls[i] = 0
+ }
+ }
+
+ return C.test_lists((**C.char)(unsafe.Pointer(&cfmts[0])),
(**C.char)(unsafe.Pointer(&cnames[0])), (*C.int)(unsafe.Pointer(&nulls[0])),
C.int(len(fmts)))
}
func testStruct(fmts, names []string, flags []int64) **CArrowSchema {
diff --git a/go/arrow/compare.go b/go/arrow/compare.go
index 5acfd94..afb4a3d 100644
--- a/go/arrow/compare.go
+++ b/go/arrow/compare.go
@@ -61,7 +61,15 @@ func TypeEqual(left, right DataType, opts
...TypeEqualOption) bool {
if cfg.metadata {
return l.Meta.Equal(right.(*ListType).Meta)
}
- return true
+ return l.NullableElem == right.(*ListType).NullableElem
+ case *FixedSizeListType:
+ if !TypeEqual(l.Elem(), right.(*FixedSizeListType).Elem(),
opts...) {
+ return false
+ }
+ if cfg.metadata {
+ return l.Meta.Equal(right.(*FixedSizeListType).Meta)
+ }
+ return l.n == right.(*FixedSizeListType).n && l.NullableElem ==
right.(*FixedSizeListType).NullableElem
case *StructType:
r := right.(*StructType)
switch {
diff --git a/go/arrow/datatype_nested.go b/go/arrow/datatype_nested.go
index 993950a..63dabf4 100644
--- a/go/arrow/datatype_nested.go
+++ b/go/arrow/datatype_nested.go
@@ -24,24 +24,41 @@ import (
// ListType describes a nested type in which each array slot contains
// a variable-size sequence of values, all having the same relative type.
type ListType struct {
- elem DataType // DataType of the list's elements
- Meta Metadata
+ elem DataType // DataType of the list's elements
+ Meta Metadata
+ NullableElem bool
}
// ListOf returns the list type with element type t.
// For example, if t represents int32, ListOf(t) represents []int32.
//
-// ListOf panics if t is nil or invalid.
+// ListOf panics if t is nil or invalid. NullableElem defaults to true
func ListOf(t DataType) *ListType {
if t == nil {
panic("arrow: nil DataType")
}
- return &ListType{elem: t}
+ return &ListType{elem: t, NullableElem: true}
+}
+
+// ListOfNonNullable is like ListOf but NullableElem defaults to false,
indicating
+// that the child type should be marked as non-nullable.
+func ListOfNonNullable(t DataType) *ListType {
+ if t == nil {
+ panic("arrow: nil DataType")
+ }
+ return &ListType{elem: t, NullableElem: false}
+}
+
+func (*ListType) ID() Type { return LIST }
+func (*ListType) Name() string { return "list" }
+
+func (t *ListType) String() string {
+ if t.NullableElem {
+ return fmt.Sprintf("list<item: %v, nullable>", t.elem)
+ }
+ return fmt.Sprintf("list<item: %v>", t.elem)
}
-func (*ListType) ID() Type { return LIST }
-func (*ListType) Name() string { return "list" }
-func (t *ListType) String() string { return fmt.Sprintf("list<item: %v>",
t.elem) }
func (t *ListType) Fingerprint() string {
child := t.elem.Fingerprint()
if len(child) > 0 {
@@ -53,11 +70,22 @@ func (t *ListType) Fingerprint() string {
// Elem returns the ListType's element type.
func (t *ListType) Elem() DataType { return t.elem }
+func (t *ListType) ElemField() Field {
+ return Field{
+ Name: "item",
+ Type: t.elem,
+ Metadata: t.Meta,
+ Nullable: t.NullableElem,
+ }
+}
+
// FixedSizeListType describes a nested type in which each array slot contains
// a fixed-size sequence of values, all having the same relative type.
type FixedSizeListType struct {
- n int32 // number of elements in the list
- elem DataType // DataType of the list's elements
+ n int32 // number of elements in the list
+ elem DataType // DataType of the list's elements
+ Meta Metadata
+ NullableElem bool
}
// FixedSizeListOf returns the list type with element type t.
@@ -65,6 +93,7 @@ type FixedSizeListType struct {
//
// FixedSizeListOf panics if t is nil or invalid.
// FixedSizeListOf panics if n is <= 0.
+// NullableElem defaults to true
func FixedSizeListOf(n int32, t DataType) *FixedSizeListType {
if t == nil {
panic("arrow: nil DataType")
@@ -72,12 +101,27 @@ func FixedSizeListOf(n int32, t DataType)
*FixedSizeListType {
if n <= 0 {
panic("arrow: invalid size")
}
- return &FixedSizeListType{elem: t, n: n}
+ return &FixedSizeListType{elem: t, n: n, NullableElem: true}
+}
+
+// FixedSizeListOfNonNullable is like FixedSizeListOf but NullableElem
defaults to false
+// indicating that the child type should be marked as non-nullable.
+func FixedSizeListOfNonNullable(n int32, t DataType) *FixedSizeListType {
+ if t == nil {
+ panic("arrow: nil DataType")
+ }
+ if n <= 0 {
+ panic("arrow: invalid size")
+ }
+ return &FixedSizeListType{elem: t, n: n, NullableElem: false}
}
func (*FixedSizeListType) ID() Type { return FIXED_SIZE_LIST }
func (*FixedSizeListType) Name() string { return "fixed_size_list" }
func (t *FixedSizeListType) String() string {
+ if t.NullableElem {
+ return fmt.Sprintf("fixed_size_list<item: %v, nullable>[%d]",
t.elem, t.n)
+ }
return fmt.Sprintf("fixed_size_list<item: %v>[%d]", t.elem, t.n)
}
@@ -87,6 +131,15 @@ func (t *FixedSizeListType) Elem() DataType { return t.elem
}
// Len returns the FixedSizeListType's size.
func (t *FixedSizeListType) Len() int32 { return t.n }
+func (t *FixedSizeListType) ElemField() Field {
+ return Field{
+ Name: "item",
+ Type: t.elem,
+ Metadata: t.Meta,
+ Nullable: t.NullableElem,
+ }
+}
+
func (t *FixedSizeListType) Fingerprint() string {
child := t.elem.Fingerprint()
if len(child) > 0 {
@@ -217,6 +270,16 @@ func (t *MapType) KeyType() DataType { return
t.KeyField().Type }
func (t *MapType) ItemField() Field { return
t.value.Elem().(*StructType).Field(1) }
func (t *MapType) ItemType() DataType { return t.ItemField().Type }
func (t *MapType) ValueType() *StructType { return
t.value.Elem().(*StructType) }
+func (t *MapType) ValueField() Field {
+ return Field{
+ Name: "entries",
+ Type: t.ValueType(),
+ }
+}
+
+func (t *MapType) SetItemNullable(nullable bool) {
+ t.value.Elem().(*StructType).fields[1].Nullable = nullable
+}
func (t *MapType) Fingerprint() string {
keyFingerprint := t.KeyType().Fingerprint()
diff --git a/go/arrow/datatype_nested_test.go b/go/arrow/datatype_nested_test.go
index 94c6a71..7fe0303 100644
--- a/go/arrow/datatype_nested_test.go
+++ b/go/arrow/datatype_nested_test.go
@@ -40,7 +40,7 @@ func TestListOf(t *testing.T) {
} {
t.Run(tc.Name(), func(t *testing.T) {
got := ListOf(tc)
- want := &ListType{elem: tc}
+ want := &ListType{elem: tc, NullableElem: true}
if !reflect.DeepEqual(got, want) {
t.Fatalf("got=%#v, want=%#v", got, want)
}
@@ -313,7 +313,7 @@ func TestFixedSizeListOf(t *testing.T) {
t.Run(tc.Name(), func(t *testing.T) {
const size = 3
got := FixedSizeListOf(size, tc)
- want := &FixedSizeListType{elem: tc, n: size}
+ want := &FixedSizeListType{elem: tc, n: size,
NullableElem: true}
if !reflect.DeepEqual(got, want) {
t.Fatalf("got=%#v, want=%#v", got, want)
}
diff --git a/go/arrow/example_test.go b/go/arrow/example_test.go
index bb4bf78..8748072 100644
--- a/go/arrow/example_test.go
+++ b/go/arrow/example_test.go
@@ -162,9 +162,11 @@ func Example_listArray() {
arr := lb.NewArray().(*array.List)
defer arr.Release()
+ arr.DataType().(*arrow.ListType).NullableElem = false
fmt.Printf("NullN() = %d\n", arr.NullN())
fmt.Printf("Len() = %d\n", arr.Len())
fmt.Printf("Offsets() = %v\n", arr.Offsets())
+ fmt.Printf("Type() = %v\n", arr.DataType())
offsets := arr.Offsets()[1:]
@@ -192,6 +194,7 @@ func Example_listArray() {
// NullN() = 2
// Len() = 7
// Offsets() = [0 3 3 4 6 9 9 10]
+ // Type() = list<item: int64>
// List[0] = [0, 1, 2]
// List[1] = (null)
// List[2] = [3]
@@ -245,7 +248,7 @@ func Example_fixedSizeListArray() {
// Output:
// NullN() = 2
// Len() = 5
- // Type() = fixed_size_list<item: int64>[3]
+ // Type() = fixed_size_list<item: int64, nullable>[3]
// List = [[0 1 2] (null) [3 4 5] [6 7 8] (null)]
}
diff --git a/go/arrow/internal/arrjson/arrjson.go
b/go/arrow/internal/arrjson/arrjson.go
index 3d5247b..a8209ea 100644
--- a/go/arrow/internal/arrjson/arrjson.go
+++ b/go/arrow/internal/arrjson/arrjson.go
@@ -353,7 +353,7 @@ func (f *FieldWrapper) UnmarshalJSON(data []byte) error {
case "list":
f.arrowType = arrow.ListOf(f.Children[0].arrowType)
f.arrowType.(*arrow.ListType).Meta = f.Children[0].arrowMeta
-
+ f.arrowType.(*arrow.ListType).NullableElem =
f.Children[0].Nullable
case "map":
t := mapJSON{}
if err := json.Unmarshal(f.Type, &t); err != nil {
@@ -376,6 +376,8 @@ func (f *FieldWrapper) UnmarshalJSON(data []byte) error {
return err
}
f.arrowType = arrow.FixedSizeListOf(t.ListSize,
f.Children[0].arrowType)
+ f.arrowType.(*arrow.FixedSizeListType).NullableElem =
f.Children[0].Nullable
+ f.arrowType.(*arrow.FixedSizeListType).Meta =
f.Children[0].arrowMeta
case "interval":
t := unitZoneJSON{}
if err := json.Unmarshal(f.Type, &t); err != nil {
@@ -548,13 +550,13 @@ func fieldsToJSON(fields []arrow.Field) []FieldWrapper {
}}
switch dt := f.Type.(type) {
case *arrow.ListType:
- o[i].Children = fieldsToJSON([]arrow.Field{{Name:
"item", Type: dt.Elem(), Nullable: f.Nullable, Metadata: dt.Meta}})
+ o[i].Children =
fieldsToJSON([]arrow.Field{dt.ElemField()})
case *arrow.FixedSizeListType:
- o[i].Children = fieldsToJSON([]arrow.Field{{Name:
"item", Type: dt.Elem(), Nullable: f.Nullable}})
+ o[i].Children =
fieldsToJSON([]arrow.Field{dt.ElemField()})
case *arrow.StructType:
o[i].Children = fieldsToJSON(dt.Fields())
case *arrow.MapType:
- o[i].Children = fieldsToJSON([]arrow.Field{{Name:
"entries", Type: dt.ValueType()}})
+ o[i].Children =
fieldsToJSON([]arrow.Field{dt.ValueField()})
}
}
return o
diff --git a/go/arrow/ipc/cmd/arrow-ls/main_test.go
b/go/arrow/ipc/cmd/arrow-ls/main_test.go
index 1ea9580..508ea68 100644
--- a/go/arrow/ipc/cmd/arrow-ls/main_test.go
+++ b/go/arrow/ipc/cmd/arrow-ls/main_test.go
@@ -72,7 +72,7 @@ records: 2
name: "lists",
want: `schema:
fields: 1
- - list_nullable: type=list<item: int32>, nullable
+ - list_nullable: type=list<item: int32, nullable>, nullable
records: 4
`,
},
@@ -89,7 +89,7 @@ records: 3
name: "fixed_size_lists",
want: `schema:
fields: 1
- - fixed_size_list_nullable: type=fixed_size_list<item: int32>[3], nullable
+ - fixed_size_list_nullable: type=fixed_size_list<item: int32,
nullable>[3], nullable
records: 3
`,
},
@@ -248,7 +248,7 @@ records: 2
name: "lists",
want: `schema:
fields: 1
- - list_nullable: type=list<item: int32>, nullable
+ - list_nullable: type=list<item: int32, nullable>, nullable
records: 4
`,
},
@@ -257,7 +257,7 @@ records: 4
want: `version: V5
schema:
fields: 1
- - list_nullable: type=list<item: int32>, nullable
+ - list_nullable: type=list<item: int32, nullable>, nullable
records: 4
`,
},
diff --git a/go/arrow/ipc/metadata.go b/go/arrow/ipc/metadata.go
index ccc231d..9f6333b 100644
--- a/go/arrow/ipc/metadata.go
+++ b/go/arrow/ipc/metadata.go
@@ -347,13 +347,13 @@ func (fv *fieldVisitor) visit(field arrow.Field) {
case *arrow.ListType:
fv.dtype = flatbuf.TypeList
- fv.kids = append(fv.kids, fieldToFB(fv.b, arrow.Field{Name:
"item", Type: dt.Elem(), Nullable: field.Nullable, Metadata: dt.Meta}, fv.memo))
+ fv.kids = append(fv.kids, fieldToFB(fv.b, dt.ElemField(),
fv.memo))
flatbuf.ListStart(fv.b)
fv.offset = flatbuf.ListEnd(fv.b)
case *arrow.FixedSizeListType:
fv.dtype = flatbuf.TypeFixedSizeList
- fv.kids = append(fv.kids, fieldToFB(fv.b, arrow.Field{Name:
"item", Type: dt.Elem(), Nullable: field.Nullable}, fv.memo))
+ fv.kids = append(fv.kids, fieldToFB(fv.b, dt.ElemField(),
fv.memo))
flatbuf.FixedSizeListStart(fv.b)
flatbuf.FixedSizeListAddListSize(fv.b, dt.Len())
fv.offset = flatbuf.FixedSizeListEnd(fv.b)
@@ -379,7 +379,7 @@ func (fv *fieldVisitor) visit(field arrow.Field) {
case *arrow.MapType:
fv.dtype = flatbuf.TypeMap
- fv.kids = append(fv.kids, fieldToFB(fv.b, arrow.Field{Name:
"entries", Type: dt.ValueType()}, fv.memo))
+ fv.kids = append(fv.kids, fieldToFB(fv.b, dt.ValueField(),
fv.memo))
flatbuf.MapStart(fv.b)
flatbuf.MapAddKeysSorted(fv.b, dt.KeysSorted)
fv.offset = flatbuf.MapEnd(fv.b)
@@ -607,6 +607,7 @@ func concreteTypeFromFB(typ flatbuf.Type, data
flatbuffers.Table, children []arr
}
dt := arrow.ListOf(children[0].Type)
dt.Meta = children[0].Metadata
+ dt.NullableElem = children[0].Nullable
return dt, nil
case flatbuf.TypeFixedSizeList:
@@ -615,7 +616,10 @@ func concreteTypeFromFB(typ flatbuf.Type, data
flatbuffers.Table, children []arr
if len(children) != 1 {
return nil, xerrors.Errorf("arrow/ipc: FixedSizeList
must have exactly 1 child field (got=%d)", len(children))
}
- return arrow.FixedSizeListOf(dt.ListSize(), children[0].Type),
nil
+ ret := arrow.FixedSizeListOf(dt.ListSize(), children[0].Type)
+ ret.Meta = children[0].Metadata
+ ret.NullableElem = children[0].Nullable
+ return ret, nil
case flatbuf.TypeStruct_:
return arrow.StructOf(children...), nil