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=