From cf9499460140d6edc2bcb0edc0d2efa7f77bd172 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 25 Jul 2026 01:28:21 +0200 Subject: [PATCH 1/3] fix(arrow/scalar): validate null extension scalars --- arrow/scalar/scalar.go | 12 +++++++++--- arrow/scalar/scalar_test.go | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/arrow/scalar/scalar.go b/arrow/scalar/scalar.go index ccbe02193..f4fcbab70 100644 --- a/arrow/scalar/scalar.go +++ b/arrow/scalar/scalar.go @@ -421,10 +421,16 @@ func (e *Extension) Validate() (err error) { } if !e.Valid { - if e.Value != nil { - err = fmt.Errorf("null %s scalar has storage value", e.Type) + if e.Value == nil { + return nil } - return + if e.Value.IsValid() { + return fmt.Errorf("null %s scalar has non-null storage value", e.Type) + } + if err = e.Value.Validate(); err != nil { + return fmt.Errorf("%s scalar fails validation for storage value: %w", e.Type, err) + } + return nil } switch { diff --git a/arrow/scalar/scalar_test.go b/arrow/scalar/scalar_test.go index 749767c78..0d7ba58bd 100644 --- a/arrow/scalar/scalar_test.go +++ b/arrow/scalar/scalar_test.go @@ -33,6 +33,7 @@ import ( "github.com/apache/arrow-go/v18/arrow/decimal256" "github.com/apache/arrow-go/v18/arrow/memory" "github.com/apache/arrow-go/v18/arrow/scalar" + "github.com/apache/arrow-go/v18/internal/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -92,6 +93,10 @@ func checkMakeNullScalar(t *testing.T, dt arrow.DataType) scalar.Scalar { return s } +func TestMakeNullExtensionScalar(t *testing.T) { + checkMakeNullScalar(t, types.NewSmallintType()) +} + func TestMakeScalarUint(t *testing.T) { three := scalar.MakeScalar(uint(3)) assert.NoError(t, three.ValidateFull()) From 8da9d8c7f9327d62130fb3f4dedd8b9a92e68f15 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 25 Jul 2026 16:59:19 +0200 Subject: [PATCH 2/3] test(arrow/scalar): assert null extension storage --- arrow/scalar/scalar_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arrow/scalar/scalar_test.go b/arrow/scalar/scalar_test.go index 0d7ba58bd..dee693e7b 100644 --- a/arrow/scalar/scalar_test.go +++ b/arrow/scalar/scalar_test.go @@ -94,7 +94,12 @@ func checkMakeNullScalar(t *testing.T, dt arrow.DataType) scalar.Scalar { } func TestMakeNullExtensionScalar(t *testing.T) { - checkMakeNullScalar(t, types.NewSmallintType()) + dt := types.NewSmallintType() + sc := checkMakeNullScalar(t, dt) + ext := sc.(*scalar.Extension) + require.NotNil(t, ext.Value) + assert.False(t, ext.Value.IsValid()) + assert.True(t, arrow.TypeEqual(dt.StorageType(), ext.Value.DataType())) } func TestMakeScalarUint(t *testing.T) { From 267460b67ce851e01c3ce602e149b78c9ccb1dcd Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sat, 25 Jul 2026 17:03:39 +0200 Subject: [PATCH 3/3] test(arrow/scalar): reject non-null storage in null extensions --- arrow/scalar/scalar_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arrow/scalar/scalar_test.go b/arrow/scalar/scalar_test.go index dee693e7b..49ef342f2 100644 --- a/arrow/scalar/scalar_test.go +++ b/arrow/scalar/scalar_test.go @@ -102,6 +102,14 @@ func TestMakeNullExtensionScalar(t *testing.T) { assert.True(t, arrow.TypeEqual(dt.StorageType(), ext.Value.DataType())) } +func TestNullExtensionScalarValidateRejectsNonNullStorage(t *testing.T) { + sc := scalar.NewExtensionScalar(scalar.NewInt16Scalar(1), types.NewSmallintType()) + sc.Valid = false + + assert.ErrorContains(t, sc.Validate(), "non-null storage value") + assert.ErrorContains(t, sc.ValidateFull(), "non-null storage value") +} + func TestMakeScalarUint(t *testing.T) { three := scalar.MakeScalar(uint(3)) assert.NoError(t, three.ValidateFull())