<p dir="auto"><strong>Migrated issue, originally created by Laurence Rowe (<a href="https://github.com/lrowe">@lrowe</a>)</strong></p>
<p dir="auto">When removing a connection event:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="from sqlalchemy import create_engine
from sqlalchemy import event

engine = create_engine('sqlite:///')
connection = engine.connect()

@event.listens_for(connection, 'after_cursor_execute')
def after_cursor_execute(conn, cursor, statement, parameters, context, executemany):
    pass

event.remove(connection, 'after_cursor_execute', after_cursor_execute)"><pre class="notranslate"><code class="notranslate">from sqlalchemy import create_engine
from sqlalchemy import event

engine = create_engine('sqlite:///')
connection = engine.connect()

@event.listens_for(connection, 'after_cursor_execute')
def after_cursor_execute(conn, cursor, statement, parameters, context, executemany):
    pass

event.remove(connection, 'after_cursor_execute', after_cursor_execute)
</code></pre></div>
<p dir="auto">I see the following error:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$ bin/py connection_event_bug.py 
Traceback (most recent call last):
  File &quot;bin/py&quot;, line 61, in &lt;module&gt;
    exec(compile(__file__f.read(), __file__, &quot;exec&quot;))
  File &quot;connection_event_bug.py&quot;, line 12, in &lt;module&gt;
    event.remove(connection, 'after_cursor_execute', after_cursor_execute)
  File &quot;/Users/lrowe/.buildout/eggs/SQLAlchemy-0.8.0-py2.7-macosx-10.6-intel.egg/sqlalchemy/event.py&quot;, line 76, in remove
    for tgt in evt_cls._accept_with(target):
TypeError: 'Connection' object is not iterable"><pre class="notranslate"><code class="notranslate">$ bin/py connection_event_bug.py 
Traceback (most recent call last):
  File "bin/py", line 61, in &lt;module&gt;
    exec(compile(__file__f.read(), __file__, "exec"))
  File "connection_event_bug.py", line 12, in &lt;module&gt;
    event.remove(connection, 'after_cursor_execute', after_cursor_execute)
  File "/Users/lrowe/.buildout/eggs/SQLAlchemy-0.8.0-py2.7-macosx-10.6-intel.egg/sqlalchemy/event.py", line 76, in remove
    for tgt in evt_cls._accept_with(target):
TypeError: 'Connection' object is not iterable
</code></pre></div>
<p dir="auto">Though this does work:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="connection.dispatch.after_cursor_execute.remove(after_cursor_execute, connection)"><pre class="notranslate"><code class="notranslate">connection.dispatch.after_cursor_execute.remove(after_cursor_execute, connection)
</code></pre></div>
<h3 dir="auto">Describe the bug</h3>
<p dir="auto">Using the below code I ended up with many copies of the resulting where clause being injected into queries.  Each select during the same session had an additional <code class="notranslate">cls.deleted_at IS NULL</code> attached to it.</p>
<p dir="auto">It's very possible that I'm using this feature incorrectly.  It appears to be a fairly advanced one.</p>
<h3 dir="auto">To Reproduce</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@declarative_mixin
class SoftDeleteMixin:
    deleted_at: Mapped[datetime.datetime | None] = Column(DateTime, nullable=True)

@event.listens_for(Session, 'do_orm_execute')
def _add_soft_delete_filter(state: ORMExecuteState) -&gt; None:
    if not state.is_select:
        return

    state.statement = state.statement.options(
        with_loader_criteria(
            SoftDeleteMixin,
            lambda cls: cls.deleted_at.is_(None),
            include_aliases=True,
        )
    )"><pre class="notranslate"><span class="pl-en">@<span class="pl-s1">declarative_mixin</span></span>
<span class="pl-k">class</span> <span class="pl-v">SoftDeleteMixin</span>:
    <span class="pl-s1">deleted_at</span>: <span class="pl-v">Mapped</span>[<span class="pl-s1">datetime</span>.<span class="pl-s1">datetime</span> <span class="pl-c1">|</span> <span class="pl-c1">None</span>] <span class="pl-c1">=</span> <span class="pl-v">Column</span>(<span class="pl-v">DateTime</span>, <span class="pl-s1">nullable</span><span class="pl-c1">=</span><span class="pl-c1">True</span>)

<span class="pl-en">@<span class="pl-s1">event</span>.<span class="pl-en">listens_for</span>(<span class="pl-v">Session</span>, <span class="pl-s">'do_orm_execute'</span>)</span>
<span class="pl-k">def</span> <span class="pl-en">_add_soft_delete_filter</span>(<span class="pl-s1">state</span>: <span class="pl-v">ORMExecuteState</span>) <span class="pl-c1">-&gt;</span> <span class="pl-c1">None</span>:
    <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-s1">state</span>.<span class="pl-s1">is_select</span>:
        <span class="pl-k">return</span>

    <span class="pl-s1">state</span>.<span class="pl-s1">statement</span> <span class="pl-c1">=</span> <span class="pl-s1">state</span>.<span class="pl-s1">statement</span>.<span class="pl-en">options</span>(
        <span class="pl-en">with_loader_criteria</span>(
            <span class="pl-v">SoftDeleteMixin</span>,
            <span class="pl-k">lambda</span> <span class="pl-s1">cls</span>: <span class="pl-s1">cls</span>.<span class="pl-s1">deleted_at</span>.<span class="pl-en">is_</span>(<span class="pl-c1">None</span>),
            <span class="pl-s1">include_aliases</span><span class="pl-c1">=</span><span class="pl-c1">True</span>,
        )
    )</pre></div>
<h3 dir="auto">Error</h3>
<div class="highlight highlight-source-sql notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="WHERE cls.deleted_at IS NULL AND cls.deleted_at IS NULL AND cls.deleted_at IS NULL"><pre class="notranslate"><span class="pl-k">WHERE</span> <span class="pl-c1">cls</span>.<span class="pl-c1">deleted_at</span> IS <span class="pl-k">NULL</span> <span class="pl-k">AND</span> <span class="pl-c1">cls</span>.<span class="pl-c1">deleted_at</span> IS <span class="pl-k">NULL</span> <span class="pl-k">AND</span> <span class="pl-c1">cls</span>.<span class="pl-c1">deleted_at</span> IS <span class="pl-k">NULL</span></pre></div>
<h3 dir="auto">Versions</h3>
<ul dir="auto">
<li>OS: macos</li>
<li>Python: 3.10</li>
<li>SQLAlchemy: 1.4</li>
<li>Database: postgresql</li>
<li>DBAPI (eg: psycopg, cx_oracle, mysqlclient): pg8000</li>
</ul>
<h3 dir="auto">Additional context</h3>
<p dir="auto">I was able to get the behavior to stop by adding <code class="notranslate">propagate_to_loaders=False,</code> to the loader criteria.  Then they all have a single one.</p>