diff --git a/cpp/src/io/parquet/experimental/variant_extract.cu b/cpp/src/io/parquet/experimental/variant_extract.cu index f463230489b5..8327e6c5cde1 100644 --- a/cpp/src/io/parquet/experimental/variant_extract.cu +++ b/cpp/src/io/parquet/experimental/variant_extract.cu @@ -723,12 +723,10 @@ struct cast_variant_fn { requires(is_variant_numerical) { rmm::device_buffer data{num_rows * sizeof(T), stream, mr}; - auto grid = cudf::detail::grid_1d{num_rows, block_size}; cast_variant_primitive_kernel<<>>( values, {static_cast(data.data()), static_cast(num_rows)}, d_null_mask); CUDF_CUDA_TRY(cudaGetLastError()); - auto const null_count = num_rows - cudf::detail::count_set_bits(d_null_mask, 0, num_rows, stream); return std::make_unique(desired_type, @@ -774,7 +772,6 @@ struct cast_variant_fn { cast_variant_string_fn fn{values, d_null_mask, nullptr, nullptr, {}}; auto [offsets_column, chars] = cudf::strings::detail::make_strings_children(fn, num_rows, stream, mr); - auto const null_count = num_rows - cudf::detail::count_set_bits(d_null_mask, 0, num_rows, stream); return make_strings_column(num_rows, @@ -926,6 +923,19 @@ std::unique_ptr cast_variant(column_view const& values, rmm::device_async_resource_ref mr) { validate_variant_child(values); + + switch (desired_type.id()) { + case type_id::INT8: + case type_id::INT16: + case type_id::INT32: + case type_id::INT64: + case type_id::FLOAT32: + case type_id::FLOAT64: + case type_id::BOOL8: + case type_id::STRING: break; + default: CUDF_FAIL("unsupported type for variant cast", std::invalid_argument); + } + size_type const num_rows = values.size(); if (num_rows == 0) { return make_empty_column(desired_type); } diff --git a/cpp/tests/io/experimental/variant_extract_test.cpp b/cpp/tests/io/experimental/variant_extract_test.cpp index e810bbe142a0..03c334f250e4 100644 --- a/cpp/tests/io/experimental/variant_extract_test.cpp +++ b/cpp/tests/io/experimental/variant_extract_test.cpp @@ -18,8 +18,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -1112,14 +1114,10 @@ TEST_F(CastVariantTest, EmptyInput) } } -TEST_F(CastVariantTest, CastToUnsupportedTargetThrows) +TEST_F(CastVariantTest, UnsupportedTypeThrows) { - // cast_variant only supports INT8/16/32/64 and STRING targets. Every other target is rejected at - // compile-time dispatch on the requested output type, independent of the input bytes, so a single - // well-formed placeholder row triggers the same throw for all of them. + // Unsupported target types must throw regardless of whether the input is empty or non-empty. auto stream = cudf::test::get_default_stream(); - std::vector const val{make_variant_primitive(variant_primitive_type::NULLVAL)}; - cudf::test::lists_column_wrapper values(val.begin(), val.end()); std::vector const ids{cudf::type_id::UINT8, cudf::type_id::UINT16, @@ -1133,11 +1131,24 @@ TEST_F(CastVariantTest, CastToUnsupportedTargetThrows) cudf::type_id::DECIMAL64, cudf::type_id::DECIMAL128}; + // Empty input: the early-return path must still validate the type. + auto const empty_values = + cudf::empty_like(cudf::structs_column_view{make_xyz_three_row_variant()}.child(1)); for (auto const id : ids) { - SCOPED_TRACE(std::string{"target type_id: "} + std::to_string(static_cast(id))); EXPECT_THROW(static_cast(cudf::io::parquet::experimental::cast_variant( - values, cudf::data_type{id}, stream)), - std::invalid_argument); + *empty_values, cudf::data_type{id}, stream)), + std::invalid_argument) + << std::format("expected throw for type_id {} on empty input", static_cast(id)); + } + + // Non-empty input: the dispatch path must also throw for unsupported types. + auto col = make_apache_variant(avf::primitive_int32); + auto const value = cudf::structs_column_view{col}.get_sliced_child(1, stream); + for (auto const id : ids) { + EXPECT_THROW(static_cast(cudf::io::parquet::experimental::cast_variant( + value, cudf::data_type{id}, stream)), + std::invalid_argument) + << std::format("expected throw for type_id {} on non-empty input", static_cast(id)); } }