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 0151136a fix(parquet/pqarrow): reject timestamp coercion overflow 
(#1008)
0151136a is described below

commit 0151136ae8ae1b879b639459b4c862ac3cb19e08
Author: Minh Vu <[email protected]>
AuthorDate: Mon Jul 27 19:08:21 2026 +0200

    fix(parquet/pqarrow): reject timestamp coercion overflow (#1008)
    
    ### Rationale for this change
    
    Timestamp coercion to a finer unit multiplies int64 values without
    checking whether the result fits. Values near the int64 limits can
    therefore wrap and be written as a different timestamp.
    
    ### What changes are included in this PR?
    
    - Check the int64 range before multiplying a valid timestamp.
    - Return `arrow.ErrInvalid` when coercion would overflow.
    - Ignore the stored value in null slots, matching the existing
    truncation checks.
    
    ### Are these changes tested?
    
    Yes. Focused tests cover positive and negative overflow as well as an
    overflowing value in a null slot.
---
 parquet/pqarrow/encode_arrow.go                    |  6 +++
 .../encode_arrow_timestamp_coercion_test.go        | 56 ++++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/parquet/pqarrow/encode_arrow.go b/parquet/pqarrow/encode_arrow.go
index 30e079f3..9ec11ff8 100644
--- a/parquet/pqarrow/encode_arrow.go
+++ b/parquet/pqarrow/encode_arrow.go
@@ -674,6 +674,12 @@ func writeCoerceTimestamps(arr *array.Timestamp, props 
*ArrowWriterProperties, o
        vals := arr.TimestampValues()
        multiply := func(factor int64) error {
                for idx, val := range vals {
+                       if !arr.IsValid(idx) {
+                               continue
+                       }
+                       if int64(val) > math.MaxInt64/factor || int64(val) < 
math.MinInt64/factor {
+                               return fmt.Errorf("%w: casting timestamp from 
%s to %s would overflow", arrow.ErrInvalid, source, target)
+                       }
                        out[idx] = int64(val) * factor
                }
                return nil
diff --git a/parquet/pqarrow/encode_arrow_timestamp_coercion_test.go 
b/parquet/pqarrow/encode_arrow_timestamp_coercion_test.go
new file mode 100644
index 00000000..676597db
--- /dev/null
+++ b/parquet/pqarrow/encode_arrow_timestamp_coercion_test.go
@@ -0,0 +1,56 @@
+// 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 pqarrow
+
+import (
+       "errors"
+       "math"
+       "testing"
+
+       "github.com/apache/arrow-go/v18/arrow"
+       "github.com/apache/arrow-go/v18/arrow/array"
+       "github.com/apache/arrow-go/v18/arrow/memory"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestWriteCoerceTimestampsOverflow(t *testing.T) {
+       b := array.NewTimestampBuilder(memory.DefaultAllocator, 
&arrow.TimestampType{Unit: arrow.Second})
+       defer b.Release()
+       b.AppendValues([]arrow.Timestamp{arrow.Timestamp(math.MaxInt64/1000 + 
1), arrow.Timestamp(math.MinInt64/1000 - 1)}, nil)
+       arr := b.NewTimestampArray()
+       defer arr.Release()
+
+       out := make([]int64, arr.Len())
+       props := 
NewArrowWriterProperties(WithCoerceTimestamps(arrow.Millisecond))
+       err := writeCoerceTimestamps(arr, &props, out)
+       require.Error(t, err)
+       assert.True(t, errors.Is(err, arrow.ErrInvalid))
+}
+
+func TestWriteCoerceTimestampsIgnoresNullOverflow(t *testing.T) {
+       b := array.NewTimestampBuilder(memory.DefaultAllocator, 
&arrow.TimestampType{Unit: arrow.Second})
+       defer b.Release()
+       b.AppendValues([]arrow.Timestamp{1, arrow.Timestamp(math.MaxInt64)}, 
[]bool{true, false})
+       arr := b.NewTimestampArray()
+       defer arr.Release()
+
+       out := make([]int64, arr.Len())
+       props := 
NewArrowWriterProperties(WithCoerceTimestamps(arrow.Millisecond))
+       require.NoError(t, writeCoerceTimestamps(arr, &props, out))
+       assert.Equal(t, int64(1000), out[0])
+}

Reply via email to