This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git


The following commit(s) were added to refs/heads/main by this push:
     new c3151ade feat(parquet/schema): Wire up logical type functionality for 
writing geo types (#960)
c3151ade is described below

commit c3151ade7001dd4524f6fc5cf6e0e420bb1647c9
Author: Colton Loftus <[email protected]>
AuthorDate: Mon Jul 20 14:28:11 2026 -0400

    feat(parquet/schema): Wire up logical type functionality for writing geo 
types (#960)
    
    Implement the LogicalType interface for Geometry and Geography so the 
Parquet write path emits proper geospatial metadata instead of plain byte-array 
blobs.
---
 parquet/internal/encoding/encoder.go               |   2 -
 .../gen-go/parquet/GoUnusedProtection__.go         |   2 +-
 parquet/internal/gen-go/parquet/parquet-consts.go  |   4 +-
 parquet/internal/gen-go/parquet/parquet.go         | 938 ++++++++++++++++++---
 parquet/schema/logical_types.go                    | 258 ++++++
 parquet/schema/logical_types_test.go               |  22 +
 parquet/schema/schema_element_test.go              |  10 +
 parquet/variant/basic_type_stringer.go             |   8 +-
 parquet/variant/primitive_type_stringer.go         |   8 +-
 parquet/version_string.go                          |   5 +-
 10 files changed, 1110 insertions(+), 147 deletions(-)

diff --git a/parquet/internal/encoding/encoder.go 
b/parquet/internal/encoding/encoder.go
index 64a992e7..e6561d69 100644
--- a/parquet/internal/encoding/encoder.go
+++ b/parquet/internal/encoding/encoder.go
@@ -31,8 +31,6 @@ import (
        "github.com/apache/arrow-go/v18/parquet/schema"
 )
 
-//go:generate go run ../../../arrow/_tools/tmpl/main.go -i 
-data=physical_types.tmpldata plain_encoder_types.gen.go.tmpl 
typed_encoder.gen.go.tmpl
-
 // EncoderTraits is an interface for the different types to make it more
 // convenient to construct encoders for specific types.
 type EncoderTraits interface {
diff --git a/parquet/internal/gen-go/parquet/GoUnusedProtection__.go 
b/parquet/internal/gen-go/parquet/GoUnusedProtection__.go
index 642f3dc7..2b484bfa 100644
--- a/parquet/internal/gen-go/parquet/GoUnusedProtection__.go
+++ b/parquet/internal/gen-go/parquet/GoUnusedProtection__.go
@@ -1,4 +1,4 @@
-// Code generated by Thrift Compiler (0.21.0). DO NOT EDIT.
+// Code generated by Thrift Compiler (0.22.0). DO NOT EDIT.
 
 package parquet
 
diff --git a/parquet/internal/gen-go/parquet/parquet-consts.go 
b/parquet/internal/gen-go/parquet/parquet-consts.go
index 4843d4af..7e3fe145 100644
--- a/parquet/internal/gen-go/parquet/parquet-consts.go
+++ b/parquet/internal/gen-go/parquet/parquet-consts.go
@@ -1,4 +1,4 @@
-// Code generated by Thrift Compiler (0.21.0). DO NOT EDIT.
+// Code generated by Thrift Compiler (0.22.0). DO NOT EDIT.
 
 package parquet
 
@@ -7,6 +7,7 @@ import (
        "context"
        "errors"
        "fmt"
+       "iter"
        "log/slog"
        "time"
        thrift "github.com/apache/thrift/lib/go/thrift"
@@ -19,6 +20,7 @@ var _ = bytes.Equal
 var _ = context.Background
 var _ = errors.New
 var _ = fmt.Printf
+var _ = iter.Pull[int]
 var _ = slog.Log
 var _ = time.Now
 var _ = thrift.ZERO
diff --git a/parquet/internal/gen-go/parquet/parquet.go 
b/parquet/internal/gen-go/parquet/parquet.go
index db24d390..54547a26 100644
--- a/parquet/internal/gen-go/parquet/parquet.go
+++ b/parquet/internal/gen-go/parquet/parquet.go
@@ -1,4 +1,4 @@
-// Code generated by Thrift Compiler (0.21.0). DO NOT EDIT.
+// Code generated by Thrift Compiler (0.22.0). DO NOT EDIT.
 
 package parquet
 
@@ -8,6 +8,7 @@ import (
        "database/sql/driver"
        "errors"
        "fmt"
+       "iter"
        "log/slog"
        "time"
        thrift "github.com/apache/thrift/lib/go/thrift"
@@ -20,6 +21,7 @@ var _ = bytes.Equal
 var _ = context.Background
 var _ = errors.New
 var _ = fmt.Printf
+var _ = iter.Pull[int]
 var _ = slog.Log
 var _ = time.Now
 var _ = thrift.ZERO
@@ -32,6 +34,7 @@ var _ = regexp.MatchString
 //For example INT16 is not included as a type since a good encoding of INT32
 //would handle this.
 type Type int64
+
 const (
        Type_BOOLEAN Type = 0
        Type_INT32 Type = 1
@@ -43,6 +46,27 @@ const (
        Type_FIXED_LEN_BYTE_ARRAY Type = 7
 )
 
+var knownTypeValues = []Type{
+       Type_BOOLEAN,
+       Type_INT32,
+       Type_INT64,
+       Type_INT96,
+       Type_FLOAT,
+       Type_DOUBLE,
+       Type_BYTE_ARRAY,
+       Type_FIXED_LEN_BYTE_ARRAY,
+}
+
+func TypeValues() iter.Seq[Type] {
+       return func(yield func(Type) bool) {
+               for _, v := range knownTypeValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p Type) String() string {
        switch p {
        case Type_BOOLEAN: return "BOOLEAN"
@@ -103,11 +127,12 @@ func (p *Type) Value() (driver.Value, error) {
        return int64(*p), nil
 }
 
-//DEPRECATED: Common types used by frameworks(e.g. hive, pig) using parquet.
+//DEPRECATED: Common types used by frameworks (e.g. Hive, Pig) using parquet.
 //ConvertedType is superseded by LogicalType.  This enum should not be 
extended.
 //
 //See LogicalTypes.md for conversion between ConvertedType and LogicalType.
 type ConvertedType int64
+
 const (
        ConvertedType_UTF8 ConvertedType = 0
        ConvertedType_MAP ConvertedType = 1
@@ -133,6 +158,41 @@ const (
        ConvertedType_INTERVAL ConvertedType = 21
 )
 
+var knownConvertedTypeValues = []ConvertedType{
+       ConvertedType_UTF8,
+       ConvertedType_MAP,
+       ConvertedType_MAP_KEY_VALUE,
+       ConvertedType_LIST,
+       ConvertedType_ENUM,
+       ConvertedType_DECIMAL,
+       ConvertedType_DATE,
+       ConvertedType_TIME_MILLIS,
+       ConvertedType_TIME_MICROS,
+       ConvertedType_TIMESTAMP_MILLIS,
+       ConvertedType_TIMESTAMP_MICROS,
+       ConvertedType_UINT_8,
+       ConvertedType_UINT_16,
+       ConvertedType_UINT_32,
+       ConvertedType_UINT_64,
+       ConvertedType_INT_8,
+       ConvertedType_INT_16,
+       ConvertedType_INT_32,
+       ConvertedType_INT_64,
+       ConvertedType_JSON,
+       ConvertedType_BSON,
+       ConvertedType_INTERVAL,
+}
+
+func ConvertedTypeValues() iter.Seq[ConvertedType] {
+       return func(yield func(ConvertedType) bool) {
+               for _, v := range knownConvertedTypeValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p ConvertedType) String() string {
        switch p {
        case ConvertedType_UTF8: return "UTF8"
@@ -223,12 +283,29 @@ func (p *ConvertedType) Value() (driver.Value, error) {
 
 //Representation of Schemas
 type FieldRepetitionType int64
+
 const (
        FieldRepetitionType_REQUIRED FieldRepetitionType = 0
        FieldRepetitionType_OPTIONAL FieldRepetitionType = 1
        FieldRepetitionType_REPEATED FieldRepetitionType = 2
 )
 
+var knownFieldRepetitionTypeValues = []FieldRepetitionType{
+       FieldRepetitionType_REQUIRED,
+       FieldRepetitionType_OPTIONAL,
+       FieldRepetitionType_REPEATED,
+}
+
+func FieldRepetitionTypeValues() iter.Seq[FieldRepetitionType] {
+       return func(yield func(FieldRepetitionType) bool) {
+               for _, v := range knownFieldRepetitionTypeValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p FieldRepetitionType) String() string {
        switch p {
        case FieldRepetitionType_REQUIRED: return "REQUIRED"
@@ -281,6 +358,7 @@ func (p *FieldRepetitionType) Value() (driver.Value, error) 
{
 
 //Edge interpolation algorithm for Geography logical type
 type EdgeInterpolationAlgorithm int64
+
 const (
        EdgeInterpolationAlgorithm_SPHERICAL EdgeInterpolationAlgorithm = 0
        EdgeInterpolationAlgorithm_VINCENTY EdgeInterpolationAlgorithm = 1
@@ -289,6 +367,24 @@ const (
        EdgeInterpolationAlgorithm_KARNEY EdgeInterpolationAlgorithm = 4
 )
 
+var knownEdgeInterpolationAlgorithmValues = []EdgeInterpolationAlgorithm{
+       EdgeInterpolationAlgorithm_SPHERICAL,
+       EdgeInterpolationAlgorithm_VINCENTY,
+       EdgeInterpolationAlgorithm_THOMAS,
+       EdgeInterpolationAlgorithm_ANDOYER,
+       EdgeInterpolationAlgorithm_KARNEY,
+}
+
+func EdgeInterpolationAlgorithmValues() iter.Seq[EdgeInterpolationAlgorithm] {
+       return func(yield func(EdgeInterpolationAlgorithm) bool) {
+               for _, v := range knownEdgeInterpolationAlgorithmValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p EdgeInterpolationAlgorithm) String() string {
        switch p {
        case EdgeInterpolationAlgorithm_SPHERICAL: return "SPHERICAL"
@@ -347,6 +443,7 @@ func (p *EdgeInterpolationAlgorithm) Value() (driver.Value, 
error) {
 //enums are also used to specify the encoding of definition and repetition 
levels.
 //See the accompanying doc for the details of the more complicated encodings.
 type Encoding int64
+
 const (
        Encoding_PLAIN Encoding = 0
        Encoding_PLAIN_DICTIONARY Encoding = 2
@@ -359,6 +456,28 @@ const (
        Encoding_BYTE_STREAM_SPLIT Encoding = 9
 )
 
+var knownEncodingValues = []Encoding{
+       Encoding_PLAIN,
+       Encoding_PLAIN_DICTIONARY,
+       Encoding_RLE,
+       Encoding_BIT_PACKED,
+       Encoding_DELTA_BINARY_PACKED,
+       Encoding_DELTA_LENGTH_BYTE_ARRAY,
+       Encoding_DELTA_BYTE_ARRAY,
+       Encoding_RLE_DICTIONARY,
+       Encoding_BYTE_STREAM_SPLIT,
+}
+
+func EncodingValues() iter.Seq[Encoding] {
+       return func(yield func(Encoding) bool) {
+               for _, v := range knownEncodingValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p Encoding) String() string {
        switch p {
        case Encoding_PLAIN: return "PLAIN"
@@ -429,6 +548,7 @@ func (p *Encoding) Value() (driver.Value, error) {
 //
 //See Compression.md for a detailed specification of these algorithms.
 type CompressionCodec int64
+
 const (
        CompressionCodec_UNCOMPRESSED CompressionCodec = 0
        CompressionCodec_SNAPPY CompressionCodec = 1
@@ -440,6 +560,27 @@ const (
        CompressionCodec_LZ4_RAW CompressionCodec = 7
 )
 
+var knownCompressionCodecValues = []CompressionCodec{
+       CompressionCodec_UNCOMPRESSED,
+       CompressionCodec_SNAPPY,
+       CompressionCodec_GZIP,
+       CompressionCodec_LZO,
+       CompressionCodec_BROTLI,
+       CompressionCodec_LZ4,
+       CompressionCodec_ZSTD,
+       CompressionCodec_LZ4_RAW,
+}
+
+func CompressionCodecValues() iter.Seq[CompressionCodec] {
+       return func(yield func(CompressionCodec) bool) {
+               for _, v := range knownCompressionCodecValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p CompressionCodec) String() string {
        switch p {
        case CompressionCodec_UNCOMPRESSED: return "UNCOMPRESSED"
@@ -501,6 +642,7 @@ func (p *CompressionCodec) Value() (driver.Value, error) {
 }
 
 type PageType int64
+
 const (
        PageType_DATA_PAGE PageType = 0
        PageType_INDEX_PAGE PageType = 1
@@ -508,6 +650,23 @@ const (
        PageType_DATA_PAGE_V2 PageType = 3
 )
 
+var knownPageTypeValues = []PageType{
+       PageType_DATA_PAGE,
+       PageType_INDEX_PAGE,
+       PageType_DICTIONARY_PAGE,
+       PageType_DATA_PAGE_V2,
+}
+
+func PageTypeValues() iter.Seq[PageType] {
+       return func(yield func(PageType) bool) {
+               for _, v := range knownPageTypeValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p PageType) String() string {
        switch p {
        case PageType_DATA_PAGE: return "DATA_PAGE"
@@ -563,12 +722,29 @@ func (p *PageType) Value() (driver.Value, error) {
 //Enum to annotate whether lists of min/max elements inside ColumnIndex
 //are ordered and if so, in which direction.
 type BoundaryOrder int64
+
 const (
        BoundaryOrder_UNORDERED BoundaryOrder = 0
        BoundaryOrder_ASCENDING BoundaryOrder = 1
        BoundaryOrder_DESCENDING BoundaryOrder = 2
 )
 
+var knownBoundaryOrderValues = []BoundaryOrder{
+       BoundaryOrder_UNORDERED,
+       BoundaryOrder_ASCENDING,
+       BoundaryOrder_DESCENDING,
+}
+
+func BoundaryOrderValues() iter.Seq[BoundaryOrder] {
+       return func(yield func(BoundaryOrder) bool) {
+               for _, v := range knownBoundaryOrderValues {
+                       if !yield(v) {
+                               return
+                       }
+               }
+       }
+}
+
 func (p BoundaryOrder) String() string {
        switch p {
        case BoundaryOrder_UNORDERED: return "UNORDERED"
@@ -1162,16 +1338,16 @@ func (p *BoundingBox) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetXmin{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Xmin is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Xmin is not set"))
        }
        if !issetXmax{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Xmax is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Xmax is not set"))
        }
        if !issetYmin{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Ymin is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Ymin is not set"))
        }
        if !issetYmax{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Ymax is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Ymax is not set"))
        }
        return nil
 }
@@ -1693,6 +1869,10 @@ func (p *GeospatialStatistics) Validate() error {
 //  - MinValue
 //  - IsMaxValueExact: If true, max_value is the actual maximum value for a 
column
 //  - IsMinValueExact: If true, min_value is the actual minimum value for a 
column
+//  - NanCount: Count of NaN values in the column; only present if physical 
type is FLOAT
+// or DOUBLE, or logical type is FLOAT16.
+// If this field is not present, readers MUST assume NaNs may be present
+// (i.e. MUST assume nan_count > 0 and MAY NOT assume nan_count == 0).
 // 
 type Statistics struct {
        Max []byte `thrift:"max,1" db:"max" json:"max,omitempty"`
@@ -1703,6 +1883,7 @@ type Statistics struct {
        MinValue []byte `thrift:"min_value,6" db:"min_value" 
json:"min_value,omitempty"`
        IsMaxValueExact *bool `thrift:"is_max_value_exact,7" 
db:"is_max_value_exact" json:"is_max_value_exact,omitempty"`
        IsMinValueExact *bool `thrift:"is_min_value_exact,8" 
db:"is_min_value_exact" json:"is_min_value_exact,omitempty"`
+       NanCount *int64 `thrift:"nan_count,9" db:"nan_count" 
json:"nan_count,omitempty"`
 }
 
 func NewStatistics() *Statistics {
@@ -1773,6 +1954,15 @@ func (p *Statistics) GetIsMinValueExact() bool {
        return *p.IsMinValueExact
 }
 
+var Statistics_NanCount_DEFAULT int64
+
+func (p *Statistics) GetNanCount() int64 {
+       if !p.IsSetNanCount() {
+               return Statistics_NanCount_DEFAULT
+       }
+       return *p.NanCount
+}
+
 func (p *Statistics) IsSetMax() bool {
        return p.Max != nil
 }
@@ -1805,6 +1995,10 @@ func (p *Statistics) IsSetIsMinValueExact() bool {
        return p.IsMinValueExact != nil
 }
 
+func (p *Statistics) IsSetNanCount() bool {
+       return p.NanCount != nil
+}
+
 func (p *Statistics) Read(ctx context.Context, iprot thrift.TProtocol) error {
        if _, err := iprot.ReadStructBegin(ctx); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T read error: ", p), 
err)
@@ -1900,6 +2094,16 @@ func (p *Statistics) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                                        return err
                                }
                        }
+               case 9:
+                       if fieldTypeId == thrift.I64 {
+                               if err := p.ReadField9(ctx, iprot); err != nil {
+                                       return err
+                               }
+                       } else {
+                               if err := iprot.Skip(ctx, fieldTypeId); err != 
nil {
+                                       return err
+                               }
+                       }
                default:
                        if err := iprot.Skip(ctx, fieldTypeId); err != nil {
                                return err
@@ -1987,6 +2191,15 @@ func (p *Statistics) ReadField8(ctx context.Context, 
iprot thrift.TProtocol) err
        return nil
 }
 
+func (p *Statistics) ReadField9(ctx context.Context, iprot thrift.TProtocol) 
error {
+       if v, err := iprot.ReadI64(ctx); err != nil {
+               return thrift.PrependError("error reading field 9: ", err)
+       } else {
+               p.NanCount = &v
+       }
+       return nil
+}
+
 func (p *Statistics) Write(ctx context.Context, oprot thrift.TProtocol) error {
        if err := oprot.WriteStructBegin(ctx, "Statistics"); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -2000,6 +2213,7 @@ func (p *Statistics) Write(ctx context.Context, oprot 
thrift.TProtocol) error {
                if err := p.writeField6(ctx, oprot); err != nil { return err }
                if err := p.writeField7(ctx, oprot); err != nil { return err }
                if err := p.writeField8(ctx, oprot); err != nil { return err }
+               if err := p.writeField9(ctx, oprot); err != nil { return err }
        }
        if err := oprot.WriteFieldStop(ctx); err != nil {
                return thrift.PrependError("write field stop error: ", err)
@@ -2130,6 +2344,21 @@ func (p *Statistics) writeField8(ctx context.Context, 
oprot thrift.TProtocol) (e
        return err
 }
 
+func (p *Statistics) writeField9(ctx context.Context, oprot thrift.TProtocol) 
(err error) {
+       if p.IsSetNanCount() {
+               if err := oprot.WriteFieldBegin(ctx, "nan_count", thrift.I64, 
9); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
begin error 9:nan_count: ", p), err)
+               }
+               if err := oprot.WriteI64(ctx, int64(*p.NanCount)); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T.nan_count 
(9) field write error: ", p), err)
+               }
+               if err := oprot.WriteFieldEnd(ctx); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
end error 9:nan_count: ", p), err)
+               }
+       }
+       return err
+}
+
 func (p *Statistics) Equals(other *Statistics) bool {
        if p == other {
                return true
@@ -2164,6 +2393,12 @@ func (p *Statistics) Equals(other *Statistics) bool {
                }
                if (*p.IsMinValueExact) != (*other.IsMinValueExact) { return 
false }
        }
+       if p.NanCount != other.NanCount {
+               if p.NanCount == nil || other.NanCount == nil {
+                       return false
+               }
+               if (*p.NanCount) != (*other.NanCount) { return false }
+       }
        return true
 }
 
@@ -2940,10 +3175,10 @@ func (p *DecimalType) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetScale{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Scale is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Scale is not set"))
        }
        if !issetPrecision{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Precision is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Precision is not set"))
        }
        return nil
 }
@@ -3446,7 +3681,7 @@ func (p *TimeUnit) ReadField3(ctx context.Context, iprot 
thrift.TProtocol) error
 
 func (p *TimeUnit) Write(ctx context.Context, oprot thrift.TProtocol) error {
        if c := p.CountSetFieldsTimeUnit(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "TimeUnit"); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -3634,10 +3869,10 @@ func (p *TimestampType) Read(ctx context.Context, iprot 
thrift.TProtocol) error
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetIsAdjustedToUTC{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field IsAdjustedToUTC is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field IsAdjustedToUTC is not set"))
        }
        if !issetUnit{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Unit is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Unit is not set"))
        }
        return nil
 }
@@ -3825,10 +4060,10 @@ func (p *TimeType) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetIsAdjustedToUTC{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field IsAdjustedToUTC is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field IsAdjustedToUTC is not set"))
        }
        if !issetUnit{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Unit is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Unit is not set"))
        }
        return nil
 }
@@ -4011,10 +4246,10 @@ func (p *IntType) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetBitWidth{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field BitWidth is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field BitWidth is not set"))
        }
        if !issetIsSigned{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field IsSigned is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field IsSigned is not set"))
        }
        return nil
 }
@@ -4434,7 +4669,7 @@ func (p *VariantType) Validate() error {
 
 // Embedded Geometry logical type annotation
 // 
-// Geospatial features in the Well-Known Binary (WKB) format and edges 
interpolation
+// Geospatial features in the Well-Known Binary (WKB) format and `edges` 
interpolation
 // is always linear/planar.
 // 
 // A custom CRS can be set by the crs field. If unset, it defaults to 
"OGC:CRS84",
@@ -4591,13 +4826,13 @@ func (p *GeometryType) Validate() error {
 // Embedded Geography logical type annotation
 // 
 // Geospatial features in the WKB format with an explicit 
(non-linear/non-planar)
-// edges interpolation algorithm.
+// `edges` interpolation algorithm.
 // 
 // A custom geographic CRS can be set by the crs field, where longitudes are
 // bound by [-180, 180] and latitudes are bound by [-90, 90]. If unset, the CRS
 // defaults to "OGC:CRS84".
 // 
-// An optional algorithm can be set to correctly interpret edges interpolation
+// An optional algorithm can be set to correctly interpret `edges` 
interpolation
 // of the geometries. If unset, the algorithm defaults to SPHERICAL.
 // 
 // Allowed for physical type: BYTE_ARRAY.
@@ -5470,7 +5705,7 @@ func (p *LogicalType) ReadField18(ctx context.Context, 
iprot thrift.TProtocol) e
 
 func (p *LogicalType) Write(ctx context.Context, oprot thrift.TProtocol) error 
{
        if c := p.CountSetFieldsLogicalType(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "LogicalType"); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -5808,7 +6043,7 @@ func (p *LogicalType) Validate() error {
        return nil
 }
 
-// Represents a element inside a schema definition.
+// Represents an element inside a schema definition.
 //  - if it is a group (inner node) then type is undefined and num_children is 
defined
 //  - if it is a primitive type (leaf) then type is defined and num_children 
is undefined
 // the nodes are listed in depth first traversal order.
@@ -6112,7 +6347,7 @@ func (p *SchemaElement) Read(ctx context.Context, iprot 
thrift.TProtocol) error
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetName{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Name is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Name is not set"))
        }
        return nil
 }
@@ -6470,7 +6705,7 @@ func (p *SchemaElement) Validate() error {
 // Attributes:
 //  - NumValues: Number of values, including NULLs, in this data page.
 // 
-// If a OffsetIndex is present, a page must begin at a row
+// If an OffsetIndex is present, a page must begin at a row
 // boundary (repetition_level = 0). Otherwise, pages may begin
 // within a row (repetition_level > 0).
 // 
@@ -6614,16 +6849,16 @@ func (p *DataPageHeader) Read(ctx context.Context, 
iprot thrift.TProtocol) error
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetNumValues{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"))
        }
        if !issetEncoding{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"))
        }
        if !issetDefinitionLevelEncoding{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field DefinitionLevelEncoding is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field DefinitionLevelEncoding is not set"))
        }
        if !issetRepetitionLevelEncoding{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field RepetitionLevelEncoding is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field RepetitionLevelEncoding is not set"))
        }
        return nil
 }
@@ -6989,10 +7224,10 @@ func (p *DictionaryPageHeader) Read(ctx 
context.Context, iprot thrift.TProtocol)
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetNumValues{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"))
        }
        if !issetEncoding{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"))
        }
        return nil
 }
@@ -7125,10 +7360,15 @@ func (p *DictionaryPageHeader) Validate() error {
        return nil
 }
 
-// New page format allowing reading levels without decompressing the data
+// Alternate page format allowing reading levels without decompressing the data
 // Repetition and definition levels are uncompressed
 // The remaining section containing the data is compressed if is_compressed is 
true
 // 
+// Implementation note - this header is not necessarily a strict improvement 
over
+// `DataPageHeader` (in particular the original header might provide better 
compression
+// in some scenarios). Page indexes require pages to start and end at row 
boundaries,
+// regardless of which page header is used.
+// 
 // 
 // Attributes:
 //  - NumValues: Number of values, including NULLs, in this data page. *
@@ -7143,7 +7383,7 @@ func (p *DictionaryPageHeader) Validate() error {
 //  - RepetitionLevelsByteLength: Length of the repetition levels
 //  - IsCompressed: Whether the values are compressed.
 // Which means the section of the page between
-// definition_levels_byte_length + repetition_levels_byte_length + 1 and 
compressed_page_size (included)
+// definition_levels_byte_length + repetition_levels_byte_length and 
compressed_page_size (included)
 // is compressed with the compression_codec.
 // If missing it is considered compressed
 //  - Statistics: Optional statistics for the data in this page *
@@ -7345,22 +7585,22 @@ func (p *DataPageHeaderV2) Read(ctx context.Context, 
iprot thrift.TProtocol) err
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetNumValues{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"))
        }
        if !issetNumNulls{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumNulls is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumNulls is not set"))
        }
        if !issetNumRows{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumRows is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumRows is not set"))
        }
        if !issetEncoding{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"))
        }
        if !issetDefinitionLevelsByteLength{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field DefinitionLevelsByteLength is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field DefinitionLevelsByteLength is not set"))
        }
        if !issetRepetitionLevelsByteLength{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field RepetitionLevelsByteLength is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field RepetitionLevelsByteLength is not set"))
        }
        return nil
 }
@@ -7777,7 +8017,7 @@ func (p *BloomFilterAlgorithm) ReadField1(ctx 
context.Context, iprot thrift.TPro
 
 func (p *BloomFilterAlgorithm) Write(ctx context.Context, oprot 
thrift.TProtocol) error {
        if c := p.CountSetFieldsBloomFilterAlgorithm(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "BloomFilterAlgorithm"); err != 
nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -8015,7 +8255,7 @@ func (p *BloomFilterHash) ReadField1(ctx context.Context, 
iprot thrift.TProtocol
 
 func (p *BloomFilterHash) Write(ctx context.Context, oprot thrift.TProtocol) 
error {
        if c := p.CountSetFieldsBloomFilterHash(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "BloomFilterHash"); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -8248,7 +8488,7 @@ func (p *BloomFilterCompression) ReadField1(ctx 
context.Context, iprot thrift.TP
 
 func (p *BloomFilterCompression) Write(ctx context.Context, oprot 
thrift.TProtocol) error {
        if c := p.CountSetFieldsBloomFilterCompression(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "BloomFilterCompression"); err != 
nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -8456,16 +8696,16 @@ func (p *BloomFilterHeader) Read(ctx context.Context, 
iprot thrift.TProtocol) er
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetNumBytes{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumBytes is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumBytes is not set"))
        }
        if !issetAlgorithm{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Algorithm is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Algorithm is not set"))
        }
        if !issetHash{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Hash is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Hash is not set"))
        }
        if !issetCompression{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Compression is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Compression is not set"))
        }
        return nil
 }
@@ -8615,10 +8855,10 @@ func (p *BloomFilterHeader) Validate() error {
 //  - Type: the type of the page: indicates which of the *_header fields is 
set *
 //  - UncompressedPageSize: Uncompressed page size in bytes (not including 
this header) *
 //  - CompressedPageSize: Compressed (and potentially encrypted) page size in 
bytes, not including this header *
-//  - Crc: The 32-bit CRC checksum for the page, to be be calculated as 
follows:
+//  - Crc: The 32-bit CRC checksum for the page, to be calculated as follows:
 // 
 // - The standard CRC32 algorithm is used (with polynomial 0x04C11DB7,
-//   the same as in e.g. GZip).
+//   the same as in e.g. GZIP).
 // - All page types can have a CRC (v1 and v2 data pages, dictionary pages,
 //   etc.).
 // - The CRC is computed on the serialization binary representation of the page
@@ -8848,13 +9088,13 @@ func (p *PageHeader) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetType{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Type is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Type is not set"))
        }
        if !issetUncompressedPageSize{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field UncompressedPageSize is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field UncompressedPageSize is not set"))
        }
        if !issetCompressedPageSize{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field CompressedPageSize is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field CompressedPageSize is not set"))
        }
        return nil
 }
@@ -9197,7 +9437,7 @@ func (p *KeyValue) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetKey{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Key is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Key is not set"))
        }
        return nil
 }
@@ -9405,13 +9645,13 @@ func (p *SortingColumn) Read(ctx context.Context, iprot 
thrift.TProtocol) error
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetColumnIdx{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field ColumnIdx is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field ColumnIdx is not set"))
        }
        if !issetDescending{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Descending is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Descending is not set"))
        }
        if !issetNullsFirst{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NullsFirst is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NullsFirst is not set"))
        }
        return nil
 }
@@ -9635,13 +9875,13 @@ func (p *PageEncodingStats) Read(ctx context.Context, 
iprot thrift.TProtocol) er
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetPageType{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PageType is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PageType is not set"))
        }
        if !issetEncoding{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encoding is not set"))
        }
        if !issetCount{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Count is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Count is not set"))
        }
        return nil
 }
@@ -10199,28 +10439,28 @@ func (p *ColumnMetaData) Read(ctx context.Context, 
iprot thrift.TProtocol) error
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetType{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Type is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Type is not set"))
        }
        if !issetEncodings{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encodings is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Encodings is not set"))
        }
        if !issetPathInSchema{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PathInSchema is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PathInSchema is not set"))
        }
        if !issetCodec{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Codec is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Codec is not set"))
        }
        if !issetNumValues{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumValues is not set"))
        }
        if !issetTotalUncompressedSize{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field TotalUncompressedSize is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field TotalUncompressedSize is not set"))
        }
        if !issetTotalCompressedSize{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field TotalCompressedSize is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field TotalCompressedSize is not set"))
        }
        if !issetDataPageOffset{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field DataPageOffset is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field DataPageOffset is not set"))
        }
        return nil
 }
@@ -10977,7 +11217,7 @@ func (p *EncryptionWithColumnKey) Read(ctx 
context.Context, iprot thrift.TProtoc
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetPathInSchema{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PathInSchema is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PathInSchema is not set"))
        }
        return nil
 }
@@ -11224,7 +11464,7 @@ func (p *ColumnCryptoMetaData) ReadField2(ctx 
context.Context, iprot thrift.TPro
 
 func (p *ColumnCryptoMetaData) Write(ctx context.Context, oprot 
thrift.TProtocol) error {
        if c := p.CountSetFieldsColumnCryptoMetaData(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "ColumnCryptoMetaData"); err != 
nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -11311,7 +11551,22 @@ func (p *ColumnCryptoMetaData) Validate() error {
 //  - FilePath: File where column data is stored.  If not set, assumed to be 
same file as
 // metadata.  This path is relative to the current file.
 // 
-//  - FileOffset: Deprecated: Byte offset in file_path to the ColumnMetaData
+// As of December 2025, the only known use-case for this field is writing 
summary
+// parquet files (i.e. "_metadata" files).  These files consolidate footers 
from
+// multiple parquet files to allow for efficient reading of footers to avoid 
file
+// listing costs and prune out files that do not need to be read based on 
statistics.
+// 
+// These files do not appear to have ever been formally specified in the 
specification.
+// and are potentially problematic from a correctness perspective [1].
+// 
+// [1] https://lists.apache.org/thread/ootf2kmyg3p01b1bvplpvp4ftd1bt72d
+// 
+// There is no other known usage of this field. Specifically, there are no 
known
+// reference implementations that will read externally stored column data if 
this field is populated
+// within a standard parquet file. Making use of the field for this purpose is
+// not considered part of the Parquet specification.
+// 
+//  - FileOffset: DEPRECATED: Byte offset in file_path to the ColumnMetaData
 // 
 // Past use of this field has been inconsistent, with some implementations
 // using it to point to the ColumnMetaData and some using it to point to
@@ -11575,7 +11830,7 @@ func (p *ColumnChunk) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetFileOffset{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field FileOffset is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field FileOffset is not set"))
        }
        return nil
 }
@@ -12083,13 +12338,13 @@ func (p *RowGroup) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetColumns{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Columns is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Columns is not set"))
        }
        if !issetTotalByteSize{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field TotalByteSize is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field TotalByteSize is not set"))
        }
        if !issetNumRows{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumRows is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumRows is not set"))
        }
        return nil
 }
@@ -12462,6 +12717,172 @@ func (p *TypeDefinedOrder) Validate() error {
        return nil
 }
 
+// Empty struct to signal IEEE 754 total order for floating point types
+type IEEE754TotalOrder struct {
+}
+
+func NewIEEE754TotalOrder() *IEEE754TotalOrder {
+       return &IEEE754TotalOrder{}
+}
+
+func (p *IEEE754TotalOrder) Read(ctx context.Context, iprot thrift.TProtocol) 
error {
+       if _, err := iprot.ReadStructBegin(ctx); err != nil {
+               return thrift.PrependError(fmt.Sprintf("%T read error: ", p), 
err)
+       }
+
+
+       for {
+               _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
+               if err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T field %d 
read error: ", p, fieldId), err)
+               }
+               if fieldTypeId == thrift.STOP {
+                       break
+               }
+               if err := iprot.Skip(ctx, fieldTypeId); err != nil {
+                       return err
+               }
+               if err := iprot.ReadFieldEnd(ctx); err != nil {
+                       return err
+               }
+       }
+       if err := iprot.ReadStructEnd(ctx); err != nil {
+               return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
+       }
+       return nil
+}
+
+func (p *IEEE754TotalOrder) Write(ctx context.Context, oprot thrift.TProtocol) 
error {
+       if err := oprot.WriteStructBegin(ctx, "IEEE754TotalOrder"); err != nil {
+               return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
+       }
+       if p != nil {
+       }
+       if err := oprot.WriteFieldStop(ctx); err != nil {
+               return thrift.PrependError("write field stop error: ", err)
+       }
+       if err := oprot.WriteStructEnd(ctx); err != nil {
+               return thrift.PrependError("write struct stop error: ", err)
+       }
+       return nil
+}
+
+func (p *IEEE754TotalOrder) Equals(other *IEEE754TotalOrder) bool {
+       if p == other {
+               return true
+       } else if p == nil || other == nil {
+               return false
+       }
+       return true
+}
+
+func (p *IEEE754TotalOrder) String() string {
+       if p == nil {
+               return "<nil>"
+       }
+       return fmt.Sprintf("IEEE754TotalOrder(%+v)", *p)
+}
+
+func (p *IEEE754TotalOrder) LogValue() slog.Value {
+       if p == nil {
+               return slog.AnyValue(nil)
+       }
+       v := thrift.SlogTStructWrapper{
+               Type: "*parquet.IEEE754TotalOrder",
+               Value: p,
+       }
+       return slog.AnyValue(v)
+}
+
+var _ slog.LogValuer = (*IEEE754TotalOrder)(nil)
+
+func (p *IEEE754TotalOrder) Validate() error {
+       return nil
+}
+
+// Empty struct to signal chronological ordering of physical type INT96
+type Int96TimestampOrder struct {
+}
+
+func NewInt96TimestampOrder() *Int96TimestampOrder {
+       return &Int96TimestampOrder{}
+}
+
+func (p *Int96TimestampOrder) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
+       if _, err := iprot.ReadStructBegin(ctx); err != nil {
+               return thrift.PrependError(fmt.Sprintf("%T read error: ", p), 
err)
+       }
+
+
+       for {
+               _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
+               if err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T field %d 
read error: ", p, fieldId), err)
+               }
+               if fieldTypeId == thrift.STOP {
+                       break
+               }
+               if err := iprot.Skip(ctx, fieldTypeId); err != nil {
+                       return err
+               }
+               if err := iprot.ReadFieldEnd(ctx); err != nil {
+                       return err
+               }
+       }
+       if err := iprot.ReadStructEnd(ctx); err != nil {
+               return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
+       }
+       return nil
+}
+
+func (p *Int96TimestampOrder) Write(ctx context.Context, oprot 
thrift.TProtocol) error {
+       if err := oprot.WriteStructBegin(ctx, "Int96TimestampOrder"); err != 
nil {
+               return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
+       }
+       if p != nil {
+       }
+       if err := oprot.WriteFieldStop(ctx); err != nil {
+               return thrift.PrependError("write field stop error: ", err)
+       }
+       if err := oprot.WriteStructEnd(ctx); err != nil {
+               return thrift.PrependError("write struct stop error: ", err)
+       }
+       return nil
+}
+
+func (p *Int96TimestampOrder) Equals(other *Int96TimestampOrder) bool {
+       if p == other {
+               return true
+       } else if p == nil || other == nil {
+               return false
+       }
+       return true
+}
+
+func (p *Int96TimestampOrder) String() string {
+       if p == nil {
+               return "<nil>"
+       }
+       return fmt.Sprintf("Int96TimestampOrder(%+v)", *p)
+}
+
+func (p *Int96TimestampOrder) LogValue() slog.Value {
+       if p == nil {
+               return slog.AnyValue(nil)
+       }
+       v := thrift.SlogTStructWrapper{
+               Type: "*parquet.Int96TimestampOrder",
+               Value: p,
+       }
+       return slog.AnyValue(v)
+}
+
+var _ slog.LogValuer = (*Int96TimestampOrder)(nil)
+
+func (p *Int96TimestampOrder) Validate() error {
+       return nil
+}
+
 // Union to specify the order used for the min_value and max_value fields for a
 // column. This union takes the role of an enhanced enum that allows rich
 // elements (which will be needed for a collation-based ordering in the 
future).
@@ -12469,6 +12890,9 @@ func (p *TypeDefinedOrder) Validate() error {
 // Possible values are:
 // * TypeDefinedOrder - the column uses the order defined by its logical or
 //                      physical type (if there is no logical type).
+// * IEEE754TotalOrder - the floating point column uses IEEE 754 total order.
+// 
+// * Int96TimestampOrder - the INT96 column uses chronological timestamp order.
 // 
 // If the reader does not support the value of this union, min and max stats
 // for this column should be ignored.
@@ -12486,6 +12910,7 @@ func (p *TypeDefinedOrder) Validate() error {
 //   UINT64 - unsigned comparison
 //   DECIMAL - signed comparison of the represented value
 //   DATE - signed comparison
+//   FLOAT16 - signed comparison of the represented value (*)
 //   TIME_MILLIS - signed comparison
 //   TIME_MICROS - signed comparison
 //   TIMESTAMP_MILLIS - signed comparison
@@ -12504,30 +12929,73 @@ func (p *TypeDefinedOrder) Validate() error {
 //   BOOLEAN - false, true
 //   INT32 - signed comparison
 //   INT64 - signed comparison
-//   INT96 (only used for legacy timestamps) - undefined
+//   INT96 (only used for legacy timestamps) - depends on sort order (+)
 //   FLOAT - signed comparison of the represented value (*)
 //   DOUBLE - signed comparison of the represented value (*)
 //   BYTE_ARRAY - unsigned byte-wise comparison
 //   FIXED_LEN_BYTE_ARRAY - unsigned byte-wise comparison
 // 
-// (*) Because the sorting order is not specified properly for floating
-//     point values (relations vs. total ordering) the following
+// (+) While the INT96 type has been deprecated, at the time of writing it is
+//     still used in many legacy systems. It is optional for writers to emit
+//     statistics for INT96 columns. Writers that emit stats for such columns
+//     should use the INT96_TIMESTAMP_ORDER for this type and order the values
+//     according to the legacy rules:
+//     - compare the last 4 bytes (days) as a little-endian 32-bit signed 
integer
+//     - if equal last 4 bytes, compare the first 8 bytes as a little-endian
+//       64-bit signed integer (nanos)
+//     If TYPE_ORDER is used for an INT96 column, readers should ignore all 
statistics
+//     (`min`/`max` fields in `Statistics` and `min_values`/`max_values` 
fields in
+//     `ColumnIndex`) for that column.
+// 
+// (*) Because TYPE_ORDER is ambiguous for floating point types due to
+//     underspecified handling of NaN and -0/+0, it is recommended that writers
+//     use IEEE_754_TOTAL_ORDER for these types.
+// 
+//     If TYPE_ORDER is used for floating point types, then the following
 //     compatibility rules should be applied when reading statistics:
 //     - If the min is a NaN, it should be ignored.
 //     - If the max is a NaN, it should be ignored.
+//     - If the nan_count field is set, a reader can compute
+//       nan_count + null_count == num_values to deduce whether all non-null
+//       values are NaN.
 //     - If the min is +0, the row group may contain -0 values as well.
 //     - If the max is -0, the row group may contain +0 values as well.
 //     - When looking for NaN values, min and max should be ignored.
-// 
-//     When writing statistics the following rules should be followed:
-//     - NaNs should not be written to min or max statistics fields.
+//       If the nan_count field is set, it can be used to check whether
+//       NaNs are present.
+// 
+//     When writing page or column chunk statistics for columns with
+//     TYPE_ORDER order, the following rules must be followed:
+//     - The nan_count field must be set for floating point types, even if
+//       it is zero.
+//     - If the nan_count field is set, min and max statistics fields, when
+//       present, must not contain NaN values and must be computed from
+//       non-NaN values only. This signals to readers that the min and max
+//       statistics are reliable for non-NaN values.
+//     - If all non-null values are NaN, min and max statistics must not be
+//       written.
 //     - If the computed max value is zero (whether negative or positive),
 //       `+0.0` should be written into the max statistics field.
 //     - If the computed min value is zero (whether negative or positive),
 //       `-0.0` should be written into the min statistics field.
 // 
+//     When writing column indexes for columns with TYPE_ORDER order, the
+//     following rules must be followed:
+//     - NaNs must not be written to min_values or max_values.
+//     - If all non-null values of a page are NaN, a column index must not
+//       be written for this column chunk because min_values and max_values
+//       are required.
+//     - If the computed max value is zero (whether negative or positive),
+//       `+0.0` should be written into the corresponding max_values entry.
+//     - If the computed min value is zero (whether negative or positive),
+//       `-0.0` should be written into the corresponding min_values entry.
+//  - IEEE_754_TOTAL_ORDER
+//  - INT96_TIMESTAMP_ORDER
+// 
 type ColumnOrder struct {
        TYPE_ORDER *TypeDefinedOrder `thrift:"TYPE_ORDER,1" db:"TYPE_ORDER" 
json:"TYPE_ORDER,omitempty"`
+       IEEE_754_TOTAL_ORDER *IEEE754TotalOrder 
`thrift:"IEEE_754_TOTAL_ORDER,2" db:"IEEE_754_TOTAL_ORDER" 
json:"IEEE_754_TOTAL_ORDER,omitempty"`
+       INT96_TIMESTAMP_ORDER *Int96TimestampOrder 
`thrift:"INT96_TIMESTAMP_ORDER,3" db:"INT96_TIMESTAMP_ORDER" 
json:"INT96_TIMESTAMP_ORDER,omitempty"`
 }
 
 func NewColumnOrder() *ColumnOrder {
@@ -12543,11 +13011,35 @@ func (p *ColumnOrder) GetTYPE_ORDER() 
*TypeDefinedOrder {
        return p.TYPE_ORDER
 }
 
+var ColumnOrder_IEEE_754_TOTAL_ORDER_DEFAULT *IEEE754TotalOrder
+
+func (p *ColumnOrder) GetIEEE_754_TOTAL_ORDER() *IEEE754TotalOrder {
+       if !p.IsSetIEEE_754_TOTAL_ORDER() {
+               return ColumnOrder_IEEE_754_TOTAL_ORDER_DEFAULT
+       }
+       return p.IEEE_754_TOTAL_ORDER
+}
+
+var ColumnOrder_INT96_TIMESTAMP_ORDER_DEFAULT *Int96TimestampOrder
+
+func (p *ColumnOrder) GetINT96_TIMESTAMP_ORDER() *Int96TimestampOrder {
+       if !p.IsSetINT96_TIMESTAMP_ORDER() {
+               return ColumnOrder_INT96_TIMESTAMP_ORDER_DEFAULT
+       }
+       return p.INT96_TIMESTAMP_ORDER
+}
+
 func (p *ColumnOrder) CountSetFieldsColumnOrder() int {
        count := 0
        if (p.IsSetTYPE_ORDER()) {
                count++
        }
+       if (p.IsSetIEEE_754_TOTAL_ORDER()) {
+               count++
+       }
+       if (p.IsSetINT96_TIMESTAMP_ORDER()) {
+               count++
+       }
        return count
 
 }
@@ -12556,6 +13048,14 @@ func (p *ColumnOrder) IsSetTYPE_ORDER() bool {
        return p.TYPE_ORDER != nil
 }
 
+func (p *ColumnOrder) IsSetIEEE_754_TOTAL_ORDER() bool {
+       return p.IEEE_754_TOTAL_ORDER != nil
+}
+
+func (p *ColumnOrder) IsSetINT96_TIMESTAMP_ORDER() bool {
+       return p.INT96_TIMESTAMP_ORDER != nil
+}
+
 func (p *ColumnOrder) Read(ctx context.Context, iprot thrift.TProtocol) error {
        if _, err := iprot.ReadStructBegin(ctx); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T read error: ", p), 
err)
@@ -12581,6 +13081,26 @@ func (p *ColumnOrder) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                                        return err
                                }
                        }
+               case 2:
+                       if fieldTypeId == thrift.STRUCT {
+                               if err := p.ReadField2(ctx, iprot); err != nil {
+                                       return err
+                               }
+                       } else {
+                               if err := iprot.Skip(ctx, fieldTypeId); err != 
nil {
+                                       return err
+                               }
+                       }
+               case 3:
+                       if fieldTypeId == thrift.STRUCT {
+                               if err := p.ReadField3(ctx, iprot); err != nil {
+                                       return err
+                               }
+                       } else {
+                               if err := iprot.Skip(ctx, fieldTypeId); err != 
nil {
+                                       return err
+                               }
+                       }
                default:
                        if err := iprot.Skip(ctx, fieldTypeId); err != nil {
                                return err
@@ -12604,15 +13124,33 @@ func (p *ColumnOrder) ReadField1(ctx context.Context, 
iprot thrift.TProtocol) er
        return nil
 }
 
+func (p *ColumnOrder) ReadField2(ctx context.Context, iprot thrift.TProtocol) 
error {
+       p.IEEE_754_TOTAL_ORDER = &IEEE754TotalOrder{}
+       if err := p.IEEE_754_TOTAL_ORDER.Read(ctx, iprot); err != nil {
+               return thrift.PrependError(fmt.Sprintf("%T error reading 
struct: ", p.IEEE_754_TOTAL_ORDER), err)
+       }
+       return nil
+}
+
+func (p *ColumnOrder) ReadField3(ctx context.Context, iprot thrift.TProtocol) 
error {
+       p.INT96_TIMESTAMP_ORDER = &Int96TimestampOrder{}
+       if err := p.INT96_TIMESTAMP_ORDER.Read(ctx, iprot); err != nil {
+               return thrift.PrependError(fmt.Sprintf("%T error reading 
struct: ", p.INT96_TIMESTAMP_ORDER), err)
+       }
+       return nil
+}
+
 func (p *ColumnOrder) Write(ctx context.Context, oprot thrift.TProtocol) error 
{
        if c := p.CountSetFieldsColumnOrder(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "ColumnOrder"); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
        }
        if p != nil {
                if err := p.writeField1(ctx, oprot); err != nil { return err }
+               if err := p.writeField2(ctx, oprot); err != nil { return err }
+               if err := p.writeField3(ctx, oprot); err != nil { return err }
        }
        if err := oprot.WriteFieldStop(ctx); err != nil {
                return thrift.PrependError("write field stop error: ", err)
@@ -12638,6 +13176,36 @@ func (p *ColumnOrder) writeField1(ctx context.Context, 
oprot thrift.TProtocol) (
        return err
 }
 
+func (p *ColumnOrder) writeField2(ctx context.Context, oprot thrift.TProtocol) 
(err error) {
+       if p.IsSetIEEE_754_TOTAL_ORDER() {
+               if err := oprot.WriteFieldBegin(ctx, "IEEE_754_TOTAL_ORDER", 
thrift.STRUCT, 2); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
begin error 2:IEEE_754_TOTAL_ORDER: ", p), err)
+               }
+               if err := p.IEEE_754_TOTAL_ORDER.Write(ctx, oprot); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T error 
writing struct: ", p.IEEE_754_TOTAL_ORDER), err)
+               }
+               if err := oprot.WriteFieldEnd(ctx); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
end error 2:IEEE_754_TOTAL_ORDER: ", p), err)
+               }
+       }
+       return err
+}
+
+func (p *ColumnOrder) writeField3(ctx context.Context, oprot thrift.TProtocol) 
(err error) {
+       if p.IsSetINT96_TIMESTAMP_ORDER() {
+               if err := oprot.WriteFieldBegin(ctx, "INT96_TIMESTAMP_ORDER", 
thrift.STRUCT, 3); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
begin error 3:INT96_TIMESTAMP_ORDER: ", p), err)
+               }
+               if err := p.INT96_TIMESTAMP_ORDER.Write(ctx, oprot); err != nil 
{
+                       return thrift.PrependError(fmt.Sprintf("%T error 
writing struct: ", p.INT96_TIMESTAMP_ORDER), err)
+               }
+               if err := oprot.WriteFieldEnd(ctx); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
end error 3:INT96_TIMESTAMP_ORDER: ", p), err)
+               }
+       }
+       return err
+}
+
 func (p *ColumnOrder) Equals(other *ColumnOrder) bool {
        if p == other {
                return true
@@ -12645,6 +13213,8 @@ func (p *ColumnOrder) Equals(other *ColumnOrder) bool {
                return false
        }
        if !p.TYPE_ORDER.Equals(other.TYPE_ORDER) { return false }
+       if !p.IEEE_754_TOTAL_ORDER.Equals(other.IEEE_754_TOTAL_ORDER) { return 
false }
+       if !p.INT96_TIMESTAMP_ORDER.Equals(other.INT96_TIMESTAMP_ORDER) { 
return false }
        return true
 }
 
@@ -12674,8 +13244,8 @@ func (p *ColumnOrder) Validate() error {
 
 // Attributes:
 //  - Offset: Offset of the page in the file *
-//  - CompressedPageSize: Size of the page, including header. Sum of 
compressed_page_size and header
-// length
+//  - CompressedPageSize: Size of the page, including header. Equal to the sum 
of the page's
+// PageHeader.compressed_page_size and the size of the serialized PageHeader.
 //  - FirstRowIndex: Index within the RowGroup of the first row of the page. 
When an
 // OffsetIndex is present, pages must begin on row boundaries
 // (repetition_level = 0).
@@ -12772,13 +13342,13 @@ func (p *PageLocation) Read(ctx context.Context, 
iprot thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetOffset{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Offset is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Offset is not set"))
        }
        if !issetCompressedPageSize{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field CompressedPageSize is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field CompressedPageSize is not set"))
        }
        if !issetFirstRowIndex{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field FirstRowIndex is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field FirstRowIndex is not set"))
        }
        return nil
 }
@@ -12914,7 +13484,7 @@ func (p *PageLocation) Validate() error {
 // that page_locations[i].first_row_index < 
page_locations[i+1].first_row_index.
 //  - UnencodedByteArrayDataBytes: Unencoded/uncompressed size for BYTE_ARRAY 
types.
 // 
-// See documention for unencoded_byte_array_data_bytes in SizeStatistics for
+// See documentation for unencoded_byte_array_data_bytes in SizeStatistics for
 // more details on this field.
 // 
 type OffsetIndex struct {
@@ -12993,7 +13563,7 @@ func (p *OffsetIndex) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetPageLocations{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PageLocations is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field PageLocations is not set"))
        }
        return nil
 }
@@ -13162,11 +13732,30 @@ func (p *OffsetIndex) Validate() error {
 //  - MinValues: Two lists containing lower and upper bounds for the values of 
each page
 // determined by the ColumnOrder of the column. These may be the actual
 // minimum and maximum values found on a page, but can also be (more compact)
-// values that do not exist on a page. For example, instead of storing ""Blart
+// values that do not exist on a page. For example, instead of storing "Blart
 // Versenwald III", a writer may set min_values[i]="B", max_values[i]="C".
 // Such more compact values must still be valid values within the column's
 // logical type. Readers must make sure that list entries are populated before
 // using them by inspecting null_pages.
+// 
+// For columns of physical type FLOAT or DOUBLE, or logical type FLOAT16,
+// NaN values are not to be included in these bounds. If all non-null values
+// of a page are NaN, then a writer must do the following:
+// - If the order of this column is TYPE_ORDER, then a column index must
+//   not be written for this column chunk. While this is unfortunate for
+//   performance, it is necessary to avoid conflict with legacy files that
+//   still included NaN in min_values and max_values even if the page had
+//   non-NaN values. To mitigate this, IEEE754_TOTAL_ORDER is recommended.
+// - If the order of this column is IEEE754_TOTAL_ORDER, then min_values[i]
+//   and max_values[i] of that page must be set to the smallest and largest
+//   NaN values as defined by IEEE 754 total order.
+// 
+// For columns of physical type INT96, the writer must do the following:
+// - If the order of this column is not INT96_TIMESTAMP_ORDER, then a column
+//   index must not be written for this column chunk.
+// - If the order of this column is INT96_TIMESTAMP_ORDER, the min_values[i]
+//   and max_values[i] of that page must be set to the smallest and largest
+//   values as defined by the INT96 chronological timestamp ordering.
 //  - MaxValues
 //  - BoundaryOrder: Stores whether both min_values and max_values are ordered 
and if so, in
 // which direction. This allows readers to perform binary searches in both
@@ -13193,6 +13782,10 @@ func (p *OffsetIndex) Validate() error {
 // 
 //  - DefinitionLevelHistograms: Same as repetition_level_histograms except 
for definitions levels.
 // 
+//  - NanCounts: A list containing the number of NaN values for each page. 
Only present
+// for columns of physical type FLOAT or DOUBLE, or logical type FLOAT16.
+// If this field is not present, readers MUST assume that there might be
+// NaN values in any page.
 // 
 type ColumnIndex struct {
        NullPages []bool `thrift:"null_pages,1,required" db:"null_pages" 
json:"null_pages"`
@@ -13202,6 +13795,7 @@ type ColumnIndex struct {
        NullCounts []int64 `thrift:"null_counts,5" db:"null_counts" 
json:"null_counts,omitempty"`
        RepetitionLevelHistograms []int64 
`thrift:"repetition_level_histograms,6" db:"repetition_level_histograms" 
json:"repetition_level_histograms,omitempty"`
        DefinitionLevelHistograms []int64 
`thrift:"definition_level_histograms,7" db:"definition_level_histograms" 
json:"definition_level_histograms,omitempty"`
+       NanCounts []int64 `thrift:"nan_counts,8" db:"nan_counts" 
json:"nan_counts,omitempty"`
 }
 
 func NewColumnIndex() *ColumnIndex {
@@ -13253,6 +13847,13 @@ func (p *ColumnIndex) GetDefinitionLevelHistograms() 
[]int64 {
        return p.DefinitionLevelHistograms
 }
 
+var ColumnIndex_NanCounts_DEFAULT []int64
+
+
+func (p *ColumnIndex) GetNanCounts() []int64 {
+       return p.NanCounts
+}
+
 func (p *ColumnIndex) IsSetNullCounts() bool {
        return p.NullCounts != nil
 }
@@ -13265,6 +13866,10 @@ func (p *ColumnIndex) IsSetDefinitionLevelHistograms() 
bool {
        return p.DefinitionLevelHistograms != nil
 }
 
+func (p *ColumnIndex) IsSetNanCounts() bool {
+       return p.NanCounts != nil
+}
+
 func (p *ColumnIndex) Read(ctx context.Context, iprot thrift.TProtocol) error {
        if _, err := iprot.ReadStructBegin(ctx); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T read error: ", p), 
err)
@@ -13358,6 +13963,16 @@ func (p *ColumnIndex) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                                        return err
                                }
                        }
+               case 8:
+                       if fieldTypeId == thrift.LIST {
+                               if err := p.ReadField8(ctx, iprot); err != nil {
+                                       return err
+                               }
+                       } else {
+                               if err := iprot.Skip(ctx, fieldTypeId); err != 
nil {
+                                       return err
+                               }
+                       }
                default:
                        if err := iprot.Skip(ctx, fieldTypeId); err != nil {
                                return err
@@ -13371,16 +13986,16 @@ func (p *ColumnIndex) Read(ctx context.Context, iprot 
thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetNullPages{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NullPages is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NullPages is not set"))
        }
        if !issetMinValues{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field MinValues is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field MinValues is not set"))
        }
        if !issetMaxValues{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field MaxValues is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field MaxValues is not set"))
        }
        if !issetBoundaryOrder{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field BoundaryOrder is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field BoundaryOrder is not set"))
        }
        return nil
 }
@@ -13527,6 +14142,28 @@ func (p *ColumnIndex) ReadField7(ctx context.Context, 
iprot thrift.TProtocol) er
        return nil
 }
 
+func (p *ColumnIndex) ReadField8(ctx context.Context, iprot thrift.TProtocol) 
error {
+       _, size, err := iprot.ReadListBegin(ctx)
+       if err != nil {
+               return thrift.PrependError("error reading list begin: ", err)
+       }
+       tSlice := make([]int64, 0, size)
+       p.NanCounts = tSlice
+       for i := 0; i < size; i++ {
+               var _elem30 int64
+               if v, err := iprot.ReadI64(ctx); err != nil {
+                       return thrift.PrependError("error reading field 0: ", 
err)
+               } else {
+                       _elem30 = v
+               }
+               p.NanCounts = append(p.NanCounts, _elem30)
+       }
+       if err := iprot.ReadListEnd(ctx); err != nil {
+               return thrift.PrependError("error reading list end: ", err)
+       }
+       return nil
+}
+
 func (p *ColumnIndex) Write(ctx context.Context, oprot thrift.TProtocol) error 
{
        if err := oprot.WriteStructBegin(ctx, "ColumnIndex"); err != nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -13539,6 +14176,7 @@ func (p *ColumnIndex) Write(ctx context.Context, oprot 
thrift.TProtocol) error {
                if err := p.writeField5(ctx, oprot); err != nil { return err }
                if err := p.writeField6(ctx, oprot); err != nil { return err }
                if err := p.writeField7(ctx, oprot); err != nil { return err }
+               if err := p.writeField8(ctx, oprot); err != nil { return err }
        }
        if err := oprot.WriteFieldStop(ctx); err != nil {
                return thrift.PrependError("write field stop error: ", err)
@@ -13694,6 +14332,29 @@ func (p *ColumnIndex) writeField7(ctx context.Context, 
oprot thrift.TProtocol) (
        return err
 }
 
+func (p *ColumnIndex) writeField8(ctx context.Context, oprot thrift.TProtocol) 
(err error) {
+       if p.IsSetNanCounts() {
+               if err := oprot.WriteFieldBegin(ctx, "nan_counts", thrift.LIST, 
8); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
begin error 8:nan_counts: ", p), err)
+               }
+               if err := oprot.WriteListBegin(ctx, thrift.I64, 
len(p.NanCounts)); err != nil {
+                       return thrift.PrependError("error writing list begin: 
", err)
+               }
+               for _, v := range p.NanCounts {
+                       if err := oprot.WriteI64(ctx, int64(v)); err != nil {
+                               return thrift.PrependError(fmt.Sprintf("%T. (0) 
field write error: ", p), err)
+                       }
+               }
+               if err := oprot.WriteListEnd(ctx); err != nil {
+                       return thrift.PrependError("error writing list end: ", 
err)
+               }
+               if err := oprot.WriteFieldEnd(ctx); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T write field 
end error 8:nan_counts: ", p), err)
+               }
+       }
+       return err
+}
+
 func (p *ColumnIndex) Equals(other *ColumnIndex) bool {
        if p == other {
                return true
@@ -13702,34 +14363,39 @@ func (p *ColumnIndex) Equals(other *ColumnIndex) bool 
{
        }
        if len(p.NullPages) != len(other.NullPages) { return false }
        for i, _tgt := range p.NullPages {
-               _src30 := other.NullPages[i]
-               if _tgt != _src30 { return false }
+               _src31 := other.NullPages[i]
+               if _tgt != _src31 { return false }
        }
        if len(p.MinValues) != len(other.MinValues) { return false }
        for i, _tgt := range p.MinValues {
-               _src31 := other.MinValues[i]
-               if bytes.Compare(_tgt, _src31) != 0 { return false }
+               _src32 := other.MinValues[i]
+               if bytes.Compare(_tgt, _src32) != 0 { return false }
        }
        if len(p.MaxValues) != len(other.MaxValues) { return false }
        for i, _tgt := range p.MaxValues {
-               _src32 := other.MaxValues[i]
-               if bytes.Compare(_tgt, _src32) != 0 { return false }
+               _src33 := other.MaxValues[i]
+               if bytes.Compare(_tgt, _src33) != 0 { return false }
        }
        if p.BoundaryOrder != other.BoundaryOrder { return false }
        if len(p.NullCounts) != len(other.NullCounts) { return false }
        for i, _tgt := range p.NullCounts {
-               _src33 := other.NullCounts[i]
-               if _tgt != _src33 { return false }
+               _src34 := other.NullCounts[i]
+               if _tgt != _src34 { return false }
        }
        if len(p.RepetitionLevelHistograms) != 
len(other.RepetitionLevelHistograms) { return false }
        for i, _tgt := range p.RepetitionLevelHistograms {
-               _src34 := other.RepetitionLevelHistograms[i]
-               if _tgt != _src34 { return false }
+               _src35 := other.RepetitionLevelHistograms[i]
+               if _tgt != _src35 { return false }
        }
        if len(p.DefinitionLevelHistograms) != 
len(other.DefinitionLevelHistograms) { return false }
        for i, _tgt := range p.DefinitionLevelHistograms {
-               _src35 := other.DefinitionLevelHistograms[i]
-               if _tgt != _src35 { return false }
+               _src36 := other.DefinitionLevelHistograms[i]
+               if _tgt != _src36 { return false }
+       }
+       if len(p.NanCounts) != len(other.NanCounts) { return false }
+       for i, _tgt := range p.NanCounts {
+               _src37 := other.NanCounts[i]
+               if _tgt != _src37 { return false }
        }
        return true
 }
@@ -14361,7 +15027,7 @@ func (p *EncryptionAlgorithm) ReadField2(ctx 
context.Context, iprot thrift.TProt
 
 func (p *EncryptionAlgorithm) Write(ctx context.Context, oprot 
thrift.TProtocol) error {
        if c := p.CountSetFieldsEncryptionAlgorithm(); c != 1 {
-               return fmt.Errorf("%T write union: exactly one field must be 
set (%d set)", p, c)
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("%T write 
union: exactly one field must be set (%d set)", p, c))
        }
        if err := oprot.WriteStructBegin(ctx, "EncryptionAlgorithm"); err != 
nil {
                return thrift.PrependError(fmt.Sprintf("%T write struct begin 
error: ", p), err)
@@ -14447,7 +15113,13 @@ func (p *EncryptionAlgorithm) Validate() error {
 // Description for file metadata
 // 
 // Attributes:
-//  - Version: Version of this file *
+//  - Version: Version of this file
+// 
+// As of December 2025, there is no agreed upon consensus of what constitutes
+// version 2 of the file. For maximum compatibility with readers, writers 
should
+// always populate "1" for version. For maximum compatibility with writers,
+// readers should accept "1" and "2" interchangeably.  All other versions are
+// reserved for potential future use-cases.
 //  - Schema: Parquet schema for this file.  This schema contains metadata for 
all the columns.
 // The schema is represented as a tree with a single root.  The nodes of the 
tree
 // are flattened to a list by doing a depth-first traversal.
@@ -14464,7 +15136,7 @@ func (p *EncryptionAlgorithm) Validate() error {
 //  - ColumnOrders: Sort order used for the min_value and max_value fields in 
the Statistics
 // objects and the min_values and max_values fields in the ColumnIndex
 // objects of each column in this file. Sort orders are listed in the order
-// matching the columns in the schema. The indexes are not necessary the same
+// matching the columns in the schema. The indexes are not necessarily the same
 // though, because only leaf nodes of the schema are represented in the list
 // of sort orders.
 // 
@@ -14706,16 +15378,16 @@ func (p *FileMetaData) Read(ctx context.Context, 
iprot thrift.TProtocol) error {
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetVersion{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Version is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Version is not set"))
        }
        if !issetSchema{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Schema is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field Schema is not set"))
        }
        if !issetNumRows{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumRows is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field NumRows is not set"))
        }
        if !issetRowGroups{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field RowGroups is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field RowGroups is not set"))
        }
        return nil
 }
@@ -14737,11 +15409,11 @@ func (p *FileMetaData) ReadField2(ctx 
context.Context, iprot thrift.TProtocol) e
        tSlice := make([]*SchemaElement, 0, size)
        p.Schema = tSlice
        for i := 0; i < size; i++ {
-               _elem36 := &SchemaElement{}
-               if err := _elem36.Read(ctx, iprot); err != nil {
-                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem36), err)
+               _elem38 := &SchemaElement{}
+               if err := _elem38.Read(ctx, iprot); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem38), err)
                }
-               p.Schema = append(p.Schema, _elem36)
+               p.Schema = append(p.Schema, _elem38)
        }
        if err := iprot.ReadListEnd(ctx); err != nil {
                return thrift.PrependError("error reading list end: ", err)
@@ -14766,11 +15438,11 @@ func (p *FileMetaData) ReadField4(ctx 
context.Context, iprot thrift.TProtocol) e
        tSlice := make([]*RowGroup, 0, size)
        p.RowGroups = tSlice
        for i := 0; i < size; i++ {
-               _elem37 := &RowGroup{}
-               if err := _elem37.Read(ctx, iprot); err != nil {
-                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem37), err)
+               _elem39 := &RowGroup{}
+               if err := _elem39.Read(ctx, iprot); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem39), err)
                }
-               p.RowGroups = append(p.RowGroups, _elem37)
+               p.RowGroups = append(p.RowGroups, _elem39)
        }
        if err := iprot.ReadListEnd(ctx); err != nil {
                return thrift.PrependError("error reading list end: ", err)
@@ -14786,11 +15458,11 @@ func (p *FileMetaData) ReadField5(ctx 
context.Context, iprot thrift.TProtocol) e
        tSlice := make([]*KeyValue, 0, size)
        p.KeyValueMetadata = tSlice
        for i := 0; i < size; i++ {
-               _elem38 := &KeyValue{}
-               if err := _elem38.Read(ctx, iprot); err != nil {
-                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem38), err)
+               _elem40 := &KeyValue{}
+               if err := _elem40.Read(ctx, iprot); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem40), err)
                }
-               p.KeyValueMetadata = append(p.KeyValueMetadata, _elem38)
+               p.KeyValueMetadata = append(p.KeyValueMetadata, _elem40)
        }
        if err := iprot.ReadListEnd(ctx); err != nil {
                return thrift.PrependError("error reading list end: ", err)
@@ -14815,11 +15487,11 @@ func (p *FileMetaData) ReadField7(ctx 
context.Context, iprot thrift.TProtocol) e
        tSlice := make([]*ColumnOrder, 0, size)
        p.ColumnOrders = tSlice
        for i := 0; i < size; i++ {
-               _elem39 := &ColumnOrder{}
-               if err := _elem39.Read(ctx, iprot); err != nil {
-                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem39), err)
+               _elem41 := &ColumnOrder{}
+               if err := _elem41.Read(ctx, iprot); err != nil {
+                       return thrift.PrependError(fmt.Sprintf("%T error 
reading struct: ", _elem41), err)
                }
-               p.ColumnOrders = append(p.ColumnOrders, _elem39)
+               p.ColumnOrders = append(p.ColumnOrders, _elem41)
        }
        if err := iprot.ReadListEnd(ctx); err != nil {
                return thrift.PrependError("error reading list end: ", err)
@@ -15036,19 +15708,19 @@ func (p *FileMetaData) Equals(other *FileMetaData) 
bool {
        if p.Version != other.Version { return false }
        if len(p.Schema) != len(other.Schema) { return false }
        for i, _tgt := range p.Schema {
-               _src40 := other.Schema[i]
-               if !_tgt.Equals(_src40) { return false }
+               _src42 := other.Schema[i]
+               if !_tgt.Equals(_src42) { return false }
        }
        if p.NumRows != other.NumRows { return false }
        if len(p.RowGroups) != len(other.RowGroups) { return false }
        for i, _tgt := range p.RowGroups {
-               _src41 := other.RowGroups[i]
-               if !_tgt.Equals(_src41) { return false }
+               _src43 := other.RowGroups[i]
+               if !_tgt.Equals(_src43) { return false }
        }
        if len(p.KeyValueMetadata) != len(other.KeyValueMetadata) { return 
false }
        for i, _tgt := range p.KeyValueMetadata {
-               _src42 := other.KeyValueMetadata[i]
-               if !_tgt.Equals(_src42) { return false }
+               _src44 := other.KeyValueMetadata[i]
+               if !_tgt.Equals(_src44) { return false }
        }
        if p.CreatedBy != other.CreatedBy {
                if p.CreatedBy == nil || other.CreatedBy == nil {
@@ -15058,8 +15730,8 @@ func (p *FileMetaData) Equals(other *FileMetaData) bool 
{
        }
        if len(p.ColumnOrders) != len(other.ColumnOrders) { return false }
        for i, _tgt := range p.ColumnOrders {
-               _src43 := other.ColumnOrders[i]
-               if !_tgt.Equals(_src43) { return false }
+               _src45 := other.ColumnOrders[i]
+               if !_tgt.Equals(_src45) { return false }
        }
        if !p.EncryptionAlgorithm.Equals(other.EncryptionAlgorithm) { return 
false }
        if bytes.Compare(p.FooterSigningKeyMetadata, 
other.FooterSigningKeyMetadata) != 0 { return false }
@@ -15182,7 +15854,7 @@ func (p *FileCryptoMetaData) Read(ctx context.Context, 
iprot thrift.TProtocol) e
                return thrift.PrependError(fmt.Sprintf("%T read struct end 
error: ", p), err)
        }
        if !issetEncryptionAlgorithm{
-               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field EncryptionAlgorithm is not set"));
+               return 
thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required 
field EncryptionAlgorithm is not set"))
        }
        return nil
 }
diff --git a/parquet/schema/logical_types.go b/parquet/schema/logical_types.go
index e7f1c29f..f1e85bd0 100644
--- a/parquet/schema/logical_types.go
+++ b/parquet/schema/logical_types.go
@@ -73,6 +73,10 @@ func getLogicalType(l *format.LogicalType) LogicalType {
                return Float16LogicalType{}
        case l.IsSetVARIANT():
                return VariantLogicalType{}
+       case l.IsSetGEOGRAPHY():
+               return geographyLogicalTypeFromThrift(l.GEOGRAPHY)
+       case l.IsSetGEOMETRY():
+               return geometryLogicalTypeFromThrift(l.GEOMETRY)
        case l == nil:
                return NoLogicalType{}
        default:
@@ -1228,3 +1232,257 @@ func (NoLogicalType) Equals(rhs LogicalType) bool {
 }
 
 func (NoLogicalType) IsNone() bool { return true }
+
+// GeometryLogicalType represents geospatial features in WKB format.
+type GeometryLogicalType struct {
+       baseLogicalType
+       // Crs optionally identifies the coordinate reference system. If empty,
+       // it defaults to OGC:CRS84.
+       Crs string
+}
+
+func geometryLogicalTypeFromThrift(t *format.GeometryType) GeometryLogicalType 
{
+       if t == nil || !t.IsSetCrs() {
+               return GeometryLogicalType{}
+       }
+       return GeometryLogicalType{Crs: t.GetCrs()}
+}
+
+// CRS returns the coordinate reference system, or OGC:CRS84 when unset.
+func (t GeometryLogicalType) CRS() string {
+       if !t.IsCRSSet() {
+               return defaultGeospatialCRS
+       }
+       return t.Crs
+}
+
+// IsCRSSet returns whether the coordinate reference system was explicitly set.
+func (t GeometryLogicalType) IsCRSSet() bool {
+       return t.Crs != ""
+}
+
+func (t GeometryLogicalType) Equals(rhs LogicalType) bool {
+       other, ok := rhs.(GeometryLogicalType)
+       return ok && t.Crs == other.Crs
+}
+
+func (GeometryLogicalType) IsCompatible(c ConvertedType, dec DecimalMetadata) 
bool {
+       if dec.IsSet {
+               return false
+       }
+       switch c {
+       case ConvertedTypes.None, ConvertedTypes.NA:
+               return true
+       }
+       return false
+}
+
+func (t GeometryLogicalType) MarshalJSON() ([]byte, error) {
+       values := map[string]interface{}{"Type": "Geometry"}
+       if t.IsCRSSet() {
+               values["crs"] = t.CRS()
+       }
+       return json.Marshal(values)
+}
+
+func (t GeometryLogicalType) String() string {
+       if t.IsCRSSet() {
+               return fmt.Sprintf("Geometry(crs=%s)", t.CRS())
+       }
+       return "Geometry"
+}
+
+func (GeometryLogicalType) SortOrder() SortOrder {
+       return SortUNKNOWN
+}
+
+func (GeometryLogicalType) ToConvertedType() (ConvertedType, DecimalMetadata) {
+       return ConvertedTypes.None, DecimalMetadata{}
+}
+
+func (GeometryLogicalType) IsApplicable(t parquet.Type, _ int32) bool {
+       return t == parquet.Types.ByteArray
+}
+
+func (t GeometryLogicalType) toThrift() *format.LogicalType {
+       typ := format.NewGeometryType()
+       if t.IsCRSSet() {
+               typ.Crs = thrift.StringPtr(t.Crs)
+       }
+       return &format.LogicalType{GEOMETRY: typ}
+}
+
+// GeographyLogicalType represents geospatial features in WKB format with a
+// non-linear edge interpolation algorithm.
+type GeographyLogicalType struct {
+       baseLogicalType
+       // Crs optionally identifies the coordinate reference system. If empty,
+       // it defaults to OGC:CRS84.
+       Crs string
+       // Algorithm optionally identifies the edge interpolation algorithm. If
+       // empty, it defaults to spherical.
+       Algorithm GeographyEdgeInterpolationAlgorithm
+}
+
+const defaultGeospatialCRS = "OGC:CRS84"
+
+func geographyLogicalTypeFromThrift(t *format.GeographyType) 
GeographyLogicalType {
+       if t == nil {
+               return GeographyLogicalType{}
+       }
+
+       ret := GeographyLogicalType{}
+       if t.IsSetCrs() {
+               ret.Crs = t.GetCrs()
+       }
+       if t.IsSetAlgorithm() {
+               ret.Algorithm.fromThrift(t.GetAlgorithm())
+       }
+       return ret
+}
+
+// CRS returns the coordinate reference system, or OGC:CRS84 when unset.
+func (t GeographyLogicalType) CRS() string {
+       if !t.IsCRSSet() {
+               return defaultGeospatialCRS
+       }
+       return t.Crs
+}
+
+// IsCRSSet returns whether the coordinate reference system was explicitly set.
+func (t GeographyLogicalType) IsCRSSet() bool {
+       return t.Crs != ""
+}
+
+// EdgeInterpolationAlgorithm returns the edge interpolation algorithm. If
+// unset, it returns GeographyEdgeSpherical.
+func (t GeographyLogicalType) EdgeInterpolationAlgorithm() 
GeographyEdgeInterpolationAlgorithm {
+       if !t.IsEdgeInterpolationAlgorithmSet() {
+               return GeographyEdgeSpherical
+       }
+       return t.Algorithm
+}
+
+// IsEdgeInterpolationAlgorithmSet returns whether the edge interpolation
+// algorithm was explicitly set.
+func (t GeographyLogicalType) IsEdgeInterpolationAlgorithmSet() bool {
+       return t.Algorithm != ""
+}
+
+func (t GeographyLogicalType) Equals(rhs LogicalType) bool {
+       other, ok := rhs.(GeographyLogicalType)
+       return ok && t.Crs == other.Crs && t.Algorithm == other.Algorithm
+}
+
+func (GeographyLogicalType) IsCompatible(c ConvertedType, dec DecimalMetadata) 
bool {
+       if dec.IsSet {
+               return false
+       }
+       switch c {
+       case ConvertedTypes.None, ConvertedTypes.NA:
+               return true
+       }
+       return false
+}
+
+func (t GeographyLogicalType) MarshalJSON() ([]byte, error) {
+       values := map[string]any{"Type": "Geography"}
+       if t.IsCRSSet() {
+               values["crs"] = t.CRS()
+       }
+       if t.IsEdgeInterpolationAlgorithmSet() {
+               values["algorithm"] = t.EdgeInterpolationAlgorithm().String()
+       }
+       return json.Marshal(values)
+}
+
+func (t GeographyLogicalType) String() string {
+       if !t.IsCRSSet() && !t.IsEdgeInterpolationAlgorithmSet() {
+               return "Geography"
+       }
+       return fmt.Sprintf("Geography(crs=%s, algorithm=%s)", t.CRS(), 
t.EdgeInterpolationAlgorithm())
+}
+
+func (GeographyLogicalType) SortOrder() SortOrder {
+       return SortUNKNOWN
+}
+
+func (GeographyLogicalType) ToConvertedType() (ConvertedType, DecimalMetadata) 
{
+       return ConvertedTypes.None, DecimalMetadata{}
+}
+
+func (GeographyLogicalType) IsApplicable(t parquet.Type, _ int32) bool {
+       return t == parquet.Types.ByteArray
+}
+
+func (t GeographyLogicalType) toThrift() *format.LogicalType {
+       typ := format.NewGeographyType()
+       if t.IsCRSSet() {
+               typ.Crs = thrift.StringPtr(t.Crs)
+       }
+       if t.IsEdgeInterpolationAlgorithmSet() {
+               alg := t.Algorithm.toThrift()
+               typ.Algorithm = &alg
+       }
+       return &format.LogicalType{GEOGRAPHY: typ}
+}
+
+// GeographyEdgeInterpolationAlgorithm describes how geography edges are
+// interpolated between points.
+type GeographyEdgeInterpolationAlgorithm string
+
+// Constants for the GeographyEdgeInterpolationAlgorithm.
+const (
+       GeographyEdgeSpherical GeographyEdgeInterpolationAlgorithm = "spherical"
+       GeographyEdgeVincenty  GeographyEdgeInterpolationAlgorithm = "vincenty"
+       GeographyEdgeThomas    GeographyEdgeInterpolationAlgorithm = "thomas"
+       GeographyEdgeAndoyer   GeographyEdgeInterpolationAlgorithm = "andoyer"
+       GeographyEdgeKarney    GeographyEdgeInterpolationAlgorithm = "karney"
+)
+
+func (alg *GeographyEdgeInterpolationAlgorithm) fromThrift(thriftAlg 
format.EdgeInterpolationAlgorithm) {
+       switch thriftAlg {
+       case format.EdgeInterpolationAlgorithm_VINCENTY:
+               *alg = GeographyEdgeVincenty
+       case format.EdgeInterpolationAlgorithm_THOMAS:
+               *alg = GeographyEdgeThomas
+       case format.EdgeInterpolationAlgorithm_ANDOYER:
+               *alg = GeographyEdgeAndoyer
+       case format.EdgeInterpolationAlgorithm_KARNEY:
+               *alg = GeographyEdgeKarney
+       case format.EdgeInterpolationAlgorithm_SPHERICAL:
+               fallthrough
+       default:
+               *alg = GeographyEdgeSpherical
+       }
+}
+
+func (alg GeographyEdgeInterpolationAlgorithm) toThrift() 
format.EdgeInterpolationAlgorithm {
+       switch alg {
+       case GeographyEdgeVincenty:
+               return format.EdgeInterpolationAlgorithm_VINCENTY
+       case GeographyEdgeThomas:
+               return format.EdgeInterpolationAlgorithm_THOMAS
+       case GeographyEdgeAndoyer:
+               return format.EdgeInterpolationAlgorithm_ANDOYER
+       case GeographyEdgeKarney:
+               return format.EdgeInterpolationAlgorithm_KARNEY
+       case GeographyEdgeSpherical:
+               fallthrough
+       default:
+               return format.EdgeInterpolationAlgorithm_SPHERICAL
+       }
+}
+
+func (alg GeographyEdgeInterpolationAlgorithm) String() string {
+       switch alg {
+       case GeographyEdgeVincenty,
+               GeographyEdgeThomas,
+               GeographyEdgeAndoyer,
+               GeographyEdgeKarney,
+               GeographyEdgeSpherical:
+               return string(alg)
+       default:
+               return string(GeographyEdgeSpherical)
+       }
+}
diff --git a/parquet/schema/logical_types_test.go 
b/parquet/schema/logical_types_test.go
index 62d9294b..f3e21333 100644
--- a/parquet/schema/logical_types_test.go
+++ b/parquet/schema/logical_types_test.go
@@ -228,6 +228,8 @@ func TestLogicalTypeProperties(t *testing.T) {
                {"uuid", schema.UUIDLogicalType{}, false, true, true},
                {"float16", schema.Float16LogicalType{}, false, true, true},
                {"variant", schema.VariantLogicalType{}, true, true, true},
+               {"geometry", schema.GeometryLogicalType{}, false, true, true},
+               {"geography", schema.GeographyLogicalType{}, false, true, true},
                {"nological", schema.NoLogicalType{}, false, false, true},
                {"unknown", schema.UnknownLogicalType{}, false, false, false},
        }
@@ -277,6 +279,8 @@ func TestLogicalSingleTypeApplicability(t *testing.T) {
                {"int64", schema.NewIntLogicalType(64 /* bitWidth */, true /* 
signed */), parquet.Types.Int64},
                {"json", schema.JSONLogicalType{}, parquet.Types.ByteArray},
                {"bson", schema.BSONLogicalType{}, parquet.Types.ByteArray},
+               {"geometry", schema.GeometryLogicalType{}, 
parquet.Types.ByteArray},
+               {"geography", schema.GeographyLogicalType{}, 
parquet.Types.ByteArray},
        }
 
        for _, tt := range tests {
@@ -459,6 +463,10 @@ func TestLogicalTypeRepresentation(t *testing.T) {
                {"uuid", schema.UUIDLogicalType{}, "UUID", `{"Type": "UUID"}`},
                {"float16", schema.Float16LogicalType{}, "Float16", `{"Type": 
"Float16"}`},
                {"variant", schema.VariantLogicalType{}, "Variant", `{"Type": 
"Variant"}`},
+               {"geometry", schema.GeometryLogicalType{}, "Geometry", 
`{"Type": "Geometry"}`},
+               {"geometry crs", schema.GeometryLogicalType{Crs: "EPSG:4326"}, 
"Geometry(crs=EPSG:4326)", `{"Type": "Geometry", "crs": "EPSG:4326"}`},
+               {"geography", schema.GeographyLogicalType{}, "Geography", 
`{"Type": "Geography"}`},
+               {"geography crs algorithm", schema.GeographyLogicalType{Crs: 
"OGC:CRS84", Algorithm: schema.GeographyEdgeKarney}, "Geography(crs=OGC:CRS84, 
algorithm=karney)", `{"Type": "Geography", "crs": "OGC:CRS84", "algorithm": 
"karney"}`},
                {"none", schema.NoLogicalType{}, "None", `{"Type": "None"}`},
        }
 
@@ -472,6 +480,18 @@ func TestLogicalTypeRepresentation(t *testing.T) {
        }
 }
 
+func TestGeospatialLogicalTypeDefaults(t *testing.T) {
+       geometry := schema.GeometryLogicalType{}
+       assert.False(t, geometry.IsCRSSet())
+       assert.Equal(t, "OGC:CRS84", geometry.CRS())
+
+       geography := schema.GeographyLogicalType{}
+       assert.False(t, geography.IsCRSSet())
+       assert.Equal(t, "OGC:CRS84", geography.CRS())
+       assert.False(t, geography.IsEdgeInterpolationAlgorithmSet())
+       assert.Equal(t, schema.GeographyEdgeSpherical, 
geography.EdgeInterpolationAlgorithm())
+}
+
 func TestLogicalTypeSortOrder(t *testing.T) {
        tests := []struct {
                name    string
@@ -506,6 +526,8 @@ func TestLogicalTypeSortOrder(t *testing.T) {
                {"uuid", schema.UUIDLogicalType{}, schema.SortUNSIGNED},
                {"float16", schema.Float16LogicalType{}, schema.SortSIGNED},
                {"variant", schema.VariantLogicalType{}, schema.SortUNKNOWN},
+               {"geometry", schema.GeometryLogicalType{}, schema.SortUNKNOWN},
+               {"geography", schema.GeographyLogicalType{}, 
schema.SortUNKNOWN},
                {"none", schema.NoLogicalType{}, schema.SortUNKNOWN},
        }
 
diff --git a/parquet/schema/schema_element_test.go 
b/parquet/schema/schema_element_test.go
index a6d1538e..4bb32c7a 100644
--- a/parquet/schema/schema_element_test.go
+++ b/parquet/schema/schema_element_test.go
@@ -163,6 +163,14 @@ func (s *SchemaElementConstructionSuite) TestSimple() {
                        "float16", Float16LogicalType{}, 
parquet.Types.FixedLenByteArray, 2, false, ConvertedTypes.NA, true,
                        func(e *format.SchemaElement) bool { return 
e.LogicalType.IsSetFLOAT16() },
                }, nil},
+               {"geometry", &schemaElementConstructArgs{
+                       "geometry", GeometryLogicalType{}, 
parquet.Types.ByteArray, -1, false, ConvertedTypes.NA, true,
+                       func(e *format.SchemaElement) bool { return 
e.LogicalType.IsSetGEOMETRY() },
+               }, nil},
+               {"geography", &schemaElementConstructArgs{
+                       "geography", GeographyLogicalType{}, 
parquet.Types.ByteArray, -1, false, ConvertedTypes.NA, true,
+                       func(e *format.SchemaElement) bool { return 
e.LogicalType.IsSetGEOGRAPHY() },
+               }, nil},
                {"none", &schemaElementConstructArgs{
                        "none", NoLogicalType{}, parquet.Types.Int64, -1, 
false, ConvertedTypes.NA, false,
                        checkNone,
@@ -493,6 +501,8 @@ func TestLogicalTypeSerializationRoundTrip(t *testing.T) {
                {"bson", BSONLogicalType{}, parquet.Types.ByteArray, -1},
                {"uuid", UUIDLogicalType{}, parquet.Types.FixedLenByteArray, 
16},
                {"float16", Float16LogicalType{}, 
parquet.Types.FixedLenByteArray, 2},
+               {"geometry", GeometryLogicalType{Crs: "EPSG:4326"}, 
parquet.Types.ByteArray, -1},
+               {"geography", GeographyLogicalType{Crs: "OGC:CRS84", Algorithm: 
GeographyEdgeKarney}, parquet.Types.ByteArray, -1},
                {"none", NoLogicalType{}, parquet.Types.Boolean, -1},
        }
 
diff --git a/parquet/variant/basic_type_stringer.go 
b/parquet/variant/basic_type_stringer.go
index e8cf83e4..bf168b51 100644
--- a/parquet/variant/basic_type_stringer.go
+++ b/parquet/variant/basic_type_stringer.go
@@ -20,9 +20,9 @@ const _BasicType_name = 
"UnknownPrimitiveShortStringObjectArray"
 var _BasicType_index = [...]uint8{0, 7, 16, 27, 33, 38}
 
 func (i BasicType) String() string {
-       i -= -1
-       if i < 0 || i >= BasicType(len(_BasicType_index)-1) {
-               return "BasicType(" + strconv.FormatInt(int64(i+-1), 10) + ")"
+       idx := int(i) - -1
+       if i < -1 || idx >= len(_BasicType_index)-1 {
+               return "BasicType(" + strconv.FormatInt(int64(i), 10) + ")"
        }
-       return _BasicType_name[_BasicType_index[i]:_BasicType_index[i+1]]
+       return _BasicType_name[_BasicType_index[idx]:_BasicType_index[idx+1]]
 }
diff --git a/parquet/variant/primitive_type_stringer.go 
b/parquet/variant/primitive_type_stringer.go
index 205724c3..89f94cb8 100644
--- a/parquet/variant/primitive_type_stringer.go
+++ b/parquet/variant/primitive_type_stringer.go
@@ -37,9 +37,9 @@ const _PrimitiveType_name = 
"UnknownNullBoolTrueBoolFalseInt8Int16Int32Int64Doub
 var _PrimitiveType_index = [...]uint8{0, 7, 11, 19, 28, 32, 37, 42, 47, 53, 
62, 71, 81, 85, 102, 122, 127, 133, 139, 154, 170, 189, 193}
 
 func (i PrimitiveType) String() string {
-       i -= -1
-       if i < 0 || i >= PrimitiveType(len(_PrimitiveType_index)-1) {
-               return "PrimitiveType(" + strconv.FormatInt(int64(i+-1), 10) + 
")"
+       idx := int(i) - -1
+       if i < -1 || idx >= len(_PrimitiveType_index)-1 {
+               return "PrimitiveType(" + strconv.FormatInt(int64(i), 10) + ")"
        }
-       return 
_PrimitiveType_name[_PrimitiveType_index[i]:_PrimitiveType_index[i+1]]
+       return 
_PrimitiveType_name[_PrimitiveType_index[idx]:_PrimitiveType_index[idx+1]]
 }
diff --git a/parquet/version_string.go b/parquet/version_string.go
index ab01aa48..c185d9b7 100644
--- a/parquet/version_string.go
+++ b/parquet/version_string.go
@@ -18,8 +18,9 @@ const _Version_name = "v1.0v2.4v2.6"
 var _Version_index = [...]uint8{0, 4, 8, 12}
 
 func (i Version) String() string {
-       if i < 0 || i >= Version(len(_Version_index)-1) {
+       idx := int(i) - 0
+       if i < 0 || idx >= len(_Version_index)-1 {
                return "Version(" + strconv.FormatInt(int64(i), 10) + ")"
        }
-       return _Version_name[_Version_index[i]:_Version_index[i+1]]
+       return _Version_name[_Version_index[idx]:_Version_index[idx+1]]
 }


Reply via email to