Skip to content

Commit ecda1e5

Browse files
committed
feat(PartitionedOutput): Add nested RowVector support in the serializer
1 parent bf93e39 commit ecda1e5

10 files changed

Lines changed: 1319 additions & 145 deletions

velox/exec/benchmarks/ExchangeBenchmark.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ enum class ExchangeInputKind {
170170
kDeep10K,
171171
kDeep50,
172172
kStruct1K,
173+
kNested1_1K,
174+
kNested2_1K,
175+
kNested3_1K,
173176
};
174177

175178
struct ExchangeInputSpec {
@@ -242,6 +245,17 @@ RowTypePtr makeDeepType() {
242245
ROW({{"s2_int", INTEGER()}, {"s2_string", VARCHAR()}})))}});
243246
}
244247

248+
// N levels of single-field ROW wrapping a BIGINT leaf, behind a BIGINT "c0"
249+
// partition key. Exercises the nested-ROW serialization path; combined with
250+
// nullPct it produces independent nulls at every ROW level and the leaf.
251+
RowTypePtr makeNestedType(int level) {
252+
TypePtr inner = BIGINT();
253+
for (int i = 0; i < level; ++i) {
254+
inner = ROW({"r"}, {inner});
255+
}
256+
return ROW({"c0", "v"}, {BIGINT(), inner});
257+
}
258+
245259
ExchangeInputSpec makeInputSpec(ExchangeInputKind kind) {
246260
switch (kind) {
247261
case ExchangeInputKind::kDeep10K:
@@ -250,6 +264,12 @@ ExchangeInputSpec makeInputSpec(ExchangeInputKind kind) {
250264
return {"Deep50", makeDeepType(), 2000, 50};
251265
case ExchangeInputKind::kStruct1K:
252266
return {"Struct1K", makeStructType(), 100, 1000};
267+
case ExchangeInputKind::kNested1_1K:
268+
return {"Nested1_1K", makeNestedType(1), 100, 1000};
269+
case ExchangeInputKind::kNested2_1K:
270+
return {"Nested2_1K", makeNestedType(2), 100, 1000};
271+
case ExchangeInputKind::kNested3_1K:
272+
return {"Nested3_1K", makeNestedType(3), 100, 1000};
253273
}
254274

255275
VELOX_UNREACHABLE();
@@ -401,6 +421,21 @@ class ExchangeBenchmark : public VectorTestBase {
401421
[](auto row) { return static_cast<int128_t>(row); },
402422
isNull,
403423
type);
424+
case TypeKind::ROW: {
425+
// Build each child independently and apply row-level nulls so a ROW
426+
// column carries nulls at this level and, recursively, at every nested
427+
// ROW level and the leaf.
428+
const auto& rowType = type->asRow();
429+
std::vector<std::string> names;
430+
std::vector<VectorPtr> children;
431+
names.reserve(rowType.size());
432+
children.reserve(rowType.size());
433+
for (auto i = 0; i < rowType.size(); ++i) {
434+
names.push_back(rowType.nameOf(i));
435+
children.push_back(makeColumn(rowType.childAt(i), numRows, nullPct));
436+
}
437+
return makeRowVector(names, children, isNull);
438+
}
404439
default:
405440
VELOX_NYI(
406441
"makeColumn does not support complex type {} yet",
@@ -718,12 +753,24 @@ EXCHANGE_BENCHMARK_CASE(
718753
Simple10K_Double_col16,
719754
makeInputSpec(SimpleColType::kDouble, 16));
720755

721-
// The complex type benchmarks are temporarily disabled.
756+
// The ARRAY/MAP complex type benchmarks are temporarily disabled because
757+
// makeColumn does not generate ARRAY/MAP yet.
722758
// EXCHANGE_BENCHMARK_CASE(Deep10K, makeInputSpec(ExchangeInputKind::kDeep10K));
723759
// EXCHANGE_BENCHMARK_CASE(Deep50, makeInputSpec(ExchangeInputKind::kDeep50));
724760
// EXCHANGE_BENCHMARK_CASE(Struct1K,
725761
// makeInputSpec(ExchangeInputKind::kStruct1K));
726762

763+
// Nested ROW columns (makeColumn supports ROW recursively).
764+
EXCHANGE_BENCHMARK_CASE(
765+
Nested1_1K,
766+
makeInputSpec(ExchangeInputKind::kNested1_1K));
767+
EXCHANGE_BENCHMARK_CASE(
768+
Nested2_1K,
769+
makeInputSpec(ExchangeInputKind::kNested2_1K));
770+
EXCHANGE_BENCHMARK_CASE(
771+
Nested3_1K,
772+
makeInputSpec(ExchangeInputKind::kNested3_1K));
773+
727774
#undef EXCHANGE_BENCHMARK_CASE
728775
#undef EXCHANGE_BENCHMARK_MODES
729776
#undef EXCHANGE_BENCHMARK_INPUT

velox/exec/tests/OptimizedPartitionedOutputTest.cpp

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,24 @@ struct TestParam {
5858
std::vector<TestParam> testParams() {
5959
std::vector<TestParam> params;
6060

61-
const std::vector<std::pair<std::string, TypePtr>> types = {
61+
// Builds an n-level nested ROW: 1 → ROW(int, bigint); 2 → ROW(int, row1);
62+
// 3 → ROW(int, row2); …. The "r" child carries the next nesting level.
63+
std::function<TypePtr(int)> nestedRow = [&](int level) -> TypePtr {
64+
if (level <= 1) {
65+
return ROW({"a", "b"}, {INTEGER(), BIGINT()});
66+
}
67+
return ROW({"a", "r"}, {INTEGER(), nestedRow(level - 1)});
68+
};
69+
70+
std::vector<std::pair<std::string, TypePtr>> types = {
6271
{"bool", BOOLEAN()},
6372
{"tinyint", TINYINT()},
6473
{"bigint", BIGINT()},
6574
{"hugeint", HUGEINT()},
6675
};
76+
for (int level = 1; level <= 3; ++level) {
77+
types.emplace_back("row" + std::to_string(level), nestedRow(level));
78+
}
6779

6880
const std::vector<std::pair<std::string, NullMode>> nullModes = {
6981
{"no_null", NullMode::kNoNull},
@@ -362,8 +374,56 @@ class OptimizedPartitionedOutputParamTest
362374
VELOX_UNREACHABLE();
363375
}
364376

365-
/// Creates a flat vector of the param's value type with random values and
366-
/// nulls applied according to nullMode.
377+
/// Recursively creates a vector of the given type with random values. Only
378+
/// the top-level row carries row-level nulls (applied by the caller via
379+
/// makeRandomValueVector); leaf flat vectors are non-null inside this helper.
380+
VectorPtr makeRandomVectorOfType(
381+
const TypePtr& type,
382+
int numRows,
383+
std::mt19937_64& rng) {
384+
switch (type->kind()) {
385+
case TypeKind::BOOLEAN:
386+
return vectorMaker_.flatVector<bool>(
387+
numRows, [&](auto /*i*/) -> bool { return rng() % 2 == 0; });
388+
case TypeKind::TINYINT:
389+
return vectorMaker_.flatVector<int8_t>(
390+
numRows,
391+
[&](auto /*i*/) -> int8_t { return static_cast<int8_t>(rng()); });
392+
case TypeKind::INTEGER:
393+
return vectorMaker_.flatVector<int32_t>(
394+
numRows,
395+
[&](auto /*i*/) -> int32_t { return static_cast<int32_t>(rng()); });
396+
case TypeKind::BIGINT:
397+
return vectorMaker_.flatVector<int64_t>(
398+
numRows,
399+
[&](auto /*i*/) -> int64_t { return static_cast<int64_t>(rng()); });
400+
case TypeKind::HUGEINT:
401+
return vectorMaker_.flatVector<int128_t>(
402+
numRows, [&](auto /*i*/) -> int128_t {
403+
int64_t hi = static_cast<int64_t>(rng());
404+
uint64_t lo = rng();
405+
return (static_cast<int128_t>(hi) << 64) |
406+
static_cast<int128_t>(lo);
407+
});
408+
case TypeKind::ROW: {
409+
const auto& rowType = type->asRow();
410+
std::vector<VectorPtr> children;
411+
children.reserve(rowType.size());
412+
for (size_t i = 0; i < rowType.size(); ++i) {
413+
children.push_back(
414+
makeRandomVectorOfType(rowType.childAt(i), numRows, rng));
415+
}
416+
return vectorMaker_.rowVector(rowType.names(), children);
417+
}
418+
default:
419+
VELOX_UNREACHABLE("Unsupported value type: {}", type->toString());
420+
}
421+
}
422+
423+
/// Creates a vector of the param's value type with random values and nulls
424+
/// applied at the top level according to nullMode. For flat scalar types the
425+
/// nulls are placed on the leaf. For ROW types the nulls are placed at the
426+
/// outermost row level only; child columns themselves remain null-free.
367427
VectorPtr makeRandomValueVector(int numRows, std::mt19937_64& rng) {
368428
auto isNullFn = [this](vector_size_t i) -> bool { return isNull(i); };
369429

@@ -393,6 +453,16 @@ class OptimizedPartitionedOutputParamTest
393453
static_cast<int128_t>(lo);
394454
},
395455
isNullFn);
456+
case TypeKind::ROW: {
457+
auto result = std::dynamic_pointer_cast<RowVector>(
458+
makeRandomVectorOfType(param().valueType, numRows, rng));
459+
for (vector_size_t i = 0; i < numRows; ++i) {
460+
if (isNull(i)) {
461+
result->setNull(i, true);
462+
}
463+
}
464+
return result;
465+
}
396466
default:
397467
VELOX_UNREACHABLE(
398468
"Unsupported value type: {}", param().valueType->toString());

0 commit comments

Comments
 (0)