summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2023-03-18 11:43:47 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2023-03-18 11:43:47 -0400
commit0a0c7c73729152b7606509b6e750371106dfdd46 (patch)
treece7e8e6b751d41df8cbfd7543dbf342bf6125a7f /lib/sqlalchemy/sql
parent5adc0c56eb4cda1e135d175f2bf1c43179b8f752 (diff)
downloadsqlalchemy-0a0c7c73729152b7606509b6e750371106dfdd46.tar.gz
implement content hashing for custom_op, not identity
Fixed critical SQL caching issue where use of the :meth:`_sql.Operators.op` custom operator function would not produce an appropriate cache key, leading to reduce the effectiveness of the SQL cache. Fixes: #9506 Change-Id: I3eab1ddb5e09a811ad717161a59df0884cdf70ed
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/operators.py20
-rw-r--r--lib/sqlalchemy/sql/traversals.py2
2 files changed, 19 insertions, 3 deletions
diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py
index c973126ca..ab9ddf85c 100644
--- a/lib/sqlalchemy/sql/operators.py
+++ b/lib/sqlalchemy/sql/operators.py
@@ -41,6 +41,7 @@ from typing import Dict
from typing import Generic
from typing import Optional
from typing import Set
+from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
@@ -52,6 +53,7 @@ from ..util.typing import Literal
from ..util.typing import Protocol
if typing.TYPE_CHECKING:
+ from .cache_key import CacheConst
from .type_api import TypeEngine
_T = TypeVar("_T", bound=Any)
@@ -415,10 +417,24 @@ class custom_op(OperatorType, Generic[_T]):
self.python_impl = python_impl
def __eq__(self, other: Any) -> bool:
- return isinstance(other, custom_op) and other.opstring == self.opstring
+ return (
+ isinstance(other, custom_op)
+ and other._hash_key() == self._hash_key()
+ )
def __hash__(self) -> int:
- return id(self)
+ return hash(self._hash_key())
+
+ def _hash_key(self) -> Union[CacheConst, Tuple[Any, ...]]:
+ return (
+ self.__class__,
+ self.opstring,
+ self.precedence,
+ self.is_comparison,
+ self.natural_self_precedent,
+ self.eager_grouping,
+ self.return_type._static_cache_key if self.return_type else None,
+ )
def __call__(
self,
diff --git a/lib/sqlalchemy/sql/traversals.py b/lib/sqlalchemy/sql/traversals.py
index 4b55560ec..96758a7ad 100644
--- a/lib/sqlalchemy/sql/traversals.py
+++ b/lib/sqlalchemy/sql/traversals.py
@@ -767,7 +767,7 @@ class TraversalComparatorStrategy(HasTraversalDispatch, util.MemoizedSlots):
def visit_operator(
self, attrname, left_parent, left, right_parent, right, **kw
):
- return left is right
+ return left == right
def visit_type(
self, attrname, left_parent, left, right_parent, right, **kw