IGNITE-14075: Fix hash code calculation for composite keys
Co-authored-by: Aleksandr Shapkin <lexwert@yandex.ru>
This closes #9
diff --git a/.gitignore b/.gitignore
index a779771..7372921 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.idea
+.vscode
.eggs
.pytest_cache
.tox
diff --git a/pyignite/binary.py b/pyignite/binary.py
index 99f2f02..5d76c1b 100644
--- a/pyignite/binary.py
+++ b/pyignite/binary.py
@@ -165,7 +165,7 @@
+ len(field_buffer)
)
header.length = header.schema_offset + ctypes.sizeof(schema_class)
- header.hash_code = hashcode(field_buffer + bytes(schema))
+ header.hash_code = hashcode(field_buffer)
# reuse the results
self._buffer = bytes(header) + field_buffer + bytes(schema)
diff --git a/pyignite/utils.py b/pyignite/utils.py
index ca9725d..ebe5501 100644
--- a/pyignite/utils.py
+++ b/pyignite/utils.py
@@ -113,7 +113,7 @@
:param string: UTF-8-encoded string identifier of binary buffer,
:return: hash code.
"""
- result = 0
+ result = 1 if isinstance(string, (bytes, bytearray)) else 0
for char in string:
try:
char = ord(char)
diff --git a/tests/test_cache_composite_key_class_sql.py b/tests/test_cache_composite_key_class_sql.py
new file mode 100644
index 0000000..2f1705f
--- /dev/null
+++ b/tests/test_cache_composite_key_class_sql.py
@@ -0,0 +1,123 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from collections import OrderedDict
+
+from pyignite import GenericObjectMeta
+from pyignite.datatypes import (
+ IntObject, String
+)
+
+
+class StudentKey(
+ metaclass=GenericObjectMeta,
+ type_name='test.model.StudentKey',
+ schema=OrderedDict([
+ ('ID', IntObject),
+ ('DEPT', String)
+ ])
+ ):
+ pass
+
+
+class Student(
+ metaclass=GenericObjectMeta,
+ type_name='test.model.Student',
+ schema=OrderedDict([
+ ('NAME', String),
+ ])
+ ):
+ pass
+
+
+create_query = '''CREATE TABLE StudentTable (
+ id INT(11),
+ dept VARCHAR,
+ name CHAR(24),
+ PRIMARY KEY (id, dept))
+ WITH "CACHE_NAME=StudentCache, KEY_TYPE=test.model.StudentKey, VALUE_TYPE=test.model.Student"'''
+
+insert_query = '''INSERT INTO StudentTable (id, dept, name) VALUES (?, ?, ?)'''
+
+select_query = 'SELECT _KEY, id, dept, name FROM StudentTable'
+
+drop_query = 'DROP TABLE StudentTable IF EXISTS'
+
+
+def test_cache_get_with_composite_key_finds_sql_value(client):
+ """
+ Should query a record with composite key and calculate
+ internal hashcode correctly.
+ """
+
+ client.sql(drop_query)
+
+ # Create table.
+ result = client.sql(create_query)
+ assert next(result)[0] == 0
+
+ student_key = StudentKey(1, 'Acct')
+ student_val = Student('John')
+
+ # Put new Strudent with StudentKey.
+ result = client.sql(insert_query, query_args=[student_key.ID, student_key.DEPT, student_val.NAME])
+ assert next(result)[0] == 1
+
+ # Cache get finds the same value.
+ studentCache = client.get_cache('StudentCache')
+ val = studentCache.get(student_key)
+ assert val is not None
+ assert val.NAME == student_val.NAME
+
+ query_result = list(client.sql(select_query, include_field_names=True))
+
+ validate_query_result(student_key, student_val, query_result)
+
+
+def test_python_sql_finds_inserted_value_with_composite_key(client):
+ """
+ Insert a record with a composite key and query it with SELECT SQL.
+ """
+
+ client.sql(drop_query)
+
+ # Create table.
+ result = client.sql(create_query)
+ assert next(result)[0] == 0
+
+ student_key = StudentKey(2, 'Business')
+ student_val = Student('Abe')
+
+ # Put new value using cache.
+ studentCache = client.get_cache('StudentCache')
+ studentCache.put(student_key, student_val)
+
+ # Find the value using SQL.
+ query_result = list(client.sql(select_query, include_field_names=True))
+
+ validate_query_result(student_key, student_val, query_result)
+
+
+def validate_query_result(student_key, student_val, query_result):
+ '''
+ Compare query result with expected key and value.
+ '''
+ assert len(query_result) == 2
+ sql_row = dict(zip(query_result[0], query_result[1]))
+
+ assert sql_row["_KEY"][0] == student_key._buffer
+ assert sql_row['ID'] == student_key.ID
+ assert sql_row['DEPT'] == student_key.DEPT
+ assert sql_row['NAME'] == student_val.NAME