diff options
| author | mike bayer <mike_mp@zzzcomputing.com> | 2022-06-09 12:37:17 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2022-06-09 12:37:17 +0000 |
| commit | e1935b1711dd10d1cb30e3990fef2dd2e0435f1f (patch) | |
| tree | 6c454d6a23771b6b539893cc0a719ea4252e790e /lib/sqlalchemy | |
| parent | d93f952b46c9cca557774d69442a7124c3309a2d (diff) | |
| parent | 117878f7870377f143917a22160320a891eb0211 (diff) | |
| download | sqlalchemy-e1935b1711dd10d1cb30e3990fef2dd2e0435f1f.tar.gz | |
Merge "fix race conditions in lambda statements" into main
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/sql/base.py | 3 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/elements.py | 9 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/lambdas.py | 18 |
3 files changed, 23 insertions, 7 deletions
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py index f5a9c10c0..391f74772 100644 --- a/lib/sqlalchemy/sql/base.py +++ b/lib/sqlalchemy/sql/base.py @@ -694,8 +694,9 @@ class Generative(HasMemoized): cls = self.__class__ s = cls.__new__(cls) if skip: + # ensure this iteration remains atomic s.__dict__ = { - k: v for k, v in self.__dict__.items() if k not in skip + k: v for k, v in self.__dict__.copy().items() if k not in skip } else: s.__dict__ = self.__dict__.copy() diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 625e1d94b..eae06377f 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -390,7 +390,14 @@ class ClauseElement( skip = self._memoized_keys c = self.__class__.__new__(self.__class__) - c.__dict__ = {k: v for k, v in self.__dict__.items() if k not in skip} + + if skip: + # ensure this iteration remains atomic + c.__dict__ = { + k: v for k, v in self.__dict__.copy().items() if k not in skip + } + else: + c.__dict__ = self.__dict__.copy() # this is a marker that helps to "equate" clauses to each other # when a Select returns its list of FROM clauses. the cloning diff --git a/lib/sqlalchemy/sql/lambdas.py b/lib/sqlalchemy/sql/lambdas.py index 3e82a9a6a..c7464c91c 100644 --- a/lib/sqlalchemy/sql/lambdas.py +++ b/lib/sqlalchemy/sql/lambdas.py @@ -12,6 +12,7 @@ import collections.abc as collections_abc import inspect import itertools import operator +import threading import types from types import CodeType from typing import Any @@ -695,6 +696,8 @@ class AnalyzedCode: CodeType, AnalyzedCode ] = weakref.WeakKeyDictionary() + _generation_mutex = threading.RLock() + @classmethod def get(cls, fn, lambda_element, lambda_kw, **kw): try: @@ -703,11 +706,16 @@ class AnalyzedCode: except KeyError: pass - analyzed: AnalyzedCode - cls._fns[fn.__code__] = analyzed = AnalyzedCode( - fn, lambda_element, lambda_kw, **kw - ) - return analyzed + with cls._generation_mutex: + # check for other thread already created object + if fn.__code__ in cls._fns: + return cls._fns[fn.__code__] + + analyzed: AnalyzedCode + cls._fns[fn.__code__] = analyzed = AnalyzedCode( + fn, lambda_element, lambda_kw, **kw + ) + return analyzed def __init__(self, fn, lambda_element, opts): if inspect.ismethod(fn): |
