Skip to content

Commit e687c59

Browse files
committed
Fix false cache hit
1 parent f6c7df1 commit e687c59

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

test/test_core.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,19 @@ def test_sa_crud(self, connection):
8383
(5, "c"),
8484
]
8585

86+
def test_cached_query(self, connection_no_trans: sa.Connection, connection: sa.Connection):
87+
table = self.tables.test
88+
89+
with connection_no_trans.begin() as transaction:
90+
connection_no_trans.execute(sa.insert(table).values([{"id": 1, "text": "foo"}]))
91+
connection_no_trans.execute(sa.insert(table).values([{"id": 2, "text": None}]))
92+
connection_no_trans.execute(sa.insert(table).values([{"id": 3, "text": "bar"}]))
93+
transaction.commit()
94+
95+
result = connection.execute(sa.select(table)).fetchall()
96+
97+
assert result == [(1, "foo"), (2, None), (3, "bar")]
98+
8699
def test_sa_crud_with_add_declare(self):
87100
engine = sa.create_engine(config.db_url, _add_declare_for_yql_stmt_vars=True)
88101
with engine.connect() as connection:

ydb_sqlalchemy/dbapi/cursor.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import collections.abc
22
import dataclasses
33
import functools
4+
import hashlib
45
import itertools
56
import logging
67
import posixpath
@@ -139,10 +140,29 @@ def _get_ydb_query(self, operation: YdbQuery) -> Union[ydb.DataQuery, str]:
139140
pragma = ""
140141
if self.root_directory:
141142
pragma = f'PRAGMA TablePathPrefix = "{self.root_directory}";\n'
143+
144+
yql_with_pragma = pragma + operation.yql_text
145+
142146
if operation.is_ddl or not operation.parameters_types:
143-
return pragma + operation.yql_text
147+
return yql_with_pragma
144148

145-
return ydb.DataQuery(pragma + operation.yql_text, operation.parameters_types)
149+
return self._make_data_query(yql_with_pragma, operation.parameters_types)
150+
151+
def _make_data_query(
152+
self,
153+
yql_text: str,
154+
parameters_types: Dict[str, Union[ydb.PrimitiveType, ydb.AbstractTypeBuilder]],
155+
) -> ydb.DataQuery:
156+
"""
157+
ydb.DataQuery uses hashed SQL text as cache key, which may cause issues if parameters change type within
158+
the same session, so we include parameter types to the key to prevent false positive cache hit.
159+
"""
160+
161+
sorted_parameters = sorted(parameters_types.items()) # dict keys are unique, so the sorting is stable
162+
163+
yql_with_params = yql_text + "".join([k + str(v) for k, v in sorted_parameters])
164+
name = hashlib.sha256(yql_with_params.encode("utf-8")).hexdigest()
165+
return ydb.DataQuery(yql_text, parameters_types, name=name)
146166

147167
@_handle_ydb_errors
148168
def _execute_dml(

0 commit comments

Comments
 (0)