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