From 4d3d8e9e0c217fa83de056832905890d906556a7 Mon Sep 17 00:00:00 2001 From: Ilya Kasnacheev Date: Tue, 18 Feb 2020 17:13:35 +0300 Subject: [PATCH] IGNITE-14075: Fix hash code calculation for composite keys Co-authored-by: Aleksandr Shapkin This closes #9 --- .gitignore | 1 + pyignite/binary.py | 2 +- pyignite/utils.py | 2 +- tests/test_cache_composite_key_class_sql.py | 123 ++++++++++++++++++++ 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 tests/test_cache_composite_key_class_sql.py 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 @@ def _build(self, client: 'Client' = None) -> int: + 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 @@ def hashcode(string: Union[str, bytes]) -> int: :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