@@ -19,7 +19,7 @@ JoinHashTable::SharedState::SharedState()
19
19
}
20
20
21
21
JoinHashTable::ProbeState::ProbeState ()
22
- : SharedState(), ht_offsets_v (LogicalType::UBIGINT), hashes_dense_v(LogicalType::HASH),
22
+ : SharedState(), ht_offsets_and_salts_v (LogicalType::UBIGINT), hashes_dense_v(LogicalType::HASH),
23
23
non_empty_sel (STANDARD_VECTOR_SIZE) {
24
24
}
25
25
@@ -168,30 +168,33 @@ static void AddPointerToCompare(JoinHashTable::ProbeState &state, const ht_entry
168
168
idx_t row_ht_offset, idx_t &keys_to_compare_count, const idx_t &row_index) {
169
169
170
170
const auto row_ptr_insert_to = FlatVector::GetData<data_ptr_t >(pointers_result_v);
171
- const auto ht_offsets = FlatVector::GetData<idx_t >(state.ht_offsets_v );
171
+ const auto ht_offsets_and_salts = FlatVector::GetData<idx_t >(state.ht_offsets_and_salts_v );
172
172
173
173
state.keys_to_compare_sel .set_index (keys_to_compare_count, row_index);
174
174
row_ptr_insert_to[row_index] = entry.GetPointer ();
175
- ht_offsets[row_index] = row_ht_offset;
175
+
176
+ // If the key does not match, we have to continue linear probing, we need to store the ht_offset and the salt
177
+ // for this element based on the row_index. We can't get the offset from the hash as we already might have
178
+ // some linear probing steps when arriving here.
179
+ ht_offsets_and_salts[row_index] = row_ht_offset | entry.GetSaltWithNulls ();
176
180
keys_to_compare_count += 1 ;
177
181
}
178
182
179
183
template <bool USE_SALTS, bool HAS_SEL>
180
184
static idx_t ProbeForPointersInternal (JoinHashTable::ProbeState &state, JoinHashTable &ht, ht_entry_t *entries,
181
- Vector &hashes_v, Vector &pointers_result_v, const SelectionVector *row_sel,
182
- idx_t &count) {
185
+ Vector &pointers_result_v, const SelectionVector *row_sel, idx_t &count) {
183
186
184
187
auto hashes_dense = FlatVector::GetData<hash_t >(state.hashes_dense_v );
185
188
186
189
idx_t keys_to_compare_count = 0 ;
187
190
188
191
for (idx_t i = 0 ; i < count; i++) {
189
192
190
- auto row_hash = hashes_dense[i]; // hashes has been flattened before -> always access dense
193
+ auto row_hash = hashes_dense[i]; // hashes have been flattened before -> always access dense
191
194
auto row_ht_offset = row_hash & ht.bitmask ;
192
195
193
196
if (USE_SALTS) {
194
- // increment the ht_offset of the entry as long as next entry is occupied and salt does not match
197
+ // increment the ht_offset of the entry as long as the next entry is occupied and salt does not match
195
198
while (true ) {
196
199
const ht_entry_t entry = entries[row_ht_offset];
197
200
const bool occupied = entry.IsOccupied ();
@@ -211,7 +214,7 @@ static idx_t ProbeForPointersInternal(JoinHashTable::ProbeState &state, JoinHash
211
214
break ;
212
215
}
213
216
214
- // full and salt does not match -> continue probing
217
+ // full and salt do not match -> continue probing
215
218
IncrementAndWrap (row_ht_offset, ht.bitmask );
216
219
}
217
220
} else {
@@ -235,14 +238,12 @@ static idx_t ProbeForPointersInternal(JoinHashTable::ProbeState &state, JoinHash
235
238
// / -> match, add to compare sel and increase found count
236
239
template <bool USE_SALTS>
237
240
static idx_t ProbeForPointers (JoinHashTable::ProbeState &state, JoinHashTable &ht, ht_entry_t *entries,
238
- Vector &hashes_v, Vector & pointers_result_v, const SelectionVector *row_sel, idx_t count,
241
+ Vector &pointers_result_v, const SelectionVector *row_sel, idx_t count,
239
242
const bool has_row_sel) {
240
243
if (has_row_sel) {
241
- return ProbeForPointersInternal<USE_SALTS, true >(state, ht, entries, hashes_v, pointers_result_v, row_sel,
242
- count);
244
+ return ProbeForPointersInternal<USE_SALTS, true >(state, ht, entries, pointers_result_v, row_sel, count);
243
245
} else {
244
- return ProbeForPointersInternal<USE_SALTS, false >(state, ht, entries, hashes_v, pointers_result_v, row_sel,
245
- count);
246
+ return ProbeForPointersInternal<USE_SALTS, false >(state, ht, entries, pointers_result_v, row_sel, count);
246
247
}
247
248
}
248
249
@@ -254,14 +255,10 @@ static void GetRowPointersInternal(DataChunk &keys, TupleDataChunkState &key_sta
254
255
ht_entry_t *entries, Vector &pointers_result_v, SelectionVector &match_sel,
255
256
bool has_row_sel) {
256
257
257
- // in case of a hash collision, we need this information to correctly retrieve the salt of this hash
258
- bool uses_unified = false ;
259
- UnifiedVectorFormat hashes_unified_v;
260
-
261
258
// densify hashes: If there is no sel, flatten the hashes, else densify via UnifiedVectorFormat
262
259
if (has_row_sel) {
260
+ UnifiedVectorFormat hashes_unified_v;
263
261
hashes_v.ToUnifiedFormat (count, hashes_unified_v);
264
- uses_unified = true ;
265
262
266
263
auto hashes_unified = UnifiedVectorFormat::GetData<hash_t >(hashes_unified_v);
267
264
auto hashes_dense = FlatVector::GetData<idx_t >(state.hashes_dense_v );
@@ -282,8 +279,8 @@ static void GetRowPointersInternal(DataChunk &keys, TupleDataChunkState &key_sta
282
279
idx_t elements_to_probe_count = count;
283
280
284
281
do {
285
- const idx_t keys_to_compare_count = ProbeForPointers<USE_SALTS>(state, ht, entries, hashes_v, pointers_result_v ,
286
- row_sel, elements_to_probe_count, has_row_sel);
282
+ const idx_t keys_to_compare_count = ProbeForPointers<USE_SALTS>(state, ht, entries, pointers_result_v, row_sel ,
283
+ elements_to_probe_count, has_row_sel);
287
284
288
285
// if there are no keys to compare, we are done
289
286
if (keys_to_compare_count == 0 ) {
@@ -305,32 +302,15 @@ static void GetRowPointersInternal(DataChunk &keys, TupleDataChunkState &key_sta
305
302
match_count++;
306
303
}
307
304
308
- // Linear probing for collisions: Move to the next entry in the HT
309
- auto hashes_unified = UnifiedVectorFormat::GetData<hash_t >(hashes_unified_v);
310
- auto hashes_dense = FlatVector::GetData<hash_t >(state.hashes_dense_v );
311
- auto ht_offsets = FlatVector::GetData<idx_t >(state.ht_offsets_v );
305
+ const auto ht_offsets_and_salts = FlatVector::GetData<idx_t >(state.ht_offsets_and_salts_v );
306
+ const auto hashes_dense = FlatVector::GetData<hash_t >(state.hashes_dense_v );
312
307
308
+ // For all the non-matches, increment the offset to continue probing but keep the salt intact
313
309
for (idx_t i = 0 ; i < keys_no_match_count; i++) {
314
310
const auto row_index = state.keys_no_match_sel .get_index (i);
315
- // The ProbeForPointers function calculates the ht_offset from the hash; therefore, we have to write the
316
- // new offset into the hashes_v; otherwise the next iteration will start at the old position. This might
317
- // seem as an overhead but assures that the first call of ProbeForPointers is optimized as conceding
318
- // calls are unlikely (Max 1-(65535/65536)^VectorSize = 3.1%)
319
- auto ht_offset = ht_offsets[row_index];
320
- IncrementAndWrap (ht_offset, ht.bitmask );
321
-
322
- // Get original hash from unified vector format to extract the salt if hashes_dense was populated that way
323
- hash_t hash;
324
- if (uses_unified) {
325
- const auto uvf_index = hashes_unified_v.sel ->get_index (row_index);
326
- hash = hashes_unified[uvf_index];
327
- } else {
328
- hash = hashes_dense[row_index];
329
- }
330
-
331
- const auto offset_and_salt = ht_offset | (hash & ht_entry_t ::SALT_MASK);
332
-
333
- hashes_dense[i] = offset_and_salt; // populate dense again
311
+ auto ht_offset_and_salt = ht_offsets_and_salts[row_index];
312
+ IncrementAndWrap (ht_offset_and_salt, ht.bitmask | ht_entry_t ::SALT_MASK);
313
+ hashes_dense[i] = ht_offset_and_salt; // populate dense again
334
314
}
335
315
336
316
// in the next interation, we have a selection vector with the keys that do not match
@@ -736,6 +716,11 @@ void JoinHashTable::AllocatePointerTable() {
736
716
capacity = PointerTableCapacity (Count ());
737
717
D_ASSERT (IsPowerOfTwo (capacity));
738
718
719
+ constexpr uint64_t MAX_HASHTABLE_CAPACITY = (1ULL << 48 ) - 1 ;
720
+ if (capacity >= MAX_HASHTABLE_CAPACITY) {
721
+ throw InternalException (" Hashtable capacity exceeds 48-bit limit (2^48 - 1)" );
722
+ }
723
+
739
724
if (hash_map.get ()) {
740
725
// There is already a hash map
741
726
auto current_capacity = hash_map.GetSize () / sizeof (ht_entry_t );
0 commit comments