This is an automated email from the ASF dual-hosted git repository.
wesm 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 701d87e ARROW-4971: [Go] Add type equality test function
701d87e is described below
commit 701d87e3f8ff6cf2353af7c4d15ae7ba1846bc48
Author: alexandreyc <[email protected]>
AuthorDate: Fri Mar 29 10:33:56 2019 -0500
ARROW-4971: [Go] Add type equality test function
Hello,
In the quest for adding support for array comparison (equality) I just
added type comparison.
For the moment it considers metadata (only applies to STRUCT type) in the
comparison. In the C++ implementation they have a flag to ignore metadata. Do
you think we should have it too?
Feel free to give me your feedback!
A
Author: alexandreyc <[email protected]>
Closes #3981 from alexandreyc/type-equals and squashes the following
commits:
049831aa8 <alexandreyc> Add documentation
f828543d6 <alexandreyc> Update to more idiomatic args list for TypeEquals
e1a49893f <alexandreyc> Update checkMetadata flag to functionnal option
20898f7ff <alexandreyc> Update to use sub tests
a9d5d8eeb <alexandreyc> Update to more idiomatic for range-based loop
0defb8961 <alexandreyc> Add flag to check for metadata equality
289c5a760 <alexandreyc> Remove old code
8cee59002 <alexandreyc> Add type equality test function
---
go/arrow/compare.go | 79 +++++++++++++++
go/arrow/compare_test.go | 252 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 331 insertions(+)
diff --git a/go/arrow/compare.go b/go/arrow/compare.go
new file mode 100644
index 0000000..17bbd63
--- /dev/null
+++ b/go/arrow/compare.go
@@ -0,0 +1,79 @@
+// 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 (
+ "reflect"
+)
+
+type typeEqualsConfig struct {
+ metadata bool
+}
+
+// TypeEqualsOption is a functional option type used for configuring type
+// equality checks.
+type TypeEqualsOption func(*typeEqualsConfig)
+
+// CheckMetadata is an option for TypeEquals that allows checking for metadata
+// equality besides type equality. It only makes sense for STRUCT type.
+func CheckMetadata() TypeEqualsOption {
+ return func(cfg *typeEqualsConfig) {
+ cfg.metadata = true
+ }
+}
+
+// TypeEquals checks if two DataType are the same, optionally checking metadata
+// equality for STRUCT types.
+func TypeEquals(left, right DataType, opts ...TypeEqualsOption) bool {
+ var cfg typeEqualsConfig
+ for _, opt := range opts {
+ opt(&cfg)
+ }
+
+ switch {
+ case left == nil || right == nil:
+ return false
+ case left.ID() != right.ID():
+ return false
+ }
+
+ // StructType is the only type that has metadata.
+ l, ok := left.(*StructType)
+ if !ok || cfg.metadata {
+ return reflect.DeepEqual(left, right)
+ }
+
+ r := right.(*StructType)
+ switch {
+ case len(l.fields) != len(r.fields):
+ return false
+ case !reflect.DeepEqual(l.index, r.index):
+ return false
+ }
+ for i := range l.fields {
+ leftField, rightField := l.fields[i], r.fields[i]
+ switch {
+ case leftField.Name != rightField.Name:
+ return false
+ case leftField.Nullable != rightField.Nullable:
+ return false
+ case !TypeEquals(leftField.Type, rightField.Type, opts...):
+ return false
+ }
+ }
+ return true
+}
diff --git a/go/arrow/compare_test.go b/go/arrow/compare_test.go
new file mode 100644
index 0000000..16c4daf
--- /dev/null
+++ b/go/arrow/compare_test.go
@@ -0,0 +1,252 @@
+// 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 (
+ "testing"
+)
+
+func TestTypeEquals(t *testing.T) {
+ tests := []struct {
+ left, right DataType
+ want bool
+ checkMetadata bool
+ }{
+ {
+ nil, nil, false, false,
+ },
+ {
+ nil, PrimitiveTypes.Uint8, false, false,
+ },
+ {
+ PrimitiveTypes.Float32, nil, false, false,
+ },
+ {
+ PrimitiveTypes.Float64, PrimitiveTypes.Int32, false,
false,
+ },
+ {
+ Null, Null, true, false,
+ },
+ {
+ &Time32Type{Unit: Second}, &Time32Type{Unit: Second},
true, false,
+ },
+ {
+ &Time32Type{Unit: Millisecond}, &Time32Type{Unit:
Second}, false, false,
+ },
+ {
+ &Time64Type{Unit: Nanosecond}, &Time64Type{Unit:
Nanosecond}, true, false,
+ },
+ {
+ &Time64Type{Unit: Nanosecond}, &Time64Type{Unit:
Microsecond}, false, false,
+ },
+ {
+ &TimestampType{Unit: Second, TimeZone: "UTC"},
&TimestampType{Unit: Second, TimeZone: "UTC"}, true, false,
+ },
+ {
+ &TimestampType{Unit: Microsecond, TimeZone: "UTC"},
&TimestampType{Unit: Millisecond, TimeZone: "UTC"}, false, false,
+ },
+ {
+ &TimestampType{Unit: Second, TimeZone: "UTC"},
&TimestampType{Unit: Second, TimeZone: "CET"}, false, false,
+ },
+ {
+ &TimestampType{Unit: Second, TimeZone: "UTC"},
&TimestampType{Unit: Nanosecond, TimeZone: "CET"}, false, false,
+ },
+ {
+ &ListType{PrimitiveTypes.Uint64},
&ListType{PrimitiveTypes.Uint64}, true, false,
+ },
+ {
+ &ListType{PrimitiveTypes.Uint64},
&ListType{PrimitiveTypes.Uint32}, false, false,
+ },
+ {
+ &ListType{&Time32Type{Unit: Millisecond}},
&ListType{&Time32Type{Unit: Millisecond}}, true, false,
+ },
+ {
+ &ListType{&Time32Type{Unit: Millisecond}},
&ListType{&Time32Type{Unit: Second}}, false, false,
+ },
+ {
+ &ListType{&ListType{PrimitiveTypes.Uint16}},
&ListType{&ListType{PrimitiveTypes.Uint16}}, true, false,
+ },
+ {
+ &ListType{&ListType{PrimitiveTypes.Uint16}},
&ListType{&ListType{PrimitiveTypes.Uint8}}, false, false,
+ },
+ {
+ &ListType{&ListType{&ListType{PrimitiveTypes.Uint16}}},
&ListType{&ListType{PrimitiveTypes.Uint8}}, false, false,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true},
+ },
+ index: map[string]int{"f1": 0},
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f1": 0},
+ },
+ false, true,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0},
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f1": 0},
+ },
+ false, false,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f0", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f0": 0},
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f1": 0},
+ },
+ false, false,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f1": 0},
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ Field{Name: "f2", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f1": 0, "f2": 0},
+ },
+ false, true,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ },
+ true, false,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ },
+ true, false,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ meta: MetadataFrom(map[string]string{"k1":
"v1"}),
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ meta: MetadataFrom(map[string]string{"k1":
"v1"}),
+ },
+ true, true,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f1": 0},
+ meta: MetadataFrom(map[string]string{"k1":
"v1"}),
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint32, Nullable: true},
+ },
+ index: map[string]int{"f1": 0},
+ meta: MetadataFrom(map[string]string{"k1":
"v2"}),
+ },
+ true, false,
+ },
+ {
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true, Metadata:
MetadataFrom(map[string]string{"k1": "v1"})},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ },
+ &StructType{
+ fields: []Field{
+ Field{Name: "f1", Type:
PrimitiveTypes.Uint16, Nullable: true, Metadata:
MetadataFrom(map[string]string{"k1": "v2"})},
+ Field{Name: "f2", Type:
PrimitiveTypes.Float32, Nullable: false},
+ },
+ index: map[string]int{"f1": 0, "f2": 1},
+ },
+ false, true,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run("", func(t *testing.T) {
+ var got bool
+ if test.checkMetadata {
+ got = TypeEquals(test.left, test.right,
CheckMetadata())
+ } else {
+ got = TypeEquals(test.left, test.right)
+ }
+ if got != test.want {
+ t.Fatalf("TypeEquals(%v, %v, %v): got=%v,
want=%v", test.left, test.right, test.checkMetadata, got, test.want)
+ }
+ })
+ }
+}