Skip to content

Commit b13d43b

Browse files
alamarashapkin
authored andcommitted
IGNITE-14075: Fix hash code calculation for composite keys
Co-authored-by: Aleksandr Shapkin <lexwert@yandex.ru> This closes #9
1 parent 18d32bb commit b13d43b

4 files changed

Lines changed: 126 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
.vscode
23
.eggs
34
.pytest_cache
45
.tox

pyignite/binary.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def _build(self, client: 'Client' = None) -> int:
165165
+ len(field_buffer)
166166
)
167167
header.length = header.schema_offset + ctypes.sizeof(schema_class)
168-
header.hash_code = hashcode(field_buffer + bytes(schema))
168+
header.hash_code = hashcode(field_buffer)
169169

170170
# reuse the results
171171
self._buffer = bytes(header) + field_buffer + bytes(schema)

pyignite/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def hashcode(string: Union[str, bytes]) -> int:
113113
:param string: UTF-8-encoded string identifier of binary buffer,
114114
:return: hash code.
115115
"""
116-
result = 0
116+
result = 1 if isinstance(string, (bytes, bytearray)) else 0
117117
for char in string:
118118
try:
119119
char = ord(char)
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright ownership.
4+
# The ASF licenses this file to You under the Apache License, Version 2.0
5+
# (the "License"); you may not use this file except in compliance with
6+
# the License. You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from collections import OrderedDict
17+
18+
from pyignite import GenericObjectMeta
19+
from pyignite.datatypes import (
20+
IntObject, String
21+
)
22+
23+
24+
class StudentKey(
25+
metaclass=GenericObjectMeta,
26+
type_name='test.model.StudentKey',
27+
schema=OrderedDict([
28+
('ID', IntObject),
29+
('DEPT', String)
30+
])
31+
):
32+
pass
33+
34+
35+
class Student(
36+
metaclass=GenericObjectMeta,
37+
type_name='test.model.Student',
38+
schema=OrderedDict([
39+
('NAME', String),
40+
])
41+
):
42+
pass
43+
44+
45+
create_query = '''CREATE TABLE StudentTable (
46+
id INT(11),
47+
dept VARCHAR,
48+
name CHAR(24),
49+
PRIMARY KEY (id, dept))
50+
WITH "CACHE_NAME=StudentCache, KEY_TYPE=test.model.StudentKey, VALUE_TYPE=test.model.Student"'''
51+
52+
insert_query = '''INSERT INTO StudentTable (id, dept, name) VALUES (?, ?, ?)'''
53+
54+
select_query = 'SELECT _KEY, id, dept, name FROM StudentTable'
55+
56+
drop_query = 'DROP TABLE StudentTable IF EXISTS'
57+
58+
59+
def test_cache_get_with_composite_key_finds_sql_value(client):
60+
"""
61+
Should query a record with composite key and calculate
62+
internal hashcode correctly.
63+
"""
64+
65+
client.sql(drop_query)
66+
67+
# Create table.
68+
result = client.sql(create_query)
69+
assert next(result)[0] == 0
70+
71+
student_key = StudentKey(1, 'Acct')
72+
student_val = Student('John')
73+
74+
# Put new Strudent with StudentKey.
75+
result = client.sql(insert_query, query_args=[student_key.ID, student_key.DEPT, student_val.NAME])
76+
assert next(result)[0] == 1
77+
78+
# Cache get finds the same value.
79+
studentCache = client.get_cache('StudentCache')
80+
val = studentCache.get(student_key)
81+
assert val is not None
82+
assert val.NAME == student_val.NAME
83+
84+
query_result = list(client.sql(select_query, include_field_names=True))
85+
86+
validate_query_result(student_key, student_val, query_result)
87+
88+
89+
def test_python_sql_finds_inserted_value_with_composite_key(client):
90+
"""
91+
Insert a record with a composite key and query it with SELECT SQL.
92+
"""
93+
94+
client.sql(drop_query)
95+
96+
# Create table.
97+
result = client.sql(create_query)
98+
assert next(result)[0] == 0
99+
100+
student_key = StudentKey(2, 'Business')
101+
student_val = Student('Abe')
102+
103+
# Put new value using cache.
104+
studentCache = client.get_cache('StudentCache')
105+
studentCache.put(student_key, student_val)
106+
107+
# Find the value using SQL.
108+
query_result = list(client.sql(select_query, include_field_names=True))
109+
110+
validate_query_result(student_key, student_val, query_result)
111+
112+
113+
def validate_query_result(student_key, student_val, query_result):
114+
'''
115+
Compare query result with expected key and value.
116+
'''
117+
assert len(query_result) == 2
118+
sql_row = dict(zip(query_result[0], query_result[1]))
119+
120+
assert sql_row["_KEY"][0] == student_key._buffer
121+
assert sql_row['ID'] == student_key.ID
122+
assert sql_row['DEPT'] == student_key.DEPT
123+
assert sql_row['NAME'] == student_val.NAME

0 commit comments

Comments
 (0)