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 3a64d23  feat(arrrow/compute/expr): support substrait timestamp and 
decimal properly (#418)
3a64d23 is described below

commit 3a64d23a89b236b3c1f5e7bcaacd43a67632fd9b
Author: Matt Topol <[email protected]>
AuthorDate: Mon Jun 30 12:38:54 2025 -0400

    feat(arrrow/compute/expr): support substrait timestamp and decimal properly 
(#418)
    
    ### Rationale for this change
    Fixes #404
    Fixes #417
    
    ### What changes are included in this PR?
    Upgrades substrait-go to v4 and adds handling and support for
    PrecisionTime and PrecisionTimestamp, fixes substrait Decimal128Type
    handling.
    
    ### Are these changes tested?
    Yes, unit test is added.
    
    ### Are there any user-facing changes?
    only the new features being usable.
    
    Relies on https://github.com/substrait-io/substrait-go/pull/139 getting
    merged before this can get merged
---
 arrow/compute/exprs/builders.go      |   6 +-
 arrow/compute/exprs/builders_test.go |   2 +-
 arrow/compute/exprs/exec.go          |  41 +++++-----
 arrow/compute/exprs/exec_test.go     | 147 ++++++++++++++++++++++++++++++++++-
 arrow/compute/exprs/field_refs.go    |   2 +-
 arrow/compute/exprs/types.go         |  73 ++++++++++++++++-
 arrow/compute/utils.go               |  52 ++++++++++---
 arrow/compute/utils_internal_test.go |  77 ++++++++++++++++++
 go.mod                               |  21 ++---
 go.sum                               |  48 +++++-------
 10 files changed, 389 insertions(+), 80 deletions(-)

diff --git a/arrow/compute/exprs/builders.go b/arrow/compute/exprs/builders.go
index 71be97f..70c0467 100644
--- a/arrow/compute/exprs/builders.go
+++ b/arrow/compute/exprs/builders.go
@@ -27,9 +27,9 @@ import (
 
        "github.com/apache/arrow-go/v18/arrow"
        "github.com/apache/arrow-go/v18/arrow/compute"
-       "github.com/substrait-io/substrait-go/v3/expr"
-       "github.com/substrait-io/substrait-go/v3/extensions"
-       "github.com/substrait-io/substrait-go/v3/types"
+       "github.com/substrait-io/substrait-go/v4/expr"
+       "github.com/substrait-io/substrait-go/v4/extensions"
+       "github.com/substrait-io/substrait-go/v4/types"
 )
 
 // NewDefaultExtensionSet constructs an empty extension set using the default
diff --git a/arrow/compute/exprs/builders_test.go 
b/arrow/compute/exprs/builders_test.go
index 9044c63..34c7334 100644
--- a/arrow/compute/exprs/builders_test.go
+++ b/arrow/compute/exprs/builders_test.go
@@ -25,7 +25,7 @@ import (
        "github.com/apache/arrow-go/v18/arrow/compute/exprs"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
-       "github.com/substrait-io/substrait-go/v3/expr"
+       "github.com/substrait-io/substrait-go/v4/expr"
 )
 
 func TestNewScalarFunc(t *testing.T) {
diff --git a/arrow/compute/exprs/exec.go b/arrow/compute/exprs/exec.go
index 0d0a139..174cb0d 100644
--- a/arrow/compute/exprs/exec.go
+++ b/arrow/compute/exprs/exec.go
@@ -32,9 +32,9 @@ import (
        "github.com/apache/arrow-go/v18/arrow/internal/debug"
        "github.com/apache/arrow-go/v18/arrow/memory"
        "github.com/apache/arrow-go/v18/arrow/scalar"
-       "github.com/substrait-io/substrait-go/v3/expr"
-       "github.com/substrait-io/substrait-go/v3/extensions"
-       "github.com/substrait-io/substrait-go/v3/types"
+       "github.com/substrait-io/substrait-go/v4/expr"
+       "github.com/substrait-io/substrait-go/v4/extensions"
+       "github.com/substrait-io/substrait-go/v4/types"
 )
 
 func makeExecBatch(ctx context.Context, schema *arrow.Schema, partial 
compute.Datum) (out compute.ExecBatch, err error) {
@@ -330,16 +330,17 @@ func literalToDatum(mem memory.Allocator, lit 
expr.Literal, ext ExtensionIDSet)
                s, err := scalar.NewStructScalarWithNames(fields, names)
                return compute.NewDatum(s), err
        case *expr.ProtoLiteral:
-               switch v := v.Value.(type) {
-               case *types.Decimal:
-                       if len(v.Value) != arrow.Decimal128SizeBytes {
+               switch t := v.Type.(type) {
+               case *types.DecimalType:
+                       byts := v.Value.([]byte)
+                       if len(byts) != arrow.Decimal128SizeBytes {
                                return nil, fmt.Errorf("%w: decimal literal had 
%d bytes (expected %d)",
-                                       arrow.ErrInvalid, len(v.Value), 
arrow.Decimal128SizeBytes)
+                                       arrow.ErrInvalid, len(byts), 
arrow.Decimal128SizeBytes)
                        }
 
                        var val decimal128.Num
                        data := 
(*(*[arrow.Decimal128SizeBytes]byte)(unsafe.Pointer(&val)))[:]
-                       copy(data, v.Value)
+                       copy(data, byts)
                        if endian.IsBigEndian {
                                // reverse the bytes
                                for i := len(data)/2 - 1; i >= 0; i-- {
@@ -349,31 +350,35 @@ func literalToDatum(mem memory.Allocator, lit 
expr.Literal, ext ExtensionIDSet)
                        }
 
                        return compute.NewDatum(scalar.NewDecimal128Scalar(val,
-                               &arrow.Decimal128Type{Precision: v.Precision, 
Scale: v.Scale})), nil
-               case *types.UserDefinedLiteral: // not yet implemented
-               case *types.IntervalYearToMonth:
+                               &arrow.Decimal128Type{Precision: t.Precision, 
Scale: t.Scale})), nil
+               case *types.UserDefinedType: // not yet implemented
+               case *types.IntervalYearToMonthType:
                        bldr := array.NewInt32Builder(memory.DefaultAllocator)
                        defer bldr.Release()
+
+                       val := v.Value.(*types.IntervalYearToMonth)
                        typ := intervalYear()
-                       bldr.Append(v.Years)
-                       bldr.Append(v.Months)
+                       bldr.Append(val.Years)
+                       bldr.Append(val.Months)
                        arr := bldr.NewArray()
                        defer arr.Release()
                        return &compute.ScalarDatum{Value: 
scalar.NewExtensionScalar(
                                scalar.NewFixedSizeListScalar(arr), typ)}, nil
-               case *types.IntervalDayToSecond:
+               case *types.IntervalDayType:
                        bldr := array.NewInt32Builder(memory.DefaultAllocator)
                        defer bldr.Release()
+
+                       val := v.Value.(*types.IntervalDayToSecond)
                        typ := intervalDay()
-                       bldr.Append(v.Days)
-                       bldr.Append(v.Seconds)
+                       bldr.Append(val.Days)
+                       bldr.Append(val.Seconds)
                        arr := bldr.NewArray()
                        defer arr.Release()
                        return &compute.ScalarDatum{Value: 
scalar.NewExtensionScalar(
                                scalar.NewFixedSizeListScalar(arr), typ)}, nil
-               case *types.VarChar:
+               case *types.VarCharType:
                        return compute.NewDatum(scalar.NewExtensionScalar(
-                               scalar.NewStringScalar(v.Value), 
varChar(int32(v.Length)))), nil
+                               scalar.NewStringScalar(v.Value.(string)), 
varChar(int32(t.Length)))), nil
                }
        }
 
diff --git a/arrow/compute/exprs/exec_test.go b/arrow/compute/exprs/exec_test.go
index ca314a9..9812643 100644
--- a/arrow/compute/exprs/exec_test.go
+++ b/arrow/compute/exprs/exec_test.go
@@ -27,14 +27,15 @@ import (
        "github.com/apache/arrow-go/v18/arrow/array"
        "github.com/apache/arrow-go/v18/arrow/compute"
        "github.com/apache/arrow-go/v18/arrow/compute/exprs"
+       "github.com/apache/arrow-go/v18/arrow/decimal"
        "github.com/apache/arrow-go/v18/arrow/extensions"
        "github.com/apache/arrow-go/v18/arrow/memory"
        "github.com/apache/arrow-go/v18/arrow/scalar"
        "github.com/google/uuid"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
-       "github.com/substrait-io/substrait-go/v3/expr"
-       "github.com/substrait-io/substrait-go/v3/types"
+       "github.com/substrait-io/substrait-go/v4/expr"
+       "github.com/substrait-io/substrait-go/v4/types"
 )
 
 var (
@@ -478,3 +479,145 @@ func TestGenerateMask(t *testing.T) {
                })
        }
 }
+
+func Test_Types(t *testing.T) {
+       t.Parallel()
+
+       tt := []struct {
+               name   string
+               schema func() *arrow.Schema
+               record func(rq *require.Assertions, schema *arrow.Schema) 
arrow.Record
+               val    func(rq *require.Assertions) expr.Literal
+       }{
+               {
+                       name: "expect arrow.TIME64 (ns) ok",
+                       schema: func() *arrow.Schema {
+                               field := arrow.Field{
+                                       Name:     "col",
+                                       Type:     &arrow.Time64Type{Unit: 
arrow.Nanosecond},
+                                       Nullable: true,
+                               }
+
+                               return arrow.NewSchema([]arrow.Field{field}, 
nil)
+                       },
+                       record: func(rq *require.Assertions, schema 
*arrow.Schema) arrow.Record {
+                               b := 
array.NewTime64Builder(memory.DefaultAllocator, &arrow.Time64Type{Unit: 
arrow.Nanosecond})
+                               defer b.Release()
+
+                               t1, err := 
arrow.Time64FromString("10:00:00.000000", arrow.Nanosecond)
+                               rq.NoError(err, "Failed to create Time64 value")
+
+                               b.AppendValues([]arrow.Time64{t1}, []bool{true})
+
+                               return array.NewRecord(schema, 
[]arrow.Array{b.NewArray()}, 1)
+                       },
+                       val: func(rq *require.Assertions) expr.Literal {
+                               v, err := 
arrow.Time64FromString("11:00:00.000000", arrow.Nanosecond)
+                               rq.NoError(err, "Failed to create Time64 value")
+
+                               return expr.NewPrimitiveLiteral(types.Time(v), 
true)
+                       },
+               },
+               {
+                       name: "expect arrow.TIMESTAMP (ns) ok",
+                       schema: func() *arrow.Schema {
+                               field := arrow.Field{
+                                       Name:     "col",
+                                       Type:     &arrow.TimestampType{Unit: 
arrow.Nanosecond},
+                                       Nullable: true,
+                               }
+
+                               return arrow.NewSchema([]arrow.Field{field}, 
nil)
+                       },
+                       record: func(rq *require.Assertions, schema 
*arrow.Schema) arrow.Record {
+                               b := 
array.NewTimestampBuilder(memory.DefaultAllocator, &arrow.TimestampType{Unit: 
arrow.Nanosecond})
+                               defer b.Release()
+
+                               t1, err := 
arrow.TimestampFromString("2021-01-01T10:00:00.000000Z", arrow.Nanosecond)
+                               rq.NoError(err, "Failed to create Timestamp 
value")
+
+                               b.AppendValues([]arrow.Timestamp{t1}, 
[]bool{true})
+
+                               return array.NewRecord(schema, 
[]arrow.Array{b.NewArray()}, 1)
+                       },
+                       val: func(rq *require.Assertions) expr.Literal {
+                               v, err := 
arrow.TimestampFromString("2021-01-01T11:00:00.000000Z", arrow.Microsecond)
+                               rq.NoError(err, "Failed to create Timestamp 
value")
+
+                               return 
expr.NewPrimitiveLiteral(types.Timestamp(v), true)
+                       },
+               },
+               {
+                       name: "expect arrow.DECIMAL128 ok",
+                       schema: func() *arrow.Schema {
+                               field := arrow.Field{
+                                       Name:     "col",
+                                       Type:     
&arrow.Decimal128Type{Precision: 38, Scale: 10},
+                                       Nullable: true,
+                               }
+
+                               return arrow.NewSchema([]arrow.Field{field}, 
nil)
+                       },
+                       record: func(rq *require.Assertions, schema 
*arrow.Schema) arrow.Record {
+                               b := 
array.NewDecimal128Builder(memory.DefaultAllocator, 
&arrow.Decimal128Type{Precision: 38, Scale: 10})
+                               defer b.Release()
+
+                               d, err := 
decimal.Decimal128FromFloat(123.456789, 38, 10)
+                               rq.NoError(err, "Failed to create Decimal128 
value")
+
+                               b.Append(d)
+
+                               return array.NewRecord(schema, 
[]arrow.Array{b.NewArray()}, 1)
+                       },
+                       val: func(rq *require.Assertions) expr.Literal {
+                               v, p, s, err := 
expr.DecimalStringToBytes("456.7890123456")
+                               rq.NoError(err, "Failed to convert decimal 
string to bytes")
+
+                               lit, err := expr.NewLiteral(&types.Decimal{
+                                       Value:     v[:16],
+                                       Precision: p,
+                                       Scale:     s,
+                               }, true)
+                               rq.NoError(err, "Failed to create Decimal128 
literal")
+
+                               return lit
+                       },
+               },
+       }
+
+       for _, tc := range tt {
+               tc := tc
+               t.Run(tc.name, func(t *testing.T) {
+                       t.Parallel()
+
+                       ctx := context.Background()
+                       rq := require.New(t)
+                       schema := tc.schema()
+                       record := tc.record(rq, schema)
+
+                       extSet := exprs.GetExtensionIDSet(ctx)
+                       builder := exprs.NewExprBuilder(extSet)
+
+                       err := builder.SetInputSchema(schema)
+                       rq.NoError(err, "Failed to set input schema")
+
+                       b, err := builder.CallScalar("less", nil,
+                               builder.FieldRef("col"),
+                               builder.Literal(tc.val(rq)),
+                       )
+
+                       rq.NoError(err, "Failed to call scalar")
+
+                       e, err := b.BuildExpr()
+                       rq.NoError(err, "Failed to build expression")
+
+                       ctx = exprs.WithExtensionIDSet(ctx, extSet)
+
+                       dr := compute.NewDatum(record)
+                       defer dr.Release()
+
+                       _, err = exprs.ExecuteScalarExpression(ctx, schema, e, 
dr)
+                       rq.NoError(err, "Failed to execute scalar expression")
+               })
+       }
+}
diff --git a/arrow/compute/exprs/field_refs.go 
b/arrow/compute/exprs/field_refs.go
index 53ba952..e988fe3 100644
--- a/arrow/compute/exprs/field_refs.go
+++ b/arrow/compute/exprs/field_refs.go
@@ -26,7 +26,7 @@ import (
        "github.com/apache/arrow-go/v18/arrow/compute"
        "github.com/apache/arrow-go/v18/arrow/memory"
        "github.com/apache/arrow-go/v18/arrow/scalar"
-       "github.com/substrait-io/substrait-go/v3/expr"
+       "github.com/substrait-io/substrait-go/v4/expr"
 )
 
 func getFields(typ arrow.DataType) []arrow.Field {
diff --git a/arrow/compute/exprs/types.go b/arrow/compute/exprs/types.go
index 314df6f..ad6e4e1 100644
--- a/arrow/compute/exprs/types.go
+++ b/arrow/compute/exprs/types.go
@@ -27,9 +27,9 @@ import (
        "github.com/apache/arrow-go/v18/arrow"
        "github.com/apache/arrow-go/v18/arrow/compute"
        "github.com/apache/arrow-go/v18/arrow/scalar"
-       "github.com/substrait-io/substrait-go/v3/expr"
-       "github.com/substrait-io/substrait-go/v3/extensions"
-       "github.com/substrait-io/substrait-go/v3/types"
+       "github.com/substrait-io/substrait-go/v4/expr"
+       "github.com/substrait-io/substrait-go/v4/extensions"
+       "github.com/substrait-io/substrait-go/v4/types"
 )
 
 const (
@@ -540,6 +540,10 @@ func FieldsFromSubstrait(typeList []types.Type, nextName 
func() string, ext Exte
        return
 }
 
+func substraitToArrowTimeUnit(in types.TimePrecision) arrow.TimeUnit {
+       return arrow.TimeUnit(in / 3)
+}
+
 // ToSubstraitType converts an arrow data type to a Substrait Type. Since
 // arrow types don't have a nullable flag (it is in the arrow.Field) but
 // Substrait types do, the nullability must be passed in here.
@@ -691,6 +695,32 @@ func ToSubstraitType(dt arrow.DataType, nullable bool, ext 
ExtensionIDSet) (type
                        Key:         keyType,
                        Value:       valueType,
                }, nil
+       case arrow.TIME32:
+               unit := dt.(*arrow.Time32Type).Unit
+               return &types.PrecisionTimeType{
+                       Nullability: nullability,
+                       Precision:   types.TimePrecision(unit * 3),
+               }, nil
+       case arrow.TIME64:
+               unit := dt.(*arrow.Time64Type).Unit
+               return &types.PrecisionTimeType{
+                       Nullability: nullability,
+                       Precision:   types.TimePrecision(unit * 3),
+               }, nil
+       case arrow.TIMESTAMP:
+               dt := dt.(*arrow.TimestampType)
+               if dt.TimeZone != "" {
+                       return &types.PrecisionTimestampTzType{
+                               PrecisionTimestampType: 
types.PrecisionTimestampType{
+                                       Nullability: nullability,
+                                       Precision:   
types.TimePrecision(dt.Unit * 3),
+                               },
+                       }, nil
+               }
+               return &types.PrecisionTimestampType{
+                       Nullability: nullability,
+                       Precision:   types.TimePrecision(dt.Unit * 3),
+               }, nil
        }
 
        return nil, arrow.ErrNotImplemented
@@ -729,6 +759,43 @@ func FromSubstraitType(t types.Type, ext ExtensionIDSet) 
(arrow.DataType, bool,
                return arrow.BinaryTypes.String, nullable, nil
        case *types.BinaryType:
                return arrow.BinaryTypes.Binary, nullable, nil
+       case *types.PrecisionTimeType:
+               switch t.Precision {
+               case types.PrecisionSeconds:
+                       return &arrow.Time32Type{Unit: arrow.Second}, nullable, 
nil
+               case types.PrecisionMilliSeconds:
+                       return &arrow.Time32Type{Unit: arrow.Millisecond}, 
nullable, nil
+               case types.PrecisionMicroSeconds:
+                       return &arrow.Time64Type{Unit: arrow.Microsecond}, 
nullable, nil
+               case types.PrecisionNanoSeconds:
+                       return &arrow.Time64Type{Unit: arrow.Nanosecond}, 
nullable, nil
+               }
+       case *types.PrecisionTimestampType:
+               switch t.Precision {
+               case types.PrecisionSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Second}, 
nullable, nil
+               case types.PrecisionMilliSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Millisecond}, 
nullable, nil
+               case types.PrecisionMicroSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Microsecond}, 
nullable, nil
+               case types.PrecisionNanoSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Nanosecond}, 
nullable, nil
+               }
+       case *types.PrecisionTimestampTzType:
+               switch t.Precision {
+               case types.PrecisionSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Second, 
TimeZone: TimestampTzTimezone},
+                               nullable, nil
+               case types.PrecisionMilliSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Millisecond, 
TimeZone: TimestampTzTimezone},
+                               nullable, nil
+               case types.PrecisionMicroSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Microsecond, 
TimeZone: TimestampTzTimezone},
+                               nullable, nil
+               case types.PrecisionNanoSeconds:
+                       return &arrow.TimestampType{Unit: arrow.Nanosecond, 
TimeZone: TimestampTzTimezone},
+                               nullable, nil
+               }
        case *types.TimestampType:
                return &arrow.TimestampType{Unit: arrow.Microsecond}, nullable, 
nil
        case *types.TimestampTzType:
diff --git a/arrow/compute/utils.go b/arrow/compute/utils.go
index 7e4df8d..7eb3070 100644
--- a/arrow/compute/utils.go
+++ b/arrow/compute/utils.go
@@ -136,10 +136,10 @@ func commonTemporalResolution(vals ...arrow.DataType) 
(arrow.TimeUnit, bool) {
                        isTimeUnit = true
                        continue
                case *arrow.Date64Type:
-                       finestUnit = exec.Max(finestUnit, arrow.Millisecond)
+                       finestUnit = max(finestUnit, arrow.Millisecond)
                        isTimeUnit = true
                case arrow.TemporalWithUnit:
-                       finestUnit = exec.Max(finestUnit, dt.TimeUnit())
+                       finestUnit = max(finestUnit, dt.TimeUnit())
                        isTimeUnit = true
                default:
                        continue
@@ -332,6 +332,7 @@ func commonTemporal(vals ...arrow.DataType) arrow.DataType {
                zone                 *string
                loc                  *time.Location
                sawDate32, sawDate64 bool
+               sawDuration, sawTime bool
        )
 
        for _, ty := range vals {
@@ -340,7 +341,7 @@ func commonTemporal(vals ...arrow.DataType) arrow.DataType {
                        // date32's unit is days, but the coarsest we have is 
seconds
                        sawDate32 = true
                case arrow.DATE64:
-                       finestUnit = exec.Max(finestUnit, arrow.Millisecond)
+                       finestUnit = max(finestUnit, arrow.Millisecond)
                        sawDate64 = true
                case arrow.TIMESTAMP:
                        ts := ty.(*arrow.TimestampType)
@@ -352,20 +353,47 @@ func commonTemporal(vals ...arrow.DataType) 
arrow.DataType {
                                loc = tz
                        }
                        zone = &ts.TimeZone
-                       finestUnit = exec.Max(finestUnit, ts.Unit)
+                       finestUnit = max(finestUnit, ts.Unit)
+               case arrow.TIME32, arrow.TIME64:
+                       ts := ty.(arrow.TemporalWithUnit)
+                       finestUnit = max(finestUnit, ts.TimeUnit())
+                       sawTime = true
+               case arrow.DURATION:
+                       ts := ty.(*arrow.DurationType)
+                       finestUnit = max(finestUnit, ts.Unit)
+                       sawDuration = true
                default:
                        return nil
                }
        }
 
-       switch {
-       case zone != nil:
-               // at least one timestamp seen
-               return &arrow.TimestampType{Unit: finestUnit, TimeZone: *zone}
-       case sawDate64:
-               return arrow.FixedWidthTypes.Date64
-       case sawDate32:
-               return arrow.FixedWidthTypes.Date32
+       sawTimestampOrDate := zone != nil || sawDate32 || sawDate64
+
+       if sawTimestampOrDate && (sawTime || sawDuration) {
+               // no common type possible
+               return nil
+       }
+
+       if sawTimestampOrDate {
+               switch {
+               case zone != nil:
+                       // at least one timestamp seen
+                       return &arrow.TimestampType{Unit: finestUnit, TimeZone: 
*zone}
+               case sawDate64:
+                       return arrow.FixedWidthTypes.Date64
+               case sawDate32:
+                       return arrow.FixedWidthTypes.Date32
+               }
+       } else if sawTime {
+               switch finestUnit {
+               case arrow.Second, arrow.Millisecond:
+                       return &arrow.Time32Type{Unit: finestUnit}
+               case arrow.Microsecond, arrow.Nanosecond:
+                       return &arrow.Time64Type{Unit: finestUnit}
+               }
+       } else if sawDuration {
+               // we can only get here if we ONLY saw durations
+               return &arrow.DurationType{Unit: finestUnit}
        }
        return nil
 }
diff --git a/arrow/compute/utils_internal_test.go 
b/arrow/compute/utils_internal_test.go
new file mode 100644
index 0000000..d339dc3
--- /dev/null
+++ b/arrow/compute/utils_internal_test.go
@@ -0,0 +1,77 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package compute
+
+import (
+       "testing"
+
+       "github.com/apache/arrow-go/v18/arrow"
+       "github.com/stretchr/testify/assert"
+)
+
+func TestCommonTemporal(t *testing.T) {
+       tests := []struct {
+               name     string
+               dtList   []arrow.DataType
+               expected arrow.DataType
+       }{
+               {"no input", []arrow.DataType{}, nil},
+               {"finest unit time32", []arrow.DataType{
+                       arrow.FixedWidthTypes.Time32ms,
+                       arrow.FixedWidthTypes.Time32s,
+               }, arrow.FixedWidthTypes.Time32ms},
+               {"time32 -> time64", []arrow.DataType{
+                       arrow.FixedWidthTypes.Time32s,
+                       arrow.FixedWidthTypes.Time64us,
+                       arrow.FixedWidthTypes.Time32ms,
+               }, arrow.FixedWidthTypes.Time64us},
+               {"timestamp units", []arrow.DataType{
+                       arrow.FixedWidthTypes.Date32,
+                       arrow.FixedWidthTypes.Timestamp_ms,
+               }, arrow.FixedWidthTypes.Timestamp_ms},
+               {"duration units", []arrow.DataType{
+                       arrow.FixedWidthTypes.Duration_s,
+                       arrow.FixedWidthTypes.Duration_ns,
+                       arrow.FixedWidthTypes.Duration_us,
+               }, arrow.FixedWidthTypes.Duration_ns},
+               {"date32 only", []arrow.DataType{
+                       arrow.FixedWidthTypes.Date32,
+                       arrow.FixedWidthTypes.Date32,
+               }, arrow.FixedWidthTypes.Date32},
+               {"date64", []arrow.DataType{
+                       arrow.FixedWidthTypes.Date32,
+                       arrow.FixedWidthTypes.Date64,
+               }, arrow.FixedWidthTypes.Date64},
+               {"ts, date, time", []arrow.DataType{
+                       arrow.FixedWidthTypes.Timestamp_s,
+                       arrow.FixedWidthTypes.Date32,
+                       arrow.FixedWidthTypes.Time64ns,
+               }, nil},
+               {"date64, duration", []arrow.DataType{
+                       arrow.FixedWidthTypes.Date64,
+                       arrow.FixedWidthTypes.Duration_ms,
+               }, nil},
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       actual := commonTemporal(tt.dtList...)
+                       assert.Truef(t, arrow.TypeEqual(tt.expected, actual),
+                               "got: %s, expected: %s", actual, tt.expected)
+               })
+       }
+}
diff --git a/go.mod b/go.mod
index fd04972..f2045bc 100644
--- a/go.mod
+++ b/go.mod
@@ -39,10 +39,10 @@ require (
        github.com/pterm/pterm v0.12.81
        github.com/stoewer/go-strcase v1.3.1
        github.com/stretchr/testify v1.10.0
-       github.com/substrait-io/substrait-go/v3 v3.9.1
+       github.com/substrait-io/substrait-go/v4 v4.1.0
        github.com/tidwall/sjson v1.2.5
        github.com/zeebo/xxh3 v1.0.2
-       golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
+       golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
        golang.org/x/sync v0.15.0
        golang.org/x/sys v0.33.0
        golang.org/x/tools v0.34.0
@@ -57,32 +57,31 @@ require (
        atomicgo.dev/cursor v0.2.0 // indirect
        atomicgo.dev/keyboard v0.2.9 // indirect
        atomicgo.dev/schedule v0.1.0 // indirect
-       cloud.google.com/go v0.118.0 // indirect
+       cloud.google.com/go v0.121.0 // indirect
        github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
        github.com/cockroachdb/apd/v3 v3.2.1 // indirect
        github.com/containerd/console v1.0.5 // indirect
        github.com/creasty/defaults v1.8.0 // indirect
-       github.com/davecgh/go-spew v1.1.1 // indirect
+       github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // 
indirect
        github.com/dustin/go-humanize v1.0.1 // indirect
-       github.com/fatih/color v1.15.0 // indirect
        github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
-       github.com/goccy/go-yaml v1.11.0 // indirect
+       github.com/goccy/go-yaml v1.17.1 // indirect
        github.com/gookit/color v1.5.4 // indirect
        github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
        github.com/kr/text v0.2.0 // indirect
        github.com/lithammer/fuzzysearch v1.1.8 // indirect
-       github.com/mattn/go-colorable v0.1.13 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
        github.com/mattn/go-runewidth v0.0.16 // indirect
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 
indirect
        github.com/modern-go/reflect2 v1.0.2 // indirect
        github.com/ncruces/go-strftime v0.1.9 // indirect
-       github.com/pmezard/go-difflib v1.0.0 // indirect
+       github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 
indirect
        github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 
indirect
        github.com/rivo/uniseg v0.4.7 // indirect
        github.com/stretchr/objx v0.5.2 // indirect
-       github.com/substrait-io/substrait v0.66.1-0.20250205013839-a30b3e2d7ec6 
// indirect
+       github.com/substrait-io/substrait v0.69.0 // indirect
+       github.com/substrait-io/substrait-protobuf/go v0.71.0 // indirect
        github.com/tidwall/gjson v1.14.2 // indirect
        github.com/tidwall/match v1.1.1 // indirect
        github.com/tidwall/pretty v1.2.0 // indirect
@@ -91,7 +90,7 @@ require (
        golang.org/x/net v0.41.0 // indirect
        golang.org/x/term v0.32.0 // indirect
        golang.org/x/text v0.26.0 // indirect
-       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250324211829-b45e905df463 // indirect
+       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250425173222-7b384671a197 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
        modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
        modernc.org/libc v1.41.0 // indirect
@@ -100,3 +99,5 @@ require (
        modernc.org/strutil v1.2.0 // indirect
        modernc.org/token v1.1.0 // indirect
 )
+
+replace github.com/substrait-io/substrait-go/v4 => 
github.com/zeroshade/substrait-go/v4 v4.0.0-20250619204834-2c8e786d8b5e
diff --git a/go.sum b/go.sum
index 5f7b4cc..5ad85c7 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,8 @@ atomicgo.dev/keyboard v0.2.9 
h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
 atomicgo.dev/keyboard v0.2.9/go.mod 
h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
 atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
 atomicgo.dev/schedule v0.1.0/go.mod 
h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
-cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ=
-cloud.google.com/go v0.118.0/go.mod 
h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM=
+cloud.google.com/go v0.121.0 h1:pgfwva8nGw7vivjZiRfrmglGWiCJBP+0OmDpenG/Fwg=
+cloud.google.com/go v0.121.0/go.mod 
h1:rS7Kytwheu/y9buoDmu5EIpMMCI4Mb8ND4aeN4Vwj7Q=
 github.com/MarvinJWendt/testza v0.1.0/go.mod 
h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
 github.com/MarvinJWendt/testza v0.2.1/go.mod 
h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
 github.com/MarvinJWendt/testza v0.2.8/go.mod 
h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
@@ -35,30 +35,23 @@ github.com/creack/pty v1.1.9/go.mod 
h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
 github.com/creasty/defaults v1.8.0 
h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
 github.com/creasty/defaults v1.8.0/go.mod 
h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
 github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc 
h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 
h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod 
h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v1.0.1 
h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod 
h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
-github.com/fatih/color v1.15.0/go.mod 
h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
 github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
 github.com/go-logr/logr v1.4.2/go.mod 
h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod 
h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-playground/locales v0.13.0 
h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
-github.com/go-playground/locales v0.13.0/go.mod 
h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
-github.com/go-playground/universal-translator v0.17.0 
h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
-github.com/go-playground/universal-translator v0.17.0/go.mod 
h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
-github.com/go-playground/validator/v10 v10.11.1 
h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
-github.com/go-playground/validator/v10 v10.11.1/go.mod 
h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
 github.com/go-viper/mapstructure/v2 v2.3.0 
h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
 github.com/go-viper/mapstructure/v2 v2.3.0/go.mod 
h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
 github.com/goccy/go-json v0.10.5 
h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
 github.com/goccy/go-json v0.10.5/go.mod 
h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/goccy/go-yaml v1.11.0 
h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
-github.com/goccy/go-yaml v1.11.0/go.mod 
h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
+github.com/goccy/go-yaml v1.17.1 
h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
+github.com/goccy/go-yaml v1.17.1/go.mod 
h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
 github.com/golang/protobuf v1.5.4 
h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod 
h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
@@ -98,15 +91,10 @@ github.com/kr/pty v1.1.1/go.mod 
h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod 
h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod 
h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/leodido/go-urn v1.2.0 
h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
-github.com/leodido/go-urn v1.2.0/go.mod 
h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
 github.com/lib/pq v1.10.9/go.mod 
h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lithammer/fuzzysearch v1.1.8 
h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
 github.com/lithammer/fuzzysearch v1.1.8/go.mod 
h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
-github.com/mattn/go-colorable v0.1.13 
h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod 
h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod 
h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 
h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod 
h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.13/go.mod 
h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -127,8 +115,9 @@ github.com/ncruces/go-strftime v0.1.9 
h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh
 github.com/ncruces/go-strftime v0.1.9/go.mod 
h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
 github.com/pierrec/lz4/v4 v4.1.22 
h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
 github.com/pierrec/lz4/v4 v4.1.22/go.mod 
h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 
h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pterm/pterm v0.12.27/go.mod 
h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
 github.com/pterm/pterm v0.12.29/go.mod 
h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
 github.com/pterm/pterm v0.12.30/go.mod 
h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
@@ -163,10 +152,10 @@ github.com/stretchr/testify v1.8.0/go.mod 
h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.1/go.mod 
h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/substrait-io/substrait v0.66.1-0.20250205013839-a30b3e2d7ec6 
h1:XqtxwYFCjS4L0o1QD4ipGHCuFG94U0f6BeldbilGQjU=
-github.com/substrait-io/substrait v0.66.1-0.20250205013839-a30b3e2d7ec6/go.mod 
h1:MPFNw6sToJgpD5Z2rj0rQrdP/Oq8HG7Z2t3CAEHtkHw=
-github.com/substrait-io/substrait-go/v3 v3.9.1 
h1:2yfHDHpK6KMcvLd0bJVzUJoeXO+K98yS+ciBruxD9po=
-github.com/substrait-io/substrait-go/v3 v3.9.1/go.mod 
h1:VG7jCqtUm28bSngHwq86FywtU74knJ25LNX63SZ53+E=
+github.com/substrait-io/substrait v0.69.0 
h1:qfwUe1qKa3PsCclMpubQOF6nqIqS14geUuvzJ1P7gsM=
+github.com/substrait-io/substrait v0.69.0/go.mod 
h1:MPFNw6sToJgpD5Z2rj0rQrdP/Oq8HG7Z2t3CAEHtkHw=
+github.com/substrait-io/substrait-protobuf/go v0.71.0 
h1:vkYGEEPJ8lWSwaJvX7Y+hEmwmrz5/qeDmGI43JpKJZE=
+github.com/substrait-io/substrait-protobuf/go v0.71.0/go.mod 
h1:hn+Szm1NmZZc91FwWK9EXD/lmuGBSRTJ5IvHhlG1YnQ=
 github.com/tidwall/gjson v1.14.2 
h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
 github.com/tidwall/gjson v1.14.2/go.mod 
h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -185,6 +174,8 @@ github.com/zeebo/assert v1.3.0 
h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
 github.com/zeebo/assert v1.3.0/go.mod 
h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
 github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
 github.com/zeebo/xxh3 v1.0.2/go.mod 
h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
+github.com/zeroshade/substrait-go/v4 v4.0.0-20250619204834-2c8e786d8b5e 
h1:wqJhcVocrp5y2RSHfjB1mcvy3Hk8/D12TzYdfZv3EqE=
+github.com/zeroshade/substrait-go/v4 v4.0.0-20250619204834-2c8e786d8b5e/go.mod 
h1:GzpaFqO5VRtMkEjATgRxGK5p82OmEtCmszAVYxE+iWc=
 go.opentelemetry.io/auto/sdk v1.1.0 
h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod 
h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/otel v1.35.0 
h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
@@ -199,10 +190,8 @@ go.opentelemetry.io/otel/trace v1.35.0 
h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt
 go.opentelemetry.io/otel/trace v1.35.0/go.mod 
h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
-golang.org/x/crypto v0.39.0/go.mod 
h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 
h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod 
h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
+golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 
h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
+golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod 
h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod 
h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
@@ -227,7 +216,6 @@ golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod 
h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -258,8 +246,8 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da 
h1:noIWHXmPHxILtqtCOPIhS
 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod 
h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
 gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
 gonum.org/v1/gonum v0.16.0/go.mod 
h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 
h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
-google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250324211829-b45e905df463/go.mod 
h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 
h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY=
+google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250425173222-7b384671a197/go.mod 
h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
 google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
 google.golang.org/grpc v1.73.0/go.mod 
h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
 google.golang.org/protobuf v1.36.6 
h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=


Reply via email to