github.com/apache/arrow/go/v14@v14.0.2/parquet/schema/schema_element_test.go (about)

     1  // Licensed to the Apache Software Foundation (ASF) under one
     2  // or more contributor license agreements.  See the NOTICE file
     3  // distributed with this work for additional information
     4  // regarding copyright ownership.  The ASF licenses this file
     5  // to you under the Apache License, Version 2.0 (the
     6  // "License"); you may not use this file except in compliance
     7  // with the License.  You may obtain a copy of the License at
     8  //
     9  // http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package schema
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/apache/arrow/go/v14/parquet"
    23  	format "github.com/apache/arrow/go/v14/parquet/internal/gen-go/parquet"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/suite"
    26  )
    27  
    28  type schemaElementConstruction struct {
    29  	node            Node
    30  	element         *format.SchemaElement
    31  	name            string
    32  	expectConverted bool
    33  	converted       ConvertedType
    34  	expectLogical   bool
    35  	checkLogical    func(*format.SchemaElement) bool
    36  }
    37  
    38  type decimalSchemaElementConstruction struct {
    39  	schemaElementConstruction
    40  	precision int
    41  	scale     int
    42  }
    43  
    44  type temporalSchemaElementConstruction struct {
    45  	schemaElementConstruction
    46  	adjusted bool
    47  	unit     TimeUnitType
    48  	getUnit  func(*format.SchemaElement) *format.TimeUnit
    49  }
    50  
    51  type intSchemaElementConstruction struct {
    52  	schemaElementConstruction
    53  	width  int8
    54  	signed bool
    55  }
    56  
    57  type legacySchemaElementConstructArgs struct {
    58  	name            string
    59  	physical        parquet.Type
    60  	len             int
    61  	expectConverted bool
    62  	converted       ConvertedType
    63  	expectLogical   bool
    64  	checkLogical    func(*format.SchemaElement) bool
    65  }
    66  
    67  type schemaElementConstructArgs struct {
    68  	name            string
    69  	logical         LogicalType
    70  	physical        parquet.Type
    71  	len             int
    72  	expectConverted bool
    73  	converted       ConvertedType
    74  	expectLogical   bool
    75  	checkLogical    func(*format.SchemaElement) bool
    76  }
    77  type SchemaElementConstructionSuite struct {
    78  	suite.Suite
    79  }
    80  
    81  func (s *SchemaElementConstructionSuite) reconstruct(c schemaElementConstructArgs) *schemaElementConstruction {
    82  	ret := &schemaElementConstruction{
    83  		node:            MustPrimitive(NewPrimitiveNodeLogical(c.name, parquet.Repetitions.Required, c.logical, c.physical, c.len, -1)),
    84  		name:            c.name,
    85  		expectConverted: c.expectConverted,
    86  		converted:       c.converted,
    87  		expectLogical:   c.expectLogical,
    88  		checkLogical:    c.checkLogical,
    89  	}
    90  	ret.element = ret.node.toThrift()
    91  	return ret
    92  }
    93  
    94  func (s *SchemaElementConstructionSuite) legacyReconstruct(c legacySchemaElementConstructArgs) *schemaElementConstruction {
    95  	ret := &schemaElementConstruction{
    96  		node:            MustPrimitive(NewPrimitiveNodeConverted(c.name, parquet.Repetitions.Required, c.physical, c.converted, c.len, 0, 0, -1)),
    97  		name:            c.name,
    98  		expectConverted: c.expectConverted,
    99  		converted:       c.converted,
   100  		expectLogical:   c.expectLogical,
   101  		checkLogical:    c.checkLogical,
   102  	}
   103  	ret.element = ret.node.toThrift()
   104  	return ret
   105  }
   106  
   107  func (s *SchemaElementConstructionSuite) inspect(c *schemaElementConstruction) {
   108  	if c.expectConverted {
   109  		s.True(c.element.IsSetConvertedType())
   110  		s.Equal(c.converted, ConvertedType(*c.element.ConvertedType))
   111  	} else {
   112  		s.False(c.element.IsSetConvertedType())
   113  	}
   114  	if c.expectLogical {
   115  		s.True(c.element.IsSetLogicalType())
   116  		s.True(c.checkLogical(c.element))
   117  	} else {
   118  		s.False(c.element.IsSetLogicalType())
   119  	}
   120  }
   121  
   122  func (s *SchemaElementConstructionSuite) TestSimple() {
   123  	checkNone := func(*format.SchemaElement) bool { return true }
   124  
   125  	tests := []struct {
   126  		name   string
   127  		args   *schemaElementConstructArgs
   128  		legacy *legacySchemaElementConstructArgs
   129  	}{
   130  		{"string", &schemaElementConstructArgs{
   131  			"string", StringLogicalType{}, parquet.Types.ByteArray, -1, true, ConvertedTypes.UTF8, true,
   132  			func(e *format.SchemaElement) bool { return e.LogicalType.IsSetSTRING() },
   133  		}, nil},
   134  		{"enum", &schemaElementConstructArgs{
   135  			"enum", EnumLogicalType{}, parquet.Types.ByteArray, -1, true, ConvertedTypes.Enum, true,
   136  			func(e *format.SchemaElement) bool { return e.LogicalType.IsSetENUM() },
   137  		}, nil},
   138  		{"date", &schemaElementConstructArgs{
   139  			"date", DateLogicalType{}, parquet.Types.Int32, -1, true, ConvertedTypes.Date, true,
   140  			func(e *format.SchemaElement) bool { return e.LogicalType.IsSetDATE() },
   141  		}, nil},
   142  		{"interval", &schemaElementConstructArgs{
   143  			"interval", IntervalLogicalType{}, parquet.Types.FixedLenByteArray, 12, true, ConvertedTypes.Interval, false,
   144  			checkNone,
   145  		}, nil},
   146  		{"null", &schemaElementConstructArgs{
   147  			"null", NullLogicalType{}, parquet.Types.Double, -1, false, ConvertedTypes.NA, true,
   148  			func(e *format.SchemaElement) bool { return e.LogicalType.IsSetUNKNOWN() },
   149  		}, nil},
   150  		{"json", &schemaElementConstructArgs{
   151  			"json", JSONLogicalType{}, parquet.Types.ByteArray, -1, true, ConvertedTypes.JSON, true,
   152  			func(e *format.SchemaElement) bool { return e.LogicalType.IsSetJSON() },
   153  		}, nil},
   154  		{"bson", &schemaElementConstructArgs{
   155  			"bson", BSONLogicalType{}, parquet.Types.ByteArray, -1, true, ConvertedTypes.BSON, true,
   156  			func(e *format.SchemaElement) bool { return e.LogicalType.IsSetBSON() },
   157  		}, nil},
   158  		{"uuid", &schemaElementConstructArgs{
   159  			"uuid", UUIDLogicalType{}, parquet.Types.FixedLenByteArray, 16, false, ConvertedTypes.NA, true,
   160  			func(e *format.SchemaElement) bool { return e.LogicalType.IsSetUUID() },
   161  		}, nil},
   162  		{"none", &schemaElementConstructArgs{
   163  			"none", NoLogicalType{}, parquet.Types.Int64, -1, false, ConvertedTypes.NA, false,
   164  			checkNone,
   165  		}, nil},
   166  		{"unknown", &schemaElementConstructArgs{
   167  			"unknown", UnknownLogicalType{}, parquet.Types.Int64, -1, true, ConvertedTypes.NA, false,
   168  			checkNone,
   169  		}, nil},
   170  		{"timestamp_ms", nil, &legacySchemaElementConstructArgs{
   171  			"timestamp_ms", parquet.Types.Int64, -1, true, ConvertedTypes.TimestampMillis, false, checkNone}},
   172  		{"timestamp_us", nil, &legacySchemaElementConstructArgs{
   173  			"timestamp_us", parquet.Types.Int64, -1, true, ConvertedTypes.TimestampMicros, false, checkNone}},
   174  	}
   175  	for _, tt := range tests {
   176  		s.Run(tt.name, func() {
   177  			var sc *schemaElementConstruction
   178  			if tt.args != nil {
   179  				sc = s.reconstruct(*tt.args)
   180  			} else {
   181  				sc = s.legacyReconstruct(*tt.legacy)
   182  			}
   183  			s.Equal(tt.name, sc.element.Name)
   184  			s.inspect(sc)
   185  		})
   186  	}
   187  }
   188  
   189  func (s *SchemaElementConstructionSuite) reconstructDecimal(c schemaElementConstructArgs) *decimalSchemaElementConstruction {
   190  	ret := s.reconstruct(c)
   191  	dec := c.logical.(*DecimalLogicalType)
   192  	return &decimalSchemaElementConstruction{*ret, int(dec.Precision()), int(dec.Scale())}
   193  }
   194  
   195  func (s *SchemaElementConstructionSuite) inspectDecimal(d *decimalSchemaElementConstruction) {
   196  	s.inspect(&d.schemaElementConstruction)
   197  	s.EqualValues(d.precision, d.element.GetPrecision())
   198  	s.EqualValues(d.scale, d.element.GetScale())
   199  	s.EqualValues(d.precision, d.element.LogicalType.DECIMAL.Precision)
   200  	s.EqualValues(d.scale, d.element.LogicalType.DECIMAL.Scale)
   201  }
   202  
   203  func (s *SchemaElementConstructionSuite) TestDecimal() {
   204  	checkDecimal := func(p *format.SchemaElement) bool { return p.LogicalType.IsSetDECIMAL() }
   205  
   206  	tests := []schemaElementConstructArgs{
   207  		{
   208  			name: "decimal16_6", logical: NewDecimalLogicalType(16 /* precision */, 6 /* scale */),
   209  			physical: parquet.Types.Int64, len: -1, expectConverted: true, converted: ConvertedTypes.Decimal,
   210  			expectLogical: true, checkLogical: checkDecimal,
   211  		},
   212  		{
   213  			name: "decimal1_0", logical: NewDecimalLogicalType(1 /* precision */, 0 /* scale */),
   214  			physical: parquet.Types.Int32, len: -1, expectConverted: true, converted: ConvertedTypes.Decimal,
   215  			expectLogical: true, checkLogical: checkDecimal,
   216  		},
   217  		{
   218  			name: "decimal10", logical: NewDecimalLogicalType(10 /* precision */, 0 /* scale */),
   219  			physical: parquet.Types.Int64, len: -1, expectConverted: true, converted: ConvertedTypes.Decimal,
   220  			expectLogical: true, checkLogical: checkDecimal,
   221  		},
   222  		{
   223  			name: "decimal11_11", logical: NewDecimalLogicalType(11 /* precision */, 11 /* scale */),
   224  			physical: parquet.Types.Int64, len: -1, expectConverted: true, converted: ConvertedTypes.Decimal,
   225  			expectLogical: true, checkLogical: checkDecimal,
   226  		},
   227  	}
   228  	for _, tt := range tests {
   229  		s.Run(tt.name, func() {
   230  			d := s.reconstructDecimal(tt)
   231  			s.Equal(tt.name, d.element.Name)
   232  			s.inspectDecimal(d)
   233  		})
   234  	}
   235  }
   236  
   237  func (s *SchemaElementConstructionSuite) reconstructTemporal(c schemaElementConstructArgs, getUnit func(*format.SchemaElement) *format.TimeUnit) *temporalSchemaElementConstruction {
   238  	base := s.reconstruct(c)
   239  	t := c.logical.(TemporalLogicalType)
   240  	return &temporalSchemaElementConstruction{
   241  		*base,
   242  		t.IsAdjustedToUTC(),
   243  		t.TimeUnit(),
   244  		getUnit,
   245  	}
   246  }
   247  
   248  func (s *SchemaElementConstructionSuite) inspectTemporal(t *temporalSchemaElementConstruction) {
   249  	s.inspect(&t.schemaElementConstruction)
   250  	switch t.unit {
   251  	case TimeUnitMillis:
   252  		s.True(t.getUnit(t.element).IsSetMILLIS())
   253  	case TimeUnitMicros:
   254  		s.True(t.getUnit(t.element).IsSetMICROS())
   255  	case TimeUnitNanos:
   256  		s.True(t.getUnit(t.element).IsSetNANOS())
   257  	case TimeUnitUnknown:
   258  		fallthrough
   259  	default:
   260  		s.Fail("invalid time unit in test case")
   261  	}
   262  }
   263  
   264  func (s *SchemaElementConstructionSuite) TestTemporal() {
   265  	checkTime := func(p *format.SchemaElement) bool {
   266  		return p.LogicalType.IsSetTIME()
   267  	}
   268  	checkTimestamp := func(p *format.SchemaElement) bool {
   269  		return p.LogicalType.IsSetTIMESTAMP()
   270  	}
   271  
   272  	getTimeUnit := func(p *format.SchemaElement) *format.TimeUnit {
   273  		return p.LogicalType.TIME.Unit
   274  	}
   275  	getTimestampUnit := func(p *format.SchemaElement) *format.TimeUnit {
   276  		return p.LogicalType.TIMESTAMP.Unit
   277  	}
   278  
   279  	timeTests := []schemaElementConstructArgs{
   280  		{
   281  			name: "time_T_ms", logical: NewTimeLogicalType(true, TimeUnitMillis), physical: parquet.Types.Int32, len: -1,
   282  			expectConverted: true, converted: ConvertedTypes.TimeMillis, expectLogical: true, checkLogical: checkTime,
   283  		},
   284  		{
   285  			name: "time_F_ms", logical: NewTimeLogicalType(false, TimeUnitMillis), physical: parquet.Types.Int32, len: -1,
   286  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTime,
   287  		},
   288  		{
   289  			name: "time_T_us", logical: NewTimeLogicalType(true, TimeUnitMicros), physical: parquet.Types.Int64, len: -1,
   290  			expectConverted: true, converted: ConvertedTypes.TimeMicros, expectLogical: true, checkLogical: checkTime,
   291  		},
   292  		{
   293  			name: "time_F_us", logical: NewTimeLogicalType(false, TimeUnitMicros), physical: parquet.Types.Int64, len: -1,
   294  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTime,
   295  		},
   296  		{
   297  			name: "time_T_ns", logical: NewTimeLogicalType(true, TimeUnitNanos), physical: parquet.Types.Int64, len: -1,
   298  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTime,
   299  		},
   300  		{
   301  			name: "time_F_ns", logical: NewTimeLogicalType(false, TimeUnitNanos), physical: parquet.Types.Int64, len: -1,
   302  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTime,
   303  		},
   304  	}
   305  	timeStampTests := []schemaElementConstructArgs{
   306  		{
   307  			name: "timestamp_T_ms", logical: NewTimestampLogicalType(true, TimeUnitMillis), physical: parquet.Types.Int64, len: -1,
   308  			expectConverted: true, converted: ConvertedTypes.TimestampMillis, expectLogical: true, checkLogical: checkTimestamp,
   309  		},
   310  		{
   311  			name: "timestamp_F_ms", logical: NewTimestampLogicalType(false, TimeUnitMillis), physical: parquet.Types.Int64, len: -1,
   312  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTimestamp,
   313  		},
   314  		{
   315  			name: "timestamp_F_ms_force", logical: NewTimestampLogicalTypeForce(false, TimeUnitMillis), physical: parquet.Types.Int64, len: -1,
   316  			expectConverted: true, converted: ConvertedTypes.TimestampMillis, expectLogical: true, checkLogical: checkTimestamp,
   317  		},
   318  		{
   319  			name: "timestamp_T_us", logical: NewTimestampLogicalType(true, TimeUnitMicros), physical: parquet.Types.Int64, len: -1,
   320  			expectConverted: true, converted: ConvertedTypes.TimestampMicros, expectLogical: true, checkLogical: checkTimestamp,
   321  		},
   322  		{
   323  			name: "timestamp_F_us", logical: NewTimestampLogicalType(false, TimeUnitMicros), physical: parquet.Types.Int64, len: -1,
   324  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTimestamp,
   325  		},
   326  		{
   327  			name: "timestamp_F_us_force", logical: NewTimestampLogicalTypeForce(false, TimeUnitMicros), physical: parquet.Types.Int64, len: -1,
   328  			expectConverted: true, converted: ConvertedTypes.TimestampMicros, expectLogical: true, checkLogical: checkTimestamp,
   329  		},
   330  		{
   331  			name: "timestamp_T_ns", logical: NewTimestampLogicalType(true, TimeUnitNanos), physical: parquet.Types.Int64, len: -1,
   332  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTimestamp,
   333  		},
   334  		{
   335  			name: "timestamp_F_ns", logical: NewTimestampLogicalType(false, TimeUnitNanos), physical: parquet.Types.Int64, len: -1,
   336  			expectConverted: false, converted: ConvertedTypes.NA, expectLogical: true, checkLogical: checkTimestamp,
   337  		},
   338  	}
   339  
   340  	for _, tt := range timeTests {
   341  		s.Run(tt.name, func() {
   342  			t := s.reconstructTemporal(tt, getTimeUnit)
   343  			s.Equal(t.adjusted, t.element.LogicalType.TIME.IsAdjustedToUTC)
   344  			s.inspectTemporal(t)
   345  		})
   346  	}
   347  	for _, tt := range timeStampTests {
   348  		s.Run(tt.name, func() {
   349  			t := s.reconstructTemporal(tt, getTimestampUnit)
   350  			s.Equal(t.adjusted, t.element.LogicalType.TIMESTAMP.IsAdjustedToUTC)
   351  			s.inspectTemporal(t)
   352  		})
   353  	}
   354  }
   355  
   356  func (s *SchemaElementConstructionSuite) reconstructInteger(c schemaElementConstructArgs) *intSchemaElementConstruction {
   357  	base := s.reconstruct(c)
   358  	l := c.logical.(*IntLogicalType)
   359  	return &intSchemaElementConstruction{
   360  		*base,
   361  		l.BitWidth(),
   362  		l.IsSigned(),
   363  	}
   364  }
   365  
   366  func (s *SchemaElementConstructionSuite) inspectInt(i *intSchemaElementConstruction) {
   367  	s.inspect(&i.schemaElementConstruction)
   368  	s.Equal(i.width, i.element.LogicalType.INTEGER.BitWidth)
   369  	s.Equal(i.signed, i.element.LogicalType.INTEGER.IsSigned)
   370  }
   371  
   372  func (s *SchemaElementConstructionSuite) TestIntegerCases() {
   373  	checkInt := func(p *format.SchemaElement) bool { return p.LogicalType.IsSetINTEGER() }
   374  
   375  	tests := []schemaElementConstructArgs{
   376  		{
   377  			name: "uint8", logical: NewIntLogicalType(8, false), physical: parquet.Types.Int32, len: -1,
   378  			expectConverted: true, converted: ConvertedTypes.Uint8, expectLogical: true, checkLogical: checkInt,
   379  		},
   380  		{
   381  			name: "uint16", logical: NewIntLogicalType(16, false), physical: parquet.Types.Int32, len: -1,
   382  			expectConverted: true, converted: ConvertedTypes.Uint16, expectLogical: true, checkLogical: checkInt,
   383  		},
   384  		{
   385  			name: "uint32", logical: NewIntLogicalType(32, false), physical: parquet.Types.Int32, len: -1,
   386  			expectConverted: true, converted: ConvertedTypes.Uint32, expectLogical: true, checkLogical: checkInt,
   387  		},
   388  		{
   389  			name: "uint64", logical: NewIntLogicalType(64, false), physical: parquet.Types.Int64, len: -1,
   390  			expectConverted: true, converted: ConvertedTypes.Uint64, expectLogical: true, checkLogical: checkInt,
   391  		},
   392  		{
   393  			name: "int8", logical: NewIntLogicalType(8, true), physical: parquet.Types.Int32, len: -1,
   394  			expectConverted: true, converted: ConvertedTypes.Int8, expectLogical: true, checkLogical: checkInt,
   395  		},
   396  		{
   397  			name: "int16", logical: NewIntLogicalType(16, true), physical: parquet.Types.Int32, len: -1,
   398  			expectConverted: true, converted: ConvertedTypes.Int16, expectLogical: true, checkLogical: checkInt,
   399  		},
   400  		{
   401  			name: "int32", logical: NewIntLogicalType(32, true), physical: parquet.Types.Int32, len: -1,
   402  			expectConverted: true, converted: ConvertedTypes.Int32, expectLogical: true, checkLogical: checkInt,
   403  		},
   404  		{
   405  			name: "int64", logical: NewIntLogicalType(64, true), physical: parquet.Types.Int64, len: -1,
   406  			expectConverted: true, converted: ConvertedTypes.Int64, expectLogical: true, checkLogical: checkInt,
   407  		},
   408  	}
   409  	for _, tt := range tests {
   410  		s.Run(tt.name, func() {
   411  			t := s.reconstructInteger(tt)
   412  			s.inspectInt(t)
   413  		})
   414  	}
   415  }
   416  
   417  func TestSchemaElementNestedSerialization(t *testing.T) {
   418  	// confirm that the intermediate thrift objects created during node serialization
   419  	// contain correct ConvertedType and ConvertedType information
   420  
   421  	strNode := MustPrimitive(NewPrimitiveNodeLogical("string" /*name */, parquet.Repetitions.Required, StringLogicalType{}, parquet.Types.ByteArray, -1 /* type len */, -1 /* fieldID */))
   422  	dateNode := MustPrimitive(NewPrimitiveNodeLogical("date" /*name */, parquet.Repetitions.Required, DateLogicalType{}, parquet.Types.Int32, -1 /* type len */, -1 /* fieldID */))
   423  	jsonNode := MustPrimitive(NewPrimitiveNodeLogical("json" /*name */, parquet.Repetitions.Required, JSONLogicalType{}, parquet.Types.ByteArray, -1 /* type len */, -1 /* fieldID */))
   424  	uuidNode := MustPrimitive(NewPrimitiveNodeLogical("uuid" /*name */, parquet.Repetitions.Required, UUIDLogicalType{}, parquet.Types.FixedLenByteArray, 16 /* type len */, - /* fieldID */ 1))
   425  	timestampNode := MustPrimitive(NewPrimitiveNodeLogical("timestamp" /*name */, parquet.Repetitions.Required, NewTimestampLogicalType(false /* adjustedToUTC */, TimeUnitNanos), parquet.Types.Int64, -1 /* type len */, -1 /* fieldID */))
   426  	intNode := MustPrimitive(NewPrimitiveNodeLogical("int" /*name */, parquet.Repetitions.Required, NewIntLogicalType(64 /* bitWidth */, false /* signed */), parquet.Types.Int64, -1 /* type len */, -1 /* fieldID */))
   427  	decimalNode := MustPrimitive(NewPrimitiveNodeLogical("decimal" /*name */, parquet.Repetitions.Required, NewDecimalLogicalType(16 /* precision */, 6 /* scale */), parquet.Types.Int64, -1 /* type len */, -1 /* fieldID */))
   428  	listNode := MustGroup(NewGroupNodeLogical("list" /*name */, parquet.Repetitions.Repeated, []Node{strNode, dateNode, jsonNode, uuidNode, timestampNode, intNode, decimalNode}, NewListLogicalType(), -1 /* fieldID */))
   429  
   430  	listElems := ToThrift(listNode)
   431  	assert.Equal(t, "list", listElems[0].Name)
   432  	assert.True(t, listElems[0].IsSetConvertedType())
   433  	assert.True(t, listElems[0].IsSetLogicalType())
   434  	assert.Equal(t, format.ConvertedType(ConvertedTypes.List), listElems[0].GetConvertedType())
   435  	assert.True(t, listElems[0].LogicalType.IsSetLIST())
   436  	assert.True(t, listElems[1].LogicalType.IsSetSTRING())
   437  	assert.True(t, listElems[2].LogicalType.IsSetDATE())
   438  	assert.True(t, listElems[3].LogicalType.IsSetJSON())
   439  	assert.True(t, listElems[4].LogicalType.IsSetUUID())
   440  	assert.True(t, listElems[5].LogicalType.IsSetTIMESTAMP())
   441  	assert.True(t, listElems[6].LogicalType.IsSetINTEGER())
   442  	assert.True(t, listElems[7].LogicalType.IsSetDECIMAL())
   443  
   444  	mapNode := MustGroup(NewGroupNodeLogical("map" /* name */, parquet.Repetitions.Required, []Node{}, MapLogicalType{}, -1 /* fieldID */))
   445  	mapElems := ToThrift(mapNode)
   446  	assert.Equal(t, "map", mapElems[0].Name)
   447  	assert.True(t, mapElems[0].IsSetConvertedType())
   448  	assert.True(t, mapElems[0].IsSetLogicalType())
   449  	assert.Equal(t, format.ConvertedType(ConvertedTypes.Map), mapElems[0].GetConvertedType())
   450  	assert.True(t, mapElems[0].LogicalType.IsSetMAP())
   451  }
   452  
   453  func TestLogicalTypeSerializationRoundTrip(t *testing.T) {
   454  	tests := []struct {
   455  		name     string
   456  		logical  LogicalType
   457  		physical parquet.Type
   458  		len      int
   459  	}{
   460  		{"string", StringLogicalType{}, parquet.Types.ByteArray, -1},
   461  		{"enum", EnumLogicalType{}, parquet.Types.ByteArray, -1},
   462  		{"decimal", NewDecimalLogicalType(16, 6), parquet.Types.Int64, -1},
   463  		{"date", DateLogicalType{}, parquet.Types.Int32, -1},
   464  		{"time_T_ms", NewTimeLogicalType(true, TimeUnitMillis), parquet.Types.Int32, -1},
   465  		{"time_T_us", NewTimeLogicalType(true, TimeUnitMicros), parquet.Types.Int64, -1},
   466  		{"time_T_ns", NewTimeLogicalType(true, TimeUnitNanos), parquet.Types.Int64, -1},
   467  		{"time_F_ms", NewTimeLogicalType(false, TimeUnitMillis), parquet.Types.Int32, -1},
   468  		{"time_F_us", NewTimeLogicalType(false, TimeUnitMicros), parquet.Types.Int64, -1},
   469  		{"time_F_ns", NewTimeLogicalType(false, TimeUnitNanos), parquet.Types.Int64, -1},
   470  		{"timestamp_T_ms", NewTimestampLogicalType(true, TimeUnitMillis), parquet.Types.Int64, -1},
   471  		{"timestamp_T_us", NewTimestampLogicalType(true, TimeUnitMicros), parquet.Types.Int64, -1},
   472  		{"timestamp_T_ns", NewTimestampLogicalType(true, TimeUnitNanos), parquet.Types.Int64, -1},
   473  		{"timestamp_F_ms", NewTimestampLogicalType(false, TimeUnitMillis), parquet.Types.Int64, -1},
   474  		{"timestamp_F_us", NewTimestampLogicalType(false, TimeUnitMicros), parquet.Types.Int64, -1},
   475  		{"timestamp_F_ns", NewTimestampLogicalType(false, TimeUnitNanos), parquet.Types.Int64, -1},
   476  		{"interval", IntervalLogicalType{}, parquet.Types.FixedLenByteArray, 12},
   477  		{"uint8", NewIntLogicalType(8, false), parquet.Types.Int32, -1},
   478  		{"uint16", NewIntLogicalType(16, false), parquet.Types.Int32, -1},
   479  		{"uint32", NewIntLogicalType(32, false), parquet.Types.Int32, -1},
   480  		{"uint64", NewIntLogicalType(64, false), parquet.Types.Int64, -1},
   481  		{"int8", NewIntLogicalType(8, true), parquet.Types.Int32, -1},
   482  		{"int16", NewIntLogicalType(16, true), parquet.Types.Int32, -1},
   483  		{"int32", NewIntLogicalType(32, true), parquet.Types.Int32, -1},
   484  		{"int64", NewIntLogicalType(64, true), parquet.Types.Int64, -1},
   485  		{"null", NullLogicalType{}, parquet.Types.Boolean, -1},
   486  		{"json", JSONLogicalType{}, parquet.Types.ByteArray, -1},
   487  		{"bson", BSONLogicalType{}, parquet.Types.ByteArray, -1},
   488  		{"uuid", UUIDLogicalType{}, parquet.Types.FixedLenByteArray, 16},
   489  		{"none", NoLogicalType{}, parquet.Types.Boolean, -1},
   490  	}
   491  
   492  	for _, tt := range tests {
   493  		t.Run(tt.name, func(t *testing.T) {
   494  			n := MustPrimitive(NewPrimitiveNodeLogical("something" /* name */, parquet.Repetitions.Required, tt.logical, tt.physical, tt.len, -1 /* fieldID */))
   495  			elem := n.toThrift()
   496  			recover := MustPrimitive(PrimitiveNodeFromThrift(elem))
   497  			assert.True(t, n.Equals(recover))
   498  		})
   499  	}
   500  
   501  	n := MustGroup(NewGroupNodeLogical("map" /* name */, parquet.Repetitions.Required, []Node{}, MapLogicalType{}, -1 /* fieldID */))
   502  	elem := n.toThrift()
   503  	recover := MustGroup(GroupNodeFromThrift(elem, []Node{}))
   504  	assert.True(t, recover.Equals(n))
   505  
   506  	n = MustGroup(NewGroupNodeLogical("list" /* name */, parquet.Repetitions.Required, []Node{}, ListLogicalType{}, -1 /* fieldID */))
   507  	elem = n.toThrift()
   508  	recover = MustGroup(GroupNodeFromThrift(elem, []Node{}))
   509  	assert.True(t, recover.Equals(n))
   510  }
   511  
   512  func TestSchemaElementConstruction(t *testing.T) {
   513  	suite.Run(t, new(SchemaElementConstructionSuite))
   514  }