Skip to content

Commit 6fa47f8

Browse files
committed
refactor(query): use stable siphash for bucket
1 parent 8154e53 commit 6fa47f8

5 files changed

Lines changed: 35 additions & 48 deletions

File tree

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,6 @@ memchr = { version = "2", default-features = false }
358358
micromarshal = "0.7.0"
359359
mlua = { version = "0.11", features = ["lua54", "vendored", "async", "serialize"] }
360360
mockall = "0.11.2"
361-
murmur3 = "0.5.2"
362361
mysql_async = { version = "0.34", default-features = false, features = ["native-tls-tls"] }
363362
naive-cityhash = "0.2.0"
364363
ndarray = "0.15.6"

src/query/functions/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ jsonb = { workspace = true }
4141
libm = { workspace = true }
4242
match-template = { workspace = true }
4343
md-5 = { workspace = true }
44-
murmur3 = { workspace = true }
4544
naive-cityhash = { workspace = true }
4645
num-traits = { workspace = true }
4746
proptest = { workspace = true }

src/query/functions/src/scalars/hash.rs

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use std::collections::hash_map::DefaultHasher;
1615
use std::hash::Hash;
1716
use std::hash::Hasher;
1817

@@ -48,6 +47,7 @@ use md5::Digest;
4847
use md5::Md5 as Md5Hasher;
4948
use naive_cityhash::cityhash64_with_seed;
5049
use num_traits::AsPrimitive;
50+
use siphasher::sip::SipHasher13;
5151
use twox_hash::XxHash32;
5252
use twox_hash::XxHash64;
5353

@@ -70,7 +70,7 @@ pub fn register(registry: &mut FunctionRegistry) {
7070
NumberClass::Decimal128 => {
7171
struct Siphash64;
7272
impl HashFunction for Siphash64 {
73-
type Hasher = DefaultHasher;
73+
type Hasher = SipHasher13;
7474
fn name() -> &'static str {
7575
"siphash64"
7676
}
@@ -115,9 +115,7 @@ pub fn register(registry: &mut FunctionRegistry) {
115115
GenericType<0>,
116116
NumberType<u64>,
117117
>(|val, output, _| {
118-
let mut hasher = DefaultHasher::default();
119-
DFHash::hash(&val.to_owned(), &mut hasher);
120-
output.push(hasher.finish());
118+
output.push(siphash64(&val.to_owned()));
121119
}))
122120
.register();
123121

@@ -231,7 +229,7 @@ fn register_bucket(registry: &mut FunctionRegistry) {
231229
|_, _, _| FunctionDomain::MayThrow,
232230
vectorize_with_builder_2_arg::<NumberType<u64>, StringType, NumberType<i32>>(
233231
|buckets, value, output, ctx| {
234-
output.push(bucket(buckets, value.as_bytes(), output.len(), ctx));
232+
output.push(bucket(buckets, value, output.len(), ctx));
235233
},
236234
),
237235
);
@@ -241,7 +239,7 @@ fn register_bucket(registry: &mut FunctionRegistry) {
241239
|_, _, _| FunctionDomain::MayThrow,
242240
vectorize_with_builder_2_arg::<NumberType<u64>, DateType, NumberType<i32>>(
243241
|buckets, value, output, ctx| {
244-
output.push(bucket(buckets, &value.to_le_bytes(), output.len(), ctx));
242+
output.push(bucket(buckets, &value, output.len(), ctx));
245243
},
246244
),
247245
);
@@ -256,12 +254,7 @@ fn register_bucket(registry: &mut FunctionRegistry) {
256254
|_, _, _| FunctionDomain::MayThrow,
257255
vectorize_with_builder_2_arg::<NumberType<u64>, TimestampType, NumberType<i32>>(
258256
|buckets, value, output, ctx| {
259-
output.push(bucket(
260-
buckets,
261-
&value.to_le_bytes(),
262-
output.len(),
263-
ctx,
264-
));
257+
output.push(bucket(buckets, &value, output.len(), ctx));
265258
},
266259
),
267260
);
@@ -282,13 +275,7 @@ fn register_bucket(registry: &mut FunctionRegistry) {
282275
NumberType<NUM_TYPE>,
283276
NumberType<i32>,
284277
>(|buckets, value, output, ctx| {
285-
let value: i64 = value.as_();
286-
output.push(bucket(
287-
buckets,
288-
&value.to_le_bytes(),
289-
output.len(),
290-
ctx,
291-
));
278+
output.push(bucket(buckets, &value, output.len(), ctx));
292279
}),
293280
);
294281
}
@@ -297,9 +284,9 @@ fn register_bucket(registry: &mut FunctionRegistry) {
297284
}
298285
}
299286

300-
fn bucket(
287+
fn bucket<T: DFHash + ?Sized>(
301288
buckets: u64,
302-
bytes: &[u8],
289+
value: &T,
303290
row: usize,
304291
ctx: &mut databend_common_expression::EvalContext,
305292
) -> i32 {
@@ -312,9 +299,14 @@ fn bucket(
312299
return 0;
313300
}
314301

315-
let mut bytes = bytes;
316-
let hash = murmur3::murmur3_32(&mut bytes, 0).unwrap() as i32;
317-
(hash & i32::MAX) % buckets
302+
(siphash64(value) % buckets as u64) as i32
303+
}
304+
305+
fn siphash64<T: DFHash + ?Sized>(value: &T) -> u64 {
306+
// Keep SQL hash results independent of Rust's unspecified DefaultHasher.
307+
let mut hasher = SipHasher13::new_with_keys(0, 0);
308+
DFHash::hash(value, &mut hasher);
309+
hasher.finish()
318310
}
319311

320312
fn register_simple_domain_type_hash<T: ArgType>(registry: &mut FunctionRegistry)
@@ -327,9 +319,7 @@ where for<'a> T::ScalarRef<'a>: DFHash {
327319
.calc_domain(|_, _| FunctionDomain::Full)
328320
.vectorized(vectorize_with_builder_1_arg::<T, NumberType<u64>>(
329321
|val, output, _| {
330-
let mut hasher = DefaultHasher::default();
331-
DFHash::hash(&val, &mut hasher);
332-
output.push(hasher.finish());
322+
output.push(siphash64(&val));
333323
},
334324
))
335325
.register();

src/query/functions/tests/it/scalars/testdata/hash.txt

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -854,37 +854,37 @@ evaluation (internal):
854854
ast : bucket(16, 34)
855855
raw expr : bucket(16, 34)
856856
checked expr : bucket<UInt64, UInt8>(CAST<UInt8>(16_u8 AS UInt64), 34_u8)
857-
optimized expr : 3_i32
857+
optimized expr : 5_i32
858858
output type : Int32
859-
output domain : {3..=3}
860-
output : 3
859+
output domain : {5..=5}
860+
output : 5
861861

862862

863863
ast : bucket(16, 'iceberg')
864864
raw expr : bucket(16, 'iceberg')
865865
checked expr : bucket<UInt64, String>(CAST<UInt8>(16_u8 AS UInt64), "iceberg")
866-
optimized expr : 9_i32
866+
optimized expr : 6_i32
867867
output type : Int32
868-
output domain : {9..=9}
869-
output : 9
868+
output domain : {6..=6}
869+
output : 6
870870

871871

872872
ast : bucket(16, to_date(10000))
873873
raw expr : bucket(16, to_date(10000))
874874
checked expr : bucket<UInt64, Date>(CAST<UInt8>(16_u8 AS UInt64), CAST<Int64>(CAST<UInt16>(10000_u16 AS Int64) AS Date))
875-
optimized expr : 12_i32
875+
optimized expr : 0_i32
876876
output type : Int32
877-
output domain : {12..=12}
878-
output : 12
877+
output domain : {0..=0}
878+
output : 0
879879

880880

881881
ast : bucket(16, to_datetime(100000))
882882
raw expr : bucket(16, to_datetime(100000))
883883
checked expr : bucket<UInt64, Timestamp>(CAST<UInt8>(16_u8 AS UInt64), CAST<Int64>(CAST<UInt32>(100000_u32 AS Int64) AS Timestamp))
884-
optimized expr : 15_i32
884+
optimized expr : 11_i32
885885
output type : Int32
886-
output domain : {15..=15}
887-
output : 15
886+
output domain : {11..=11}
887+
output : 11
888888

889889

890890
error:
@@ -905,17 +905,17 @@ evaluation:
905905
+--------+---------+---------+
906906
| Type | Int64 | Int32 |
907907
| Domain | {1..=4} | Unknown |
908-
| Row 0 | 1 | 4 |
909-
| Row 1 | 2 | 4 |
910-
| Row 2 | 3 | 3 |
911-
| Row 3 | 4 | 6 |
908+
| Row 0 | 1 | 1 |
909+
| Row 1 | 2 | 6 |
910+
| Row 2 | 3 | 2 |
911+
| Row 3 | 4 | 1 |
912912
+--------+---------+---------+
913913
evaluation (internal):
914914
+--------+-----------------------------+
915915
| Column | Data |
916916
+--------+-----------------------------+
917917
| a | Column(Int64([1, 2, 3, 4])) |
918-
| Output | Int32([4, 4, 3, 6]) |
918+
| Output | Int32([1, 6, 2, 1]) |
919919
+--------+-----------------------------+
920920

921921

0 commit comments

Comments
 (0)