diff --git a/cpp/include/cudf/detail/stream_compaction.hpp b/cpp/include/cudf/detail/stream_compaction.hpp
index 1e7b34587968..acdb6fc8a4b5 100644
--- a/cpp/include/cudf/detail/stream_compaction.hpp
+++ b/cpp/include/cudf/detail/stream_compaction.hpp
@@ -48,6 +48,9 @@ enum class mask_type : bool {
/**
* @brief Filters @p input using @p boolean_mask.
*
+ * @note An empty mask returns an empty table for @p mask_kind `RETENTION` and a copy of @p input
+ * for @p mask_kind `DELETION`.
+ *
* @param input The input table to filter
* @param boolean_mask A nullable BOOL8 column used to filter @p input
* @param mask_kind Specifies how the boolean mask is treated (retentions or deletions)
diff --git a/cpp/include/cudf/stream_compaction.hpp b/cpp/include/cudf/stream_compaction.hpp
index cc49bb8c4ba0..2b6a2e435914 100644
--- a/cpp/include/cudf/stream_compaction.hpp
+++ b/cpp/include/cudf/stream_compaction.hpp
@@ -244,7 +244,8 @@ std::unique_ptr
apply_retention_mask(
* if the corresponding element `i` in the mask is non-null and `false`.
* This operation is stable: the input order is preserved.
*
- * @note If @p deletion_mask is empty, or @p input has zero rows, an empty table is returned.
+ * @note If @p deletion_mask is empty, a copy of @p input is returned. If @p input has zero rows,
+ * an empty table is returned.
*
* @throws cudf::logic_error if non-empty @p input has different number of rows than @p
* deletion_mask.
diff --git a/cpp/src/stream_compaction/apply_mask.cu b/cpp/src/stream_compaction/apply_mask.cu
index b77e97b96f9f..7d53d8b7bac3 100644
--- a/cpp/src/stream_compaction/apply_mask.cu
+++ b/cpp/src/stream_compaction/apply_mask.cu
@@ -67,7 +67,11 @@ std::unique_ptr apply_mask(table_view const& input,
cuda::stream_ref stream,
rmm::device_async_resource_ref mr)
{
- if (boolean_mask.is_empty()) { return empty_like(input); }
+ auto const is_retention = (mask_kind == mask_type::RETENTION);
+
+ if (boolean_mask.is_empty()) {
+ return is_retention ? empty_like(input) : std::make_unique(input, stream, mr);
+ }
CUDF_EXPECTS(boolean_mask.type().id() == type_id::BOOL8, "Mask must be Boolean type");
CUDF_EXPECTS(input.num_rows() == 0 || input.num_rows() == boolean_mask.size(),
@@ -75,7 +79,6 @@ std::unique_ptr apply_mask(table_view const& input,
auto device_boolean_mask = cudf::column_device_view::create(boolean_mask, stream);
- auto const is_retention = (mask_kind == mask_type::RETENTION);
if (boolean_mask.has_nulls()) {
if (is_retention) {
return detail::copy_if(input, retention_mask_filter{*device_boolean_mask}, stream, mr);
diff --git a/cpp/tests/stream_compaction/apply_mask_tests.cpp b/cpp/tests/stream_compaction/apply_mask_tests.cpp
index b4b7a6c45c1b..f6feb0e7a1b2 100644
--- a/cpp/tests/stream_compaction/apply_mask_tests.cpp
+++ b/cpp/tests/stream_compaction/apply_mask_tests.cpp
@@ -415,14 +415,10 @@ TEST_F(ApplyDeletionMask, EmptyMask)
cudf::test::fixed_width_column_wrapper col3{{10, 40, 70, 5, 2, 10}, {1, 1, 0, 1, 1, 0}};
cudf::table_view input{{col1, col2, col3}};
cudf::test::fixed_width_column_wrapper deletion_mask{};
- cudf::test::fixed_width_column_wrapper col1_expected{};
- cudf::test::fixed_width_column_wrapper col2_expected{};
- cudf::test::fixed_width_column_wrapper col3_expected{};
- cudf::table_view expected{{col1_expected, col2_expected, col3_expected}};
auto got = cudf::apply_deletion_mask(input, deletion_mask);
- CUDF_TEST_EXPECT_TABLES_EQUAL(expected, got->view());
+ CUDF_TEST_EXPECT_TABLES_EQUAL(input, got->view());
}
TEST_F(ApplyDeletionMask, WrongMaskType)
diff --git a/python/pylibcudf/pylibcudf/lists.pyx b/python/pylibcudf/pylibcudf/lists.pyx
index 42b80cb6fed5..0b3c0ce8a16f 100644
--- a/python/pylibcudf/pylibcudf/lists.pyx
+++ b/python/pylibcudf/pylibcudf/lists.pyx
@@ -860,14 +860,14 @@ cpdef Column apply_retention_mask(
input : Column
The input column.
retention_mask : Column
- The boolean mask whose true values retain input elements.
+ A lists-of-bools column used as a retention mask.
stream : Stream | None
CUDA stream on which to perform the operation.
Returns
-------
Column
- A Column of filtered elements based upon the retention mask.
+ Lists column with elements kept where retention mask is valid and true.
"""
cdef unique_ptr[column] c_result
cdef ListsColumnView list_view = input.list_view()
@@ -924,7 +924,7 @@ cpdef Column apply_deletion_mask(
Returns
-------
Column
- Lists column with elements removed where deletion_mask is true.
+ Lists column with elements removed where deletion mask is valid and true.
"""
cdef unique_ptr[column] c_result
cdef ListsColumnView list_view = input.list_view()
diff --git a/python/pylibcudf/pylibcudf/stream_compaction.pyx b/python/pylibcudf/pylibcudf/stream_compaction.pyx
index 0c7244572353..f7548c5ebcf0 100644
--- a/python/pylibcudf/pylibcudf/stream_compaction.pyx
+++ b/python/pylibcudf/pylibcudf/stream_compaction.pyx
@@ -143,12 +143,12 @@ cpdef Table apply_retention_mask(
source_table : Table
The input table to filter.
retention_mask : Column
- The boolean mask whose true values retain input rows.
+ A boolean column used as a retention mask.
Returns
-------
Table
- A new table with rows removed based on the boolean mask.
+ A new table with rows kept where retention mask is valid and true.
"""
cdef unique_ptr[table] c_result
@@ -200,7 +200,7 @@ cpdef Table apply_deletion_mask(
Returns
-------
Table
- Table with rows removed where deletion_mask is true.
+ Table with rows removed where deletion mask is valid and true.
"""
cdef unique_ptr[table] c_result