--- /dev/null
+.. change::
+ :tags: bug, engine
+ :tickets: 5048
+
+ Fixed issue where the collection of value processors on a
+ :class:`.Compiled` object would be mutated when "expanding IN" parameters
+ were used with a datatype that has bind value processors; in particular,
+ this would mean that when using statement caching and/or baked queries, the
+ same compiled._bind_processors collection would be mutated concurrently.
+ Since these processors are the same function for a given bind parameter
+ namespace every time, there was no actual negative effect of this issue,
+ however, the execution of a :class:`.Compiled` object should never be
+ causing any changes in its state, especially given that they are intended
+ to be thread-safe and reusable once fully constructed.
+
processors = compiled._bind_processors
if compiled.contains_expanding_parameters:
+ # copy processors for this case as they will be mutated
+ processors = dict(processors)
positiontup = self._expand_in_parameters(compiled, processors)
elif compiled.positional:
positiontup = self.compiled.positiontup
[(7, "jack"), (8, "fred")],
)
+ def test_expanding_in_dont_alter_compiled(self):
+ """test for issue #5048 """
+
+ class NameWithProcess(TypeDecorator):
+ impl = String
+
+ def process_bind_param(self, value, dialect):
+ return value[3:]
+
+ users = Table(
+ "query_users",
+ MetaData(),
+ Column("user_id", Integer, primary_key=True),
+ Column("user_name", NameWithProcess()),
+ )
+
+ with testing.db.connect() as conn:
+ conn.execute(
+ users.insert(),
+ [
+ dict(user_id=7, user_name="AB jack"),
+ dict(user_id=8, user_name="BE fred"),
+ dict(user_id=9, user_name="GP ed"),
+ ],
+ )
+
+ stmt = (
+ select([users])
+ .where(
+ users.c.user_name.in_(bindparam("uname", expanding=True))
+ )
+ .order_by(users.c.user_id)
+ )
+
+ compiled = stmt.compile(testing.db)
+ eq_(len(compiled._bind_processors), 1)
+
+ eq_(
+ conn.execute(
+ compiled, {"uname": ["HJ jack", "RR fred"]}
+ ).fetchall(),
+ [(7, "jack"), (8, "fred")],
+ )
+
+ eq_(len(compiled._bind_processors), 1)
+
@testing.fails_on("firebird", "uses sql-92 rules")
@testing.fails_on("sybase", "uses sql-92 rules")
@testing.skip_if(["mssql"])