From 290db00de341af7cc17861b4e08e54c34b51b9d4 Mon Sep 17 00:00:00 2001
From: Denis Smirnov <sd@picodata.io>
Date: Fri, 16 Jun 2023 18:23:52 +0700
Subject: [PATCH] test: add hash calculation tests

Check that picodata SQL uses `key_def` module from Tarantool to
calculate the tuple hash.
---
 Cargo.lock           |  2 +-
 sbroad               |  2 +-
 test/conftest.py     | 30 ++++++++++++++++++++++++++++++
 test/int/test_sql.py | 34 ++++++++++++++++++++++++++++++++++
 4 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index f6ceb42f8f..d685ac69d4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1376,7 +1376,7 @@ dependencies = [
  "pest",
  "pest_derive",
  "rand",
- "rmp-serde 0.14.4",
+ "rmp-serde 1.0.0",
  "sbroad-proc",
  "serde",
  "serde_yaml",
diff --git a/sbroad b/sbroad
index ccbd6c822c..aafa4f7af8 160000
--- a/sbroad
+++ b/sbroad
@@ -1 +1 @@
-Subproject commit ccbd6c822c83b52e2563bb96e4424b6e102674b6
+Subproject commit aafa4f7af83f14f0db175f92bf6e66d56f97abb3
diff --git a/test/conftest.py b/test/conftest.py
index b8232f3399..7fb8f82824 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -186,6 +186,27 @@ def normalize_net_box_result(func):
     return inner
 
 
+@dataclass
+class KeyPart:
+    fieldno: int
+    type: str
+    is_nullable: bool = False
+
+    def __str__(self):
+        return """{{ fieldno = {}, type = "{}", is_nullable = {} }}""".format(
+            self.fieldno, self.type, self.is_nullable
+        )
+
+
+@dataclass
+class KeyDef:
+    parts: list[KeyPart]
+
+    def __str__(self):
+        parts = ", ".join(str(part) for part in self.parts)
+        return """{{ {} }}""".format(parts)
+
+
 @dataclass(frozen=True)
 class RaftStatus:
     id: int
@@ -480,6 +501,15 @@ class Instance:
             eprint(f"{self} killed")
         self.process = None
 
+    def hash(self, tup: tuple, key_def: KeyDef) -> int:
+        tup_str = "{{ {} }}".format(", ".join(str(x) for x in tup))
+        lua = """
+            return require("key_def").new({kd}):hash(box.tuple.new({t}))
+        """.format(
+            t=tup_str, kd=str(key_def)
+        )
+        return self.eval(lua)
+
     def sql(self, sql: str, *params, timeout: int | float = 1) -> dict:
         """Run SQL query and return result"""
         return self.call("pico.sql", sql, params, timeout=timeout)
diff --git a/test/int/test_sql.py b/test/int/test_sql.py
index f1cf7de021..54533c7d85 100644
--- a/test/int/test_sql.py
+++ b/test/int/test_sql.py
@@ -3,6 +3,8 @@ import re
 
 from conftest import (
     Cluster,
+    KeyDef,
+    KeyPart,
     ReturnError,
 )
 
@@ -87,3 +89,35 @@ def test_select(cluster: Cluster):
         2,
     )
     assert data["rows"] == [[2, 2]]
+
+
+def test_hash(cluster: Cluster):
+    cluster.deploy(instance_count=1)
+    i1 = cluster.instances[0]
+
+    space_id = 777
+    index = i1.propose_create_space(
+        dict(
+            id=space_id,
+            name="T",
+            format=[
+                dict(name="A", type="integer", is_nullable=True),
+            ],
+            primary_key=[dict(field="A")],
+            # sharding function is implicitly murmur3
+            distribution=dict(kind="sharded_implicitly", sharding_key=["A"]),
+        )
+    )
+    i1.raft_wait_index(index, 3)
+
+    # Calculate tuple hash with Lua
+    tup = (1,)
+    key_def = KeyDef([KeyPart(1, "integer", True)])
+    lua_hash = i1.hash(tup, key_def)
+    bucket_count = 3000
+
+    # Compare SQL and Lua bucket_id
+    data = i1.sql("""insert into t values(?);""", 1)
+    assert data["row_count"] == 1
+    data = i1.sql(""" select "bucket_id" from t where a = ?""", 1)
+    assert data["rows"] == [[lua_hash % bucket_count]]
-- 
GitLab