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)
+                       }
+               })
+       }
+}

Reply via email to