@@ -112,22 +112,21 @@ struct MethodBaseInner {
112112 }
113113
114114 template <bool read>
115- ALWAYS_INLINE void prefetch (size_t i) {
115+ void prefetch (size_t i) {
116116 if (LIKELY (i + HASH_MAP_PREFETCH_DIST < hash_values.size ())) {
117117 hash_table->template prefetch <read>(keys[i + HASH_MAP_PREFETCH_DIST],
118118 hash_values[i + HASH_MAP_PREFETCH_DIST]);
119119 }
120120 }
121121
122122 template <typename State>
123- ALWAYS_INLINE auto find (State& state, size_t i) {
123+ auto find (State& state, size_t i) {
124124 prefetch<true >(i);
125125 return state.find_key_with_hash (*hash_table, i, keys[i], hash_values[i]);
126126 }
127127
128128 template <typename State, typename F, typename FF>
129- ALWAYS_INLINE auto lazy_emplace (State& state, size_t i, F&& creator,
130- FF&& creator_for_null_key) {
129+ auto lazy_emplace (State& state, size_t i, F&& creator, FF&& creator_for_null_key) {
131130 prefetch<false >(i);
132131 return state.lazy_emplace_key (*hash_table, i, keys[i], hash_values[i], creator,
133132 creator_for_null_key);
@@ -432,7 +431,7 @@ struct IsNullableStringHashMap<DataWithNullKey<StringHashMap<Mapped, Allocator>>
432431
433432// / Helper: get the underlying StringHashTable from a hash table (handles DataWithNullKey wrapper).
434433template <typename HashMap>
435- ALWAYS_INLINE auto & get_string_hash_table (HashMap& data) {
434+ auto & get_string_hash_table (HashMap& data) {
436435 return data;
437436}
438437
@@ -441,7 +440,7 @@ ALWAYS_INLINE auto& get_string_hash_table(HashMap& data) {
441440// / Returns a pair: (converted_key, needs_copy).
442441// / For groups 0 and 5, the key must be a non-const copy (lazy_emplace_if_zero takes Key&).
443442template <int GroupIdx>
444- ALWAYS_INLINE auto convert_key_for_submap (const StringRef& origin) {
443+ auto convert_key_for_submap (const StringRef& origin) {
445444 if constexpr (GroupIdx == 0 ) {
446445 return StringRef (origin); // copy — m0 needs non-const Key&
447446 } else if constexpr (GroupIdx == 1 ) {
@@ -459,7 +458,7 @@ ALWAYS_INLINE auto convert_key_for_submap(const StringRef& origin) {
459458
460459// / Hash value to use for a given group. Group 0 (empty string) always uses hash=0.
461460template <int GroupIdx, typename HashValues>
462- ALWAYS_INLINE size_t hash_for_group (const HashValues& hash_values, uint32_t row) {
461+ size_t hash_for_group (const HashValues& hash_values, uint32_t row) {
463462 if constexpr (GroupIdx == 0 ) {
464463 return 0 ;
465464 } else {
@@ -474,13 +473,14 @@ static constexpr bool group_needs_prefetch = (GroupIdx != 0);
474473
475474// / Process one sub-table group for emplace with result_handler.
476475// / Handles nullable null-key check, prefetch, key conversion, and emplace.
476+ // / pre_handler(row) is called before each emplace, allowing callers to set per-row state
477+ // / (e.g., current row index used inside creator lambdas).
477478template <int GroupIdx, bool is_nullable, typename Submap, typename HashMethodType, typename State,
478- typename HashMap, typename F, typename FF, typename ResultHandler>
479- ALWAYS_INLINE void process_submap_emplace (Submap& submap, const uint32_t * indices, size_t count,
480- HashMethodType& agg_method, State& state,
481- HashMap& hash_table, F&& creator,
482- FF&& creator_for_null_key,
483- ResultHandler&& result_handler) {
479+ typename HashMap, typename F, typename FF, typename PreHandler, typename ResultHandler>
480+ void process_submap_emplace (Submap& submap, const uint32_t * indices, size_t count,
481+ HashMethodType& agg_method, State& state, HashMap& hash_table,
482+ F&& creator, FF&& creator_for_null_key, PreHandler&& pre_handler,
483+ ResultHandler&& result_handler) {
484484 using Mapped = typename HashMethodType::Mapped;
485485 for (size_t j = 0 ; j < count; j++) {
486486 if constexpr (group_needs_prefetch<GroupIdx>) {
@@ -490,6 +490,7 @@ ALWAYS_INLINE void process_submap_emplace(Submap& submap, const uint32_t* indice
490490 }
491491 }
492492 uint32_t row = indices[j];
493+ pre_handler (row);
493494 if constexpr (is_nullable) {
494495 if (state.key_column ->is_null_at (row)) {
495496 bool has_null_key = hash_table.has_null_key_data ();
@@ -524,11 +525,9 @@ ALWAYS_INLINE void process_submap_emplace(Submap& submap, const uint32_t* indice
524525// / pre_handler(row) is called before each emplace.
525526template <int GroupIdx, bool is_nullable, typename Submap, typename HashMethodType, typename State,
526527 typename HashMap, typename F, typename FF, typename PreHandler>
527- ALWAYS_INLINE void process_submap_emplace_void (Submap& submap, const uint32_t * indices,
528- size_t count, HashMethodType& agg_method,
529- State& state, HashMap& hash_table, F&& creator,
530- FF&& creator_for_null_key,
531- PreHandler&& pre_handler) {
528+ void process_submap_emplace_void (Submap& submap, const uint32_t * indices, size_t count,
529+ HashMethodType& agg_method, State& state, HashMap& hash_table,
530+ F&& creator, FF&& creator_for_null_key, PreHandler&& pre_handler) {
532531 for (size_t j = 0 ; j < count; j++) {
533532 if constexpr (group_needs_prefetch<GroupIdx>) {
534533 if (j + HASH_MAP_PREFETCH_DIST < count) {
@@ -566,9 +565,9 @@ ALWAYS_INLINE void process_submap_emplace_void(Submap& submap, const uint32_t* i
566565// / Process one sub-table group for find with result_handler.
567566template <int GroupIdx, bool is_nullable, typename Submap, typename HashMethodType, typename State,
568567 typename HashMap, typename ResultHandler>
569- ALWAYS_INLINE void process_submap_find (Submap& submap, const uint32_t * indices, size_t count,
570- HashMethodType& agg_method, State& state,
571- HashMap& hash_table, ResultHandler&& result_handler) {
568+ void process_submap_find (Submap& submap, const uint32_t * indices, size_t count,
569+ HashMethodType& agg_method, State& state, HashMap& hash_table ,
570+ ResultHandler&& result_handler) {
572571 using Mapped = typename HashMethodType::Mapped;
573572 using FindResult = typename ColumnsHashing::columns_hashing_impl::FindResultImpl<Mapped>;
574573 for (size_t j = 0 ; j < count; j++) {
@@ -606,10 +605,14 @@ ALWAYS_INLINE void process_submap_find(Submap& submap, const uint32_t* indices,
606605
607606// / Batch emplace helper: for StringHashMap, directly accesses sub-tables bypassing dispatch();
608607// / for other hash maps, does per-row loop with standard prefetch.
609- // / The result_handler is called for each row: result_handler(row_index, emplace_result)
610- template <typename HashMethodType, typename State, typename F, typename FF, typename ResultHandler>
608+ // / pre_handler(row) is called before each emplace, allowing callers to set per-row state
609+ // / (e.g., current row index used inside creator lambdas).
610+ // / result_handler(row_index, mapped) is called after each emplace.
611+ template <typename HashMethodType, typename State, typename F, typename FF, typename PreHandler,
612+ typename ResultHandler>
611613void lazy_emplace_batch (HashMethodType& agg_method, State& state, uint32_t num_rows, F&& creator,
612- FF&& creator_for_null_key, ResultHandler&& result_handler) {
614+ FF&& creator_for_null_key, PreHandler&& pre_handler,
615+ ResultHandler&& result_handler) {
613616 if constexpr (HashMethodType::is_string_hash_map ()) {
614617 using HashMap = typename HashMethodType::HashMapType;
615618 constexpr bool is_nullable = IsNullableStringHashMap<HashMap>::value;
@@ -622,22 +625,33 @@ void lazy_emplace_batch(HashMethodType& agg_method, State& state, uint32_t num_r
622625 constexpr int G = decltype (group_idx)::value;
623626 const auto & indices = groups.group_row_indices [G];
624627 if (!indices.empty ()) {
625- process_submap_emplace<G, is_nullable>(submap, indices. data (), indices. size (),
626- agg_method, state, hash_table, creator ,
627- creator_for_null_key, result_handler);
628+ process_submap_emplace<G, is_nullable>(
629+ submap, indices. data (), indices. size (), agg_method, state, hash_table,
630+ creator, creator_for_null_key, pre_handler , result_handler);
628631 }
629632 });
630633 } else {
631634 // Standard per-row loop with ahead prefetch
632635 for (uint32_t i = 0 ; i < num_rows; ++i) {
633636 agg_method.template prefetch <false >(i);
637+ pre_handler (i);
634638 result_handler (i, *state.lazy_emplace_key (*agg_method.hash_table , i, agg_method.keys [i],
635639 agg_method.hash_values [i], creator,
636640 creator_for_null_key));
637641 }
638642 }
639643}
640644
645+ // / Convenience overload without pre_handler (uses no-op).
646+ template <typename HashMethodType, typename State, typename F, typename FF, typename ResultHandler>
647+ void lazy_emplace_batch (HashMethodType& agg_method, State& state, uint32_t num_rows, F&& creator,
648+ FF&& creator_for_null_key, ResultHandler&& result_handler) {
649+ lazy_emplace_batch (
650+ agg_method, state, num_rows, std::forward<F>(creator),
651+ std::forward<FF>(creator_for_null_key), [](uint32_t ) {},
652+ std::forward<ResultHandler>(result_handler));
653+ }
654+
641655// / Batch emplace helper (void version): like lazy_emplace_batch but ignores the return value.
642656// / pre_handler(row) is called before each emplace, allowing callers to update captured state
643657// / (e.g., the current row index used inside creator lambdas).
0 commit comments