github.com/apache/arrow/go/v7@v7.0.1/parquet/schema/node.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  	"github.com/apache/arrow/go/v7/parquet"
    21  	format "github.com/apache/arrow/go/v7/parquet/internal/gen-go/parquet"
    22  	"github.com/apache/thrift/lib/go/thrift"
    23  	"golang.org/x/xerrors"
    24  )
    25  
    26  // NodeType describes whether the Node is a Primitive or Group node
    27  type NodeType int
    28  
    29  // the available constants for NodeType
    30  const (
    31  	Primitive NodeType = iota
    32  	Group
    33  )
    34  
    35  // Node is the interface for both Group and Primitive Nodes.
    36  // A logical schema type has a name, repetition level, and optionally
    37  // a logical type (converted type is the deprecated version of the logical
    38  // type concept, which is maintained for forward compatibility)
    39  type Node interface {
    40  	Name() string
    41  	Type() NodeType
    42  	RepetitionType() parquet.Repetition
    43  	ConvertedType() ConvertedType
    44  	LogicalType() LogicalType
    45  	FieldID() int32
    46  	Parent() Node
    47  	SetParent(Node)
    48  	Path() string
    49  	Equals(Node) bool
    50  	Visit(v Visitor)
    51  	toThrift() *format.SchemaElement
    52  }
    53  
    54  // Visitor is an interface for creating functionality to walk the schema tree.
    55  //
    56  // A visitor can be passed to the Visit function of a Node in order to walk
    57  // the tree. VisitPre is called the first time a node is encountered. If
    58  // it is a group node, the return is checked and if it is false, the children
    59  // will be skipped.
    60  //
    61  // VisitPost is called after visiting any children
    62  type Visitor interface {
    63  	VisitPre(Node) bool
    64  	VisitPost(Node)
    65  }
    66  
    67  // ColumnPathFromNode walks the parents of the given node to construct it's
    68  // column path
    69  func ColumnPathFromNode(n Node) parquet.ColumnPath {
    70  	if n == nil {
    71  		return nil
    72  	}
    73  
    74  	c := make([]string, 0)
    75  
    76  	// build the path in reverse order as we traverse nodes to the top
    77  	cursor := n
    78  	for cursor.Parent() != nil {
    79  		c = append(c, cursor.Name())
    80  		cursor = cursor.Parent()
    81  	}
    82  
    83  	// reverse the order of the list in place so that our result
    84  	// is in the proper, correct order.
    85  	for i := len(c)/2 - 1; i >= 0; i-- {
    86  		opp := len(c) - 1 - i
    87  		c[i], c[opp] = c[opp], c[i]
    88  	}
    89  
    90  	return c
    91  }
    92  
    93  // node is the base embedded struct for both group and primitive nodes
    94  type node struct {
    95  	typ    NodeType
    96  	parent Node
    97  
    98  	name          string
    99  	repetition    parquet.Repetition
   100  	fieldID       int32
   101  	logicalType   LogicalType
   102  	convertedType ConvertedType
   103  	colPath       parquet.ColumnPath
   104  }
   105  
   106  func (n *node) toThrift() *format.SchemaElement    { return nil }
   107  func (n *node) Name() string                       { return n.name }
   108  func (n *node) Type() NodeType                     { return n.typ }
   109  func (n *node) RepetitionType() parquet.Repetition { return n.repetition }
   110  func (n *node) ConvertedType() ConvertedType       { return n.convertedType }
   111  func (n *node) LogicalType() LogicalType           { return n.logicalType }
   112  func (n *node) FieldID() int32                     { return n.fieldID }
   113  func (n *node) Parent() Node                       { return n.parent }
   114  func (n *node) SetParent(p Node)                   { n.parent = p }
   115  func (n *node) Path() string {
   116  	return n.columnPath().String()
   117  }
   118  func (n *node) columnPath() parquet.ColumnPath {
   119  	if n.colPath == nil {
   120  		n.colPath = ColumnPathFromNode(n)
   121  	}
   122  	return n.colPath
   123  }
   124  
   125  func (n *node) Equals(rhs Node) bool {
   126  	return n.typ == rhs.Type() &&
   127  		n.Name() == rhs.Name() &&
   128  		n.RepetitionType() == rhs.RepetitionType() &&
   129  		n.ConvertedType() == rhs.ConvertedType() &&
   130  		n.FieldID() == rhs.FieldID() &&
   131  		n.LogicalType().Equals(rhs.LogicalType())
   132  }
   133  
   134  func (n *node) Visit(v Visitor) {}
   135  
   136  // A PrimitiveNode is a type that is one of the primitive Parquet storage types. In addition to
   137  // the other type metadata (name, repetition level, logical type), also has the
   138  // physical storage type and their type-specific metadata (byte width, decimal
   139  // parameters)
   140  type PrimitiveNode struct {
   141  	node
   142  
   143  	ColumnOrder     parquet.ColumnOrder
   144  	physicalType    parquet.Type
   145  	typeLen         int
   146  	decimalMetaData DecimalMetadata
   147  }
   148  
   149  // NewPrimitiveNodeLogical constructs a Primtive node using the provided logical type for a given
   150  // physical type and typelength.
   151  func NewPrimitiveNodeLogical(name string, repetition parquet.Repetition, logicalType LogicalType, physicalType parquet.Type, typeLen int, id int32) (*PrimitiveNode, error) {
   152  	n := &PrimitiveNode{
   153  		node:         node{typ: Primitive, name: name, repetition: repetition, logicalType: logicalType, fieldID: id},
   154  		physicalType: physicalType,
   155  		typeLen:      typeLen,
   156  	}
   157  
   158  	if logicalType != nil {
   159  		if !logicalType.IsNested() {
   160  			if logicalType.IsApplicable(physicalType, int32(typeLen)) {
   161  				n.convertedType, n.decimalMetaData = n.logicalType.ToConvertedType()
   162  			} else {
   163  				return nil, xerrors.Errorf("%s cannot be applied to primitive type %s", logicalType, physicalType)
   164  			}
   165  		} else {
   166  			return nil, xerrors.Errorf("nested logical type %s can not be applied to a non-group node", logicalType)
   167  		}
   168  	} else {
   169  		n.logicalType = NoLogicalType{}
   170  		n.convertedType, n.decimalMetaData = n.logicalType.ToConvertedType()
   171  	}
   172  
   173  	if !(n.logicalType != nil && !n.logicalType.IsNested() && n.logicalType.IsCompatible(n.convertedType, n.decimalMetaData)) {
   174  		return nil, xerrors.Errorf("invalid logical type %s", n.logicalType)
   175  	}
   176  
   177  	if n.physicalType == parquet.Types.FixedLenByteArray && n.typeLen <= 0 {
   178  		return nil, xerrors.New("invalid fixed length byte array length")
   179  	}
   180  	return n, nil
   181  }
   182  
   183  // NewPrimitiveNodeConverted constructs a primitive node from the given physical type and converted type,
   184  // determining the logical type from the converted type.
   185  func NewPrimitiveNodeConverted(name string, repetition parquet.Repetition, typ parquet.Type, converted ConvertedType, typeLen, precision, scale int, id int32) (*PrimitiveNode, error) {
   186  	n := &PrimitiveNode{
   187  		node:         node{typ: Primitive, name: name, repetition: repetition, convertedType: converted, fieldID: id},
   188  		physicalType: typ,
   189  		typeLen:      -1,
   190  	}
   191  
   192  	switch converted {
   193  	case ConvertedTypes.None:
   194  	case ConvertedTypes.UTF8, ConvertedTypes.JSON, ConvertedTypes.BSON:
   195  		if typ != parquet.Types.ByteArray {
   196  			return nil, xerrors.Errorf("parquet: %s can only annotate BYTE_LEN fields", typ)
   197  		}
   198  	case ConvertedTypes.Decimal:
   199  		switch typ {
   200  		case parquet.Types.Int32, parquet.Types.Int64, parquet.Types.ByteArray, parquet.Types.FixedLenByteArray:
   201  		default:
   202  			return nil, xerrors.New("parquet: DECIMAL can only annotate INT32, INT64, BYTE_ARRAY and FIXED")
   203  		}
   204  
   205  		switch {
   206  		case precision <= 0:
   207  			return nil, xerrors.Errorf("parquet: invalid decimal precision: %d, must be between 1 and 38 inclusive", precision)
   208  		case scale < 0:
   209  			return nil, xerrors.Errorf("parquet: invalid decimal scale: %d, must be a number between 0 and precision inclusive", scale)
   210  		case scale > precision:
   211  			return nil, xerrors.Errorf("parquet: invalid decimal scale %d, cannot be greater than precision: %d", scale, precision)
   212  		}
   213  		n.decimalMetaData.IsSet = true
   214  		n.decimalMetaData.Precision = int32(precision)
   215  		n.decimalMetaData.Scale = int32(scale)
   216  	case ConvertedTypes.Date,
   217  		ConvertedTypes.TimeMillis,
   218  		ConvertedTypes.Int8,
   219  		ConvertedTypes.Int16,
   220  		ConvertedTypes.Int32,
   221  		ConvertedTypes.Uint8,
   222  		ConvertedTypes.Uint16,
   223  		ConvertedTypes.Uint32:
   224  		if typ != parquet.Types.Int32 {
   225  			return nil, xerrors.Errorf("parquet: %s can only annotate INT32", converted)
   226  		}
   227  	case ConvertedTypes.TimeMicros,
   228  		ConvertedTypes.TimestampMicros,
   229  		ConvertedTypes.TimestampMillis,
   230  		ConvertedTypes.Int64,
   231  		ConvertedTypes.Uint64:
   232  		if typ != parquet.Types.Int64 {
   233  			return nil, xerrors.Errorf("parquet: %s can only annotate INT64", converted)
   234  		}
   235  	case ConvertedTypes.Interval:
   236  		if typ != parquet.Types.FixedLenByteArray || typeLen != 12 {
   237  			return nil, xerrors.New("parquet: INTERVAL can only annotate FIXED_LEN_BYTE_ARRAY(12)")
   238  		}
   239  	case ConvertedTypes.Enum:
   240  		if typ != parquet.Types.ByteArray {
   241  			return nil, xerrors.New("parquet: ENUM can only annotate BYTE_ARRAY fields")
   242  		}
   243  	case ConvertedTypes.NA:
   244  	default:
   245  		return nil, xerrors.Errorf("parquet: %s cannot be applied to a primitive type", converted.String())
   246  	}
   247  
   248  	n.logicalType = n.convertedType.ToLogicalType(n.decimalMetaData)
   249  	if !(n.logicalType != nil && !n.logicalType.IsNested() && n.logicalType.IsCompatible(n.convertedType, n.decimalMetaData)) {
   250  		return nil, xerrors.Errorf("invalid logical type %s", n.logicalType)
   251  	}
   252  
   253  	if n.physicalType == parquet.Types.FixedLenByteArray {
   254  		if typeLen <= 0 {
   255  			return nil, xerrors.New("invalid fixed len byte array length")
   256  		}
   257  		n.typeLen = typeLen
   258  	}
   259  
   260  	return n, nil
   261  }
   262  
   263  func PrimitiveNodeFromThrift(elem *format.SchemaElement) (*PrimitiveNode, error) {
   264  	fieldID := int32(-1)
   265  	if elem.IsSetFieldID() {
   266  		fieldID = elem.GetFieldID()
   267  	}
   268  
   269  	if elem.IsSetLogicalType() {
   270  		return NewPrimitiveNodeLogical(elem.GetName(), parquet.Repetition(elem.GetRepetitionType()),
   271  			getLogicalType(elem.GetLogicalType()), parquet.Type(elem.GetType()), int(elem.GetTypeLength()),
   272  			fieldID)
   273  	} else if elem.IsSetConvertedType() {
   274  		return NewPrimitiveNodeConverted(elem.GetName(), parquet.Repetition(elem.GetRepetitionType()),
   275  			parquet.Type(elem.GetType()), ConvertedType(elem.GetConvertedType()),
   276  			int(elem.GetTypeLength()), int(elem.GetPrecision()), int(elem.GetScale()), fieldID)
   277  	}
   278  	return NewPrimitiveNodeLogical(elem.GetName(), parquet.Repetition(elem.GetRepetitionType()), NoLogicalType{}, parquet.Type(elem.GetType()), int(elem.GetTypeLength()), fieldID)
   279  }
   280  
   281  // NewPrimitiveNode constructs a primitive node with the ConvertedType of None and no logical type.
   282  //
   283  // Use NewPrimitiveNodeLogical and NewPrimitiveNodeConverted to specify the logical or converted type.
   284  func NewPrimitiveNode(name string, repetition parquet.Repetition, typ parquet.Type, fieldID, typeLength int32) (*PrimitiveNode, error) {
   285  	return NewPrimitiveNodeLogical(name, repetition, nil, typ, int(typeLength), fieldID)
   286  }
   287  
   288  // Equals returns true if both nodes are primitive nodes with the same physical
   289  // and converted/logical types.
   290  func (p *PrimitiveNode) Equals(rhs Node) bool {
   291  	if !p.node.Equals(rhs) {
   292  		return false
   293  	}
   294  
   295  	other := rhs.(*PrimitiveNode)
   296  	if p == other {
   297  		return true
   298  	}
   299  
   300  	if p.PhysicalType() != other.PhysicalType() {
   301  		return false
   302  	}
   303  
   304  	equal := true
   305  	if p.ConvertedType() == ConvertedTypes.Decimal {
   306  		equal = equal &&
   307  			(p.decimalMetaData.Precision == other.decimalMetaData.Precision &&
   308  				p.decimalMetaData.Scale == other.decimalMetaData.Scale)
   309  	}
   310  	if p.PhysicalType() == parquet.Types.FixedLenByteArray {
   311  		equal = equal && p.TypeLength() == other.TypeLength()
   312  	}
   313  	return equal
   314  }
   315  
   316  // PhysicalType returns the proper Physical parquet.Type primitive that is used
   317  // to store the values in this column.
   318  func (p *PrimitiveNode) PhysicalType() parquet.Type { return p.physicalType }
   319  
   320  // SetTypeLength will change the type length of the node, has no effect if the
   321  // physical type is not FixedLength Byte Array
   322  func (p *PrimitiveNode) SetTypeLength(length int) {
   323  	if p.PhysicalType() == parquet.Types.FixedLenByteArray {
   324  		p.typeLen = length
   325  	}
   326  }
   327  
   328  // TypeLength will be -1 if not a FixedLenByteArray column, otherwise will be the
   329  // length of the FixedLen Byte Array
   330  func (p *PrimitiveNode) TypeLength() int { return p.typeLen }
   331  
   332  // DecimalMetadata returns the current metadata for the node. If not a decimal
   333  // typed column, the return should have IsSet == false.
   334  func (p *PrimitiveNode) DecimalMetadata() DecimalMetadata { return p.decimalMetaData }
   335  
   336  // Visit is for implementing a Visitor pattern handler to walk a schema's tree. One
   337  // example is the Schema Printer which walks the tree to print out the schema in order.
   338  func (p *PrimitiveNode) Visit(v Visitor) {
   339  	v.VisitPre(p)
   340  	v.VisitPost(p)
   341  }
   342  
   343  func (p *PrimitiveNode) toThrift() *format.SchemaElement {
   344  	elem := &format.SchemaElement{
   345  		Name:           p.Name(),
   346  		RepetitionType: format.FieldRepetitionTypePtr(format.FieldRepetitionType(p.RepetitionType())),
   347  		Type:           format.TypePtr(format.Type(p.PhysicalType())),
   348  	}
   349  	if p.ConvertedType() != ConvertedTypes.None {
   350  		elem.ConvertedType = format.ConvertedTypePtr(format.ConvertedType(p.ConvertedType()))
   351  	}
   352  	if p.FieldID() >= 0 {
   353  		elem.FieldID = thrift.Int32Ptr(p.FieldID())
   354  	}
   355  	if p.logicalType != nil && p.logicalType.IsSerialized() && !p.logicalType.Equals(IntervalLogicalType{}) {
   356  		elem.LogicalType = p.logicalType.toThrift()
   357  	}
   358  	if p.physicalType == parquet.Types.FixedLenByteArray {
   359  		elem.TypeLength = thrift.Int32Ptr(int32(p.typeLen))
   360  	}
   361  	if p.decimalMetaData.IsSet {
   362  		elem.Precision = &p.decimalMetaData.Precision
   363  		elem.Scale = &p.decimalMetaData.Scale
   364  	}
   365  	return elem
   366  }
   367  
   368  // FieldList is an alias for a slice of Nodes
   369  type FieldList []Node
   370  
   371  // Len is equivalent to len(fieldlist)
   372  func (f FieldList) Len() int { return len(f) }
   373  
   374  // GroupNode is for mananging nested nodes like List, Map, etc.
   375  type GroupNode struct {
   376  	node
   377  	fields    FieldList
   378  	nameToIdx strIntMultimap
   379  }
   380  
   381  // NewGroupNodeConverted constructs a group node with the provided fields and converted type,
   382  // determining the logical type from that converted type.
   383  func NewGroupNodeConverted(name string, repetition parquet.Repetition, fields FieldList, converted ConvertedType, id int32) (n *GroupNode, err error) {
   384  	n = &GroupNode{
   385  		node:   node{typ: Group, name: name, repetition: repetition, convertedType: converted, fieldID: id},
   386  		fields: fields,
   387  	}
   388  	n.logicalType = n.convertedType.ToLogicalType(DecimalMetadata{})
   389  	if !(n.logicalType != nil && (n.logicalType.IsNested() || n.logicalType.IsNone()) && n.logicalType.IsCompatible(n.convertedType, DecimalMetadata{})) {
   390  		err = xerrors.Errorf("invalid logical type %s", n.logicalType.String())
   391  		return
   392  	}
   393  
   394  	n.nameToIdx = make(strIntMultimap)
   395  	for idx, f := range n.fields {
   396  		f.SetParent(n)
   397  		n.nameToIdx.Add(f.Name(), idx)
   398  	}
   399  	return
   400  }
   401  
   402  // NewGroupNodeLogical constructs a group node with the provided fields and logical type,
   403  // determining the converted type from the provided logical type.
   404  func NewGroupNodeLogical(name string, repetition parquet.Repetition, fields FieldList, logical LogicalType, id int32) (n *GroupNode, err error) {
   405  	n = &GroupNode{
   406  		node:   node{typ: Group, name: name, repetition: repetition, logicalType: logical, fieldID: id},
   407  		fields: fields,
   408  	}
   409  
   410  	if logical != nil {
   411  		if logical.IsNested() {
   412  			n.convertedType, _ = logical.ToConvertedType()
   413  		} else {
   414  			err = xerrors.Errorf("logical type %s cannot be applied to group node", logical)
   415  			return
   416  		}
   417  	} else {
   418  		n.logicalType = NoLogicalType{}
   419  		n.convertedType, _ = n.logicalType.ToConvertedType()
   420  	}
   421  
   422  	if !(n.logicalType != nil && (n.logicalType.IsNested() || n.logicalType.IsNone()) && n.logicalType.IsCompatible(n.convertedType, DecimalMetadata{})) {
   423  		err = xerrors.Errorf("invalid logical type %s", n.logicalType)
   424  		return
   425  	}
   426  
   427  	n.nameToIdx = make(strIntMultimap)
   428  	for idx, f := range n.fields {
   429  		f.SetParent(n)
   430  		n.nameToIdx.Add(f.Name(), idx)
   431  	}
   432  	return
   433  }
   434  
   435  // NewGroupNode constructs a new group node with the provided fields,
   436  // but with converted type None and No Logical Type
   437  func NewGroupNode(name string, repetition parquet.Repetition, fields FieldList, fieldID int32) (*GroupNode, error) {
   438  	return NewGroupNodeConverted(name, repetition, fields, ConvertedTypes.None, fieldID)
   439  }
   440  
   441  // Must is a convenience function for the NewNode functions that return a Node
   442  // and an error, panic'ing if err != nil or returning the node
   443  func Must(n Node, err error) Node {
   444  	if err != nil {
   445  		panic(err)
   446  	}
   447  	return n
   448  }
   449  
   450  // MustGroup is like Must, except it casts the node to a *GroupNode, which will panic
   451  // if it is a primitive node.
   452  func MustGroup(n Node, err error) *GroupNode {
   453  	if err != nil {
   454  		panic(err)
   455  	}
   456  	return n.(*GroupNode)
   457  }
   458  
   459  // MustPrimitive is like Must except it casts the node to *PrimitiveNode which will panic
   460  // if it is a group node.
   461  func MustPrimitive(n Node, err error) *PrimitiveNode {
   462  	if err != nil {
   463  		panic(err)
   464  	}
   465  	return n.(*PrimitiveNode)
   466  }
   467  
   468  func GroupNodeFromThrift(elem *format.SchemaElement, fields FieldList) (*GroupNode, error) {
   469  	id := int32(-1)
   470  	if elem.IsSetFieldID() {
   471  		id = elem.GetFieldID()
   472  	}
   473  
   474  	if elem.IsSetLogicalType() {
   475  		return NewGroupNodeLogical(elem.GetName(), parquet.Repetition(elem.GetRepetitionType()), fields, getLogicalType(elem.GetLogicalType()), id)
   476  	}
   477  
   478  	converted := ConvertedTypes.None
   479  	if elem.IsSetConvertedType() {
   480  		converted = ConvertedType(elem.GetConvertedType())
   481  	}
   482  	return NewGroupNodeConverted(elem.GetName(), parquet.Repetition(elem.GetRepetitionType()), fields, converted, id)
   483  }
   484  
   485  func (g *GroupNode) toThrift() *format.SchemaElement {
   486  	elem := &format.SchemaElement{
   487  		Name:           g.name,
   488  		NumChildren:    thrift.Int32Ptr(int32(len(g.fields))),
   489  		RepetitionType: format.FieldRepetitionTypePtr(format.FieldRepetitionType(g.RepetitionType())),
   490  	}
   491  	if g.convertedType != ConvertedTypes.None {
   492  		elem.ConvertedType = format.ConvertedTypePtr(format.ConvertedType(g.convertedType))
   493  	}
   494  	if g.fieldID >= 0 {
   495  		elem.FieldID = &g.fieldID
   496  	}
   497  	if g.logicalType != nil && g.logicalType.IsSerialized() {
   498  		elem.LogicalType = g.logicalType.toThrift()
   499  	}
   500  	return elem
   501  }
   502  
   503  // Equals will compare this node to the provided node and only return true if
   504  // this node and all of it's children are the same as the passed in node and its
   505  // children.
   506  func (g *GroupNode) Equals(rhs Node) bool {
   507  	if !g.node.Equals(rhs) {
   508  		return false
   509  	}
   510  
   511  	other := rhs.(*GroupNode)
   512  	if g == other {
   513  		return true
   514  	}
   515  	if len(g.fields) != len(other.fields) {
   516  		return false
   517  	}
   518  
   519  	for idx, field := range g.fields {
   520  		if !field.Equals(other.fields[idx]) {
   521  			return false
   522  		}
   523  	}
   524  	return true
   525  }
   526  
   527  // NumFields returns the number of direct child fields for this group node
   528  func (g *GroupNode) NumFields() int {
   529  	return len(g.fields)
   530  }
   531  
   532  // Field returns the node in the field list which is of the provided (0-based) index
   533  func (g *GroupNode) Field(i int) Node {
   534  	return g.fields[i]
   535  }
   536  
   537  // FieldIndexByName provides the index for the field of the given name. Returns
   538  // -1 if not found.
   539  //
   540  // If there are more than one field of this name, it returns the index for the first one.
   541  func (g *GroupNode) FieldIndexByName(name string) int {
   542  	if idx, ok := g.nameToIdx[name]; ok {
   543  		return idx[0]
   544  	}
   545  	return -1
   546  }
   547  
   548  // FieldIndexByField looks up the index child of this node. Returns -1
   549  // if n isn't a child of this group
   550  func (g *GroupNode) FieldIndexByField(n Node) int {
   551  	if search, ok := g.nameToIdx[n.Name()]; ok {
   552  		for _, idx := range search {
   553  			if n == g.fields[idx] {
   554  				return idx
   555  			}
   556  		}
   557  	}
   558  	return -1
   559  }
   560  
   561  // Visit is for implementing a Visitor pattern handler to walk a schema's tree. One
   562  // example is the Schema Printer which walks the tree to print out the schema in order.
   563  func (g *GroupNode) Visit(v Visitor) {
   564  	if v.VisitPre(g) {
   565  		for _, field := range g.fields {
   566  			field.Visit(v)
   567  		}
   568  	}
   569  	v.VisitPost(g)
   570  }
   571  
   572  // HasRepeatedFields returns true if any of the children of this node have
   573  // Repeated as its repetition type.
   574  //
   575  // This is recursive and will check the children of any group nodes that are children.
   576  func (g *GroupNode) HasRepeatedFields() bool {
   577  	for _, field := range g.fields {
   578  		if field.RepetitionType() == parquet.Repetitions.Repeated {
   579  			return true
   580  		}
   581  		if field.Type() == Group {
   582  			return field.(*GroupNode).HasRepeatedFields()
   583  		}
   584  	}
   585  	return false
   586  }
   587  
   588  // NewInt32Node is a convenience factory for constructing an Int32 Primitive Node
   589  func NewInt32Node(name string, rep parquet.Repetition, fieldID int32) *PrimitiveNode {
   590  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.Int32, fieldID, -1))
   591  }
   592  
   593  // NewInt64Node is a convenience factory for constructing an Int64 Primitive Node
   594  func NewInt64Node(name string, rep parquet.Repetition, fieldID int32) *PrimitiveNode {
   595  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.Int64, fieldID, -1))
   596  }
   597  
   598  // NewInt96Node is a convenience factory for constructing an Int96 Primitive Node
   599  func NewInt96Node(name string, rep parquet.Repetition, fieldID int32) *PrimitiveNode {
   600  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.Int96, fieldID, -1))
   601  }
   602  
   603  // NewFloat32Node is a convenience factory for constructing an Float Primitive Node
   604  func NewFloat32Node(name string, rep parquet.Repetition, fieldID int32) *PrimitiveNode {
   605  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.Float, fieldID, -1))
   606  }
   607  
   608  // NewFloat64Node is a convenience factory for constructing an Double Primitive Node
   609  func NewFloat64Node(name string, rep parquet.Repetition, fieldID int32) *PrimitiveNode {
   610  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.Double, fieldID, -1))
   611  }
   612  
   613  // NewBooleanNode is a convenience factory for constructing an Boolean Primitive Node
   614  func NewBooleanNode(name string, rep parquet.Repetition, fieldID int32) *PrimitiveNode {
   615  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.Boolean, fieldID, -1))
   616  }
   617  
   618  // NewByteArrayNode is a convenience factory for constructing an Byte Array Primitive Node
   619  func NewByteArrayNode(name string, rep parquet.Repetition, fieldID int32) *PrimitiveNode {
   620  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.ByteArray, fieldID, -1))
   621  }
   622  
   623  // NewFixedLenByteArrayNode is a convenience factory for constructing an Fixed Length
   624  // Byte Array Primitive Node of the given length
   625  func NewFixedLenByteArrayNode(name string, rep parquet.Repetition, length int32, fieldID int32) *PrimitiveNode {
   626  	return MustPrimitive(NewPrimitiveNode(name, rep, parquet.Types.FixedLenByteArray, fieldID, length))
   627  }