diff --git a/python/pyarrow/table.pxi b/python/pyarrow/table.pxi index 1abe4235c41..c26e5eef9bf 100644 --- a/python/pyarrow/table.pxi +++ b/python/pyarrow/table.pxi @@ -2896,6 +2896,8 @@ cdef class RecordBatch(_Tabular): if isinstance(field_, Field): c_field = field_ + if c_arr.type == null() and c_field.type != null(): + c_arr = c_arr.cast(c_field.type) else: c_field = field(field_, c_arr.type) @@ -2994,6 +2996,8 @@ cdef class RecordBatch(_Tabular): if isinstance(field_, Field): c_field = field_ + if c_arr.type == null() and c_field.type != null(): + c_arr = c_arr.cast(c_field.type) else: c_field = field(field_, c_arr.type) @@ -5430,6 +5434,8 @@ cdef class Table(_Tabular): if isinstance(field_, Field): c_field = field_ + if c_arr.type == null() and c_field.type != null(): + c_arr = c_arr.cast(c_field.type) else: c_field = field(field_, c_arr.type) @@ -5519,6 +5525,8 @@ cdef class Table(_Tabular): if isinstance(field_, Field): c_field = field_ + if c_arr.type == null() and c_field.type != null(): + c_arr = c_arr.cast(c_field.type) else: c_field = field(field_, c_arr.type) diff --git a/python/pyarrow/tests/test_table.py b/python/pyarrow/tests/test_table.py index cb010f4387b..e9c28f17d17 100644 --- a/python/pyarrow/tests/test_table.py +++ b/python/pyarrow/tests/test_table.py @@ -1680,6 +1680,57 @@ def test_table_add_column(cls): assert t4.equals(expected) +@pytest.mark.parametrize( + ('cls'), + [ + (pa.Table), + (pa.RecordBatch) + ] +) +@pytest.mark.parametrize('method', ['add_column', 'set_column']) +def test_table_add_column_all_null_data_typed_field(cls, method): + # GH-46918: an explicitly typed field must win over the null type + # inferred from all-null column data + table = cls.from_arrays([pa.array([1.0, 2.0])], names=('a',)) + column = [[None, None]] if cls is pa.Table else [None, None] + + new_field = pa.field('b', pa.float64(), nullable=True) + result = getattr(table, method)(0, new_field, column) + + assert result.schema.field('b').type == pa.float64() + assert result.column('b').to_pylist() == [None, None] + + # a null field with null data stays null + null_field = pa.field('b', pa.null()) + result = getattr(table, method)(0, null_field, column) + assert result.schema.field('b').type == pa.null() + + # non-null data that does not match the field type is still rejected + values = [[1.5, 2.5]] if cls is pa.Table else [1.5, 2.5] + with pytest.raises(pa.ArrowInvalid if cls is pa.Table + else pa.ArrowTypeError): + getattr(table, method)(0, pa.field('b', pa.int64()), values) + + +@pytest.mark.parametrize( + ('cls'), + [ + (pa.Table), + (pa.RecordBatch) + ] +) +def test_table_append_column_all_null_data_typed_field(cls): + # GH-46918 + table = cls.from_arrays([pa.array([1.0, 2.0])], names=('a',)) + column = [[None, None]] if cls is pa.Table else [None, None] + + result = table.append_column( + pa.field('b', pa.float64(), nullable=True), column) + + assert result.schema.field('b').type == pa.float64() + assert result.column('b').to_pylist() == [None, None] + + @pytest.mark.parametrize( ('cls'), [