|
1 | 1 | /* |
2 | | - * SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. |
| 2 | + * SPDX-FileCopyrightText: Copyright (c) 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
3 | 3 | * SPDX-License-Identifier: Apache-2.0 |
4 | 4 | */ |
5 | 5 |
|
|
14 | 14 | #include <cudf/table/table.hpp> |
15 | 15 | #include <cudf/table/table_view.hpp> |
16 | 16 |
|
| 17 | +#include <limits> |
| 18 | +#include <stdexcept> |
| 19 | + |
17 | 20 | template <typename T, typename SourceT = T> |
18 | 21 | using column_wrapper = cudf::test::fixed_width_column_wrapper<T, SourceT>; |
19 | 22 |
|
@@ -67,26 +70,47 @@ TYPED_TEST(CrossJoinTypeTests, CrossJoin) |
67 | 70 | CUDF_TEST_EXPECT_TABLES_EQUAL(join_table->view(), table_expect); |
68 | 71 | } |
69 | 72 |
|
70 | | -class CrossJoinInvalidInputs : public cudf::test::BaseFixture {}; |
| 73 | +class CrossJoinZeroColumnOperand : public cudf::test::BaseFixture {}; |
71 | 74 |
|
72 | | -TEST_F(CrossJoinInvalidInputs, EmptyTable) |
| 75 | +TEST_F(CrossJoinZeroColumnOperand, PreservesRowCount) |
73 | 76 | { |
74 | 77 | auto b_0 = column_wrapper<int32_t>{10, 20, 20, 50}; |
75 | 78 | auto b_1 = column_wrapper<float>{5.0, .7, .7, .7}; |
76 | 79 | auto b_2 = column_wrapper<int8_t>{90, 75, 62, 41}; |
77 | 80 | auto b_3 = cudf::test::strings_column_wrapper({"quick", "words", "result", ""}); |
78 | 81 |
|
79 | | - auto column_a = std::vector<std::unique_ptr<cudf::column>>{}; |
80 | | - auto table_a = cudf::table(std::move(column_a)); |
81 | | - auto table_b = cudf::table_view{{b_0, b_1, b_2, b_3}}; |
82 | | - |
83 | | - // |
84 | | - // table_a has no columns, table_b has columns |
85 | | - // Let's check different permutations of passing table |
86 | | - // with no columns to verify that exceptions are thrown |
87 | | - // |
88 | | - EXPECT_THROW(cudf::cross_join(table_a, table_b), cudf::logic_error); |
89 | | - EXPECT_THROW(cudf::cross_join(table_b, table_a), cudf::logic_error); |
| 82 | + // A zero-column operand with a non-zero row count is valid: it contributes |
| 83 | + // only to the output row count, not to the output columns. |
| 84 | + auto table_a = cudf::table_view{std::vector<cudf::column_view>{}, 3}; |
| 85 | + auto table_b = cudf::table_view{{b_0, b_1, b_2, b_3}}; |
| 86 | + |
| 87 | + auto join_ab = cudf::cross_join(table_a, table_b); |
| 88 | + EXPECT_EQ(join_ab->num_columns(), table_b.num_columns()); |
| 89 | + EXPECT_EQ(join_ab->num_rows(), table_a.num_rows() * table_b.num_rows()); |
| 90 | + |
| 91 | + auto join_ba = cudf::cross_join(table_b, table_a); |
| 92 | + EXPECT_EQ(join_ba->num_columns(), table_b.num_columns()); |
| 93 | + EXPECT_EQ(join_ba->num_rows(), table_a.num_rows() * table_b.num_rows()); |
| 94 | + |
| 95 | + // Both operands zero-column: result is a zero-column table whose row count is |
| 96 | + // the product of the operands' row counts. |
| 97 | + auto table_c = cudf::table_view{std::vector<cudf::column_view>{}, 5}; |
| 98 | + auto join_ac = cudf::cross_join(table_a, table_c); |
| 99 | + EXPECT_EQ(join_ac->num_columns(), 0); |
| 100 | + EXPECT_EQ(join_ac->num_rows(), table_a.num_rows() * table_c.num_rows()); |
| 101 | +} |
| 102 | + |
| 103 | +TEST_F(CrossJoinZeroColumnOperand, OverflowThrows) |
| 104 | +{ |
| 105 | + // A cross join whose row-count product exceeds size_type::max() throws |
| 106 | + // std::overflow_error. Using zero-column operands makes the check happen |
| 107 | + // before any output is allocated. |
| 108 | + auto table_a = |
| 109 | + cudf::table_view{std::vector<cudf::column_view>{}, std::numeric_limits<cudf::size_type>::max()}; |
| 110 | + auto table_b = cudf::table_view{std::vector<cudf::column_view>{}, 2}; |
| 111 | + |
| 112 | + EXPECT_THROW(cudf::cross_join(table_a, table_b), std::overflow_error); |
| 113 | + EXPECT_THROW(cudf::cross_join(table_b, table_a), std::overflow_error); |
90 | 114 | } |
91 | 115 |
|
92 | 116 | class CrossJoinEmptyResult : public cudf::test::BaseFixture {}; |
|
0 commit comments