# Buggy code
def _store_room_members_txn(self, txn, events, backfilled):
    """Store a room member in the database."""
    self.db_pool.simple_insert_many_txn(
        txn,
        table="room_memberships",
        values=[
            {
                "event_id": event.event_id,
                "user_id": event.state_key,
                "sender": event.user_id,
                "room_id": event.room_id,
                "membership": event.membership,
                "display_name": event.content.get("displayname", None),
                "avatar_url": event.content.get("avatar_url", None),
            }
            for event in events
        ],
    )

    for event in events:
        txn.call_after(
            self.store._membership_stream_cache.entity_has_changed,
            event.state_key,
            event.internal_metadata.stream_ordering,
        )
        txn.call_after(
            self.store.get_invited_rooms_for_local_user.invalidate,
            (event.state_key,),
        )

        # We update the local_current_membership table only if the event is
        # "current", i.e., its something that has just happened.
        #
        # This will usually get updated by the `current_state_events` handling,
        # unless its an outlier, and an outlier is only "current" if it's an "out of
        # band membership", like a remote invite or a rejection of a remote invite.
        if (
            self.is_mine_id(event.state_key)
            and not backfilled
            and event.internal_metadata.is_outlier()
            and event.internal_metadata.is_out_of_band_membership()
        ):
            self.db_pool.simple_upsert_txn(
                txn,
                table="local_current_membership",
                keyvalues={"room_id": event.room_id, "user_id": event.state_key},
                values={
                    "event_id": event.event_id,
                    "membership": event.membership,
                },
            )


# Traceback (consumeError)
synapse_1  | 2020-09-17 15:44:59,053 - synapse.http.server - 84 - ERROR - POST-1715 - Failed handle request via 'JoinRoomAliasServlet': <XForwardedForRequest at 0x7f175a2b9550 method='POST' uri='/_matrix/client/r0/join/%23matrix-spec%3Amatrix.org' clientproto='HTTP/1.1' site=8008>
synapse_1  | Traceback (most recent call last):
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/http/server.py", line 229, in _async_render_wrapper
synapse_1  |     callback_return = await self._async_render(request)
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/http/server.py", line 406, in _async_render
synapse_1  |     callback_return = await raw_callback_return
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/rest/client/v1/room.py", line 318, in on_POST
synapse_1  |     third_party_signed=content.get("third_party_signed", None),
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/handlers/room_member.py", line 317, in update_membership
synapse_1  |     require_consent=require_consent,
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/handlers/room_member.py", line 503, in _update_membership
synapse_1  |     requester, remote_room_hosts, room_id, target, content
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/handlers/room_member.py", line 1032, in _remote_join
synapse_1  |     remote_room_hosts, room_id, user.to_string(), content
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/handlers/federation.py", line 1360, in do_invite_join
synapse_1  |     origin, auth_chain, state, event, room_version_obj
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/handlers/federation.py", line 2045, in _persist_auth_tree
synapse_1  |     for e in itertools.chain(auth_events, state)
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/handlers/federation.py", line 2923, in persist_events_and_notify
synapse_1  |     event_and_contexts, backfilled=backfilled
synapse_1  |   File "/usr/local/lib/python3.7/site-packages/synapse/storage/persist_events.py", line 226, in persist_events
synapse_1  |     defer.gatherResults(deferreds, consumeErrors=True)
synapse_1  | twisted.internet.defer.FirstError: FirstError[#0, [Failure instance: Traceback: <class 'sqlite3.InterfaceError'>: Error binding parameter 1 - probably unsupported type.
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/internet/defer.py:654:_runCallbacks
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/internet/defer.py:1475:gotResult
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/internet/defer.py:1416:_inlineCallbacks
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/failure.py:512:throwExceptionIntoGenerator
synapse_1  | --- <exception caught here> ---
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/persist_events.py:148:handle_queue_loop
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/persist_events.py:254:persisting_queue
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/persist_events.py:430:_persist_events
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/internet/defer.py:1416:_inlineCallbacks
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/failure.py:512:throwExceptionIntoGenerator
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/databases/main/events.py:177:_persist_events_and_state_updates
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/internet/defer.py:1416:_inlineCallbacks
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/failure.py:512:throwExceptionIntoGenerator
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/database.py:527:runInteraction
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/internet/defer.py:1416:_inlineCallbacks
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/failure.py:512:throwExceptionIntoGenerator
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/database.py:575:runWithConnection
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/threadpool.py:250:inContext
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/threadpool.py:266:<lambda>
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/context.py:122:callWithContext
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/context.py:85:callWithContext
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/enterprise/adbapi.py:306:_runWithConnection
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/python/compat.py:464:reraise
synapse_1  | /usr/local/lib/python3.7/site-packages/twisted/enterprise/adbapi.py:297:_runWithConnection
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/database.py:572:inner_func
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/database.py:416:new_transaction
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/logging/utils.py:71:wrapped
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/databases/main/events.py:409:_persist_events_txn
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/databases/main/events.py:947:_update_metadata_tables_txn
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/databases/main/events.py:1116:_store_room_members_txn
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/database.py:685:simple_insert_many_txn
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/database.py:211:executemany
synapse_1  | /usr/local/lib/python3.7/site-packages/synapse/storage/database.py:234:_do_execute
synapse_1  | ]]

# Fixed code
def _store_room_members_txn(self, txn, events, backfilled):
    """Store a room member in the database."""

    def str_or_none(val: Any) -> Optional[str]:
        return val if isinstance(val, str) else None

    self.db_pool.simple_insert_many_txn(
        txn,
        table="room_memberships",
        values=[
            {
                "event_id": event.event_id,
                "user_id": event.state_key,
                "sender": event.user_id,
                "room_id": event.room_id,
                "membership": event.membership,
                "display_name": str_or_none(event.content.get("displayname")),
                "avatar_url": str_or_none(event.content.get("avatar_url")),
            }
            for event in events
        ],
    )

    for event in events:
        txn.call_after(
            self.store._membership_stream_cache.entity_has_changed,
            event.state_key,
            event.internal_metadata.stream_ordering,
        )
        txn.call_after(
            self.store.get_invited_rooms_for_local_user.invalidate,
            (event.state_key,),
        )

        # We update the local_current_membership table only if the event is
        # "current", i.e., its something that has just happened.
        #
        # This will usually get updated by the `current_state_events` handling,
        # unless its an outlier, and an outlier is only "current" if it's an "out of
        # band membership", like a remote invite or a rejection of a remote invite.
        if (
            self.is_mine_id(event.state_key)
            and not backfilled
            and event.internal_metadata.is_outlier()
            and event.internal_metadata.is_out_of_band_membership()
        ):
            self.db_pool.simple_upsert_txn(
                txn,
                table="local_current_membership",
                keyvalues={"room_id": event.room_id, "user_id": event.state_key},
                values={
                    "event_id": event.event_id,
                    "membership": event.membership,
                },
            )