Repository: jorgecarleitao/arrow2
Pull request #212 (2021-07-22 18:36:17)

Fixed nested FFI.
This PR fixes a bug in reading FFI children of child (nested types).

codecov[bot]: # [Codecov](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) Report
> Merging [#212](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) (a5035bd) into [main](https://codecov.io/gh/jorgecarleitao/arrow2/commit/8d7d08fc6a8343bded84d596196857433df40eda?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) (8d7d08f) will **increase** coverage by `0.04%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212/graphs/tree.svg?width=650&height=150&src=pr&token=AgyTF60R3D&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao)](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao)

```diff
@@            Coverage Diff             @@
##             main     #212      +/-   ##
==========================================
+ Coverage   76.81%   76.86%   +0.04%     
==========================================
  Files         226      226              
  Lines       19446    19459      +13     
==========================================
+ Hits        14938    14957      +19     
+ Misses       4508     4502       -6     
```


| [Impacted Files](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) | Coverage Δ | |
|---|---|---|
| [src/ffi/array.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2ZmaS9hcnJheS5ycw==) | `82.71% <100.00%> (+3.00%)` | :arrow_up: |
| [src/ffi/ffi.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2ZmaS9mZmkucnM=) | `76.27% <100.00%> (+1.18%)` | :arrow_up: |
| [src/compute/nullif.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2NvbXB1dGUvbnVsbGlmLnJz) | `1.81% <0.00%> (+1.81%)` | :arrow_up: |
| [src/array/list/mutable.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2FycmF5L2xpc3QvbXV0YWJsZS5ycw==) | `79.20% <0.00%> (+1.98%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao). Last update [8d7d08f...a5035bd](https://codecov.io/gh/jorgecarleitao/arrow2/pull/212?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao).


Patch:
diff --git a/README.md b/README.md
index c9c71f9d68..b958fb4712 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
 # Arrow2: Transmute-free Arrow
 
-![test](https://github.com/jorgecarleitao/arrow2/actions/workflows/test.yml/badge.svg)
+[![test](https://github.com/jorgecarleitao/arrow2/actions/workflows/test.yml/badge.svg)](https://github.com/jorgecarleitao/arrow2/actions/workflows/Build.yml)
+[![](https://img.shields.io/crates/d/arrow2.svg)](https://crates.io/crates/arrow2)
+[![](https://img.shields.io/crates/dv/arrow2.svg)](https://crates.io/crates/arrow2)
+[![](https://docs.rs/arrow2/badge.svg)](https://docs.rs/arrow2/)
 
 This repository contains a Rust library to work with the [Arrow format](https://arrow.apache.org/).
 It is a re-write of the [official Arrow crate](https://github.com/apache/arrow) using
diff --git a/src/compute/sort/primitive/sort.rs b/src/compute/sort/primitive/sort.rs
index 224a9fd9bc..34dbf37313 100644
--- a/src/compute/sort/primitive/sort.rs
+++ b/src/compute/sort/primitive/sort.rs
@@ -46,17 +46,15 @@ where
     let validity = array.validity();
 
     let (buffer, validity) = if let Some(validity) = validity {
-        let nulls = (0..validity.null_count()).map(|_| false);
-        let valids = (validity.null_count()..array.len()).map(|_| true);
+        let nulls = std::iter::repeat(false).take(validity.null_count());
+        let valids = std::iter::repeat(true).take(array.len() - validity.null_count());
 
         let mut buffer = MutableBuffer::<T>::with_capacity(array.len());
         let mut new_validity = MutableBitmap::with_capacity(array.len());
         let slices = SlicesIterator::new(validity);
 
         if options.nulls_first {
-            nulls
-                .chain(valids)
-                .for_each(|value| unsafe { new_validity.push_unchecked(value) });
+            new_validity.extend_from_trusted_len_iter(nulls.chain(valids));
             (0..validity.null_count()).for_each(|_| buffer.push(T::default()));
             for (start, len) in slices {
                 buffer.extend_from_slice(&values[start..start + len])
@@ -67,9 +65,7 @@ where
                 options.descending,
             )
         } else {
-            valids
-                .chain(nulls)
-                .for_each(|value| unsafe { new_validity.push_unchecked(value) });
+            new_validity.extend_from_trusted_len_iter(valids.chain(nulls));
             for (start, len) in slices {
                 buffer.extend_from_slice(&values[start..start + len])
             }
diff --git a/src/ffi/array.rs b/src/ffi/array.rs
index 65f708cf2e..3a74cc5187 100644
--- a/src/ffi/array.rs
+++ b/src/ffi/array.rs
@@ -168,4 +168,25 @@ mod tests {
 
         test_round_trip(array)
     }
+
+    #[test]
+    fn test_list_list() -> Result<()> {
+        let data = vec![
+            Some(vec![
+                Some(vec![None]),
+                Some(vec![Some(2)]),
+                Some(vec![Some(3)]),
+            ]),
+            None,
+            Some(vec![Some(vec![Some(4), None, Some(6)])]),
+        ];
+
+        let mut array =
+            MutableListArray::<i32, MutableListArray<i32, MutablePrimitiveArray<i32>>>::new();
+        array.try_extend(data)?;
+
+        let array: ListArray<i32> = array.into();
+
+        test_round_trip(array)
+    }
 }
diff --git a/src/ffi/ffi.rs b/src/ffi/ffi.rs
index 70ca45409b..05569d7875 100644
--- a/src/ffi/ffi.rs
+++ b/src/ffi/ffi.rs
@@ -643,10 +643,6 @@ impl<'a> ArrowArrayRef for ArrowArrayChild<'a> {
     fn schema(&self) -> &Ffi_ArrowSchema {
         self.schema
     }
-
-    fn child(&self, _: usize) -> Result<ArrowArrayChild> {
-        todo!()
-    }
 }
 
 impl<'a> ArrowArrayChild<'a> {


Test patch:
diff --git a/arrow-pyarrow-integration-testing/tests/test_sql.py b/arrow-pyarrow-integration-testing/tests/test_sql.py
index 2da732b746..c3b6e7843a 100644
--- a/arrow-pyarrow-integration-testing/tests/test_sql.py
+++ b/arrow-pyarrow-integration-testing/tests/test_sql.py
@@ -24,12 +24,17 @@ import arrow_pyarrow_integration_testing
 
 class TestCase(unittest.TestCase):
     def setUp(self):
-        self.old_allocated_rust = arrow_pyarrow_integration_testing.total_allocated_bytes()
+        self.old_allocated_rust = (
+            arrow_pyarrow_integration_testing.total_allocated_bytes()
+        )
         self.old_allocated_cpp = pyarrow.total_allocated_bytes()
 
     def tearDown(self):
         # No leak of Rust
-        self.assertEqual(self.old_allocated_rust, arrow_pyarrow_integration_testing.total_allocated_bytes())
+        self.assertEqual(
+            self.old_allocated_rust,
+            arrow_pyarrow_integration_testing.total_allocated_bytes(),
+        )
 
         # No leak of C++ memory
         self.assertEqual(self.old_allocated_cpp, pyarrow.total_allocated_bytes())
@@ -46,6 +51,7 @@ class TestCase(unittest.TestCase):
         """
         Rust -> Python -> Rust
         """
+
         def double(array):
             array = array.to_pylist()
             return pyarrow.array([x * 2 if x is not None else None for x in array])
@@ -102,9 +108,9 @@ class TestCase(unittest.TestCase):
         """
         Python -> Rust -> Python
         """
-        a = pyarrow.array([None, 1, 2], pyarrow.time32('s'))
+        a = pyarrow.array([None, 1, 2], pyarrow.time32("s"))
         b = arrow_pyarrow_integration_testing.concatenate(a)
-        expected = pyarrow.array([None, 1, 2] + [None, 1, 2], pyarrow.time32('s'))
+        expected = pyarrow.array([None, 1, 2] + [None, 1, 2], pyarrow.time32("s"))
         self.assertEqual(b, expected)
 
     def test_list_array(self):
@@ -112,7 +118,9 @@ class TestCase(unittest.TestCase):
         Python -> Rust -> Python
         """
         for _ in range(2):
-            a = pyarrow.array([[], None, [1, 2], [4, 5, 6]], pyarrow.list_(pyarrow.int64()))
+            a = pyarrow.array(
+                [[], None, [1, 2], [4, 5, 6]], pyarrow.list_(pyarrow.int64())
+            )
             b = arrow_pyarrow_integration_testing.round_trip(a)
 
             b.validate(full=True)
@@ -124,18 +132,36 @@ class TestCase(unittest.TestCase):
         Python -> Rust -> Python
         """
         fields = [
-            ('f1', pyarrow.int32()),
-            ('f2', pyarrow.string()),
+            ("f1", pyarrow.int32()),
+            ("f2", pyarrow.string()),
         ]
-        a = pyarrow.array([
-            {"f1": 1, "f2": "a"},
-            None,
-            {"f1": 3, "f2": None},
-            {"f1": None, "f2": "d"},
-            {"f1": None, "f2": None},
-        ], pyarrow.struct(fields))
+        a = pyarrow.array(
+            [
+                {"f1": 1, "f2": "a"},
+                None,
+                {"f1": 3, "f2": None},
+                {"f1": None, "f2": "d"},
+                {"f1": None, "f2": None},
+            ],
+            pyarrow.struct(fields),
+        )
         b = arrow_pyarrow_integration_testing.round_trip(a)
 
         b.validate(full=True)
         assert a.to_pylist() == b.to_pylist()
         assert a.type == b.type
+
+    def test_list_list_array(self):
+        """
+        Python -> Rust -> Python
+        """
+        for _ in range(2):
+            a = pyarrow.array(
+                [[None], None, [[1], [2]], [[4, 5], [6]]],
+                pyarrow.list_(pyarrow.list_(pyarrow.int64())),
+            )
+            b = arrow_pyarrow_integration_testing.round_trip(a)
+
+            b.validate(full=True)
+            assert a.to_pylist() == b.to_pylist()
+            assert a.type == b.type