Copy values from 'rows' of 'table' according to 'projections' in 'result'. Reuses 'result' children where possible. 'allowSorting': If false, disables sorting even if the hybridData supports it. This is needed when the caller also has probe-side columns that won't be reordered - we must keep build-side and probe-side in the same order.
| 83 | // it. This is needed when the caller also has probe-side columns that won't be |
| 84 | // reordered - we must keep build-side and probe-side in the same order. |
| 85 | void extractColumns( |
| 86 | BaseHashTable* table, |
| 87 | folly::Range<char**> rows, |
| 88 | folly::Range<const IdentityProjection*> projections, |
| 89 | memory::MemoryPool* pool, |
| 90 | const std::vector<TypePtr>& resultTypes, |
| 91 | std::vector<VectorPtr>& resultVectors, |
| 92 | bool allowSorting = true) { |
| 93 | BOLT_CHECK_EQ(resultTypes.size(), resultVectors.size()) |
| 94 | auto hybridData = table->hybridData(); |
| 95 | if (hybridData != nullptr) { |
| 96 | std::vector<HybridRowId> outputRowIds; |
| 97 | outputRowIds.resize(rows.size()); |
| 98 | hybridData->getRowIds(rows.data(), rows.size(), outputRowIds); |
| 99 | |
| 100 | // For single container, extract directly without sorting overhead. |
| 101 | // For multiple containers, sort by containerId for better cache locality. |
| 102 | // Note: sorting is safe here because the output order of hash join results |
| 103 | // does not need to match any specific order (SQL doesn't guarantee order). |
| 104 | // Sorting can be disabled via query config for deterministic testing, |
| 105 | // or disabled by caller when probe-side columns must stay in sync. |
| 106 | const bool useSorting = allowSorting && hybridData->shouldUseSorting(); |
| 107 | |
| 108 | const char* const* extractRows = rows.data(); |
| 109 | std::vector<HybridRowId>* extractRowIds = &outputRowIds; |
| 110 | HybridContainer::SortedRows sorted; |
| 111 | |
| 112 | if (useSorting) { |
| 113 | sorted = hybridData->sortByContainerId( |
| 114 | rows.data(), folly::Range<const vector_size_t*>{}, outputRowIds); |
| 115 | extractRows = sorted.rows.data(); |
| 116 | extractRowIds = &sorted.rowIds; |
| 117 | } |
| 118 | |
| 119 | for (auto projection : projections) { |
| 120 | const auto resultChannel = projection.outputChannel; |
| 121 | BOLT_CHECK_LT(resultChannel, resultVectors.size()) |
| 122 | auto& child = resultVectors[resultChannel]; |
| 123 | // TODO: Consider reuse of complex types. |
| 124 | if (!child || !BaseVector::isVectorWritable(child) || |
| 125 | !child->isFlatEncoding()) { |
| 126 | child = |
| 127 | BaseVector::create(resultTypes[resultChannel], rows.size(), pool); |
| 128 | } |
| 129 | child->resize(rows.size()); |
| 130 | hybridData->extractColumn( |
| 131 | extractRows, |
| 132 | extractRowIds->size(), |
| 133 | projection.inputChannel, |
| 134 | child, |
| 135 | *extractRowIds); |
| 136 | } |
| 137 | } else { |
| 138 | for (auto projection : projections) { |
| 139 | const auto resultChannel = projection.outputChannel; |
| 140 | BOLT_CHECK_LT(resultChannel, resultVectors.size()) |
| 141 | |
| 142 | auto& child = resultVectors[resultChannel]; |
no test coverage detected