summary refs log tree commit diff
path: root/synapse
diff options
context:
space:
mode:
Diffstat (limited to 'synapse')
-rwxr-xr-xsynapse/_scripts/generate_signing_key.py13
-rw-r--r--synapse/api/auth/base.py48
-rw-r--r--synapse/api/auth/internal.py39
-rw-r--r--synapse/api/auth/msc3861_delegated.py4
-rw-r--r--synapse/app/_base.py1
-rw-r--r--synapse/config/emailconfig.py5
-rw-r--r--synapse/config/experimental.py4
-rw-r--r--synapse/config/key.py8
-rw-r--r--synapse/config/metrics.py1
-rw-r--r--synapse/config/server_notices.py14
-rw-r--r--synapse/federation/federation_client.py38
-rw-r--r--synapse/federation/transport/client.py53
-rw-r--r--synapse/handlers/account_validity.py16
-rw-r--r--synapse/handlers/auth.py8
-rw-r--r--synapse/handlers/message.py8
-rw-r--r--synapse/handlers/profile.py10
-rw-r--r--synapse/handlers/room.py10
-rw-r--r--synapse/handlers/room_member.py11
-rw-r--r--synapse/http/matrixfederationclient.py77
-rw-r--r--synapse/media/media_repository.py17
-rw-r--r--synapse/module_api/__init__.py3
-rw-r--r--synapse/module_api/callbacks/account_validity_callbacks.py6
-rw-r--r--synapse/push/emailpusher.py15
-rw-r--r--synapse/replication/tcp/streams/_base.py2
-rw-r--r--synapse/rest/__init__.py2
-rw-r--r--synapse/rest/admin/federation.py4
-rw-r--r--synapse/rest/admin/users.py10
-rw-r--r--synapse/rest/client/auth_issuer.py63
-rw-r--r--synapse/rest/client/login.py8
-rw-r--r--synapse/rest/client/profile.py31
-rw-r--r--synapse/rest/client/versions.py5
-rw-r--r--synapse/server_notices/server_notices_manager.py133
-rw-r--r--synapse/storage/databases/main/client_ips.py38
-rw-r--r--synapse/storage/databases/main/deviceinbox.py31
-rw-r--r--synapse/storage/databases/main/event_federation.py5
-rw-r--r--synapse/storage/schema/__init__.py4
-rw-r--r--synapse/storage/schema/common/full_schemas/54/full.sql8
-rw-r--r--synapse/storage/schema/main/delta/83/01_drop_old_tables.sql (renamed from synapse/storage/schema/main/full_schemas/16/room_aliases.sql)23
-rw-r--r--synapse/storage/schema/main/full_schemas/16/application_services.sql56
-rw-r--r--synapse/storage/schema/main/full_schemas/16/event_edges.sql89
-rw-r--r--synapse/storage/schema/main/full_schemas/16/event_signatures.sql57
-rw-r--r--synapse/storage/schema/main/full_schemas/16/im.sql139
-rw-r--r--synapse/storage/schema/main/full_schemas/16/keys.sql45
-rw-r--r--synapse/storage/schema/main/full_schemas/16/media_repository.sql87
-rw-r--r--synapse/storage/schema/main/full_schemas/16/presence.sql51
-rw-r--r--synapse/storage/schema/main/full_schemas/16/profiles.sql39
-rw-r--r--synapse/storage/schema/main/full_schemas/16/push.sql93
-rw-r--r--synapse/storage/schema/main/full_schemas/16/redactions.sql41
-rw-r--r--synapse/storage/schema/main/full_schemas/16/state.sql59
-rw-r--r--synapse/storage/schema/main/full_schemas/16/transactions.sql63
-rw-r--r--synapse/storage/schema/main/full_schemas/16/users.sql61
-rw-r--r--synapse/storage/schema/main/full_schemas/54/full.sql.postgres1983
-rw-r--r--synapse/storage/schema/main/full_schemas/54/full.sql.sqlite243
-rw-r--r--synapse/storage/schema/main/full_schemas/54/stream_positions.sql8
-rw-r--r--synapse/storage/schema/state/full_schemas/54/full.sql56
-rw-r--r--synapse/storage/schema/state/full_schemas/54/sequence.sql.postgres21
56 files changed, 613 insertions, 3354 deletions
diff --git a/synapse/_scripts/generate_signing_key.py b/synapse/_scripts/generate_signing_key.py
index dc0b853ed6..a329416495 100755
--- a/synapse/_scripts/generate_signing_key.py
+++ b/synapse/_scripts/generate_signing_key.py
@@ -19,6 +19,7 @@
 #
 #
 import argparse
+import os
 import sys
 
 from signedjson.key import generate_signing_key, write_signing_keys
@@ -32,15 +33,21 @@ def main() -> None:
     parser.add_argument(
         "-o",
         "--output_file",
-        type=argparse.FileType("w"),
-        default=sys.stdout,
+        type=str,
+        default="-",
         help="Where to write the output to",
     )
     args = parser.parse_args()
 
     key_id = "a_" + random_string(4)
     key = (generate_signing_key(key_id),)
-    write_signing_keys(args.output_file, key)
+    if args.output_file == "-":
+        write_signing_keys(sys.stdout, key)
+    else:
+        with open(
+            args.output_file, "w", opener=lambda p, f: os.open(p, f, mode=0o640)
+        ) as signing_key_file:
+            write_signing_keys(signing_key_file, key)
 
 
 if __name__ == "__main__":
diff --git a/synapse/api/auth/base.py b/synapse/api/auth/base.py
index bfce57158f..71b8237087 100644
--- a/synapse/api/auth/base.py
+++ b/synapse/api/auth/base.py
@@ -33,6 +33,8 @@ from synapse.api.errors import (
     UnstableSpecAuthError,
 )
 from synapse.appservice import ApplicationService
+from synapse.http import get_request_user_agent
+from synapse.http.site import SynapseRequest
 from synapse.logging.opentracing import trace
 from synapse.types import Requester, create_requester
 from synapse.util.cancellation import cancellable
@@ -51,6 +53,9 @@ class BaseAuth:
         self.store = hs.get_datastores().main
         self._storage_controllers = hs.get_storage_controllers()
 
+        self._track_appservice_user_ips = hs.config.appservice.track_appservice_user_ips
+        self._track_puppeted_user_ips = hs.config.api.track_puppeted_user_ips
+
     async def check_user_in_room(
         self,
         room_id: str,
@@ -355,3 +360,46 @@ class BaseAuth:
         return create_requester(
             effective_user_id, app_service=app_service, device_id=effective_device_id
         )
+
+    async def _record_request(
+        self, request: SynapseRequest, requester: Requester
+    ) -> None:
+        """Record that this request was made.
+
+        This updates the client_ips and monthly_active_user tables.
+        """
+        ip_addr = request.get_client_ip_if_available()
+
+        if ip_addr and (not requester.app_service or self._track_appservice_user_ips):
+            user_agent = get_request_user_agent(request)
+            access_token = self.get_access_token_from_request(request)
+
+            # XXX(quenting): I'm 95% confident that we could skip setting the
+            # device_id to "dummy-device" for appservices, and that the only impact
+            # would be some rows which whould not deduplicate in the 'user_ips'
+            # table during the transition
+            recorded_device_id = (
+                "dummy-device"
+                if requester.device_id is None and requester.app_service is not None
+                else requester.device_id
+            )
+            await self.store.insert_client_ip(
+                user_id=requester.authenticated_entity,
+                access_token=access_token,
+                ip=ip_addr,
+                user_agent=user_agent,
+                device_id=recorded_device_id,
+            )
+
+            # Track also the puppeted user client IP if enabled and the user is puppeting
+            if (
+                requester.user.to_string() != requester.authenticated_entity
+                and self._track_puppeted_user_ips
+            ):
+                await self.store.insert_client_ip(
+                    user_id=requester.user.to_string(),
+                    access_token=access_token,
+                    ip=ip_addr,
+                    user_agent=user_agent,
+                    device_id=requester.device_id,
+                )
diff --git a/synapse/api/auth/internal.py b/synapse/api/auth/internal.py
index cacf5ebbe3..832dfd0b0a 100644
--- a/synapse/api/auth/internal.py
+++ b/synapse/api/auth/internal.py
@@ -28,7 +28,6 @@ from synapse.api.errors import (
     InvalidClientTokenError,
     MissingClientTokenError,
 )
-from synapse.http import get_request_user_agent
 from synapse.http.site import SynapseRequest
 from synapse.logging.opentracing import active_span, force_tracing, start_active_span
 from synapse.types import Requester, create_requester
@@ -54,8 +53,6 @@ class InternalAuth(BaseAuth):
         self._account_validity_handler = hs.get_account_validity_handler()
         self._macaroon_generator = hs.get_macaroon_generator()
 
-        self._track_appservice_user_ips = hs.config.appservice.track_appservice_user_ips
-        self._track_puppeted_user_ips = hs.config.api.track_puppeted_user_ips
         self._force_tracing_for_users = hs.config.tracing.force_tracing_for_users
 
     @cancellable
@@ -121,9 +118,6 @@ class InternalAuth(BaseAuth):
         Once get_user_by_req has set up the opentracing span, this does the actual work.
         """
         try:
-            ip_addr = request.get_client_ip_if_available()
-            user_agent = get_request_user_agent(request)
-
             access_token = self.get_access_token_from_request(request)
 
             # First check if it could be a request from an appservice
@@ -160,38 +154,7 @@ class InternalAuth(BaseAuth):
                             errcode=Codes.EXPIRED_ACCOUNT,
                         )
 
-            if ip_addr and (
-                not requester.app_service or self._track_appservice_user_ips
-            ):
-                # XXX(quenting): I'm 95% confident that we could skip setting the
-                # device_id to "dummy-device" for appservices, and that the only impact
-                # would be some rows which whould not deduplicate in the 'user_ips'
-                # table during the transition
-                recorded_device_id = (
-                    "dummy-device"
-                    if requester.device_id is None and requester.app_service is not None
-                    else requester.device_id
-                )
-                await self.store.insert_client_ip(
-                    user_id=requester.authenticated_entity,
-                    access_token=access_token,
-                    ip=ip_addr,
-                    user_agent=user_agent,
-                    device_id=recorded_device_id,
-                )
-
-                # Track also the puppeted user client IP if enabled and the user is puppeting
-                if (
-                    requester.user.to_string() != requester.authenticated_entity
-                    and self._track_puppeted_user_ips
-                ):
-                    await self.store.insert_client_ip(
-                        user_id=requester.user.to_string(),
-                        access_token=access_token,
-                        ip=ip_addr,
-                        user_agent=user_agent,
-                        device_id=requester.device_id,
-                    )
+            await self._record_request(request, requester)
 
             if requester.is_guest and not allow_guest:
                 raise AuthError(
diff --git a/synapse/api/auth/msc3861_delegated.py b/synapse/api/auth/msc3861_delegated.py
index 75256b67ea..86b9ad96c9 100644
--- a/synapse/api/auth/msc3861_delegated.py
+++ b/synapse/api/auth/msc3861_delegated.py
@@ -233,6 +233,10 @@ class MSC3861DelegatedAuth(BaseAuth):
             # so that we don't provision the user if they don't have enough permission:
             requester = await self.get_user_by_access_token(access_token, allow_expired)
 
+        # Do not record requests from MAS using the virtual `__oidc_admin` user.
+        if access_token != self._admin_token:
+            await self._record_request(request, requester)
+
         if not allow_guest and requester.is_guest:
             raise OAuthInsufficientScopeError([SCOPE_MATRIX_API])
 
diff --git a/synapse/app/_base.py b/synapse/app/_base.py
index c24890ce28..09df846d86 100644
--- a/synapse/app/_base.py
+++ b/synapse/app/_base.py
@@ -670,6 +670,7 @@ def setup_sentry(hs: "HomeServer") -> None:
     sentry_sdk.init(
         dsn=hs.config.metrics.sentry_dsn,
         release=SYNAPSE_VERSION,
+        environment=hs.config.metrics.sentry_environment,
     )
 
     # We set some default tags that give some context to this instance
diff --git a/synapse/config/emailconfig.py b/synapse/config/emailconfig.py
index 19925006cc..7dadaa5843 100644
--- a/synapse/config/emailconfig.py
+++ b/synapse/config/emailconfig.py
@@ -298,6 +298,11 @@ class EmailConfig(Config):
             self.email_riot_base_url = email_config.get(
                 "client_base_url", email_config.get("riot_base_url", None)
             )
+            # The amount of time we always wait before ever emailing about a notification
+            # (to give the user a chance to respond to other push or notice the window)
+            self.notif_delay_before_mail_ms = Config.parse_duration(
+                email_config.get("notif_delay_before_mail", "10m")
+            )
 
         if self.root.account_validity.account_validity_renew_by_email_enabled:
             expiry_template_html = email_config.get(
diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py
index 15e51913a9..149b7ffd0e 100644
--- a/synapse/config/experimental.py
+++ b/synapse/config/experimental.py
@@ -425,3 +425,7 @@ class ExperimentalConfig(Config):
         self.msc4028_push_encrypted_events = experimental.get(
             "msc4028_push_encrypted_events", False
         )
+
+        self.msc4069_profile_inhibit_propagation = experimental.get(
+            "msc4069_profile_inhibit_propagation", False
+        )
diff --git a/synapse/config/key.py b/synapse/config/key.py
index 8004cc6401..7697de010b 100644
--- a/synapse/config/key.py
+++ b/synapse/config/key.py
@@ -268,7 +268,9 @@ class KeyConfig(Config):
 
         if not self.path_exists(signing_key_path):
             print("Generating signing key file %s" % (signing_key_path,))
-            with open(signing_key_path, "w") as signing_key_file:
+            with open(
+                signing_key_path, "w", opener=lambda p, f: os.open(p, f, mode=0o640)
+            ) as signing_key_file:
                 key_id = "a_" + random_string(4)
                 write_signing_keys(signing_key_file, (generate_signing_key(key_id),))
         else:
@@ -279,7 +281,9 @@ class KeyConfig(Config):
                 key = decode_signing_key_base64(
                     NACL_ED25519, key_id, signing_keys.split("\n")[0]
                 )
-                with open(signing_key_path, "w") as signing_key_file:
+                with open(
+                    signing_key_path, "w", opener=lambda p, f: os.open(p, f, mode=0o640)
+                ) as signing_key_file:
                     write_signing_keys(signing_key_file, (key,))
 
 
diff --git a/synapse/config/metrics.py b/synapse/config/metrics.py
index f092a55063..cc782b9ae2 100644
--- a/synapse/config/metrics.py
+++ b/synapse/config/metrics.py
@@ -66,6 +66,7 @@ class MetricsConfig(Config):
             check_requirements("sentry")
 
             self.sentry_dsn = config["sentry"].get("dsn")
+            self.sentry_environment = config["sentry"].get("environment")
             if not self.sentry_dsn:
                 raise ConfigError(
                     "sentry.dsn field is required when sentry integration is enabled"
diff --git a/synapse/config/server_notices.py b/synapse/config/server_notices.py
index 591a4f6e79..4de2d62b54 100644
--- a/synapse/config/server_notices.py
+++ b/synapse/config/server_notices.py
@@ -44,6 +44,14 @@ class ServerNoticesConfig(Config):
         server_notices_room_name (str|None):
             The name to use for the server notices room.
             None if server notices are not enabled.
+
+        server_notices_room_avatar_url (str|None):
+            The avatar URL to use for the server notices room.
+            None if server notices are not enabled.
+
+        server_notices_room_topic (str|None):
+            The topic to use for the server notices room.
+            None if server notices are not enabled.
     """
 
     section = "servernotices"
@@ -54,6 +62,9 @@ class ServerNoticesConfig(Config):
         self.server_notices_mxid_display_name: Optional[str] = None
         self.server_notices_mxid_avatar_url: Optional[str] = None
         self.server_notices_room_name: Optional[str] = None
+        self.server_notices_room_avatar_url: Optional[str] = None
+        self.server_notices_room_topic: Optional[str] = None
+        self.server_notices_auto_join: bool = False
 
     def read_config(self, config: JsonDict, **kwargs: Any) -> None:
         c = config.get("server_notices")
@@ -68,3 +79,6 @@ class ServerNoticesConfig(Config):
         self.server_notices_mxid_avatar_url = c.get("system_mxid_avatar_url", None)
         # todo: i18n
         self.server_notices_room_name = c.get("room_name", "Server Notices")
+        self.server_notices_room_avatar_url = c.get("room_avatar_url", None)
+        self.server_notices_room_topic = c.get("room_topic", None)
+        self.server_notices_auto_join = c.get("auto_join", False)
diff --git a/synapse/federation/federation_client.py b/synapse/federation/federation_client.py
index 50e70e6164..c412063091 100644
--- a/synapse/federation/federation_client.py
+++ b/synapse/federation/federation_client.py
@@ -26,6 +26,7 @@ from typing import (
     TYPE_CHECKING,
     AbstractSet,
     Awaitable,
+    BinaryIO,
     Callable,
     Collection,
     Container,
@@ -1867,6 +1868,43 @@ class FederationClient(FederationBase):
 
         return filtered_statuses, filtered_failures
 
+    async def download_media(
+        self,
+        destination: str,
+        media_id: str,
+        output_stream: BinaryIO,
+        max_size: int,
+        max_timeout_ms: int,
+    ) -> Tuple[int, Dict[bytes, List[bytes]]]:
+        try:
+            return await self.transport_layer.download_media_v3(
+                destination,
+                media_id,
+                output_stream=output_stream,
+                max_size=max_size,
+                max_timeout_ms=max_timeout_ms,
+            )
+        except HttpResponseException as e:
+            # If an error is received that is due to an unrecognised endpoint,
+            # fallback to the r0 endpoint. Otherwise, consider it a legitimate error
+            # and raise.
+            if not is_unknown_endpoint(e):
+                raise
+
+        logger.debug(
+            "Couldn't download media %s/%s with the v3 API, falling back to the r0 API",
+            destination,
+            media_id,
+        )
+
+        return await self.transport_layer.download_media_r0(
+            destination,
+            media_id,
+            output_stream=output_stream,
+            max_size=max_size,
+            max_timeout_ms=max_timeout_ms,
+        )
+
 
 @attr.s(frozen=True, slots=True, auto_attribs=True)
 class TimestampToEventResponse:
diff --git a/synapse/federation/transport/client.py b/synapse/federation/transport/client.py
index 0f2af0e482..8cd3142313 100644
--- a/synapse/federation/transport/client.py
+++ b/synapse/federation/transport/client.py
@@ -23,6 +23,7 @@ import urllib
 from typing import (
     TYPE_CHECKING,
     Any,
+    BinaryIO,
     Callable,
     Collection,
     Dict,
@@ -809,6 +810,58 @@ class TransportLayerClient:
             destination=destination, path=path, data={"user_ids": user_ids}
         )
 
+    async def download_media_r0(
+        self,
+        destination: str,
+        media_id: str,
+        output_stream: BinaryIO,
+        max_size: int,
+        max_timeout_ms: int,
+    ) -> Tuple[int, Dict[bytes, List[bytes]]]:
+        path = f"/_matrix/media/r0/download/{destination}/{media_id}"
+
+        return await self.client.get_file(
+            destination,
+            path,
+            output_stream=output_stream,
+            max_size=max_size,
+            args={
+                # tell the remote server to 404 if it doesn't
+                # recognise the server_name, to make sure we don't
+                # end up with a routing loop.
+                "allow_remote": "false",
+                "timeout_ms": str(max_timeout_ms),
+            },
+        )
+
+    async def download_media_v3(
+        self,
+        destination: str,
+        media_id: str,
+        output_stream: BinaryIO,
+        max_size: int,
+        max_timeout_ms: int,
+    ) -> Tuple[int, Dict[bytes, List[bytes]]]:
+        path = f"/_matrix/media/v3/download/{destination}/{media_id}"
+
+        return await self.client.get_file(
+            destination,
+            path,
+            output_stream=output_stream,
+            max_size=max_size,
+            args={
+                # tell the remote server to 404 if it doesn't
+                # recognise the server_name, to make sure we don't
+                # end up with a routing loop.
+                "allow_remote": "false",
+                "timeout_ms": str(max_timeout_ms),
+                # Matrix 1.7 allows for this to redirect to another URL, this should
+                # just be ignored for an old homeserver, so always provide it.
+                "allow_redirect": "true",
+            },
+            follow_redirects=True,
+        )
+
 
 def _create_path(federation_prefix: str, path: str, *args: str) -> str:
     """
diff --git a/synapse/handlers/account_validity.py b/synapse/handlers/account_validity.py
index 61ce4931ba..7004d95a0f 100644
--- a/synapse/handlers/account_validity.py
+++ b/synapse/handlers/account_validity.py
@@ -104,6 +104,22 @@ class AccountValidityHandler:
         for callback in self._module_api_callbacks.on_user_registration_callbacks:
             await callback(user_id)
 
+    async def on_user_login(
+        self,
+        user_id: str,
+        auth_provider_type: Optional[str],
+        auth_provider_id: Optional[str],
+    ) -> None:
+        """Tell third-party modules about a user logins.
+
+        Args:
+            user_id: The mxID of the user.
+            auth_provider_type: The type of login.
+            auth_provider_id: The ID of the auth provider.
+        """
+        for callback in self._module_api_callbacks.on_user_login_callbacks:
+            await callback(user_id, auth_provider_type, auth_provider_id)
+
     @wrap_as_background_process("send_renewals")
     async def _send_renewal_emails(self) -> None:
         """Gets the list of users whose account is expiring in the amount of time
diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py
index f9a0ac9adc..8635111694 100644
--- a/synapse/handlers/auth.py
+++ b/synapse/handlers/auth.py
@@ -216,6 +216,7 @@ class AuthHandler:
         self._password_enabled_for_reauth = hs.config.auth.password_enabled_for_reauth
         self._password_localdb_enabled = hs.config.auth.password_localdb_enabled
         self._third_party_rules = hs.get_module_api_callbacks().third_party_event_rules
+        self._account_validity_handler = hs.get_account_validity_handler()
 
         # Ratelimiter for failed auth during UIA. Uses same ratelimit config
         # as per `rc_login.failed_attempts`.
@@ -1787,6 +1788,13 @@ class AuthHandler:
             client_redirect_url, "loginToken", login_token
         )
 
+        # Run post-login module callback handlers
+        await self._account_validity_handler.on_user_login(
+            user_id=registered_user_id,
+            auth_provider_type=LoginType.SSO,
+            auth_provider_id=auth_provider_id,
+        )
+
         # if the client is whitelisted, we can redirect straight to it
         if client_redirect_url.startswith(self._whitelisted_sso_clients):
             request.redirect(redirect_url)
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index 1a228717ad..862151ee3d 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -696,13 +696,9 @@ class EventCreationHandler:
         if require_consent and not is_exempt:
             await self.assert_accepted_privacy_policy(requester)
 
-        # Save the access token ID, the device ID and the transaction ID in the event
-        # internal metadata. This is useful to determine if we should echo the
-        # transaction_id in events.
+        # Save the the device ID and the transaction ID in the event internal metadata.
+        # This is useful to determine if we should echo the transaction_id in events.
         # See `synapse.events.utils.EventClientSerializer.serialize_event`
-        if requester.access_token_id is not None:
-            builder.internal_metadata.token_id = requester.access_token_id
-
         if requester.device_id is not None:
             builder.internal_metadata.device_id = requester.device_id
 
diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py
index e6b97e1335..3adee8869d 100644
--- a/synapse/handlers/profile.py
+++ b/synapse/handlers/profile.py
@@ -135,6 +135,7 @@ class ProfileHandler:
         new_displayname: str,
         by_admin: bool = False,
         deactivation: bool = False,
+        propagate: bool = True,
     ) -> None:
         """Set the displayname of a user
 
@@ -144,6 +145,7 @@ class ProfileHandler:
             new_displayname: The displayname to give this user.
             by_admin: Whether this change was made by an administrator.
             deactivation: Whether this change was made while deactivating the user.
+            propagate: Whether this change also applies to the user's membership events.
         """
         if not self.hs.is_mine(target_user):
             raise SynapseError(400, "User is not hosted on this homeserver")
@@ -194,7 +196,8 @@ class ProfileHandler:
             target_user.to_string(), profile, by_admin, deactivation
         )
 
-        await self._update_join_states(requester, target_user)
+        if propagate:
+            await self._update_join_states(requester, target_user)
 
     async def get_avatar_url(self, target_user: UserID) -> Optional[str]:
         if self.hs.is_mine(target_user):
@@ -227,6 +230,7 @@ class ProfileHandler:
         new_avatar_url: str,
         by_admin: bool = False,
         deactivation: bool = False,
+        propagate: bool = True,
     ) -> None:
         """Set a new avatar URL for a user.
 
@@ -236,6 +240,7 @@ class ProfileHandler:
             new_avatar_url: The avatar URL to give this user.
             by_admin: Whether this change was made by an administrator.
             deactivation: Whether this change was made while deactivating the user.
+            propagate: Whether this change also applies to the user's membership events.
         """
         if not self.hs.is_mine(target_user):
             raise SynapseError(400, "User is not hosted on this homeserver")
@@ -284,7 +289,8 @@ class ProfileHandler:
             target_user.to_string(), profile, by_admin, deactivation
         )
 
-        await self._update_join_states(requester, target_user)
+        if propagate:
+            await self._update_join_states(requester, target_user)
 
     @cached()
     async def check_avatar_size_and_mime_type(self, mxc: str) -> bool:
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 66470d186f..e78e598d5e 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -555,7 +555,7 @@ class RoomCreationHandler:
         except (TypeError, ValueError):
             ban = 50
         needed_power_level = max(
-            state_default_int, ban, max(event_power_levels.values())
+            state_default_int, ban, max(event_power_levels.values(), default=0)
         )
 
         # Get the user's current power level, this matches the logic in get_user_power_level,
@@ -704,6 +704,7 @@ class RoomCreationHandler:
         config: JsonDict,
         ratelimit: bool = True,
         creator_join_profile: Optional[JsonDict] = None,
+        ignore_forced_encryption: bool = False,
     ) -> Tuple[str, Optional[RoomAlias], int]:
         """Creates a new room.
 
@@ -720,6 +721,8 @@ class RoomCreationHandler:
                 derived from the user's profile. If set, should contain the
                 values to go in the body of the 'join' event (typically
                 `avatar_url` and/or `displayname`.
+            ignore_forced_encryption:
+                Ignore encryption forced by `encryption_enabled_by_default_for_room_type` setting.
 
         Returns:
             A 3-tuple containing:
@@ -1021,6 +1024,7 @@ class RoomCreationHandler:
         room_alias: Optional[RoomAlias] = None,
         power_level_content_override: Optional[JsonDict] = None,
         creator_join_profile: Optional[JsonDict] = None,
+        ignore_forced_encryption: bool = False,
     ) -> Tuple[int, str, int]:
         """Sends the initial events into a new room. Sends the room creation, membership,
         and power level events into the room sequentially, then creates and batches up the
@@ -1055,6 +1059,8 @@ class RoomCreationHandler:
             creator_join_profile:
                 Set to override the displayname and avatar for the creating
                 user in this room.
+            ignore_forced_encryption:
+                Ignore encryption forced by `encryption_enabled_by_default_for_room_type` setting.
 
         Returns:
             A tuple containing the stream ID, event ID and depth of the last
@@ -1257,7 +1263,7 @@ class RoomCreationHandler:
             )
             events_to_send.append((event, context))
 
-        if config["encrypted"]:
+        if config["encrypted"] and not ignore_forced_encryption:
             encryption_event, encryption_context = await create_event(
                 EventTypes.RoomEncryption,
                 {"algorithm": RoomEncryptionAlgorithms.DEFAULT},
diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py
index 9fd13c41c4..23dbf515dd 100644
--- a/synapse/handlers/room_member.py
+++ b/synapse/handlers/room_member.py
@@ -2116,9 +2116,14 @@ class RoomForgetterHandler(StateDeltasHandler):
                 self.pos = room_max_stream_ordering
 
         if not self._hs.config.room.forget_on_leave:
-            # Update the processing position, so that if the server admin turns the
-            # feature on at a later date, we don't decide to forget every room that
-            # has ever been left in the past.
+            # Update the processing position, so that if the server admin turns
+            # the feature on at a later date, we don't decide to forget every
+            # room that has ever been left in the past.
+            #
+            # We wait for a short time so that we don't "tight" loop just
+            # keeping the table up to date.
+            await self._clock.sleep(0.5)
+
             self.pos = self._store.get_room_max_stream_ordering()
             await self._store.update_room_forgetter_stream_pos(self.pos)
             return
diff --git a/synapse/http/matrixfederationclient.py b/synapse/http/matrixfederationclient.py
index 04497a426c..8f139c1bdf 100644
--- a/synapse/http/matrixfederationclient.py
+++ b/synapse/http/matrixfederationclient.py
@@ -159,12 +159,18 @@ class MatrixFederationRequest:
     """Query arguments.
     """
 
-    txn_id: Optional[str] = None
-    """Unique ID for this request (for logging)
+    txn_id: str = attr.ib(init=False)
+    """Unique ID for this request (for logging), this is autogenerated.
     """
 
-    uri: bytes = attr.ib(init=False)
-    """The URI of this request
+    uri: bytes = b""
+    """The URI of this request, usually generated from the above information.
+    """
+
+    _generate_uri: bool = True
+    """True to automatically generate the uri field based on the above information.
+
+    Set to False if manually configuring the URI.
     """
 
     def __attrs_post_init__(self) -> None:
@@ -174,22 +180,23 @@ class MatrixFederationRequest:
 
         object.__setattr__(self, "txn_id", txn_id)
 
-        destination_bytes = self.destination.encode("ascii")
-        path_bytes = self.path.encode("ascii")
-        query_bytes = encode_query_args(self.query)
-
-        # The object is frozen so we can pre-compute this.
-        uri = urllib.parse.urlunparse(
-            (
-                b"matrix-federation",
-                destination_bytes,
-                path_bytes,
-                None,
-                query_bytes,
-                b"",
+        if self._generate_uri:
+            destination_bytes = self.destination.encode("ascii")
+            path_bytes = self.path.encode("ascii")
+            query_bytes = encode_query_args(self.query)
+
+            # The object is frozen so we can pre-compute this.
+            uri = urllib.parse.urlunparse(
+                (
+                    b"matrix-federation",
+                    destination_bytes,
+                    path_bytes,
+                    None,
+                    query_bytes,
+                    b"",
+                )
             )
-        )
-        object.__setattr__(self, "uri", uri)
+            object.__setattr__(self, "uri", uri)
 
     def get_json(self) -> Optional[JsonDict]:
         if self.json_callback:
@@ -519,6 +526,7 @@ class MatrixFederationHttpClient:
         ignore_backoff: bool = False,
         backoff_on_404: bool = False,
         backoff_on_all_error_codes: bool = False,
+        follow_redirects: bool = False,
     ) -> IResponse:
         """
         Sends a request to the given server.
@@ -561,6 +569,9 @@ class MatrixFederationHttpClient:
             backoff_on_404: Back off if we get a 404
             backoff_on_all_error_codes: Back off if we get any error response
 
+            follow_redirects: True to follow the Location header of 307/308 redirect
+                responses. This does not recurse.
+
         Returns:
             Resolves with the HTTP response object on success.
 
@@ -720,6 +731,26 @@ class MatrixFederationHttpClient:
                             response.code,
                             response_phrase,
                         )
+                    elif (
+                        response.code in (307, 308)
+                        and follow_redirects
+                        and response.headers.hasHeader("Location")
+                    ):
+                        # The Location header *might* be relative so resolve it.
+                        location = response.headers.getRawHeaders(b"Location")[0]
+                        new_uri = urllib.parse.urljoin(request.uri, location)
+
+                        return await self._send_request(
+                            attr.evolve(request, uri=new_uri, generate_uri=False),
+                            retry_on_dns_fail,
+                            timeout,
+                            long_retries,
+                            ignore_backoff,
+                            backoff_on_404,
+                            backoff_on_all_error_codes,
+                            # Do not continue following redirects.
+                            follow_redirects=False,
+                        )
                     else:
                         logger.info(
                             "{%s} [%s] Got response headers: %d %s",
@@ -1389,6 +1420,7 @@ class MatrixFederationHttpClient:
         retry_on_dns_fail: bool = True,
         max_size: Optional[int] = None,
         ignore_backoff: bool = False,
+        follow_redirects: bool = False,
     ) -> Tuple[int, Dict[bytes, List[bytes]]]:
         """GETs a file from a given homeserver
         Args:
@@ -1398,6 +1430,8 @@ class MatrixFederationHttpClient:
             args: Optional dictionary used to create the query string.
             ignore_backoff: true to ignore the historical backoff data
                 and try the request anyway.
+            follow_redirects: True to follow the Location header of 307/308 redirect
+                responses. This does not recurse.
 
         Returns:
             Resolves with an (int,dict) tuple of
@@ -1418,7 +1452,10 @@ class MatrixFederationHttpClient:
         )
 
         response = await self._send_request(
-            request, retry_on_dns_fail=retry_on_dns_fail, ignore_backoff=ignore_backoff
+            request,
+            retry_on_dns_fail=retry_on_dns_fail,
+            ignore_backoff=ignore_backoff,
+            follow_redirects=follow_redirects,
         )
 
         headers = dict(response.headers.getAllRawHeaders())
diff --git a/synapse/media/media_repository.py b/synapse/media/media_repository.py
index f1e1eb09df..4cb975d377 100644
--- a/synapse/media/media_repository.py
+++ b/synapse/media/media_repository.py
@@ -82,7 +82,7 @@ class MediaRepository:
     def __init__(self, hs: "HomeServer"):
         self.hs = hs
         self.auth = hs.get_auth()
-        self.client = hs.get_federation_http_client()
+        self.client = hs.get_federation_client()
         self.clock = hs.get_clock()
         self.server_name = hs.hostname
         self.store = hs.get_datastores().main
@@ -649,22 +649,13 @@ class MediaRepository:
         file_info = FileInfo(server_name=server_name, file_id=file_id)
 
         with self.media_storage.store_into_file(file_info) as (f, fname, finish):
-            request_path = "/".join(
-                ("/_matrix/media/r0/download", server_name, media_id)
-            )
             try:
-                length, headers = await self.client.get_file(
+                length, headers = await self.client.download_media(
                     server_name,
-                    request_path,
+                    media_id,
                     output_stream=f,
                     max_size=self.max_upload_size,
-                    args={
-                        # tell the remote server to 404 if it doesn't
-                        # recognise the server_name, to make sure we don't
-                        # end up with a routing loop.
-                        "allow_remote": "false",
-                        "timeout_ms": str(max_timeout_ms),
-                    },
+                    max_timeout_ms=max_timeout_ms,
                 )
             except RequestSendFailed as e:
                 logger.warning(
diff --git a/synapse/module_api/__init__.py b/synapse/module_api/__init__.py
index 256e80f4fb..f7ad0993a0 100644
--- a/synapse/module_api/__init__.py
+++ b/synapse/module_api/__init__.py
@@ -85,6 +85,7 @@ from synapse.module_api.callbacks.account_validity_callbacks import (
     ON_LEGACY_ADMIN_REQUEST,
     ON_LEGACY_RENEW_CALLBACK,
     ON_LEGACY_SEND_MAIL_CALLBACK,
+    ON_USER_LOGIN_CALLBACK,
     ON_USER_REGISTRATION_CALLBACK,
 )
 from synapse.module_api.callbacks.spamchecker_callbacks import (
@@ -339,6 +340,7 @@ class ModuleApi:
         *,
         is_user_expired: Optional[IS_USER_EXPIRED_CALLBACK] = None,
         on_user_registration: Optional[ON_USER_REGISTRATION_CALLBACK] = None,
+        on_user_login: Optional[ON_USER_LOGIN_CALLBACK] = None,
         on_legacy_send_mail: Optional[ON_LEGACY_SEND_MAIL_CALLBACK] = None,
         on_legacy_renew: Optional[ON_LEGACY_RENEW_CALLBACK] = None,
         on_legacy_admin_request: Optional[ON_LEGACY_ADMIN_REQUEST] = None,
@@ -350,6 +352,7 @@ class ModuleApi:
         return self._callbacks.account_validity.register_callbacks(
             is_user_expired=is_user_expired,
             on_user_registration=on_user_registration,
+            on_user_login=on_user_login,
             on_legacy_send_mail=on_legacy_send_mail,
             on_legacy_renew=on_legacy_renew,
             on_legacy_admin_request=on_legacy_admin_request,
diff --git a/synapse/module_api/callbacks/account_validity_callbacks.py b/synapse/module_api/callbacks/account_validity_callbacks.py
index 1c731c69ce..0a99049a1c 100644
--- a/synapse/module_api/callbacks/account_validity_callbacks.py
+++ b/synapse/module_api/callbacks/account_validity_callbacks.py
@@ -28,6 +28,7 @@ logger = logging.getLogger(__name__)
 # Types for callbacks to be registered via the module api
 IS_USER_EXPIRED_CALLBACK = Callable[[str], Awaitable[Optional[bool]]]
 ON_USER_REGISTRATION_CALLBACK = Callable[[str], Awaitable]
+ON_USER_LOGIN_CALLBACK = Callable[[str, Optional[str], Optional[str]], Awaitable]
 # Temporary hooks to allow for a transition from `/_matrix/client` endpoints
 # to `/_synapse/client/account_validity`. See `register_callbacks` below.
 ON_LEGACY_SEND_MAIL_CALLBACK = Callable[[str], Awaitable]
@@ -39,6 +40,7 @@ class AccountValidityModuleApiCallbacks:
     def __init__(self) -> None:
         self.is_user_expired_callbacks: List[IS_USER_EXPIRED_CALLBACK] = []
         self.on_user_registration_callbacks: List[ON_USER_REGISTRATION_CALLBACK] = []
+        self.on_user_login_callbacks: List[ON_USER_LOGIN_CALLBACK] = []
         self.on_legacy_send_mail_callback: Optional[ON_LEGACY_SEND_MAIL_CALLBACK] = None
         self.on_legacy_renew_callback: Optional[ON_LEGACY_RENEW_CALLBACK] = None
 
@@ -50,6 +52,7 @@ class AccountValidityModuleApiCallbacks:
         self,
         is_user_expired: Optional[IS_USER_EXPIRED_CALLBACK] = None,
         on_user_registration: Optional[ON_USER_REGISTRATION_CALLBACK] = None,
+        on_user_login: Optional[ON_USER_LOGIN_CALLBACK] = None,
         on_legacy_send_mail: Optional[ON_LEGACY_SEND_MAIL_CALLBACK] = None,
         on_legacy_renew: Optional[ON_LEGACY_RENEW_CALLBACK] = None,
         on_legacy_admin_request: Optional[ON_LEGACY_ADMIN_REQUEST] = None,
@@ -61,6 +64,9 @@ class AccountValidityModuleApiCallbacks:
         if on_user_registration is not None:
             self.on_user_registration_callbacks.append(on_user_registration)
 
+        if on_user_login is not None:
+            self.on_user_login_callbacks.append(on_user_login)
+
         # The builtin account validity feature exposes 3 endpoints (send_mail, renew, and
         # an admin one). As part of moving the feature into a module, we need to change
         # the path from /_matrix/client/unstable/account_validity/... to
diff --git a/synapse/push/emailpusher.py b/synapse/push/emailpusher.py
index 5b2791bbd9..df2497643e 100644
--- a/synapse/push/emailpusher.py
+++ b/synapse/push/emailpusher.py
@@ -36,14 +36,9 @@ if TYPE_CHECKING:
 
 logger = logging.getLogger(__name__)
 
-# The amount of time we always wait before ever emailing about a notification
-# (to give the user a chance to respond to other push or notice the window)
-DELAY_BEFORE_MAIL_MS = 10 * 60 * 1000
-
 # THROTTLE is the minimum time between mail notifications sent for a given room.
 # Each room maintains its own throttle counter, but each new mail notification
 # sends the pending notifications for all rooms.
-THROTTLE_START_MS = 10 * 60 * 1000
 THROTTLE_MAX_MS = 24 * 60 * 60 * 1000  # 24h
 # THROTTLE_MULTIPLIER = 6              # 10 mins, 1 hour, 6 hours, 24 hours
 THROTTLE_MULTIPLIER = 144  # 10 mins, 24 hours - i.e. jump straight to 1 day
@@ -86,6 +81,8 @@ class EmailPusher(Pusher):
         except ValueError:
             raise PusherConfigException("Invalid email")
 
+        self._delay_before_mail_ms = self.hs.config.email.notif_delay_before_mail_ms
+
     def on_started(self, should_check_for_notifs: bool) -> None:
         """Called when this pusher has been started.
 
@@ -186,7 +183,7 @@ class EmailPusher(Pusher):
             received_at = push_action.received_ts
             if received_at is None:
                 received_at = 0
-            notif_ready_at = received_at + DELAY_BEFORE_MAIL_MS
+            notif_ready_at = received_at + self._delay_before_mail_ms
 
             room_ready_at = self.room_ready_to_notify_at(push_action.room_id)
 
@@ -202,7 +199,7 @@ class EmailPusher(Pusher):
                     "room_id": push_action.room_id,
                     "now": self.clock.time_msec(),
                     "received_at": received_at,
-                    "delay_before_mail_ms": DELAY_BEFORE_MAIL_MS,
+                    "delay_before_mail_ms": self._delay_before_mail_ms,
                     "last_sent_ts": self.get_room_last_sent_ts(push_action.room_id),
                     "throttle_ms": self.get_room_throttle_ms(push_action.room_id),
                 }
@@ -306,10 +303,10 @@ class EmailPusher(Pusher):
         current_throttle_ms = self.get_room_throttle_ms(room_id)
 
         if gap > THROTTLE_RESET_AFTER_MS:
-            new_throttle_ms = THROTTLE_START_MS
+            new_throttle_ms = self._delay_before_mail_ms
         else:
             if current_throttle_ms == 0:
-                new_throttle_ms = THROTTLE_START_MS
+                new_throttle_ms = self._delay_before_mail_ms
             else:
                 new_throttle_ms = min(
                     current_throttle_ms * THROTTLE_MULTIPLIER, THROTTLE_MAX_MS
diff --git a/synapse/replication/tcp/streams/_base.py b/synapse/replication/tcp/streams/_base.py
index 13cc1a10f7..09b20588af 100644
--- a/synapse/replication/tcp/streams/_base.py
+++ b/synapse/replication/tcp/streams/_base.py
@@ -626,7 +626,7 @@ class ToDeviceStream(_StreamFromIdGen):
         super().__init__(
             hs.get_instance_name(),
             store.get_all_new_device_messages,
-            store._device_inbox_id_gen,
+            store._to_device_msg_id_gen,
         )
 
 
diff --git a/synapse/rest/__init__.py b/synapse/rest/__init__.py
index 817c459128..b0beec58b5 100644
--- a/synapse/rest/__init__.py
+++ b/synapse/rest/__init__.py
@@ -27,6 +27,7 @@ from synapse.rest.client import (
     account_validity,
     appservice_ping,
     auth,
+    auth_issuer,
     capabilities,
     devices,
     directory,
@@ -153,3 +154,4 @@ class ClientRestResource(JsonResource):
             mutual_rooms.register_servlets(hs, client_resource)
             login_token_request.register_servlets(hs, client_resource)
             rendezvous.register_servlets(hs, client_resource)
+            auth_issuer.register_servlets(hs, client_resource)
diff --git a/synapse/rest/admin/federation.py b/synapse/rest/admin/federation.py
index f2407b5596..76b4de756b 100644
--- a/synapse/rest/admin/federation.py
+++ b/synapse/rest/admin/federation.py
@@ -95,8 +95,8 @@ class ListDestinationsRestServlet(RestServlet):
             "destinations": [
                 {
                     "destination": r[0],
-                    "retry_last_ts": r[1],
-                    "retry_interval": r[2],
+                    "retry_last_ts": r[1] or 0,
+                    "retry_interval": r[2] or 0,
                     "failure_ts": r[3],
                     "last_successful_stream_ordering": r[4],
                 }
diff --git a/synapse/rest/admin/users.py b/synapse/rest/admin/users.py
index c727d8526c..4059039bab 100644
--- a/synapse/rest/admin/users.py
+++ b/synapse/rest/admin/users.py
@@ -636,6 +636,12 @@ class UserRegisterServlet(RestServlet):
         if not hmac.compare_digest(want_mac.encode("ascii"), got_mac.encode("ascii")):
             raise SynapseError(HTTPStatus.FORBIDDEN, "HMAC incorrect")
 
+        should_issue_refresh_token = body.get("refresh_token", False)
+        if not isinstance(should_issue_refresh_token, bool):
+            raise SynapseError(
+                HTTPStatus.BAD_REQUEST, "refresh_token must be a boolean"
+            )
+
         # Reuse the parts of RegisterRestServlet to reduce code duplication
         from synapse.rest.client.register import RegisterRestServlet
 
@@ -651,7 +657,9 @@ class UserRegisterServlet(RestServlet):
             approved=True,
         )
 
-        result = await register._create_registration_details(user_id, body)
+        result = await register._create_registration_details(
+            user_id, body, should_issue_refresh_token=should_issue_refresh_token
+        )
         return HTTPStatus.OK, result
 
 
diff --git a/synapse/rest/client/auth_issuer.py b/synapse/rest/client/auth_issuer.py
new file mode 100644
index 0000000000..77b9720956
--- /dev/null
+++ b/synapse/rest/client/auth_issuer.py
@@ -0,0 +1,63 @@
+# Copyright 2023 The Matrix.org Foundation C.I.C.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+import typing
+from typing import Tuple
+
+from synapse.api.errors import Codes, SynapseError
+from synapse.http.server import HttpServer
+from synapse.http.servlet import RestServlet
+from synapse.http.site import SynapseRequest
+from synapse.rest.client._base import client_patterns
+from synapse.types import JsonDict
+
+if typing.TYPE_CHECKING:
+    from synapse.server import HomeServer
+
+
+logger = logging.getLogger(__name__)
+
+
+class AuthIssuerServlet(RestServlet):
+    """
+    Advertises what OpenID Connect issuer clients should use to authorise users.
+    """
+
+    PATTERNS = client_patterns(
+        "/org.matrix.msc2965/auth_issuer$",
+        unstable=True,
+        releases=(),
+    )
+
+    def __init__(self, hs: "HomeServer"):
+        super().__init__()
+        self._config = hs.config
+
+    async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
+        if self._config.experimental.msc3861.enabled:
+            return 200, {"issuer": self._config.experimental.msc3861.issuer}
+        else:
+            # Wouldn't expect this to be reached: the servelet shouldn't have been
+            # registered. Still, fail gracefully if we are registered for some reason.
+            raise SynapseError(
+                404,
+                "OIDC discovery has not been configured on this homeserver",
+                Codes.NOT_FOUND,
+            )
+
+
+def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
+    # We use the MSC3861 values as they are used by multiple MSCs
+    if hs.config.experimental.msc3861.enabled:
+        AuthIssuerServlet(hs).register(http_server)
diff --git a/synapse/rest/client/login.py b/synapse/rest/client/login.py
index b8441b8eb7..00d511bc5b 100644
--- a/synapse/rest/client/login.py
+++ b/synapse/rest/client/login.py
@@ -121,6 +121,7 @@ class LoginRestServlet(RestServlet):
         self.registration_handler = hs.get_registration_handler()
         self._sso_handler = hs.get_sso_handler()
         self._spam_checker = hs.get_module_api_callbacks().spam_checker
+        self._account_validity_handler = hs.get_account_validity_handler()
 
         self._well_known_builder = WellKnownBuilder(hs)
         self._address_ratelimiter = Ratelimiter(
@@ -476,6 +477,13 @@ class LoginRestServlet(RestServlet):
             device_id=device_id,
         )
 
+        # execute the callback
+        await self._account_validity_handler.on_user_login(
+            user_id,
+            auth_provider_type=login_submission.get("type"),
+            auth_provider_id=auth_provider_id,
+        )
+
         if valid_until_ms is not None:
             expires_in_ms = valid_until_ms - self.clock.time_msec()
             result["expires_in_ms"] = expires_in_ms
diff --git a/synapse/rest/client/profile.py b/synapse/rest/client/profile.py
index 59f11357b0..f99a119904 100644
--- a/synapse/rest/client/profile.py
+++ b/synapse/rest/client/profile.py
@@ -19,12 +19,17 @@
 #
 
 """ This module contains REST servlets to do with profile: /profile/<paths> """
+
 from http import HTTPStatus
 from typing import TYPE_CHECKING, Tuple
 
 from synapse.api.errors import Codes, SynapseError
 from synapse.http.server import HttpServer
-from synapse.http.servlet import RestServlet, parse_json_object_from_request
+from synapse.http.servlet import (
+    RestServlet,
+    parse_boolean,
+    parse_json_object_from_request,
+)
 from synapse.http.site import SynapseRequest
 from synapse.rest.client._base import client_patterns
 from synapse.types import JsonDict, UserID
@@ -33,6 +38,20 @@ if TYPE_CHECKING:
     from synapse.server import HomeServer
 
 
+def _read_propagate(hs: "HomeServer", request: SynapseRequest) -> bool:
+    # This will always be set by the time Twisted calls us.
+    assert request.args is not None
+
+    propagate = True
+    if hs.config.experimental.msc4069_profile_inhibit_propagation:
+        do_propagate = request.args.get(b"org.matrix.msc4069.propagate")
+        if do_propagate is not None:
+            propagate = parse_boolean(
+                request, "org.matrix.msc4069.propagate", default=False
+            )
+    return propagate
+
+
 class ProfileDisplaynameRestServlet(RestServlet):
     PATTERNS = client_patterns("/profile/(?P<user_id>[^/]*)/displayname", v1=True)
     CATEGORY = "Event sending requests"
@@ -86,7 +105,11 @@ class ProfileDisplaynameRestServlet(RestServlet):
                 errcode=Codes.BAD_JSON,
             )
 
-        await self.profile_handler.set_displayname(user, requester, new_name, is_admin)
+        propagate = _read_propagate(self.hs, request)
+
+        await self.profile_handler.set_displayname(
+            user, requester, new_name, is_admin, propagate=propagate
+        )
 
         return 200, {}
 
@@ -141,8 +164,10 @@ class ProfileAvatarURLRestServlet(RestServlet):
                 400, "Missing key 'avatar_url'", errcode=Codes.MISSING_PARAM
             )
 
+        propagate = _read_propagate(self.hs, request)
+
         await self.profile_handler.set_avatar_url(
-            user, requester, new_avatar_url, is_admin
+            user, requester, new_avatar_url, is_admin, propagate=propagate
         )
 
         return 200, {}
diff --git a/synapse/rest/client/versions.py b/synapse/rest/client/versions.py
index 5da6b2e873..4c5d3dba66 100644
--- a/synapse/rest/client/versions.py
+++ b/synapse/rest/client/versions.py
@@ -83,6 +83,9 @@ class VersionsRestServlet(RestServlet):
                     "v1.4",
                     "v1.5",
                     "v1.6",
+                    "v1.7",
+                    "v1.8",
+                    "v1.9",
                 ],
                 # as per MSC1497:
                 "unstable_features": {
@@ -129,6 +132,8 @@ class VersionsRestServlet(RestServlet):
                     "org.matrix.msc3981": self.config.experimental.msc3981_recurse_relations,
                     # Adds support for deleting account data.
                     "org.matrix.msc3391": self.config.experimental.msc3391_enabled,
+                    # Allows clients to inhibit profile update propagation.
+                    "org.matrix.msc4069": self.config.experimental.msc4069_profile_inhibit_propagation,
                 },
             },
         )
diff --git a/synapse/server_notices/server_notices_manager.py b/synapse/server_notices/server_notices_manager.py
index bbeb936285..001a290e87 100644
--- a/synapse/server_notices/server_notices_manager.py
+++ b/synapse/server_notices/server_notices_manager.py
@@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Optional
 
 from synapse.api.constants import EventTypes, Membership, RoomCreationPreset
 from synapse.events import EventBase
-from synapse.types import Requester, StreamKeyType, UserID, create_requester
+from synapse.types import JsonDict, Requester, StreamKeyType, UserID, create_requester
 from synapse.util.caches.descriptors import cached
 
 if TYPE_CHECKING:
@@ -42,6 +42,7 @@ class ServerNoticesManager:
         self._room_member_handler = hs.get_room_member_handler()
         self._event_creation_handler = hs.get_event_creation_handler()
         self._message_handler = hs.get_message_handler()
+        self._storage_controllers = hs.get_storage_controllers()
         self._is_mine_id = hs.is_mine_id
         self._server_name = hs.hostname
 
@@ -166,6 +167,27 @@ class ServerNoticesManager:
                 self._config.servernotices.server_notices_mxid_display_name,
                 self._config.servernotices.server_notices_mxid_avatar_url,
             )
+            await self._update_room_info(
+                requester,
+                room_id,
+                EventTypes.Name,
+                "name",
+                self._config.servernotices.server_notices_room_name,
+            )
+            await self._update_room_info(
+                requester,
+                room_id,
+                EventTypes.RoomAvatar,
+                "url",
+                self._config.servernotices.server_notices_room_avatar_url,
+            )
+            await self._update_room_info(
+                requester,
+                room_id,
+                EventTypes.Topic,
+                "topic",
+                self._config.servernotices.server_notices_room_topic,
+            )
             return room_id
 
         # apparently no existing notice room: create a new one
@@ -184,15 +206,34 @@ class ServerNoticesManager:
                 "avatar_url": self._config.servernotices.server_notices_mxid_avatar_url,
             }
 
+        room_config: JsonDict = {
+            "preset": RoomCreationPreset.PRIVATE_CHAT,
+            "power_level_content_override": {"users_default": -10},
+        }
+
+        if self._config.servernotices.server_notices_room_name:
+            room_config["name"] = self._config.servernotices.server_notices_room_name
+        if self._config.servernotices.server_notices_room_topic:
+            room_config["topic"] = self._config.servernotices.server_notices_room_topic
+        if self._config.servernotices.server_notices_room_avatar_url:
+            room_config["initial_state"] = [
+                {
+                    "type": EventTypes.RoomAvatar,
+                    "state_key": "",
+                    "content": {
+                        "url": self._config.servernotices.server_notices_room_avatar_url,
+                    },
+                }
+            ]
+
+        # `ignore_forced_encryption` is used to bypass `encryption_enabled_by_default_for_room_type`
+        # setting if it set, since the server notices will not be encrypted anyway.
         room_id, _, _ = await self._room_creation_handler.create_room(
             requester,
-            config={
-                "preset": RoomCreationPreset.PRIVATE_CHAT,
-                "name": self._config.servernotices.server_notices_room_name,
-                "power_level_content_override": {"users_default": -10},
-            },
+            config=room_config,
             ratelimit=False,
             creator_join_profile=join_profile,
+            ignore_forced_encryption=True,
         )
 
         self.maybe_get_notice_room_for_user.invalidate((user_id,))
@@ -227,13 +268,27 @@ class ServerNoticesManager:
             if room.room_id == room_id:
                 return
 
+        user_id_obj = UserID.from_string(user_id)
         await self._room_member_handler.update_membership(
             requester=requester,
-            target=UserID.from_string(user_id),
+            target=user_id_obj,
             room_id=room_id,
             action="invite",
+            ratelimit=False,
         )
 
+        if self._config.servernotices.server_notices_auto_join:
+            user_requester = create_requester(
+                user_id, authenticated_entity=self._server_name
+            )
+            await self._room_member_handler.update_membership(
+                requester=user_requester,
+                target=user_id_obj,
+                room_id=room_id,
+                action="join",
+                ratelimit=False,
+            )
+
     async def _update_notice_user_profile_if_changed(
         self,
         requester: Requester,
@@ -254,11 +309,12 @@ class ServerNoticesManager:
 
         assert self.server_notices_mxid is not None
 
-        notice_user_data_in_room = await self._message_handler.get_room_data(
-            create_requester(self.server_notices_mxid),
-            room_id,
-            EventTypes.Member,
-            self.server_notices_mxid,
+        notice_user_data_in_room = (
+            await self._storage_controllers.state.get_current_state_event(
+                room_id,
+                EventTypes.Member,
+                self.server_notices_mxid,
+            )
         )
 
         assert notice_user_data_in_room is not None
@@ -274,5 +330,58 @@ class ServerNoticesManager:
                 target=UserID.from_string(self.server_notices_mxid),
                 room_id=room_id,
                 action="join",
+                ratelimit=False,
                 content={"displayname": display_name, "avatar_url": avatar_url},
             )
+
+    async def _update_room_info(
+        self,
+        requester: Requester,
+        room_id: str,
+        info_event_type: str,
+        info_content_key: str,
+        info_value: Optional[str],
+    ) -> None:
+        """
+        Updates a specific notice room's info if it's different from what is set.
+
+        Args:
+            requester: The user who is performing the update.
+            room_id: The ID of the server notice room
+            info_event_type: The event type holding the specific info
+            info_content_key: The key containing the specific info in the event's content
+            info_value: The expected value for the specific info
+        """
+        room_info_event = await self._storage_controllers.state.get_current_state_event(
+            room_id,
+            info_event_type,
+            "",
+        )
+
+        existing_info_value = None
+        if room_info_event:
+            existing_info_value = room_info_event.get(info_content_key)
+        if existing_info_value == info_value:
+            return
+        if not existing_info_value and not info_value:
+            # A missing `info_value` can either be represented by a None
+            # or an empty string, so we assume that if they're both falsey
+            # they're equivalent.
+            return
+
+        if info_value is None:
+            info_value = ""
+
+        room_info_event_dict = {
+            "type": info_event_type,
+            "room_id": room_id,
+            "sender": requester.user.to_string(),
+            "state_key": "",
+            "content": {
+                info_content_key: info_value,
+            },
+        }
+
+        event, _ = await self._event_creation_handler.create_and_send_nonmember_event(
+            requester, room_info_event_dict, ratelimit=False
+        )
diff --git a/synapse/storage/databases/main/client_ips.py b/synapse/storage/databases/main/client_ips.py
index 5a6c56b2d9..400d742bce 100644
--- a/synapse/storage/databases/main/client_ips.py
+++ b/synapse/storage/databases/main/client_ips.py
@@ -471,18 +471,15 @@ class ClientIpWorkerStore(ClientIpBackgroundUpdateStore, MonthlyActiveUsersWorke
         #
         # This works by finding the max last_seen that is less than the given
         # time, but has no more than N rows before it, deleting all rows with
-        # a lesser last_seen time. (We COALESCE so that the sub-SELECT always
-        # returns exactly one row).
+        # a lesser last_seen time. (We use an `IN` clause to force postgres to
+        # use the index, otherwise it tends to do a seq scan).
         sql = """
             DELETE FROM user_ips
-            WHERE last_seen <= (
-                SELECT COALESCE(MAX(last_seen), -1)
-                FROM (
-                    SELECT last_seen FROM user_ips
-                    WHERE last_seen <= ?
-                    ORDER BY last_seen ASC
-                    LIMIT 5000
-                ) AS u
+            WHERE last_seen IN (
+                SELECT last_seen FROM user_ips
+                WHERE last_seen <= ?
+                ORDER BY last_seen ASC
+                LIMIT 5000
             )
         """
 
@@ -595,6 +592,27 @@ class ClientIpWorkerStore(ClientIpBackgroundUpdateStore, MonthlyActiveUsersWorke
         device_id: Optional[str],
         now: Optional[int] = None,
     ) -> None:
+        """Record that `user_id` used `access_token` from this `ip` address.
+
+        This method does two things.
+
+        1. It queues up a row to be upserted into the `client_ips` table. These happen
+           periodically; see _update_client_ips_batch.
+        2. It immediately records this user as having taken action for the purposes of
+           MAU tracking.
+
+        Any DB writes take place on the background tasks worker, falling back to the
+        main process. If we're not that worker, this method emits a replication payload
+        to run this logic on that worker.
+
+        Two caveats to note:
+
+         - We only take action once per LAST_SEEN_GRANULARITY, to avoid spamming the
+           DB with writes.
+         - Requests using the sliding-sync proxy's user agent are excluded, as its
+           requests are not directly driven by end-users. This is a hack and we're not
+           very proud of it.
+        """
         # The sync proxy continuously triggers /sync even if the user is not
         # present so should be excluded from user_ips entries.
         if user_agent == "sync-v3-proxy-":
diff --git a/synapse/storage/databases/main/deviceinbox.py b/synapse/storage/databases/main/deviceinbox.py
index 753bb32a46..40477b9da0 100644
--- a/synapse/storage/databases/main/deviceinbox.py
+++ b/synapse/storage/databases/main/deviceinbox.py
@@ -92,25 +92,32 @@ class DeviceInboxWorkerStore(SQLBaseStore):
                 self._instance_name in hs.config.worker.writers.to_device
             )
 
-            self._device_inbox_id_gen: AbstractStreamIdGenerator = (
+            self._to_device_msg_id_gen: AbstractStreamIdGenerator = (
                 MultiWriterIdGenerator(
                     db_conn=db_conn,
                     db=database,
                     notifier=hs.get_replication_notifier(),
                     stream_name="to_device",
                     instance_name=self._instance_name,
-                    tables=[("device_inbox", "instance_name", "stream_id")],
+                    tables=[
+                        ("device_inbox", "instance_name", "stream_id"),
+                        ("device_federation_outbox", "instance_name", "stream_id"),
+                    ],
                     sequence_name="device_inbox_sequence",
                     writers=hs.config.worker.writers.to_device,
                 )
             )
         else:
             self._can_write_to_device = True
-            self._device_inbox_id_gen = StreamIdGenerator(
-                db_conn, hs.get_replication_notifier(), "device_inbox", "stream_id"
+            self._to_device_msg_id_gen = StreamIdGenerator(
+                db_conn,
+                hs.get_replication_notifier(),
+                "device_inbox",
+                "stream_id",
+                extra_tables=[("device_federation_outbox", "stream_id")],
             )
 
-        max_device_inbox_id = self._device_inbox_id_gen.get_current_token()
+        max_device_inbox_id = self._to_device_msg_id_gen.get_current_token()
         device_inbox_prefill, min_device_inbox_id = self.db_pool.get_cache_dict(
             db_conn,
             "device_inbox",
@@ -150,8 +157,8 @@ class DeviceInboxWorkerStore(SQLBaseStore):
     ) -> None:
         if stream_name == ToDeviceStream.NAME:
             # If replication is happening than postgres must be being used.
-            assert isinstance(self._device_inbox_id_gen, MultiWriterIdGenerator)
-            self._device_inbox_id_gen.advance(instance_name, token)
+            assert isinstance(self._to_device_msg_id_gen, MultiWriterIdGenerator)
+            self._to_device_msg_id_gen.advance(instance_name, token)
             for row in rows:
                 if row.entity.startswith("@"):
                     self._device_inbox_stream_cache.entity_has_changed(
@@ -167,11 +174,11 @@ class DeviceInboxWorkerStore(SQLBaseStore):
         self, stream_name: str, instance_name: str, token: int
     ) -> None:
         if stream_name == ToDeviceStream.NAME:
-            self._device_inbox_id_gen.advance(instance_name, token)
+            self._to_device_msg_id_gen.advance(instance_name, token)
         super().process_replication_position(stream_name, instance_name, token)
 
     def get_to_device_stream_token(self) -> int:
-        return self._device_inbox_id_gen.get_current_token()
+        return self._to_device_msg_id_gen.get_current_token()
 
     async def get_messages_for_user_devices(
         self,
@@ -806,7 +813,7 @@ class DeviceInboxWorkerStore(SQLBaseStore):
                                 msg.get(EventContentFields.TO_DEVICE_MSGID),
                             )
 
-        async with self._device_inbox_id_gen.get_next() as stream_id:
+        async with self._to_device_msg_id_gen.get_next() as stream_id:
             now_ms = self._clock.time_msec()
             await self.db_pool.runInteraction(
                 "add_messages_to_device_inbox", add_messages_txn, now_ms, stream_id
@@ -818,7 +825,7 @@ class DeviceInboxWorkerStore(SQLBaseStore):
                     destination, stream_id
                 )
 
-        return self._device_inbox_id_gen.get_current_token()
+        return self._to_device_msg_id_gen.get_current_token()
 
     async def add_messages_from_remote_to_device_inbox(
         self,
@@ -862,7 +869,7 @@ class DeviceInboxWorkerStore(SQLBaseStore):
                 txn, stream_id, local_messages_by_user_then_device
             )
 
-        async with self._device_inbox_id_gen.get_next() as stream_id:
+        async with self._to_device_msg_id_gen.get_next() as stream_id:
             now_ms = self._clock.time_msec()
             await self.db_pool.runInteraction(
                 "add_messages_from_remote_to_device_inbox",
diff --git a/synapse/storage/databases/main/event_federation.py b/synapse/storage/databases/main/event_federation.py
index c83b26b702..ddc2baf95d 100644
--- a/synapse/storage/databases/main/event_federation.py
+++ b/synapse/storage/databases/main/event_federation.py
@@ -307,6 +307,11 @@ class EventFederationWorkerStore(SignatureWorkerStore, EventsWorkerStore, SQLBas
         # Add the initial set of chains, excluding the sequence corresponding to
         # initial event.
         for chain_id, seq_no in event_chains.items():
+            # Check if the initial event is the first item in the chain. If so, then
+            # there is nothing new to add from this chain.
+            if seq_no == 1:
+                continue
+
             chains[chain_id] = max(seq_no - 1, chains.get(chain_id, 0))
 
         # Now for each chain we figure out the maximum sequence number reachable
diff --git a/synapse/storage/schema/__init__.py b/synapse/storage/schema/__init__.py
index 9a43ab2bae..c8aec2f69d 100644
--- a/synapse/storage/schema/__init__.py
+++ b/synapse/storage/schema/__init__.py
@@ -135,8 +135,8 @@ Changes in SCHEMA_VERSION = 83
 
 
 SCHEMA_COMPAT_VERSION = (
-    # The `event_txn_id_device_id` must be written to for new events.
-    80
+    # The event_txn_id table and tables from MSC2716 no longer exist.
+    83
 )
 """Limit on how far the synapse codebase can be rolled back without breaking db compat
 
diff --git a/synapse/storage/schema/common/full_schemas/54/full.sql b/synapse/storage/schema/common/full_schemas/54/full.sql
deleted file mode 100644
index 1005880466..0000000000
--- a/synapse/storage/schema/common/full_schemas/54/full.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-CREATE TABLE background_updates (
-    update_name text NOT NULL,
-    progress_json text NOT NULL,
-    depends_on text,
-    CONSTRAINT background_updates_uniqueness UNIQUE (update_name)
-);
diff --git a/synapse/storage/schema/main/full_schemas/16/room_aliases.sql b/synapse/storage/schema/main/delta/83/01_drop_old_tables.sql
index ad373f0303..e0a06ba4da 100644
--- a/synapse/storage/schema/main/full_schemas/16/room_aliases.sql
+++ b/synapse/storage/schema/main/delta/83/01_drop_old_tables.sql
@@ -17,7 +17,7 @@
 -- [This file includes modifications made by New Vector Limited]
 --
 --
-/* Copyright 2014-2016 OpenMarket Ltd
+/* Copyright 2023 The Matrix.org Foundation C.I.C
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -32,17 +32,12 @@
  * limitations under the License.
  */
 
-CREATE TABLE IF NOT EXISTS room_aliases(
-    room_alias TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    UNIQUE (room_alias)
-);
+-- Drop the old event transaction ID table, the event_txn_id_device_id table
+-- should be used instead.
+DROP TABLE IF EXISTS event_txn_id;
 
-CREATE INDEX room_aliases_id ON room_aliases(room_id);
-
-CREATE TABLE IF NOT EXISTS room_alias_servers(
-    room_alias TEXT NOT NULL,
-    server TEXT NOT NULL
-);
-
-CREATE INDEX room_alias_servers_alias ON room_alias_servers(room_alias);
+-- Drop tables related to MSC2716 since the implementation is being removed
+DROP TABLE IF EXISTS insertion_events;
+DROP TABLE IF EXISTS insertion_event_edges;
+DROP TABLE IF EXISTS insertion_event_extremities;
+DROP TABLE IF EXISTS batch_events;
diff --git a/synapse/storage/schema/main/full_schemas/16/application_services.sql b/synapse/storage/schema/main/full_schemas/16/application_services.sql
deleted file mode 100644
index 85151ef98f..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/application_services.sql
+++ /dev/null
@@ -1,56 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2015, 2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* We used to create tables called application_services and
- * application_services_regex, but these are no longer used and are removed in
- * delta 54.
- */
-
-
-CREATE TABLE IF NOT EXISTS application_services_state(
-    as_id TEXT PRIMARY KEY,
-    state VARCHAR(5),
-    last_txn INTEGER
-);
-
-CREATE TABLE IF NOT EXISTS application_services_txns(
-    as_id TEXT NOT NULL,
-    txn_id INTEGER NOT NULL,
-    event_ids TEXT NOT NULL,
-    UNIQUE(as_id, txn_id)
-);
-
-CREATE INDEX application_services_txns_id ON application_services_txns (
-    as_id
-);
diff --git a/synapse/storage/schema/main/full_schemas/16/event_edges.sql b/synapse/storage/schema/main/full_schemas/16/event_edges.sql
deleted file mode 100644
index 03f98f02a6..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/event_edges.sql
+++ /dev/null
@@ -1,89 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* We used to create tables called event_destinations and
- * state_forward_extremities, but these are no longer used and are removed in
- * delta 54.
- */
-
-CREATE TABLE IF NOT EXISTS event_forward_extremities(
-    event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    UNIQUE (event_id, room_id)
-);
-
-CREATE INDEX ev_extrem_room ON event_forward_extremities(room_id);
-CREATE INDEX ev_extrem_id ON event_forward_extremities(event_id);
-
-
-CREATE TABLE IF NOT EXISTS event_backward_extremities(
-    event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    UNIQUE (event_id, room_id)
-);
-
-CREATE INDEX ev_b_extrem_room ON event_backward_extremities(room_id);
-CREATE INDEX ev_b_extrem_id ON event_backward_extremities(event_id);
-
-
-CREATE TABLE IF NOT EXISTS event_edges(
-    event_id TEXT NOT NULL,
-    prev_event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    is_state BOOL NOT NULL,  -- true if this is a prev_state edge rather than a regular
-                             -- event dag edge.
-    UNIQUE (event_id, prev_event_id, room_id, is_state)
-);
-
-CREATE INDEX ev_edges_id ON event_edges(event_id);
-CREATE INDEX ev_edges_prev_id ON event_edges(prev_event_id);
-
-
-CREATE TABLE IF NOT EXISTS room_depth(
-    room_id TEXT NOT NULL,
-    min_depth INTEGER NOT NULL,
-    UNIQUE (room_id)
-);
-
-CREATE INDEX room_depth_room ON room_depth(room_id);
-
-CREATE TABLE IF NOT EXISTS event_auth(
-    event_id TEXT NOT NULL,
-    auth_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    UNIQUE (event_id, auth_id, room_id)
-);
-
-CREATE INDEX evauth_edges_id ON event_auth(event_id);
-CREATE INDEX evauth_edges_auth_id ON event_auth(auth_id);
diff --git a/synapse/storage/schema/main/full_schemas/16/event_signatures.sql b/synapse/storage/schema/main/full_schemas/16/event_signatures.sql
deleted file mode 100644
index a37216aaba..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/event_signatures.sql
+++ /dev/null
@@ -1,57 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* We used to create tables called event_content_hashes and event_edge_hashes,
-  * but these are no longer used and are removed in delta 54.
-  */
-
-CREATE TABLE IF NOT EXISTS event_reference_hashes (
-    event_id TEXT,
-    algorithm TEXT,
-    hash bytea,
-    UNIQUE (event_id, algorithm)
-);
-
-CREATE INDEX event_reference_hashes_id ON event_reference_hashes(event_id);
-
-
-CREATE TABLE IF NOT EXISTS event_signatures (
-    event_id TEXT,
-    signature_name TEXT,
-    key_id TEXT,
-    signature bytea,
-    UNIQUE (event_id, signature_name, key_id)
-);
-
-CREATE INDEX event_signatures_id ON event_signatures(event_id);
diff --git a/synapse/storage/schema/main/full_schemas/16/im.sql b/synapse/storage/schema/main/full_schemas/16/im.sql
deleted file mode 100644
index de9a00b2e6..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/im.sql
+++ /dev/null
@@ -1,139 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* We used to create tables called room_hosts and feedback,
- * but these are no longer used and are removed in delta 54.
- */
-
-CREATE TABLE IF NOT EXISTS events(
-    stream_ordering INTEGER PRIMARY KEY,
-    topological_ordering BIGINT NOT NULL,
-    event_id TEXT NOT NULL,
-    type TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-
-    -- 'content' used to be created NULLable, but as of delta 50 we drop that constraint.
-    -- the hack we use to drop the constraint doesn't work for an in-memory sqlite
-    -- database, which breaks the sytests. Hence, we no longer make it nullable.
-    content TEXT,
-
-    unrecognized_keys TEXT,
-    processed BOOL NOT NULL,
-    outlier BOOL NOT NULL,
-    depth BIGINT DEFAULT 0 NOT NULL,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX events_stream_ordering ON events (stream_ordering);
-CREATE INDEX events_topological_ordering ON events (topological_ordering);
-CREATE INDEX events_order ON events (topological_ordering, stream_ordering);
-CREATE INDEX events_room_id ON events (room_id);
-CREATE INDEX events_order_room ON events (
-    room_id, topological_ordering, stream_ordering
-);
-
-
-CREATE TABLE IF NOT EXISTS event_json(
-    event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    internal_metadata TEXT NOT NULL,
-    json TEXT NOT NULL,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX event_json_room_id ON event_json(room_id);
-
-
-CREATE TABLE IF NOT EXISTS state_events(
-    event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    type TEXT NOT NULL,
-    state_key TEXT NOT NULL,
-    prev_state TEXT,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX state_events_room_id ON state_events (room_id);
-CREATE INDEX state_events_type ON state_events (type);
-CREATE INDEX state_events_state_key ON state_events (state_key);
-
-
-CREATE TABLE IF NOT EXISTS current_state_events(
-    event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    type TEXT NOT NULL,
-    state_key TEXT NOT NULL,
-    UNIQUE (event_id),
-    UNIQUE (room_id, type, state_key)
-);
-
-CREATE INDEX current_state_events_room_id ON current_state_events (room_id);
-CREATE INDEX current_state_events_type ON current_state_events (type);
-CREATE INDEX current_state_events_state_key ON current_state_events (state_key);
-
-CREATE TABLE IF NOT EXISTS room_memberships(
-    event_id TEXT NOT NULL,
-    user_id TEXT NOT NULL,
-    sender TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    membership TEXT NOT NULL,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX room_memberships_room_id ON room_memberships (room_id);
-CREATE INDEX room_memberships_user_id ON room_memberships (user_id);
-
-CREATE TABLE IF NOT EXISTS topics(
-    event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    topic TEXT NOT NULL,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX topics_room_id ON topics(room_id);
-
-CREATE TABLE IF NOT EXISTS room_names(
-    event_id TEXT NOT NULL,
-    room_id TEXT NOT NULL,
-    name TEXT NOT NULL,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX room_names_room_id ON room_names(room_id);
-
-CREATE TABLE IF NOT EXISTS rooms(
-    room_id TEXT PRIMARY KEY NOT NULL,
-    is_public BOOL,
-    creator TEXT
-);
diff --git a/synapse/storage/schema/main/full_schemas/16/keys.sql b/synapse/storage/schema/main/full_schemas/16/keys.sql
deleted file mode 100644
index 68eff1460b..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/keys.sql
+++ /dev/null
@@ -1,45 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
--- we used to create a table called server_tls_certificates, but this is no
--- longer used, and is removed in delta 54.
-
-CREATE TABLE IF NOT EXISTS server_signature_keys(
-  server_name TEXT, -- Server name.
-  key_id TEXT, -- Key version.
-  from_server TEXT, -- Which key server the key was fetched form.
-  ts_added_ms BIGINT, -- When the key was added.
-  verify_key bytea, -- NACL verification key.
-  UNIQUE (server_name, key_id)
-);
diff --git a/synapse/storage/schema/main/full_schemas/16/media_repository.sql b/synapse/storage/schema/main/full_schemas/16/media_repository.sql
deleted file mode 100644
index e430bd001e..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/media_repository.sql
+++ /dev/null
@@ -1,87 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-CREATE TABLE IF NOT EXISTS local_media_repository (
-    media_id TEXT, -- The id used to refer to the media.
-    media_type TEXT, -- The MIME-type of the media.
-    media_length INTEGER, -- Length of the media in bytes.
-    created_ts BIGINT, -- When the content was uploaded in ms.
-    upload_name TEXT, -- The name the media was uploaded with.
-    user_id TEXT, -- The user who uploaded the file.
-    UNIQUE (media_id)
-);
-
-CREATE TABLE IF NOT EXISTS local_media_repository_thumbnails (
-    media_id TEXT, -- The id used to refer to the media.
-    thumbnail_width INTEGER, -- The width of the thumbnail in pixels.
-    thumbnail_height INTEGER, -- The height of the thumbnail in pixels.
-    thumbnail_type TEXT, -- The MIME-type of the thumbnail.
-    thumbnail_method TEXT, -- The method used to make the thumbnail.
-    thumbnail_length INTEGER, -- The length of the thumbnail in bytes.
-    UNIQUE (
-        media_id, thumbnail_width, thumbnail_height, thumbnail_type
-    )
-);
-
-CREATE INDEX local_media_repository_thumbnails_media_id
-    ON local_media_repository_thumbnails (media_id);
-
-CREATE TABLE IF NOT EXISTS remote_media_cache (
-    media_origin TEXT, -- The remote HS the media came from.
-    media_id TEXT, -- The id used to refer to the media on that server.
-    media_type TEXT, -- The MIME-type of the media.
-    created_ts BIGINT, -- When the content was uploaded in ms.
-    upload_name TEXT, -- The name the media was uploaded with.
-    media_length INTEGER, -- Length of the media in bytes.
-    filesystem_id TEXT, -- The name used to store the media on disk.
-    UNIQUE (media_origin, media_id)
-);
-
-CREATE TABLE IF NOT EXISTS remote_media_cache_thumbnails (
-    media_origin TEXT, -- The remote HS the media came from.
-    media_id TEXT, -- The id used to refer to the media.
-    thumbnail_width INTEGER, -- The width of the thumbnail in pixels.
-    thumbnail_height INTEGER, -- The height of the thumbnail in pixels.
-    thumbnail_method TEXT, -- The method used to make the thumbnail
-    thumbnail_type TEXT, -- The MIME-type of the thumbnail.
-    thumbnail_length INTEGER, -- The length of the thumbnail in bytes.
-    filesystem_id TEXT, -- The name used to store the media on disk.
-    UNIQUE (
-        media_origin, media_id, thumbnail_width, thumbnail_height,
-        thumbnail_type
-     )
-);
-
-CREATE INDEX remote_media_cache_thumbnails_media_id
-    ON remote_media_cache_thumbnails (media_id);
diff --git a/synapse/storage/schema/main/full_schemas/16/presence.sql b/synapse/storage/schema/main/full_schemas/16/presence.sql
deleted file mode 100644
index fb3cca0a8a..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/presence.sql
+++ /dev/null
@@ -1,51 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-CREATE TABLE IF NOT EXISTS presence(
-  user_id TEXT NOT NULL,
-  state VARCHAR(20),
-  status_msg TEXT,
-  mtime BIGINT, -- miliseconds since last state change
-  UNIQUE (user_id)
-);
-
--- For each of /my/ users which possibly-remote users are allowed to see their
--- presence state
-CREATE TABLE IF NOT EXISTS presence_allow_inbound(
-  observed_user_id TEXT NOT NULL,
-  observer_user_id TEXT NOT NULL, -- a UserID,
-  UNIQUE (observed_user_id, observer_user_id)
-);
-
--- We used to create a table called presence_list, but this is no longer used
--- and is removed in delta 54.
\ No newline at end of file
diff --git a/synapse/storage/schema/main/full_schemas/16/profiles.sql b/synapse/storage/schema/main/full_schemas/16/profiles.sql
deleted file mode 100644
index ffd1d7fd06..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/profiles.sql
+++ /dev/null
@@ -1,39 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-CREATE TABLE IF NOT EXISTS profiles(
-    user_id TEXT NOT NULL,
-    displayname TEXT,
-    avatar_url TEXT,
-    UNIQUE(user_id)
-);
diff --git a/synapse/storage/schema/main/full_schemas/16/push.sql b/synapse/storage/schema/main/full_schemas/16/push.sql
deleted file mode 100644
index 9868f3381c..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/push.sql
+++ /dev/null
@@ -1,93 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2015, 2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-CREATE TABLE IF NOT EXISTS rejections(
-    event_id TEXT NOT NULL,
-    reason TEXT NOT NULL,
-    last_check TEXT NOT NULL,
-    UNIQUE (event_id)
-);
-
--- Push notification endpoints that users have configured
-CREATE TABLE IF NOT EXISTS pushers (
-  id BIGINT PRIMARY KEY,
-  user_name TEXT NOT NULL,
-  access_token BIGINT DEFAULT NULL,
-  profile_tag VARCHAR(32) NOT NULL,
-  kind VARCHAR(8) NOT NULL,
-  app_id VARCHAR(64) NOT NULL,
-  app_display_name VARCHAR(64) NOT NULL,
-  device_display_name VARCHAR(128) NOT NULL,
-  pushkey bytea NOT NULL,
-  ts BIGINT NOT NULL,
-  lang VARCHAR(8),
-  data bytea,
-  last_token TEXT,
-  last_success BIGINT,
-  failing_since BIGINT,
-  UNIQUE (app_id, pushkey)
-);
-
-CREATE TABLE IF NOT EXISTS push_rules (
-  id BIGINT PRIMARY KEY,
-  user_name TEXT NOT NULL,
-  rule_id TEXT NOT NULL,
-  priority_class SMALLINT NOT NULL,
-  priority INTEGER NOT NULL DEFAULT 0,
-  conditions TEXT NOT NULL,
-  actions TEXT NOT NULL,
-  UNIQUE(user_name, rule_id)
-);
-
-CREATE INDEX push_rules_user_name on push_rules (user_name);
-
-CREATE TABLE IF NOT EXISTS user_filters(
-  user_id TEXT,
-  filter_id BIGINT,
-  filter_json bytea
-);
-
-CREATE INDEX user_filters_by_user_id_filter_id ON user_filters(
-    user_id, filter_id
-);
-
-CREATE TABLE IF NOT EXISTS push_rules_enable (
-  id BIGINT PRIMARY KEY,
-  user_name TEXT NOT NULL,
-  rule_id TEXT NOT NULL,
-  enabled SMALLINT,
-  UNIQUE(user_name, rule_id)
-);
-
-CREATE INDEX push_rules_enable_user_name on push_rules_enable (user_name);
diff --git a/synapse/storage/schema/main/full_schemas/16/redactions.sql b/synapse/storage/schema/main/full_schemas/16/redactions.sql
deleted file mode 100644
index 3b69df991f..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/redactions.sql
+++ /dev/null
@@ -1,41 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-CREATE TABLE IF NOT EXISTS redactions (
-    event_id TEXT NOT NULL,
-    redacts TEXT NOT NULL,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX redactions_event_id ON redactions (event_id);
-CREATE INDEX redactions_redacts ON redactions (redacts);
diff --git a/synapse/storage/schema/main/full_schemas/16/state.sql b/synapse/storage/schema/main/full_schemas/16/state.sql
deleted file mode 100644
index beefccd8b0..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/state.sql
+++ /dev/null
@@ -1,59 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-CREATE TABLE IF NOT EXISTS state_groups(
-    id BIGINT PRIMARY KEY,
-    room_id TEXT NOT NULL,
-    event_id TEXT NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS state_groups_state(
-    state_group BIGINT NOT NULL,
-    room_id TEXT NOT NULL,
-    type TEXT NOT NULL,
-    state_key TEXT NOT NULL,
-    event_id TEXT NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS event_to_state_groups(
-    event_id TEXT NOT NULL,
-    state_group BIGINT NOT NULL,
-    UNIQUE (event_id)
-);
-
-CREATE INDEX state_groups_id ON state_groups(id);
-
-CREATE INDEX state_groups_state_id ON state_groups_state(state_group);
-CREATE INDEX state_groups_state_tuple ON state_groups_state(room_id, type, state_key);
-CREATE INDEX event_to_state_groups_id ON event_to_state_groups(event_id);
diff --git a/synapse/storage/schema/main/full_schemas/16/transactions.sql b/synapse/storage/schema/main/full_schemas/16/transactions.sql
deleted file mode 100644
index 53b71a3109..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/transactions.sql
+++ /dev/null
@@ -1,63 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--- Stores what transaction ids we have received and what our response was
-CREATE TABLE IF NOT EXISTS received_transactions(
-    transaction_id TEXT,
-    origin TEXT,
-    ts BIGINT,
-    response_code INTEGER,
-    response_json bytea,
-    has_been_referenced smallint default 0, -- Whether thishas been referenced by a prev_tx
-    UNIQUE (transaction_id, origin)
-);
-
-CREATE INDEX transactions_have_ref ON received_transactions(origin, has_been_referenced);-- WHERE has_been_referenced = 0;
-
--- For sent transactions only.
-CREATE TABLE IF NOT EXISTS transaction_id_to_pdu(
-    transaction_id INTEGER,
-    destination TEXT,
-    pdu_id TEXT,
-    pdu_origin TEXT,
-    UNIQUE (transaction_id, destination)
-);
-
-CREATE INDEX transaction_id_to_pdu_dest ON transaction_id_to_pdu(destination);
-
--- To track destination health
-CREATE TABLE IF NOT EXISTS destinations(
-    destination TEXT PRIMARY KEY,
-    retry_last_ts BIGINT,
-    retry_interval INTEGER
-);
diff --git a/synapse/storage/schema/main/full_schemas/16/users.sql b/synapse/storage/schema/main/full_schemas/16/users.sql
deleted file mode 100644
index 9ad2a8f132..0000000000
--- a/synapse/storage/schema/main/full_schemas/16/users.sql
+++ /dev/null
@@ -1,61 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2014-2016 OpenMarket Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-CREATE TABLE IF NOT EXISTS users(
-    name TEXT,
-    password_hash TEXT,
-    creation_ts BIGINT,
-    admin SMALLINT DEFAULT 0 NOT NULL,
-    UNIQUE(name)
-);
-
-CREATE TABLE IF NOT EXISTS access_tokens(
-    id BIGINT PRIMARY KEY,
-    user_id TEXT NOT NULL,
-    device_id TEXT,
-    token TEXT NOT NULL,
-    last_used BIGINT,
-    UNIQUE(token)
-);
-
-CREATE TABLE IF NOT EXISTS user_ips (
-    user_id TEXT NOT NULL,
-    access_token TEXT NOT NULL,
-    device_id TEXT,
-    ip TEXT NOT NULL,
-    user_agent TEXT NOT NULL,
-    last_seen BIGINT NOT NULL
-);
-
-CREATE INDEX user_ips_user ON user_ips(user_id);
-CREATE INDEX user_ips_user_ip ON user_ips(user_id, access_token, ip);
diff --git a/synapse/storage/schema/main/full_schemas/54/full.sql.postgres b/synapse/storage/schema/main/full_schemas/54/full.sql.postgres
deleted file mode 100644
index 889a9a0ce4..0000000000
--- a/synapse/storage/schema/main/full_schemas/54/full.sql.postgres
+++ /dev/null
@@ -1,1983 +0,0 @@
-
-
-
-
-
-CREATE TABLE access_tokens (
-    id bigint NOT NULL,
-    user_id text NOT NULL,
-    device_id text,
-    token text NOT NULL,
-    last_used bigint
-);
-
-
-
-CREATE TABLE account_data (
-    user_id text NOT NULL,
-    account_data_type text NOT NULL,
-    stream_id bigint NOT NULL,
-    content text NOT NULL
-);
-
-
-
-CREATE TABLE account_data_max_stream_id (
-    lock character(1) DEFAULT 'X'::bpchar NOT NULL,
-    stream_id bigint NOT NULL,
-    CONSTRAINT private_user_data_max_stream_id_lock_check CHECK ((lock = 'X'::bpchar))
-);
-
-
-
-CREATE TABLE account_validity (
-    user_id text NOT NULL,
-    expiration_ts_ms bigint NOT NULL,
-    email_sent boolean NOT NULL,
-    renewal_token text
-);
-
-
-
-CREATE TABLE application_services_state (
-    as_id text NOT NULL,
-    state character varying(5),
-    last_txn integer
-);
-
-
-
-CREATE TABLE application_services_txns (
-    as_id text NOT NULL,
-    txn_id integer NOT NULL,
-    event_ids text NOT NULL
-);
-
-
-
-CREATE TABLE appservice_room_list (
-    appservice_id text NOT NULL,
-    network_id text NOT NULL,
-    room_id text NOT NULL
-);
-
-
-
-CREATE TABLE appservice_stream_position (
-    lock character(1) DEFAULT 'X'::bpchar NOT NULL,
-    stream_ordering bigint,
-    CONSTRAINT appservice_stream_position_lock_check CHECK ((lock = 'X'::bpchar))
-);
-
-
-CREATE TABLE blocked_rooms (
-    room_id text NOT NULL,
-    user_id text NOT NULL
-);
-
-
-
-CREATE TABLE cache_invalidation_stream (
-    stream_id bigint,
-    cache_func text,
-    keys text[],
-    invalidation_ts bigint
-);
-
-
-
-CREATE TABLE current_state_delta_stream (
-    stream_id bigint NOT NULL,
-    room_id text NOT NULL,
-    type text NOT NULL,
-    state_key text NOT NULL,
-    event_id text,
-    prev_event_id text
-);
-
-
-
-CREATE TABLE current_state_events (
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    type text NOT NULL,
-    state_key text NOT NULL
-);
-
-
-
-CREATE TABLE deleted_pushers (
-    stream_id bigint NOT NULL,
-    app_id text NOT NULL,
-    pushkey text NOT NULL,
-    user_id text NOT NULL
-);
-
-
-
-CREATE TABLE destinations (
-    destination text NOT NULL,
-    retry_last_ts bigint,
-    retry_interval integer
-);
-
-
-
-CREATE TABLE device_federation_inbox (
-    origin text NOT NULL,
-    message_id text NOT NULL,
-    received_ts bigint NOT NULL
-);
-
-
-
-CREATE TABLE device_federation_outbox (
-    destination text NOT NULL,
-    stream_id bigint NOT NULL,
-    queued_ts bigint NOT NULL,
-    messages_json text NOT NULL
-);
-
-
-
-CREATE TABLE device_inbox (
-    user_id text NOT NULL,
-    device_id text NOT NULL,
-    stream_id bigint NOT NULL,
-    message_json text NOT NULL
-);
-
-
-
-CREATE TABLE device_lists_outbound_last_success (
-    destination text NOT NULL,
-    user_id text NOT NULL,
-    stream_id bigint NOT NULL
-);
-
-
-
-CREATE TABLE device_lists_outbound_pokes (
-    destination text NOT NULL,
-    stream_id bigint NOT NULL,
-    user_id text NOT NULL,
-    device_id text NOT NULL,
-    sent boolean NOT NULL,
-    ts bigint NOT NULL
-);
-
-
-
-CREATE TABLE device_lists_remote_cache (
-    user_id text NOT NULL,
-    device_id text NOT NULL,
-    content text NOT NULL
-);
-
-
-
-CREATE TABLE device_lists_remote_extremeties (
-    user_id text NOT NULL,
-    stream_id text NOT NULL
-);
-
-
-
-CREATE TABLE device_lists_stream (
-    stream_id bigint NOT NULL,
-    user_id text NOT NULL,
-    device_id text NOT NULL
-);
-
-
-
-CREATE TABLE device_max_stream_id (
-    stream_id bigint NOT NULL
-);
-
-
-
-CREATE TABLE devices (
-    user_id text NOT NULL,
-    device_id text NOT NULL,
-    display_name text
-);
-
-
-
-CREATE TABLE e2e_device_keys_json (
-    user_id text NOT NULL,
-    device_id text NOT NULL,
-    ts_added_ms bigint NOT NULL,
-    key_json text NOT NULL
-);
-
-
-
-CREATE TABLE e2e_one_time_keys_json (
-    user_id text NOT NULL,
-    device_id text NOT NULL,
-    algorithm text NOT NULL,
-    key_id text NOT NULL,
-    ts_added_ms bigint NOT NULL,
-    key_json text NOT NULL
-);
-
-
-
-CREATE TABLE e2e_room_keys (
-    user_id text NOT NULL,
-    room_id text NOT NULL,
-    session_id text NOT NULL,
-    version bigint NOT NULL,
-    first_message_index integer,
-    forwarded_count integer,
-    is_verified boolean,
-    session_data text NOT NULL
-);
-
-
-
-CREATE TABLE e2e_room_keys_versions (
-    user_id text NOT NULL,
-    version bigint NOT NULL,
-    algorithm text NOT NULL,
-    auth_data text NOT NULL,
-    deleted smallint DEFAULT 0 NOT NULL
-);
-
-
-
-CREATE TABLE erased_users (
-    user_id text NOT NULL
-);
-
-
-
-CREATE TABLE event_auth (
-    event_id text NOT NULL,
-    auth_id text NOT NULL,
-    room_id text NOT NULL
-);
-
-
-
-CREATE TABLE event_backward_extremities (
-    event_id text NOT NULL,
-    room_id text NOT NULL
-);
-
-
-
-CREATE TABLE event_edges (
-    event_id text NOT NULL,
-    prev_event_id text NOT NULL,
-    room_id text NOT NULL,
-    is_state boolean NOT NULL
-);
-
-
-
-CREATE TABLE event_forward_extremities (
-    event_id text NOT NULL,
-    room_id text NOT NULL
-);
-
-
-
-CREATE TABLE event_json (
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    internal_metadata text NOT NULL,
-    json text NOT NULL,
-    format_version integer
-);
-
-
-
-CREATE TABLE event_push_actions (
-    room_id text NOT NULL,
-    event_id text NOT NULL,
-    user_id text NOT NULL,
-    profile_tag character varying(32),
-    actions text NOT NULL,
-    topological_ordering bigint,
-    stream_ordering bigint,
-    notif smallint,
-    highlight smallint
-);
-
-
-
-CREATE TABLE event_push_actions_staging (
-    event_id text NOT NULL,
-    user_id text NOT NULL,
-    actions text NOT NULL,
-    notif smallint NOT NULL,
-    highlight smallint NOT NULL
-);
-
-
-
-CREATE TABLE event_push_summary (
-    user_id text NOT NULL,
-    room_id text NOT NULL,
-    notif_count bigint NOT NULL,
-    stream_ordering bigint NOT NULL
-);
-
-
-
-CREATE TABLE event_push_summary_stream_ordering (
-    lock character(1) DEFAULT 'X'::bpchar NOT NULL,
-    stream_ordering bigint NOT NULL,
-    CONSTRAINT event_push_summary_stream_ordering_lock_check CHECK ((lock = 'X'::bpchar))
-);
-
-
-
-CREATE TABLE event_reference_hashes (
-    event_id text,
-    algorithm text,
-    hash bytea
-);
-
-
-
-CREATE TABLE event_relations (
-    event_id text NOT NULL,
-    relates_to_id text NOT NULL,
-    relation_type text NOT NULL,
-    aggregation_key text
-);
-
-
-
-CREATE TABLE event_reports (
-    id bigint NOT NULL,
-    received_ts bigint NOT NULL,
-    room_id text NOT NULL,
-    event_id text NOT NULL,
-    user_id text NOT NULL,
-    reason text,
-    content text
-);
-
-
-
-CREATE TABLE event_search (
-    event_id text,
-    room_id text,
-    sender text,
-    key text,
-    vector tsvector,
-    origin_server_ts bigint,
-    stream_ordering bigint
-);
-
-
-
-CREATE TABLE event_to_state_groups (
-    event_id text NOT NULL,
-    state_group bigint NOT NULL
-);
-
-
-
-CREATE TABLE events (
-    stream_ordering integer NOT NULL,
-    topological_ordering bigint NOT NULL,
-    event_id text NOT NULL,
-    type text NOT NULL,
-    room_id text NOT NULL,
-    content text,
-    unrecognized_keys text,
-    processed boolean NOT NULL,
-    outlier boolean NOT NULL,
-    depth bigint DEFAULT 0 NOT NULL,
-    origin_server_ts bigint,
-    received_ts bigint,
-    sender text,
-    contains_url boolean
-);
-
-
-
-CREATE TABLE ex_outlier_stream (
-    event_stream_ordering bigint NOT NULL,
-    event_id text NOT NULL,
-    state_group bigint NOT NULL
-);
-
-
-
-CREATE TABLE federation_stream_position (
-    type text NOT NULL,
-    stream_id integer NOT NULL
-);
-
-
-
-CREATE TABLE group_attestations_remote (
-    group_id text NOT NULL,
-    user_id text NOT NULL,
-    valid_until_ms bigint NOT NULL,
-    attestation_json text NOT NULL
-);
-
-
-
-CREATE TABLE group_attestations_renewals (
-    group_id text NOT NULL,
-    user_id text NOT NULL,
-    valid_until_ms bigint NOT NULL
-);
-
-
-
-CREATE TABLE group_invites (
-    group_id text NOT NULL,
-    user_id text NOT NULL
-);
-
-
-
-CREATE TABLE group_roles (
-    group_id text NOT NULL,
-    role_id text NOT NULL,
-    profile text NOT NULL,
-    is_public boolean NOT NULL
-);
-
-
-
-CREATE TABLE group_room_categories (
-    group_id text NOT NULL,
-    category_id text NOT NULL,
-    profile text NOT NULL,
-    is_public boolean NOT NULL
-);
-
-
-
-CREATE TABLE group_rooms (
-    group_id text NOT NULL,
-    room_id text NOT NULL,
-    is_public boolean NOT NULL
-);
-
-
-
-CREATE TABLE group_summary_roles (
-    group_id text NOT NULL,
-    role_id text NOT NULL,
-    role_order bigint NOT NULL,
-    CONSTRAINT group_summary_roles_role_order_check CHECK ((role_order > 0))
-);
-
-
-
-CREATE TABLE group_summary_room_categories (
-    group_id text NOT NULL,
-    category_id text NOT NULL,
-    cat_order bigint NOT NULL,
-    CONSTRAINT group_summary_room_categories_cat_order_check CHECK ((cat_order > 0))
-);
-
-
-
-CREATE TABLE group_summary_rooms (
-    group_id text NOT NULL,
-    room_id text NOT NULL,
-    category_id text NOT NULL,
-    room_order bigint NOT NULL,
-    is_public boolean NOT NULL,
-    CONSTRAINT group_summary_rooms_room_order_check CHECK ((room_order > 0))
-);
-
-
-
-CREATE TABLE group_summary_users (
-    group_id text NOT NULL,
-    user_id text NOT NULL,
-    role_id text NOT NULL,
-    user_order bigint NOT NULL,
-    is_public boolean NOT NULL
-);
-
-
-
-CREATE TABLE group_users (
-    group_id text NOT NULL,
-    user_id text NOT NULL,
-    is_admin boolean NOT NULL,
-    is_public boolean NOT NULL
-);
-
-
-
-CREATE TABLE groups (
-    group_id text NOT NULL,
-    name text,
-    avatar_url text,
-    short_description text,
-    long_description text,
-    is_public boolean NOT NULL,
-    join_policy text DEFAULT 'invite'::text NOT NULL
-);
-
-
-
-CREATE TABLE guest_access (
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    guest_access text NOT NULL
-);
-
-
-
-CREATE TABLE history_visibility (
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    history_visibility text NOT NULL
-);
-
-
-
-CREATE TABLE local_group_membership (
-    group_id text NOT NULL,
-    user_id text NOT NULL,
-    is_admin boolean NOT NULL,
-    membership text NOT NULL,
-    is_publicised boolean NOT NULL,
-    content text NOT NULL
-);
-
-
-
-CREATE TABLE local_group_updates (
-    stream_id bigint NOT NULL,
-    group_id text NOT NULL,
-    user_id text NOT NULL,
-    type text NOT NULL,
-    content text NOT NULL
-);
-
-
-
-CREATE TABLE local_invites (
-    stream_id bigint NOT NULL,
-    inviter text NOT NULL,
-    invitee text NOT NULL,
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    locally_rejected text,
-    replaced_by text
-);
-
-
-
-CREATE TABLE local_media_repository (
-    media_id text,
-    media_type text,
-    media_length integer,
-    created_ts bigint,
-    upload_name text,
-    user_id text,
-    quarantined_by text,
-    url_cache text,
-    last_access_ts bigint
-);
-
-
-
-CREATE TABLE local_media_repository_thumbnails (
-    media_id text,
-    thumbnail_width integer,
-    thumbnail_height integer,
-    thumbnail_type text,
-    thumbnail_method text,
-    thumbnail_length integer
-);
-
-
-
-CREATE TABLE local_media_repository_url_cache (
-    url text,
-    response_code integer,
-    etag text,
-    expires_ts bigint,
-    og text,
-    media_id text,
-    download_ts bigint
-);
-
-
-
-CREATE TABLE monthly_active_users (
-    user_id text NOT NULL,
-    "timestamp" bigint NOT NULL
-);
-
-
-
-CREATE TABLE open_id_tokens (
-    token text NOT NULL,
-    ts_valid_until_ms bigint NOT NULL,
-    user_id text NOT NULL
-);
-
-
-
-CREATE TABLE presence (
-    user_id text NOT NULL,
-    state character varying(20),
-    status_msg text,
-    mtime bigint
-);
-
-
-
-CREATE TABLE presence_allow_inbound (
-    observed_user_id text NOT NULL,
-    observer_user_id text NOT NULL
-);
-
-
-
-CREATE TABLE presence_stream (
-    stream_id bigint,
-    user_id text,
-    state text,
-    last_active_ts bigint,
-    last_federation_update_ts bigint,
-    last_user_sync_ts bigint,
-    status_msg text,
-    currently_active boolean
-);
-
-
-
-CREATE TABLE profiles (
-    user_id text NOT NULL,
-    displayname text,
-    avatar_url text
-);
-
-
-
-CREATE TABLE public_room_list_stream (
-    stream_id bigint NOT NULL,
-    room_id text NOT NULL,
-    visibility boolean NOT NULL,
-    appservice_id text,
-    network_id text
-);
-
-
-
-CREATE TABLE push_rules (
-    id bigint NOT NULL,
-    user_name text NOT NULL,
-    rule_id text NOT NULL,
-    priority_class smallint NOT NULL,
-    priority integer DEFAULT 0 NOT NULL,
-    conditions text NOT NULL,
-    actions text NOT NULL
-);
-
-
-
-CREATE TABLE push_rules_enable (
-    id bigint NOT NULL,
-    user_name text NOT NULL,
-    rule_id text NOT NULL,
-    enabled smallint
-);
-
-
-
-CREATE TABLE push_rules_stream (
-    stream_id bigint NOT NULL,
-    event_stream_ordering bigint NOT NULL,
-    user_id text NOT NULL,
-    rule_id text NOT NULL,
-    op text NOT NULL,
-    priority_class smallint,
-    priority integer,
-    conditions text,
-    actions text
-);
-
-
-
-CREATE TABLE pusher_throttle (
-    pusher bigint NOT NULL,
-    room_id text NOT NULL,
-    last_sent_ts bigint,
-    throttle_ms bigint
-);
-
-
-
-CREATE TABLE pushers (
-    id bigint NOT NULL,
-    user_name text NOT NULL,
-    access_token bigint,
-    profile_tag text NOT NULL,
-    kind text NOT NULL,
-    app_id text NOT NULL,
-    app_display_name text NOT NULL,
-    device_display_name text NOT NULL,
-    pushkey text NOT NULL,
-    ts bigint NOT NULL,
-    lang text,
-    data text,
-    last_stream_ordering integer,
-    last_success bigint,
-    failing_since bigint
-);
-
-
-
-CREATE TABLE ratelimit_override (
-    user_id text NOT NULL,
-    messages_per_second bigint,
-    burst_count bigint
-);
-
-
-
-CREATE TABLE receipts_graph (
-    room_id text NOT NULL,
-    receipt_type text NOT NULL,
-    user_id text NOT NULL,
-    event_ids text NOT NULL,
-    data text NOT NULL
-);
-
-
-
-CREATE TABLE receipts_linearized (
-    stream_id bigint NOT NULL,
-    room_id text NOT NULL,
-    receipt_type text NOT NULL,
-    user_id text NOT NULL,
-    event_id text NOT NULL,
-    data text NOT NULL
-);
-
-
-
-CREATE TABLE received_transactions (
-    transaction_id text,
-    origin text,
-    ts bigint,
-    response_code integer,
-    response_json bytea,
-    has_been_referenced smallint DEFAULT 0
-);
-
-
-
-CREATE TABLE redactions (
-    event_id text NOT NULL,
-    redacts text NOT NULL
-);
-
-
-
-CREATE TABLE rejections (
-    event_id text NOT NULL,
-    reason text NOT NULL,
-    last_check text NOT NULL
-);
-
-
-
-CREATE TABLE remote_media_cache (
-    media_origin text,
-    media_id text,
-    media_type text,
-    created_ts bigint,
-    upload_name text,
-    media_length integer,
-    filesystem_id text,
-    last_access_ts bigint,
-    quarantined_by text
-);
-
-
-
-CREATE TABLE remote_media_cache_thumbnails (
-    media_origin text,
-    media_id text,
-    thumbnail_width integer,
-    thumbnail_height integer,
-    thumbnail_method text,
-    thumbnail_type text,
-    thumbnail_length integer,
-    filesystem_id text
-);
-
-
-
-CREATE TABLE remote_profile_cache (
-    user_id text NOT NULL,
-    displayname text,
-    avatar_url text,
-    last_check bigint NOT NULL
-);
-
-
-
-CREATE TABLE room_account_data (
-    user_id text NOT NULL,
-    room_id text NOT NULL,
-    account_data_type text NOT NULL,
-    stream_id bigint NOT NULL,
-    content text NOT NULL
-);
-
-
-
-CREATE TABLE room_alias_servers (
-    room_alias text NOT NULL,
-    server text NOT NULL
-);
-
-
-
-CREATE TABLE room_aliases (
-    room_alias text NOT NULL,
-    room_id text NOT NULL,
-    creator text
-);
-
-
-
-CREATE TABLE room_depth (
-    room_id text NOT NULL,
-    min_depth integer NOT NULL
-);
-
-
-
-CREATE TABLE room_memberships (
-    event_id text NOT NULL,
-    user_id text NOT NULL,
-    sender text NOT NULL,
-    room_id text NOT NULL,
-    membership text NOT NULL,
-    forgotten integer DEFAULT 0,
-    display_name text,
-    avatar_url text
-);
-
-
-
-CREATE TABLE room_names (
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    name text NOT NULL
-);
-
-
-
-CREATE TABLE room_state (
-    room_id text NOT NULL,
-    join_rules text,
-    history_visibility text,
-    encryption text,
-    name text,
-    topic text,
-    avatar text,
-    canonical_alias text
-);
-
-
-
-CREATE TABLE room_stats (
-    room_id text NOT NULL,
-    ts bigint NOT NULL,
-    bucket_size integer NOT NULL,
-    current_state_events integer NOT NULL,
-    joined_members integer NOT NULL,
-    invited_members integer NOT NULL,
-    left_members integer NOT NULL,
-    banned_members integer NOT NULL,
-    state_events integer NOT NULL
-);
-
-
-
-CREATE TABLE room_stats_earliest_token (
-    room_id text NOT NULL,
-    token bigint NOT NULL
-);
-
-
-
-CREATE TABLE room_tags (
-    user_id text NOT NULL,
-    room_id text NOT NULL,
-    tag text NOT NULL,
-    content text NOT NULL
-);
-
-
-
-CREATE TABLE room_tags_revisions (
-    user_id text NOT NULL,
-    room_id text NOT NULL,
-    stream_id bigint NOT NULL
-);
-
-
-
-CREATE TABLE rooms (
-    room_id text NOT NULL,
-    is_public boolean,
-    creator text
-);
-
-
-
-CREATE TABLE server_keys_json (
-    server_name text NOT NULL,
-    key_id text NOT NULL,
-    from_server text NOT NULL,
-    ts_added_ms bigint NOT NULL,
-    ts_valid_until_ms bigint NOT NULL,
-    key_json bytea NOT NULL
-);
-
-
-
-CREATE TABLE server_signature_keys (
-    server_name text,
-    key_id text,
-    from_server text,
-    ts_added_ms bigint,
-    verify_key bytea,
-    ts_valid_until_ms bigint
-);
-
-
-
-CREATE TABLE state_events (
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    type text NOT NULL,
-    state_key text NOT NULL,
-    prev_state text
-);
-
-
-
-CREATE TABLE stats_stream_pos (
-    lock character(1) DEFAULT 'X'::bpchar NOT NULL,
-    stream_id bigint,
-    CONSTRAINT stats_stream_pos_lock_check CHECK ((lock = 'X'::bpchar))
-);
-
-
-
-CREATE TABLE stream_ordering_to_exterm (
-    stream_ordering bigint NOT NULL,
-    room_id text NOT NULL,
-    event_id text NOT NULL
-);
-
-
-
-CREATE TABLE threepid_guest_access_tokens (
-    medium text,
-    address text,
-    guest_access_token text,
-    first_inviter text
-);
-
-
-
-CREATE TABLE topics (
-    event_id text NOT NULL,
-    room_id text NOT NULL,
-    topic text NOT NULL
-);
-
-
-
-CREATE TABLE user_daily_visits (
-    user_id text NOT NULL,
-    device_id text,
-    "timestamp" bigint NOT NULL
-);
-
-
-
-CREATE TABLE user_directory (
-    user_id text NOT NULL,
-    room_id text,
-    display_name text,
-    avatar_url text
-);
-
-
-
-CREATE TABLE user_directory_search (
-    user_id text NOT NULL,
-    vector tsvector
-);
-
-
-
-CREATE TABLE user_directory_stream_pos (
-    lock character(1) DEFAULT 'X'::bpchar NOT NULL,
-    stream_id bigint,
-    CONSTRAINT user_directory_stream_pos_lock_check CHECK ((lock = 'X'::bpchar))
-);
-
-
-
-CREATE TABLE user_filters (
-    user_id text,
-    filter_id bigint,
-    filter_json bytea
-);
-
-
-
-CREATE TABLE user_ips (
-    user_id text NOT NULL,
-    access_token text NOT NULL,
-    device_id text,
-    ip text NOT NULL,
-    user_agent text NOT NULL,
-    last_seen bigint NOT NULL
-);
-
-
-
-CREATE TABLE user_stats (
-    user_id text NOT NULL,
-    ts bigint NOT NULL,
-    bucket_size integer NOT NULL,
-    public_rooms integer NOT NULL,
-    private_rooms integer NOT NULL
-);
-
-
-
-CREATE TABLE user_threepid_id_server (
-    user_id text NOT NULL,
-    medium text NOT NULL,
-    address text NOT NULL,
-    id_server text NOT NULL
-);
-
-
-
-CREATE TABLE user_threepids (
-    user_id text NOT NULL,
-    medium text NOT NULL,
-    address text NOT NULL,
-    validated_at bigint NOT NULL,
-    added_at bigint NOT NULL
-);
-
-
-
-CREATE TABLE users (
-    name text,
-    password_hash text,
-    creation_ts bigint,
-    admin smallint DEFAULT 0 NOT NULL,
-    upgrade_ts bigint,
-    is_guest smallint DEFAULT 0 NOT NULL,
-    appservice_id text,
-    consent_version text,
-    consent_server_notice_sent text,
-    user_type text
-);
-
-
-
-CREATE TABLE users_in_public_rooms (
-    user_id text NOT NULL,
-    room_id text NOT NULL
-);
-
-
-
-CREATE TABLE users_pending_deactivation (
-    user_id text NOT NULL
-);
-
-
-
-CREATE TABLE users_who_share_private_rooms (
-    user_id text NOT NULL,
-    other_user_id text NOT NULL,
-    room_id text NOT NULL
-);
-
-
-
-ALTER TABLE ONLY access_tokens
-    ADD CONSTRAINT access_tokens_pkey PRIMARY KEY (id);
-
-
-
-ALTER TABLE ONLY access_tokens
-    ADD CONSTRAINT access_tokens_token_key UNIQUE (token);
-
-
-
-ALTER TABLE ONLY account_data
-    ADD CONSTRAINT account_data_uniqueness UNIQUE (user_id, account_data_type);
-
-
-
-ALTER TABLE ONLY account_validity
-    ADD CONSTRAINT account_validity_pkey PRIMARY KEY (user_id);
-
-
-
-ALTER TABLE ONLY application_services_state
-    ADD CONSTRAINT application_services_state_pkey PRIMARY KEY (as_id);
-
-
-
-ALTER TABLE ONLY application_services_txns
-    ADD CONSTRAINT application_services_txns_as_id_txn_id_key UNIQUE (as_id, txn_id);
-
-
-
-ALTER TABLE ONLY appservice_stream_position
-    ADD CONSTRAINT appservice_stream_position_lock_key UNIQUE (lock);
-
-
-
-ALTER TABLE ONLY current_state_events
-    ADD CONSTRAINT current_state_events_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY current_state_events
-    ADD CONSTRAINT current_state_events_room_id_type_state_key_key UNIQUE (room_id, type, state_key);
-
-
-
-ALTER TABLE ONLY destinations
-    ADD CONSTRAINT destinations_pkey PRIMARY KEY (destination);
-
-
-
-ALTER TABLE ONLY devices
-    ADD CONSTRAINT device_uniqueness UNIQUE (user_id, device_id);
-
-
-
-ALTER TABLE ONLY e2e_device_keys_json
-    ADD CONSTRAINT e2e_device_keys_json_uniqueness UNIQUE (user_id, device_id);
-
-
-
-ALTER TABLE ONLY e2e_one_time_keys_json
-    ADD CONSTRAINT e2e_one_time_keys_json_uniqueness UNIQUE (user_id, device_id, algorithm, key_id);
-
-
-
-ALTER TABLE ONLY event_backward_extremities
-    ADD CONSTRAINT event_backward_extremities_event_id_room_id_key UNIQUE (event_id, room_id);
-
-
-
-ALTER TABLE ONLY event_edges
-    ADD CONSTRAINT event_edges_event_id_prev_event_id_room_id_is_state_key UNIQUE (event_id, prev_event_id, room_id, is_state);
-
-
-
-ALTER TABLE ONLY event_forward_extremities
-    ADD CONSTRAINT event_forward_extremities_event_id_room_id_key UNIQUE (event_id, room_id);
-
-
-
-ALTER TABLE ONLY event_push_actions
-    ADD CONSTRAINT event_id_user_id_profile_tag_uniqueness UNIQUE (room_id, event_id, user_id, profile_tag);
-
-
-
-ALTER TABLE ONLY event_json
-    ADD CONSTRAINT event_json_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY event_push_summary_stream_ordering
-    ADD CONSTRAINT event_push_summary_stream_ordering_lock_key UNIQUE (lock);
-
-
-
-ALTER TABLE ONLY event_reference_hashes
-    ADD CONSTRAINT event_reference_hashes_event_id_algorithm_key UNIQUE (event_id, algorithm);
-
-
-
-ALTER TABLE ONLY event_reports
-    ADD CONSTRAINT event_reports_pkey PRIMARY KEY (id);
-
-
-
-ALTER TABLE ONLY event_to_state_groups
-    ADD CONSTRAINT event_to_state_groups_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY events
-    ADD CONSTRAINT events_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY events
-    ADD CONSTRAINT events_pkey PRIMARY KEY (stream_ordering);
-
-
-
-ALTER TABLE ONLY ex_outlier_stream
-    ADD CONSTRAINT ex_outlier_stream_pkey PRIMARY KEY (event_stream_ordering);
-
-
-
-ALTER TABLE ONLY group_roles
-    ADD CONSTRAINT group_roles_group_id_role_id_key UNIQUE (group_id, role_id);
-
-
-
-ALTER TABLE ONLY group_room_categories
-    ADD CONSTRAINT group_room_categories_group_id_category_id_key UNIQUE (group_id, category_id);
-
-
-
-ALTER TABLE ONLY group_summary_roles
-    ADD CONSTRAINT group_summary_roles_group_id_role_id_role_order_key UNIQUE (group_id, role_id, role_order);
-
-
-
-ALTER TABLE ONLY group_summary_room_categories
-    ADD CONSTRAINT group_summary_room_categories_group_id_category_id_cat_orde_key UNIQUE (group_id, category_id, cat_order);
-
-
-
-ALTER TABLE ONLY group_summary_rooms
-    ADD CONSTRAINT group_summary_rooms_group_id_category_id_room_id_room_order_key UNIQUE (group_id, category_id, room_id, room_order);
-
-
-
-ALTER TABLE ONLY guest_access
-    ADD CONSTRAINT guest_access_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY history_visibility
-    ADD CONSTRAINT history_visibility_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY local_media_repository
-    ADD CONSTRAINT local_media_repository_media_id_key UNIQUE (media_id);
-
-
-
-ALTER TABLE ONLY local_media_repository_thumbnails
-    ADD CONSTRAINT local_media_repository_thumbn_media_id_thumbnail_width_thum_key UNIQUE (media_id, thumbnail_width, thumbnail_height, thumbnail_type);
-
-
-
-ALTER TABLE ONLY user_threepids
-    ADD CONSTRAINT medium_address UNIQUE (medium, address);
-
-
-
-ALTER TABLE ONLY open_id_tokens
-    ADD CONSTRAINT open_id_tokens_pkey PRIMARY KEY (token);
-
-
-
-ALTER TABLE ONLY presence_allow_inbound
-    ADD CONSTRAINT presence_allow_inbound_observed_user_id_observer_user_id_key UNIQUE (observed_user_id, observer_user_id);
-
-
-
-ALTER TABLE ONLY presence
-    ADD CONSTRAINT presence_user_id_key UNIQUE (user_id);
-
-
-
-ALTER TABLE ONLY account_data_max_stream_id
-    ADD CONSTRAINT private_user_data_max_stream_id_lock_key UNIQUE (lock);
-
-
-
-ALTER TABLE ONLY profiles
-    ADD CONSTRAINT profiles_user_id_key UNIQUE (user_id);
-
-
-
-ALTER TABLE ONLY push_rules_enable
-    ADD CONSTRAINT push_rules_enable_pkey PRIMARY KEY (id);
-
-
-
-ALTER TABLE ONLY push_rules_enable
-    ADD CONSTRAINT push_rules_enable_user_name_rule_id_key UNIQUE (user_name, rule_id);
-
-
-
-ALTER TABLE ONLY push_rules
-    ADD CONSTRAINT push_rules_pkey PRIMARY KEY (id);
-
-
-
-ALTER TABLE ONLY push_rules
-    ADD CONSTRAINT push_rules_user_name_rule_id_key UNIQUE (user_name, rule_id);
-
-
-
-ALTER TABLE ONLY pusher_throttle
-    ADD CONSTRAINT pusher_throttle_pkey PRIMARY KEY (pusher, room_id);
-
-
-
-ALTER TABLE ONLY pushers
-    ADD CONSTRAINT pushers2_app_id_pushkey_user_name_key UNIQUE (app_id, pushkey, user_name);
-
-
-
-ALTER TABLE ONLY pushers
-    ADD CONSTRAINT pushers2_pkey PRIMARY KEY (id);
-
-
-
-ALTER TABLE ONLY receipts_graph
-    ADD CONSTRAINT receipts_graph_uniqueness UNIQUE (room_id, receipt_type, user_id);
-
-
-
-ALTER TABLE ONLY receipts_linearized
-    ADD CONSTRAINT receipts_linearized_uniqueness UNIQUE (room_id, receipt_type, user_id);
-
-
-
-ALTER TABLE ONLY received_transactions
-    ADD CONSTRAINT received_transactions_transaction_id_origin_key UNIQUE (transaction_id, origin);
-
-
-
-ALTER TABLE ONLY redactions
-    ADD CONSTRAINT redactions_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY rejections
-    ADD CONSTRAINT rejections_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY remote_media_cache
-    ADD CONSTRAINT remote_media_cache_media_origin_media_id_key UNIQUE (media_origin, media_id);
-
-
-
-ALTER TABLE ONLY remote_media_cache_thumbnails
-    ADD CONSTRAINT remote_media_cache_thumbnails_media_origin_media_id_thumbna_key UNIQUE (media_origin, media_id, thumbnail_width, thumbnail_height, thumbnail_type);
-
-
-
-ALTER TABLE ONLY room_account_data
-    ADD CONSTRAINT room_account_data_uniqueness UNIQUE (user_id, room_id, account_data_type);
-
-
-
-ALTER TABLE ONLY room_aliases
-    ADD CONSTRAINT room_aliases_room_alias_key UNIQUE (room_alias);
-
-
-
-ALTER TABLE ONLY room_depth
-    ADD CONSTRAINT room_depth_room_id_key UNIQUE (room_id);
-
-
-
-ALTER TABLE ONLY room_memberships
-    ADD CONSTRAINT room_memberships_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY room_names
-    ADD CONSTRAINT room_names_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY room_tags_revisions
-    ADD CONSTRAINT room_tag_revisions_uniqueness UNIQUE (user_id, room_id);
-
-
-
-ALTER TABLE ONLY room_tags
-    ADD CONSTRAINT room_tag_uniqueness UNIQUE (user_id, room_id, tag);
-
-
-
-ALTER TABLE ONLY rooms
-    ADD CONSTRAINT rooms_pkey PRIMARY KEY (room_id);
-
-
-
-ALTER TABLE ONLY server_keys_json
-    ADD CONSTRAINT server_keys_json_uniqueness UNIQUE (server_name, key_id, from_server);
-
-
-
-ALTER TABLE ONLY server_signature_keys
-    ADD CONSTRAINT server_signature_keys_server_name_key_id_key UNIQUE (server_name, key_id);
-
-
-
-ALTER TABLE ONLY state_events
-    ADD CONSTRAINT state_events_event_id_key UNIQUE (event_id);
-
-
-ALTER TABLE ONLY stats_stream_pos
-    ADD CONSTRAINT stats_stream_pos_lock_key UNIQUE (lock);
-
-
-
-ALTER TABLE ONLY topics
-    ADD CONSTRAINT topics_event_id_key UNIQUE (event_id);
-
-
-
-ALTER TABLE ONLY user_directory_stream_pos
-    ADD CONSTRAINT user_directory_stream_pos_lock_key UNIQUE (lock);
-
-
-
-ALTER TABLE ONLY users
-    ADD CONSTRAINT users_name_key UNIQUE (name);
-
-
-
-CREATE INDEX access_tokens_device_id ON access_tokens USING btree (user_id, device_id);
-
-
-
-CREATE INDEX account_data_stream_id ON account_data USING btree (user_id, stream_id);
-
-
-
-CREATE INDEX application_services_txns_id ON application_services_txns USING btree (as_id);
-
-
-
-CREATE UNIQUE INDEX appservice_room_list_idx ON appservice_room_list USING btree (appservice_id, network_id, room_id);
-
-
-
-CREATE UNIQUE INDEX blocked_rooms_idx ON blocked_rooms USING btree (room_id);
-
-
-
-CREATE INDEX cache_invalidation_stream_id ON cache_invalidation_stream USING btree (stream_id);
-
-
-
-CREATE INDEX current_state_delta_stream_idx ON current_state_delta_stream USING btree (stream_id);
-
-
-
-CREATE INDEX current_state_events_member_index ON current_state_events USING btree (state_key) WHERE (type = 'm.room.member'::text);
-
-
-
-CREATE INDEX deleted_pushers_stream_id ON deleted_pushers USING btree (stream_id);
-
-
-
-CREATE INDEX device_federation_inbox_sender_id ON device_federation_inbox USING btree (origin, message_id);
-
-
-
-CREATE INDEX device_federation_outbox_destination_id ON device_federation_outbox USING btree (destination, stream_id);
-
-
-
-CREATE INDEX device_federation_outbox_id ON device_federation_outbox USING btree (stream_id);
-
-
-
-CREATE INDEX device_inbox_stream_id_user_id ON device_inbox USING btree (stream_id, user_id);
-
-
-
-CREATE INDEX device_inbox_user_stream_id ON device_inbox USING btree (user_id, device_id, stream_id);
-
-
-
-CREATE INDEX device_lists_outbound_last_success_idx ON device_lists_outbound_last_success USING btree (destination, user_id, stream_id);
-
-
-
-CREATE INDEX device_lists_outbound_pokes_id ON device_lists_outbound_pokes USING btree (destination, stream_id);
-
-
-
-CREATE INDEX device_lists_outbound_pokes_stream ON device_lists_outbound_pokes USING btree (stream_id);
-
-
-
-CREATE INDEX device_lists_outbound_pokes_user ON device_lists_outbound_pokes USING btree (destination, user_id);
-
-
-
-CREATE UNIQUE INDEX device_lists_remote_cache_unique_id ON device_lists_remote_cache USING btree (user_id, device_id);
-
-
-
-CREATE UNIQUE INDEX device_lists_remote_extremeties_unique_idx ON device_lists_remote_extremeties USING btree (user_id);
-
-
-
-CREATE INDEX device_lists_stream_id ON device_lists_stream USING btree (stream_id, user_id);
-
-
-
-CREATE INDEX device_lists_stream_user_id ON device_lists_stream USING btree (user_id, device_id);
-
-
-
-CREATE UNIQUE INDEX e2e_room_keys_idx ON e2e_room_keys USING btree (user_id, room_id, session_id);
-
-
-
-CREATE UNIQUE INDEX e2e_room_keys_versions_idx ON e2e_room_keys_versions USING btree (user_id, version);
-
-
-
-CREATE UNIQUE INDEX erased_users_user ON erased_users USING btree (user_id);
-
-
-
-CREATE INDEX ev_b_extrem_id ON event_backward_extremities USING btree (event_id);
-
-
-
-CREATE INDEX ev_b_extrem_room ON event_backward_extremities USING btree (room_id);
-
-
-
-CREATE INDEX ev_edges_id ON event_edges USING btree (event_id);
-
-
-
-CREATE INDEX ev_edges_prev_id ON event_edges USING btree (prev_event_id);
-
-
-
-CREATE INDEX ev_extrem_id ON event_forward_extremities USING btree (event_id);
-
-
-
-CREATE INDEX ev_extrem_room ON event_forward_extremities USING btree (room_id);
-
-
-
-CREATE INDEX evauth_edges_id ON event_auth USING btree (event_id);
-
-
-
-CREATE INDEX event_contains_url_index ON events USING btree (room_id, topological_ordering, stream_ordering) WHERE ((contains_url = true) AND (outlier = false));
-
-
-
-CREATE INDEX event_json_room_id ON event_json USING btree (room_id);
-
-
-
-CREATE INDEX event_push_actions_highlights_index ON event_push_actions USING btree (user_id, room_id, topological_ordering, stream_ordering) WHERE (highlight = 1);
-
-
-
-CREATE INDEX event_push_actions_rm_tokens ON event_push_actions USING btree (user_id, room_id, topological_ordering, stream_ordering);
-
-
-
-CREATE INDEX event_push_actions_room_id_user_id ON event_push_actions USING btree (room_id, user_id);
-
-
-
-CREATE INDEX event_push_actions_staging_id ON event_push_actions_staging USING btree (event_id);
-
-
-
-CREATE INDEX event_push_actions_stream_ordering ON event_push_actions USING btree (stream_ordering, user_id);
-
-
-
-CREATE INDEX event_push_actions_u_highlight ON event_push_actions USING btree (user_id, stream_ordering);
-
-
-
-CREATE INDEX event_push_summary_user_rm ON event_push_summary USING btree (user_id, room_id);
-
-
-
-CREATE INDEX event_reference_hashes_id ON event_reference_hashes USING btree (event_id);
-
-
-
-CREATE UNIQUE INDEX event_relations_id ON event_relations USING btree (event_id);
-
-
-
-CREATE INDEX event_relations_relates ON event_relations USING btree (relates_to_id, relation_type, aggregation_key);
-
-
-
-CREATE INDEX event_search_ev_ridx ON event_search USING btree (room_id);
-
-
-
-CREATE UNIQUE INDEX event_search_event_id_idx ON event_search USING btree (event_id);
-
-
-
-CREATE INDEX event_search_fts_idx ON event_search USING gin (vector);
-
-
-
-CREATE INDEX event_to_state_groups_sg_index ON event_to_state_groups USING btree (state_group);
-
-
-
-CREATE INDEX events_order_room ON events USING btree (room_id, topological_ordering, stream_ordering);
-
-
-
-CREATE INDEX events_room_stream ON events USING btree (room_id, stream_ordering);
-
-
-
-CREATE INDEX events_ts ON events USING btree (origin_server_ts, stream_ordering);
-
-
-
-CREATE INDEX group_attestations_remote_g_idx ON group_attestations_remote USING btree (group_id, user_id);
-
-
-
-CREATE INDEX group_attestations_remote_u_idx ON group_attestations_remote USING btree (user_id);
-
-
-
-CREATE INDEX group_attestations_remote_v_idx ON group_attestations_remote USING btree (valid_until_ms);
-
-
-
-CREATE INDEX group_attestations_renewals_g_idx ON group_attestations_renewals USING btree (group_id, user_id);
-
-
-
-CREATE INDEX group_attestations_renewals_u_idx ON group_attestations_renewals USING btree (user_id);
-
-
-
-CREATE INDEX group_attestations_renewals_v_idx ON group_attestations_renewals USING btree (valid_until_ms);
-
-
-
-CREATE UNIQUE INDEX group_invites_g_idx ON group_invites USING btree (group_id, user_id);
-
-
-
-CREATE INDEX group_invites_u_idx ON group_invites USING btree (user_id);
-
-
-
-CREATE UNIQUE INDEX group_rooms_g_idx ON group_rooms USING btree (group_id, room_id);
-
-
-
-CREATE INDEX group_rooms_r_idx ON group_rooms USING btree (room_id);
-
-
-
-CREATE UNIQUE INDEX group_summary_rooms_g_idx ON group_summary_rooms USING btree (group_id, room_id, category_id);
-
-
-
-CREATE INDEX group_summary_users_g_idx ON group_summary_users USING btree (group_id);
-
-
-
-CREATE UNIQUE INDEX group_users_g_idx ON group_users USING btree (group_id, user_id);
-
-
-
-CREATE INDEX group_users_u_idx ON group_users USING btree (user_id);
-
-
-
-CREATE UNIQUE INDEX groups_idx ON groups USING btree (group_id);
-
-
-
-CREATE INDEX local_group_membership_g_idx ON local_group_membership USING btree (group_id);
-
-
-
-CREATE INDEX local_group_membership_u_idx ON local_group_membership USING btree (user_id, group_id);
-
-
-
-CREATE INDEX local_invites_for_user_idx ON local_invites USING btree (invitee, locally_rejected, replaced_by, room_id);
-
-
-
-CREATE INDEX local_invites_id ON local_invites USING btree (stream_id);
-
-
-
-CREATE INDEX local_media_repository_thumbnails_media_id ON local_media_repository_thumbnails USING btree (media_id);
-
-
-
-CREATE INDEX local_media_repository_url_cache_by_url_download_ts ON local_media_repository_url_cache USING btree (url, download_ts);
-
-
-
-CREATE INDEX local_media_repository_url_cache_expires_idx ON local_media_repository_url_cache USING btree (expires_ts);
-
-
-
-CREATE INDEX local_media_repository_url_cache_media_idx ON local_media_repository_url_cache USING btree (media_id);
-
-
-
-CREATE INDEX local_media_repository_url_idx ON local_media_repository USING btree (created_ts) WHERE (url_cache IS NOT NULL);
-
-
-
-CREATE INDEX monthly_active_users_time_stamp ON monthly_active_users USING btree ("timestamp");
-
-
-
-CREATE UNIQUE INDEX monthly_active_users_users ON monthly_active_users USING btree (user_id);
-
-
-
-CREATE INDEX open_id_tokens_ts_valid_until_ms ON open_id_tokens USING btree (ts_valid_until_ms);
-
-
-
-CREATE INDEX presence_stream_id ON presence_stream USING btree (stream_id, user_id);
-
-
-
-CREATE INDEX presence_stream_user_id ON presence_stream USING btree (user_id);
-
-
-
-CREATE INDEX public_room_index ON rooms USING btree (is_public);
-
-
-
-CREATE INDEX public_room_list_stream_idx ON public_room_list_stream USING btree (stream_id);
-
-
-
-CREATE INDEX public_room_list_stream_rm_idx ON public_room_list_stream USING btree (room_id, stream_id);
-
-
-
-CREATE INDEX push_rules_enable_user_name ON push_rules_enable USING btree (user_name);
-
-
-
-CREATE INDEX push_rules_stream_id ON push_rules_stream USING btree (stream_id);
-
-
-
-CREATE INDEX push_rules_stream_user_stream_id ON push_rules_stream USING btree (user_id, stream_id);
-
-
-
-CREATE INDEX push_rules_user_name ON push_rules USING btree (user_name);
-
-
-
-CREATE UNIQUE INDEX ratelimit_override_idx ON ratelimit_override USING btree (user_id);
-
-
-
-CREATE INDEX receipts_linearized_id ON receipts_linearized USING btree (stream_id);
-
-
-
-CREATE INDEX receipts_linearized_room_stream ON receipts_linearized USING btree (room_id, stream_id);
-
-
-
-CREATE INDEX receipts_linearized_user ON receipts_linearized USING btree (user_id);
-
-
-
-CREATE INDEX received_transactions_ts ON received_transactions USING btree (ts);
-
-
-
-CREATE INDEX redactions_redacts ON redactions USING btree (redacts);
-
-
-
-CREATE INDEX remote_profile_cache_time ON remote_profile_cache USING btree (last_check);
-
-
-
-CREATE UNIQUE INDEX remote_profile_cache_user_id ON remote_profile_cache USING btree (user_id);
-
-
-
-CREATE INDEX room_account_data_stream_id ON room_account_data USING btree (user_id, stream_id);
-
-
-
-CREATE INDEX room_alias_servers_alias ON room_alias_servers USING btree (room_alias);
-
-
-
-CREATE INDEX room_aliases_id ON room_aliases USING btree (room_id);
-
-
-
-CREATE INDEX room_depth_room ON room_depth USING btree (room_id);
-
-
-
-CREATE INDEX room_memberships_room_id ON room_memberships USING btree (room_id);
-
-
-
-CREATE INDEX room_memberships_user_id ON room_memberships USING btree (user_id);
-
-
-
-CREATE INDEX room_names_room_id ON room_names USING btree (room_id);
-
-
-
-CREATE UNIQUE INDEX room_state_room ON room_state USING btree (room_id);
-
-
-
-CREATE UNIQUE INDEX room_stats_earliest_token_idx ON room_stats_earliest_token USING btree (room_id);
-
-
-
-CREATE UNIQUE INDEX room_stats_room_ts ON room_stats USING btree (room_id, ts);
-
-
-
-CREATE INDEX stream_ordering_to_exterm_idx ON stream_ordering_to_exterm USING btree (stream_ordering);
-
-
-
-CREATE INDEX stream_ordering_to_exterm_rm_idx ON stream_ordering_to_exterm USING btree (room_id, stream_ordering);
-
-
-
-CREATE UNIQUE INDEX threepid_guest_access_tokens_index ON threepid_guest_access_tokens USING btree (medium, address);
-
-
-
-CREATE INDEX topics_room_id ON topics USING btree (room_id);
-
-
-
-CREATE INDEX user_daily_visits_ts_idx ON user_daily_visits USING btree ("timestamp");
-
-
-
-CREATE INDEX user_daily_visits_uts_idx ON user_daily_visits USING btree (user_id, "timestamp");
-
-
-
-CREATE INDEX user_directory_room_idx ON user_directory USING btree (room_id);
-
-
-
-CREATE INDEX user_directory_search_fts_idx ON user_directory_search USING gin (vector);
-
-
-
-CREATE UNIQUE INDEX user_directory_search_user_idx ON user_directory_search USING btree (user_id);
-
-
-
-CREATE UNIQUE INDEX user_directory_user_idx ON user_directory USING btree (user_id);
-
-
-
-CREATE INDEX user_filters_by_user_id_filter_id ON user_filters USING btree (user_id, filter_id);
-
-
-
-CREATE INDEX user_ips_device_id ON user_ips USING btree (user_id, device_id, last_seen);
-
-
-
-CREATE INDEX user_ips_last_seen ON user_ips USING btree (user_id, last_seen);
-
-
-
-CREATE INDEX user_ips_last_seen_only ON user_ips USING btree (last_seen);
-
-
-
-CREATE UNIQUE INDEX user_ips_user_token_ip_unique_index ON user_ips USING btree (user_id, access_token, ip);
-
-
-
-CREATE UNIQUE INDEX user_stats_user_ts ON user_stats USING btree (user_id, ts);
-
-
-
-CREATE UNIQUE INDEX user_threepid_id_server_idx ON user_threepid_id_server USING btree (user_id, medium, address, id_server);
-
-
-
-CREATE INDEX user_threepids_medium_address ON user_threepids USING btree (medium, address);
-
-
-
-CREATE INDEX user_threepids_user_id ON user_threepids USING btree (user_id);
-
-
-
-CREATE INDEX users_creation_ts ON users USING btree (creation_ts);
-
-
-
-CREATE UNIQUE INDEX users_in_public_rooms_u_idx ON users_in_public_rooms USING btree (user_id, room_id);
-
-
-
-CREATE INDEX users_who_share_private_rooms_o_idx ON users_who_share_private_rooms USING btree (other_user_id);
-
-
-
-CREATE INDEX users_who_share_private_rooms_r_idx ON users_who_share_private_rooms USING btree (room_id);
-
-
-
-CREATE UNIQUE INDEX users_who_share_private_rooms_u_idx ON users_who_share_private_rooms USING btree (user_id, other_user_id, room_id);
diff --git a/synapse/storage/schema/main/full_schemas/54/full.sql.sqlite b/synapse/storage/schema/main/full_schemas/54/full.sql.sqlite
deleted file mode 100644
index 308124e531..0000000000
--- a/synapse/storage/schema/main/full_schemas/54/full.sql.sqlite
+++ /dev/null
@@ -1,243 +0,0 @@
-CREATE TABLE application_services_state( as_id TEXT PRIMARY KEY, state VARCHAR(5), last_txn INTEGER );
-CREATE TABLE application_services_txns( as_id TEXT NOT NULL, txn_id INTEGER NOT NULL, event_ids TEXT NOT NULL, UNIQUE(as_id, txn_id) );
-CREATE INDEX application_services_txns_id ON application_services_txns ( as_id );
-CREATE TABLE presence( user_id TEXT NOT NULL, state VARCHAR(20), status_msg TEXT, mtime BIGINT, UNIQUE (user_id) );
-CREATE TABLE presence_allow_inbound( observed_user_id TEXT NOT NULL, observer_user_id TEXT NOT NULL, UNIQUE (observed_user_id, observer_user_id) );
-CREATE TABLE users( name TEXT, password_hash TEXT, creation_ts BIGINT, admin SMALLINT DEFAULT 0 NOT NULL, upgrade_ts BIGINT, is_guest SMALLINT DEFAULT 0 NOT NULL, appservice_id TEXT, consent_version TEXT, consent_server_notice_sent TEXT, user_type TEXT DEFAULT NULL, UNIQUE(name) );
-CREATE TABLE access_tokens( id BIGINT PRIMARY KEY, user_id TEXT NOT NULL, device_id TEXT, token TEXT NOT NULL, last_used BIGINT, UNIQUE(token) );
-CREATE TABLE user_ips ( user_id TEXT NOT NULL, access_token TEXT NOT NULL, device_id TEXT, ip TEXT NOT NULL, user_agent TEXT NOT NULL, last_seen BIGINT NOT NULL );
-CREATE TABLE profiles( user_id TEXT NOT NULL, displayname TEXT, avatar_url TEXT, UNIQUE(user_id) );
-CREATE TABLE received_transactions( transaction_id TEXT, origin TEXT, ts BIGINT, response_code INTEGER, response_json bytea, has_been_referenced smallint default 0, UNIQUE (transaction_id, origin) );
-CREATE TABLE destinations( destination TEXT PRIMARY KEY, retry_last_ts BIGINT, retry_interval INTEGER );
-CREATE TABLE events( stream_ordering INTEGER PRIMARY KEY, topological_ordering BIGINT NOT NULL, event_id TEXT NOT NULL, type TEXT NOT NULL, room_id TEXT NOT NULL, content TEXT, unrecognized_keys TEXT, processed BOOL NOT NULL, outlier BOOL NOT NULL, depth BIGINT DEFAULT 0 NOT NULL, origin_server_ts BIGINT, received_ts BIGINT, sender TEXT, contains_url BOOLEAN, UNIQUE (event_id) );
-CREATE INDEX events_order_room ON events ( room_id, topological_ordering, stream_ordering );
-CREATE TABLE event_json( event_id TEXT NOT NULL, room_id TEXT NOT NULL, internal_metadata TEXT NOT NULL, json TEXT NOT NULL, format_version INTEGER, UNIQUE (event_id) );
-CREATE INDEX event_json_room_id ON event_json(room_id);
-CREATE TABLE state_events( event_id TEXT NOT NULL, room_id TEXT NOT NULL, type TEXT NOT NULL, state_key TEXT NOT NULL, prev_state TEXT, UNIQUE (event_id) );
-CREATE TABLE current_state_events( event_id TEXT NOT NULL, room_id TEXT NOT NULL, type TEXT NOT NULL, state_key TEXT NOT NULL, UNIQUE (event_id), UNIQUE (room_id, type, state_key) );
-CREATE TABLE room_memberships( event_id TEXT NOT NULL, user_id TEXT NOT NULL, sender TEXT NOT NULL, room_id TEXT NOT NULL, membership TEXT NOT NULL, forgotten INTEGER DEFAULT 0, display_name TEXT, avatar_url TEXT, UNIQUE (event_id) );
-CREATE INDEX room_memberships_room_id ON room_memberships (room_id);
-CREATE INDEX room_memberships_user_id ON room_memberships (user_id);
-CREATE TABLE topics( event_id TEXT NOT NULL, room_id TEXT NOT NULL, topic TEXT NOT NULL, UNIQUE (event_id) );
-CREATE INDEX topics_room_id ON topics(room_id);
-CREATE TABLE room_names( event_id TEXT NOT NULL, room_id TEXT NOT NULL, name TEXT NOT NULL, UNIQUE (event_id) );
-CREATE INDEX room_names_room_id ON room_names(room_id);
-CREATE TABLE rooms( room_id TEXT PRIMARY KEY NOT NULL, is_public BOOL, creator TEXT );
-CREATE TABLE server_signature_keys( server_name TEXT, key_id TEXT, from_server TEXT, ts_added_ms BIGINT, verify_key bytea, ts_valid_until_ms BIGINT, UNIQUE (server_name, key_id) );
-CREATE TABLE rejections( event_id TEXT NOT NULL, reason TEXT NOT NULL, last_check TEXT NOT NULL, UNIQUE (event_id) );
-CREATE TABLE push_rules ( id BIGINT PRIMARY KEY, user_name TEXT NOT NULL, rule_id TEXT NOT NULL, priority_class SMALLINT NOT NULL, priority INTEGER NOT NULL DEFAULT 0, conditions TEXT NOT NULL, actions TEXT NOT NULL, UNIQUE(user_name, rule_id) );
-CREATE INDEX push_rules_user_name on push_rules (user_name);
-CREATE TABLE user_filters( user_id TEXT, filter_id BIGINT, filter_json bytea );
-CREATE INDEX user_filters_by_user_id_filter_id ON user_filters( user_id, filter_id );
-CREATE TABLE push_rules_enable ( id BIGINT PRIMARY KEY, user_name TEXT NOT NULL, rule_id TEXT NOT NULL, enabled SMALLINT, UNIQUE(user_name, rule_id) );
-CREATE INDEX push_rules_enable_user_name on push_rules_enable (user_name);
-CREATE TABLE event_forward_extremities( event_id TEXT NOT NULL, room_id TEXT NOT NULL, UNIQUE (event_id, room_id) );
-CREATE INDEX ev_extrem_room ON event_forward_extremities(room_id);
-CREATE INDEX ev_extrem_id ON event_forward_extremities(event_id);
-CREATE TABLE event_backward_extremities( event_id TEXT NOT NULL, room_id TEXT NOT NULL, UNIQUE (event_id, room_id) );
-CREATE INDEX ev_b_extrem_room ON event_backward_extremities(room_id);
-CREATE INDEX ev_b_extrem_id ON event_backward_extremities(event_id);
-CREATE TABLE event_edges( event_id TEXT NOT NULL, prev_event_id TEXT NOT NULL, room_id TEXT NOT NULL, is_state BOOL NOT NULL, UNIQUE (event_id, prev_event_id, room_id, is_state) );
-CREATE INDEX ev_edges_id ON event_edges(event_id);
-CREATE INDEX ev_edges_prev_id ON event_edges(prev_event_id);
-CREATE TABLE room_depth( room_id TEXT NOT NULL, min_depth INTEGER NOT NULL, UNIQUE (room_id) );
-CREATE INDEX room_depth_room ON room_depth(room_id);
-CREATE TABLE event_to_state_groups( event_id TEXT NOT NULL, state_group BIGINT NOT NULL, UNIQUE (event_id) );
-CREATE TABLE local_media_repository ( media_id TEXT, media_type TEXT, media_length INTEGER, created_ts BIGINT, upload_name TEXT, user_id TEXT, quarantined_by TEXT, url_cache TEXT, last_access_ts BIGINT, UNIQUE (media_id) );
-CREATE TABLE local_media_repository_thumbnails ( media_id TEXT, thumbnail_width INTEGER, thumbnail_height INTEGER, thumbnail_type TEXT, thumbnail_method TEXT, thumbnail_length INTEGER, UNIQUE ( media_id, thumbnail_width, thumbnail_height, thumbnail_type ) );
-CREATE INDEX local_media_repository_thumbnails_media_id ON local_media_repository_thumbnails (media_id);
-CREATE TABLE remote_media_cache ( media_origin TEXT, media_id TEXT, media_type TEXT, created_ts BIGINT, upload_name TEXT, media_length INTEGER, filesystem_id TEXT, last_access_ts BIGINT, quarantined_by TEXT, UNIQUE (media_origin, media_id) );
-CREATE TABLE remote_media_cache_thumbnails ( media_origin TEXT, media_id TEXT, thumbnail_width INTEGER, thumbnail_height INTEGER, thumbnail_method TEXT, thumbnail_type TEXT, thumbnail_length INTEGER, filesystem_id TEXT, UNIQUE ( media_origin, media_id, thumbnail_width, thumbnail_height, thumbnail_type ) );
-CREATE TABLE redactions ( event_id TEXT NOT NULL, redacts TEXT NOT NULL, UNIQUE (event_id) );
-CREATE INDEX redactions_redacts ON redactions (redacts);
-CREATE TABLE room_aliases( room_alias TEXT NOT NULL, room_id TEXT NOT NULL, creator TEXT, UNIQUE (room_alias) );
-CREATE INDEX room_aliases_id ON room_aliases(room_id);
-CREATE TABLE room_alias_servers( room_alias TEXT NOT NULL, server TEXT NOT NULL );
-CREATE INDEX room_alias_servers_alias ON room_alias_servers(room_alias);
-CREATE TABLE event_reference_hashes ( event_id TEXT, algorithm TEXT, hash bytea, UNIQUE (event_id, algorithm) );
-CREATE INDEX event_reference_hashes_id ON event_reference_hashes(event_id);
-CREATE TABLE IF NOT EXISTS "server_keys_json" ( server_name TEXT NOT NULL, key_id TEXT NOT NULL, from_server TEXT NOT NULL, ts_added_ms BIGINT NOT NULL, ts_valid_until_ms BIGINT NOT NULL, key_json bytea NOT NULL, CONSTRAINT server_keys_json_uniqueness UNIQUE (server_name, key_id, from_server) );
-CREATE TABLE e2e_device_keys_json ( user_id TEXT NOT NULL, device_id TEXT NOT NULL, ts_added_ms BIGINT NOT NULL, key_json TEXT NOT NULL, CONSTRAINT e2e_device_keys_json_uniqueness UNIQUE (user_id, device_id) );
-CREATE TABLE e2e_one_time_keys_json ( user_id TEXT NOT NULL, device_id TEXT NOT NULL, algorithm TEXT NOT NULL, key_id TEXT NOT NULL, ts_added_ms BIGINT NOT NULL, key_json TEXT NOT NULL, CONSTRAINT e2e_one_time_keys_json_uniqueness UNIQUE (user_id, device_id, algorithm, key_id) );
-CREATE TABLE receipts_graph( room_id TEXT NOT NULL, receipt_type TEXT NOT NULL, user_id TEXT NOT NULL, event_ids TEXT NOT NULL, data TEXT NOT NULL, CONSTRAINT receipts_graph_uniqueness UNIQUE (room_id, receipt_type, user_id) );
-CREATE TABLE receipts_linearized ( stream_id BIGINT NOT NULL, room_id TEXT NOT NULL, receipt_type TEXT NOT NULL, user_id TEXT NOT NULL, event_id TEXT NOT NULL, data TEXT NOT NULL, CONSTRAINT receipts_linearized_uniqueness UNIQUE (room_id, receipt_type, user_id) );
-CREATE INDEX receipts_linearized_id ON receipts_linearized( stream_id );
-CREATE INDEX receipts_linearized_room_stream ON receipts_linearized( room_id, stream_id );
-CREATE TABLE IF NOT EXISTS "user_threepids" ( user_id TEXT NOT NULL, medium TEXT NOT NULL, address TEXT NOT NULL, validated_at BIGINT NOT NULL, added_at BIGINT NOT NULL, CONSTRAINT medium_address UNIQUE (medium, address) );
-CREATE INDEX user_threepids_user_id ON user_threepids(user_id);
-CREATE VIRTUAL TABLE event_search USING fts4 ( event_id, room_id, sender, key, value )
-/* event_search(event_id,room_id,sender,"key",value) */;
-CREATE TABLE guest_access( event_id TEXT NOT NULL, room_id TEXT NOT NULL, guest_access TEXT NOT NULL, UNIQUE (event_id) );
-CREATE TABLE history_visibility( event_id TEXT NOT NULL, room_id TEXT NOT NULL, history_visibility TEXT NOT NULL, UNIQUE (event_id) );
-CREATE TABLE room_tags( user_id TEXT NOT NULL, room_id TEXT NOT NULL, tag     TEXT NOT NULL, content TEXT NOT NULL, CONSTRAINT room_tag_uniqueness UNIQUE (user_id, room_id, tag) );
-CREATE TABLE room_tags_revisions ( user_id TEXT NOT NULL, room_id TEXT NOT NULL, stream_id BIGINT NOT NULL, CONSTRAINT room_tag_revisions_uniqueness UNIQUE (user_id, room_id) );
-CREATE TABLE IF NOT EXISTS "account_data_max_stream_id"( Lock CHAR(1) NOT NULL DEFAULT 'X' UNIQUE, stream_id  BIGINT NOT NULL, CHECK (Lock='X') );
-CREATE TABLE account_data( user_id TEXT NOT NULL, account_data_type TEXT NOT NULL, stream_id BIGINT NOT NULL, content TEXT NOT NULL, CONSTRAINT account_data_uniqueness UNIQUE (user_id, account_data_type) );
-CREATE TABLE room_account_data( user_id TEXT NOT NULL, room_id TEXT NOT NULL, account_data_type TEXT NOT NULL, stream_id BIGINT NOT NULL, content TEXT NOT NULL, CONSTRAINT room_account_data_uniqueness UNIQUE (user_id, room_id, account_data_type) );
-CREATE INDEX account_data_stream_id on account_data(user_id, stream_id);
-CREATE INDEX room_account_data_stream_id on room_account_data(user_id, stream_id);
-CREATE INDEX events_ts ON events(origin_server_ts, stream_ordering);
-CREATE TABLE event_push_actions( room_id TEXT NOT NULL, event_id TEXT NOT NULL, user_id TEXT NOT NULL, profile_tag VARCHAR(32), actions TEXT NOT NULL, topological_ordering BIGINT, stream_ordering BIGINT, notif SMALLINT, highlight SMALLINT, CONSTRAINT event_id_user_id_profile_tag_uniqueness UNIQUE (room_id, event_id, user_id, profile_tag) );
-CREATE INDEX event_push_actions_room_id_user_id on event_push_actions(room_id, user_id);
-CREATE INDEX events_room_stream on events(room_id, stream_ordering);
-CREATE INDEX public_room_index on rooms(is_public);
-CREATE INDEX receipts_linearized_user ON receipts_linearized( user_id );
-CREATE INDEX event_push_actions_rm_tokens on event_push_actions( user_id, room_id, topological_ordering, stream_ordering );
-CREATE TABLE presence_stream( stream_id BIGINT, user_id TEXT, state TEXT, last_active_ts BIGINT, last_federation_update_ts BIGINT, last_user_sync_ts BIGINT, status_msg TEXT, currently_active BOOLEAN );
-CREATE INDEX presence_stream_id ON presence_stream(stream_id, user_id);
-CREATE INDEX presence_stream_user_id ON presence_stream(user_id);
-CREATE TABLE push_rules_stream( stream_id BIGINT NOT NULL, event_stream_ordering BIGINT NOT NULL, user_id TEXT NOT NULL, rule_id TEXT NOT NULL, op TEXT NOT NULL, priority_class SMALLINT, priority INTEGER, conditions TEXT, actions TEXT );
-CREATE INDEX push_rules_stream_id ON push_rules_stream(stream_id);
-CREATE INDEX push_rules_stream_user_stream_id on push_rules_stream(user_id, stream_id);
-CREATE TABLE ex_outlier_stream( event_stream_ordering BIGINT PRIMARY KEY NOT NULL, event_id TEXT NOT NULL, state_group BIGINT NOT NULL );
-CREATE TABLE threepid_guest_access_tokens( medium TEXT, address TEXT, guest_access_token TEXT, first_inviter TEXT );
-CREATE UNIQUE INDEX threepid_guest_access_tokens_index ON threepid_guest_access_tokens(medium, address);
-CREATE TABLE local_invites( stream_id BIGINT NOT NULL, inviter TEXT NOT NULL, invitee TEXT NOT NULL, event_id TEXT NOT NULL, room_id TEXT NOT NULL, locally_rejected TEXT, replaced_by TEXT );
-CREATE INDEX local_invites_id ON local_invites(stream_id);
-CREATE INDEX local_invites_for_user_idx ON local_invites(invitee, locally_rejected, replaced_by, room_id);
-CREATE INDEX event_push_actions_stream_ordering on event_push_actions( stream_ordering, user_id );
-CREATE TABLE open_id_tokens ( token TEXT NOT NULL PRIMARY KEY, ts_valid_until_ms bigint NOT NULL, user_id TEXT NOT NULL, UNIQUE (token) );
-CREATE INDEX open_id_tokens_ts_valid_until_ms ON open_id_tokens(ts_valid_until_ms);
-CREATE TABLE pusher_throttle( pusher BIGINT NOT NULL, room_id TEXT NOT NULL, last_sent_ts BIGINT, throttle_ms BIGINT, PRIMARY KEY (pusher, room_id) );
-CREATE TABLE event_reports( id BIGINT NOT NULL PRIMARY KEY, received_ts BIGINT NOT NULL, room_id TEXT NOT NULL, event_id TEXT NOT NULL, user_id TEXT NOT NULL, reason TEXT, content TEXT );
-CREATE TABLE devices ( user_id TEXT NOT NULL, device_id TEXT NOT NULL, display_name TEXT, CONSTRAINT device_uniqueness UNIQUE (user_id, device_id) );
-CREATE TABLE appservice_stream_position( Lock CHAR(1) NOT NULL DEFAULT 'X' UNIQUE, stream_ordering BIGINT, CHECK (Lock='X') );
-CREATE TABLE device_inbox ( user_id TEXT NOT NULL, device_id TEXT NOT NULL, stream_id BIGINT NOT NULL, message_json TEXT NOT NULL );
-CREATE INDEX device_inbox_user_stream_id ON device_inbox(user_id, device_id, stream_id);
-CREATE INDEX received_transactions_ts ON received_transactions(ts);
-CREATE TABLE device_federation_outbox ( destination TEXT NOT NULL, stream_id BIGINT NOT NULL, queued_ts BIGINT NOT NULL, messages_json TEXT NOT NULL );
-CREATE INDEX device_federation_outbox_destination_id ON device_federation_outbox(destination, stream_id);
-CREATE TABLE device_federation_inbox ( origin TEXT NOT NULL, message_id TEXT NOT NULL, received_ts BIGINT NOT NULL );
-CREATE INDEX device_federation_inbox_sender_id ON device_federation_inbox(origin, message_id);
-CREATE TABLE device_max_stream_id ( stream_id BIGINT NOT NULL );
-CREATE TABLE public_room_list_stream ( stream_id BIGINT NOT NULL, room_id TEXT NOT NULL, visibility BOOLEAN NOT NULL , appservice_id TEXT, network_id TEXT);
-CREATE INDEX public_room_list_stream_idx on public_room_list_stream( stream_id );
-CREATE INDEX public_room_list_stream_rm_idx on public_room_list_stream( room_id, stream_id );
-CREATE TABLE stream_ordering_to_exterm ( stream_ordering BIGINT NOT NULL, room_id TEXT NOT NULL, event_id TEXT NOT NULL );
-CREATE INDEX stream_ordering_to_exterm_idx on stream_ordering_to_exterm( stream_ordering );
-CREATE INDEX stream_ordering_to_exterm_rm_idx on stream_ordering_to_exterm( room_id, stream_ordering );
-CREATE TABLE IF NOT EXISTS "event_auth"( event_id TEXT NOT NULL, auth_id TEXT NOT NULL, room_id TEXT NOT NULL );
-CREATE INDEX evauth_edges_id ON event_auth(event_id);
-CREATE INDEX user_threepids_medium_address on user_threepids (medium, address);
-CREATE TABLE appservice_room_list( appservice_id TEXT NOT NULL, network_id TEXT NOT NULL, room_id TEXT NOT NULL );
-CREATE UNIQUE INDEX appservice_room_list_idx ON appservice_room_list( appservice_id, network_id, room_id );
-CREATE INDEX device_federation_outbox_id ON device_federation_outbox(stream_id);
-CREATE TABLE federation_stream_position( type TEXT NOT NULL, stream_id INTEGER NOT NULL );
-CREATE TABLE device_lists_remote_cache ( user_id TEXT NOT NULL, device_id TEXT NOT NULL, content TEXT NOT NULL );
-CREATE TABLE device_lists_remote_extremeties ( user_id TEXT NOT NULL, stream_id TEXT NOT NULL );
-CREATE TABLE device_lists_stream ( stream_id BIGINT NOT NULL, user_id TEXT NOT NULL, device_id TEXT NOT NULL );
-CREATE INDEX device_lists_stream_id ON device_lists_stream(stream_id, user_id);
-CREATE TABLE device_lists_outbound_pokes ( destination TEXT NOT NULL, stream_id BIGINT NOT NULL, user_id TEXT NOT NULL, device_id TEXT NOT NULL, sent BOOLEAN NOT NULL, ts BIGINT NOT NULL );
-CREATE INDEX device_lists_outbound_pokes_id ON device_lists_outbound_pokes(destination, stream_id);
-CREATE INDEX device_lists_outbound_pokes_user ON device_lists_outbound_pokes(destination, user_id);
-CREATE TABLE event_push_summary ( user_id TEXT NOT NULL, room_id TEXT NOT NULL, notif_count BIGINT NOT NULL, stream_ordering BIGINT NOT NULL );
-CREATE INDEX event_push_summary_user_rm ON event_push_summary(user_id, room_id);
-CREATE TABLE event_push_summary_stream_ordering ( Lock CHAR(1) NOT NULL DEFAULT 'X' UNIQUE, stream_ordering BIGINT NOT NULL, CHECK (Lock='X') );
-CREATE TABLE IF NOT EXISTS "pushers" ( id BIGINT PRIMARY KEY, user_name TEXT NOT NULL, access_token BIGINT DEFAULT NULL, profile_tag TEXT NOT NULL, kind TEXT NOT NULL, app_id TEXT NOT NULL, app_display_name TEXT NOT NULL, device_display_name TEXT NOT NULL, pushkey TEXT NOT NULL, ts BIGINT NOT NULL, lang TEXT, data TEXT, last_stream_ordering INTEGER, last_success BIGINT, failing_since BIGINT, UNIQUE (app_id, pushkey, user_name) );
-CREATE INDEX device_lists_outbound_pokes_stream ON device_lists_outbound_pokes(stream_id);
-CREATE TABLE ratelimit_override ( user_id TEXT NOT NULL, messages_per_second BIGINT, burst_count BIGINT );
-CREATE UNIQUE INDEX ratelimit_override_idx ON ratelimit_override(user_id);
-CREATE TABLE current_state_delta_stream ( stream_id BIGINT NOT NULL, room_id TEXT NOT NULL, type TEXT NOT NULL, state_key TEXT NOT NULL, event_id TEXT, prev_event_id TEXT );
-CREATE INDEX current_state_delta_stream_idx ON current_state_delta_stream(stream_id);
-CREATE TABLE device_lists_outbound_last_success ( destination TEXT NOT NULL, user_id TEXT NOT NULL, stream_id BIGINT NOT NULL );
-CREATE INDEX device_lists_outbound_last_success_idx ON device_lists_outbound_last_success( destination, user_id, stream_id );
-CREATE TABLE user_directory_stream_pos ( Lock CHAR(1) NOT NULL DEFAULT 'X' UNIQUE, stream_id BIGINT, CHECK (Lock='X') );
-CREATE VIRTUAL TABLE user_directory_search USING fts4 ( user_id, value )
-/* user_directory_search(user_id,value) */;
-CREATE TABLE blocked_rooms ( room_id TEXT NOT NULL, user_id TEXT NOT NULL );
-CREATE UNIQUE INDEX blocked_rooms_idx ON blocked_rooms(room_id);
-CREATE TABLE IF NOT EXISTS "local_media_repository_url_cache"( url TEXT, response_code INTEGER, etag TEXT, expires_ts BIGINT, og TEXT, media_id TEXT, download_ts BIGINT );
-CREATE INDEX local_media_repository_url_cache_expires_idx ON local_media_repository_url_cache(expires_ts);
-CREATE INDEX local_media_repository_url_cache_by_url_download_ts ON local_media_repository_url_cache(url, download_ts);
-CREATE INDEX local_media_repository_url_cache_media_idx ON local_media_repository_url_cache(media_id);
-CREATE TABLE group_users ( group_id TEXT NOT NULL, user_id TEXT NOT NULL, is_admin BOOLEAN NOT NULL, is_public BOOLEAN NOT NULL );
-CREATE TABLE group_invites ( group_id TEXT NOT NULL, user_id TEXT NOT NULL );
-CREATE TABLE group_rooms ( group_id TEXT NOT NULL, room_id TEXT NOT NULL, is_public BOOLEAN NOT NULL );
-CREATE TABLE group_summary_rooms ( group_id TEXT NOT NULL, room_id TEXT NOT NULL, category_id TEXT NOT NULL, room_order BIGINT NOT NULL, is_public BOOLEAN NOT NULL, UNIQUE (group_id, category_id, room_id, room_order), CHECK (room_order > 0) );
-CREATE UNIQUE INDEX group_summary_rooms_g_idx ON group_summary_rooms(group_id, room_id, category_id);
-CREATE TABLE group_summary_room_categories ( group_id TEXT NOT NULL, category_id TEXT NOT NULL, cat_order BIGINT NOT NULL, UNIQUE (group_id, category_id, cat_order), CHECK (cat_order > 0) );
-CREATE TABLE group_room_categories ( group_id TEXT NOT NULL, category_id TEXT NOT NULL, profile TEXT NOT NULL, is_public BOOLEAN NOT NULL, UNIQUE (group_id, category_id) );
-CREATE TABLE group_summary_users ( group_id TEXT NOT NULL, user_id TEXT NOT NULL, role_id TEXT NOT NULL, user_order BIGINT NOT NULL, is_public BOOLEAN NOT NULL );
-CREATE INDEX group_summary_users_g_idx ON group_summary_users(group_id);
-CREATE TABLE group_summary_roles ( group_id TEXT NOT NULL, role_id TEXT NOT NULL, role_order BIGINT NOT NULL, UNIQUE (group_id, role_id, role_order), CHECK (role_order > 0) );
-CREATE TABLE group_roles ( group_id TEXT NOT NULL, role_id TEXT NOT NULL, profile TEXT NOT NULL, is_public BOOLEAN NOT NULL, UNIQUE (group_id, role_id) );
-CREATE TABLE group_attestations_renewals ( group_id TEXT NOT NULL, user_id TEXT NOT NULL, valid_until_ms BIGINT NOT NULL );
-CREATE INDEX group_attestations_renewals_g_idx ON group_attestations_renewals(group_id, user_id);
-CREATE INDEX group_attestations_renewals_u_idx ON group_attestations_renewals(user_id);
-CREATE INDEX group_attestations_renewals_v_idx ON group_attestations_renewals(valid_until_ms);
-CREATE TABLE group_attestations_remote ( group_id TEXT NOT NULL, user_id TEXT NOT NULL, valid_until_ms BIGINT NOT NULL, attestation_json TEXT NOT NULL );
-CREATE INDEX group_attestations_remote_g_idx ON group_attestations_remote(group_id, user_id);
-CREATE INDEX group_attestations_remote_u_idx ON group_attestations_remote(user_id);
-CREATE INDEX group_attestations_remote_v_idx ON group_attestations_remote(valid_until_ms);
-CREATE TABLE local_group_membership ( group_id TEXT NOT NULL, user_id TEXT NOT NULL, is_admin BOOLEAN NOT NULL, membership TEXT NOT NULL, is_publicised BOOLEAN NOT NULL, content TEXT NOT NULL );
-CREATE INDEX local_group_membership_u_idx ON local_group_membership(user_id, group_id);
-CREATE INDEX local_group_membership_g_idx ON local_group_membership(group_id);
-CREATE TABLE local_group_updates ( stream_id BIGINT NOT NULL, group_id TEXT NOT NULL, user_id TEXT NOT NULL, type TEXT NOT NULL, content TEXT NOT NULL );
-CREATE TABLE remote_profile_cache ( user_id TEXT NOT NULL, displayname TEXT, avatar_url TEXT, last_check BIGINT NOT NULL );
-CREATE UNIQUE INDEX remote_profile_cache_user_id ON remote_profile_cache(user_id);
-CREATE INDEX remote_profile_cache_time ON remote_profile_cache(last_check);
-CREATE TABLE IF NOT EXISTS "deleted_pushers" ( stream_id BIGINT NOT NULL, app_id TEXT NOT NULL, pushkey TEXT NOT NULL, user_id TEXT NOT NULL );
-CREATE INDEX deleted_pushers_stream_id ON deleted_pushers (stream_id);
-CREATE TABLE IF NOT EXISTS "groups" ( group_id TEXT NOT NULL, name TEXT, avatar_url TEXT, short_description TEXT, long_description TEXT, is_public BOOL NOT NULL , join_policy TEXT NOT NULL DEFAULT 'invite');
-CREATE UNIQUE INDEX groups_idx ON groups(group_id);
-CREATE TABLE IF NOT EXISTS "user_directory" ( user_id TEXT NOT NULL, room_id TEXT, display_name TEXT, avatar_url TEXT );
-CREATE INDEX user_directory_room_idx ON user_directory(room_id);
-CREATE UNIQUE INDEX user_directory_user_idx ON user_directory(user_id);
-CREATE TABLE event_push_actions_staging ( event_id TEXT NOT NULL, user_id TEXT NOT NULL, actions TEXT NOT NULL, notif SMALLINT NOT NULL, highlight SMALLINT NOT NULL );
-CREATE INDEX event_push_actions_staging_id ON event_push_actions_staging(event_id);
-CREATE TABLE users_pending_deactivation ( user_id TEXT NOT NULL );
-CREATE UNIQUE INDEX group_invites_g_idx ON group_invites(group_id, user_id);
-CREATE UNIQUE INDEX group_users_g_idx ON group_users(group_id, user_id);
-CREATE INDEX group_users_u_idx ON group_users(user_id);
-CREATE INDEX group_invites_u_idx ON group_invites(user_id);
-CREATE UNIQUE INDEX group_rooms_g_idx ON group_rooms(group_id, room_id);
-CREATE INDEX group_rooms_r_idx ON group_rooms(room_id);
-CREATE TABLE user_daily_visits ( user_id TEXT NOT NULL, device_id TEXT, timestamp BIGINT NOT NULL );
-CREATE INDEX user_daily_visits_uts_idx ON user_daily_visits(user_id, timestamp);
-CREATE INDEX user_daily_visits_ts_idx ON user_daily_visits(timestamp);
-CREATE TABLE erased_users ( user_id TEXT NOT NULL );
-CREATE UNIQUE INDEX erased_users_user ON erased_users(user_id);
-CREATE TABLE monthly_active_users ( user_id TEXT NOT NULL, timestamp BIGINT NOT NULL );
-CREATE UNIQUE INDEX monthly_active_users_users ON monthly_active_users(user_id);
-CREATE INDEX monthly_active_users_time_stamp ON monthly_active_users(timestamp);
-CREATE TABLE IF NOT EXISTS "e2e_room_keys_versions" ( user_id TEXT NOT NULL, version BIGINT NOT NULL, algorithm TEXT NOT NULL, auth_data TEXT NOT NULL, deleted SMALLINT DEFAULT 0 NOT NULL );
-CREATE UNIQUE INDEX e2e_room_keys_versions_idx ON e2e_room_keys_versions(user_id, version);
-CREATE TABLE IF NOT EXISTS "e2e_room_keys" ( user_id TEXT NOT NULL, room_id TEXT NOT NULL, session_id TEXT NOT NULL, version BIGINT NOT NULL, first_message_index INT, forwarded_count INT, is_verified BOOLEAN, session_data TEXT NOT NULL );
-CREATE UNIQUE INDEX e2e_room_keys_idx ON e2e_room_keys(user_id, room_id, session_id);
-CREATE TABLE users_who_share_private_rooms ( user_id TEXT NOT NULL, other_user_id TEXT NOT NULL, room_id TEXT NOT NULL );
-CREATE UNIQUE INDEX users_who_share_private_rooms_u_idx ON users_who_share_private_rooms(user_id, other_user_id, room_id);
-CREATE INDEX users_who_share_private_rooms_r_idx ON users_who_share_private_rooms(room_id);
-CREATE INDEX users_who_share_private_rooms_o_idx ON users_who_share_private_rooms(other_user_id);
-CREATE TABLE user_threepid_id_server ( user_id TEXT NOT NULL, medium TEXT NOT NULL, address TEXT NOT NULL, id_server TEXT NOT NULL );
-CREATE UNIQUE INDEX user_threepid_id_server_idx ON user_threepid_id_server( user_id, medium, address, id_server );
-CREATE TABLE users_in_public_rooms ( user_id TEXT NOT NULL, room_id TEXT NOT NULL );
-CREATE UNIQUE INDEX users_in_public_rooms_u_idx ON users_in_public_rooms(user_id, room_id);
-CREATE TABLE account_validity ( user_id TEXT PRIMARY KEY, expiration_ts_ms BIGINT NOT NULL, email_sent BOOLEAN NOT NULL, renewal_token TEXT );
-CREATE TABLE event_relations ( event_id TEXT NOT NULL, relates_to_id TEXT NOT NULL, relation_type TEXT NOT NULL, aggregation_key TEXT );
-CREATE UNIQUE INDEX event_relations_id ON event_relations(event_id);
-CREATE INDEX event_relations_relates ON event_relations(relates_to_id, relation_type, aggregation_key);
-CREATE TABLE stats_stream_pos ( Lock CHAR(1) NOT NULL DEFAULT 'X' UNIQUE, stream_id BIGINT, CHECK (Lock='X') );
-CREATE TABLE user_stats ( user_id TEXT NOT NULL, ts BIGINT NOT NULL, bucket_size INT NOT NULL, public_rooms INT NOT NULL, private_rooms INT NOT NULL );
-CREATE UNIQUE INDEX user_stats_user_ts ON user_stats(user_id, ts);
-CREATE TABLE room_stats ( room_id TEXT NOT NULL, ts BIGINT NOT NULL, bucket_size INT NOT NULL, current_state_events INT NOT NULL, joined_members INT NOT NULL, invited_members INT NOT NULL, left_members INT NOT NULL, banned_members INT NOT NULL, state_events INT NOT NULL );
-CREATE UNIQUE INDEX room_stats_room_ts ON room_stats(room_id, ts);
-CREATE TABLE room_state ( room_id TEXT NOT NULL, join_rules TEXT, history_visibility TEXT, encryption TEXT, name TEXT, topic TEXT, avatar TEXT, canonical_alias TEXT );
-CREATE UNIQUE INDEX room_state_room ON room_state(room_id);
-CREATE TABLE room_stats_earliest_token ( room_id TEXT NOT NULL, token BIGINT NOT NULL );
-CREATE UNIQUE INDEX room_stats_earliest_token_idx ON room_stats_earliest_token(room_id);
-CREATE INDEX access_tokens_device_id ON access_tokens (user_id, device_id);
-CREATE INDEX user_ips_device_id ON user_ips (user_id, device_id, last_seen);
-CREATE INDEX event_contains_url_index ON events (room_id, topological_ordering, stream_ordering);
-CREATE INDEX event_push_actions_u_highlight ON event_push_actions (user_id, stream_ordering);
-CREATE INDEX event_push_actions_highlights_index ON event_push_actions (user_id, room_id, topological_ordering, stream_ordering);
-CREATE INDEX current_state_events_member_index ON current_state_events (state_key);
-CREATE INDEX device_inbox_stream_id_user_id ON device_inbox (stream_id, user_id);
-CREATE INDEX device_lists_stream_user_id ON device_lists_stream (user_id, device_id);
-CREATE INDEX local_media_repository_url_idx ON local_media_repository (created_ts);
-CREATE INDEX user_ips_last_seen ON user_ips (user_id, last_seen);
-CREATE INDEX user_ips_last_seen_only ON user_ips (last_seen);
-CREATE INDEX users_creation_ts ON users (creation_ts);
-CREATE INDEX event_to_state_groups_sg_index ON event_to_state_groups (state_group);
-CREATE UNIQUE INDEX device_lists_remote_cache_unique_id ON device_lists_remote_cache (user_id, device_id);
-CREATE UNIQUE INDEX device_lists_remote_extremeties_unique_idx ON device_lists_remote_extremeties (user_id);
-CREATE UNIQUE INDEX user_ips_user_token_ip_unique_index ON user_ips (user_id, access_token, ip);
diff --git a/synapse/storage/schema/main/full_schemas/54/stream_positions.sql b/synapse/storage/schema/main/full_schemas/54/stream_positions.sql
deleted file mode 100644
index 91d21b2921..0000000000
--- a/synapse/storage/schema/main/full_schemas/54/stream_positions.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-
-INSERT INTO appservice_stream_position (stream_ordering) SELECT COALESCE(MAX(stream_ordering), 0) FROM events;
-INSERT INTO federation_stream_position (type, stream_id) VALUES ('federation', -1);
-INSERT INTO federation_stream_position (type, stream_id) SELECT 'events', coalesce(max(stream_ordering), -1) FROM events;
-INSERT INTO user_directory_stream_pos (stream_id) VALUES (0);
-INSERT INTO stats_stream_pos (stream_id) VALUES (0);
-INSERT INTO event_push_summary_stream_ordering (stream_ordering) VALUES (0);
--- device_max_stream_id is handled separately in 56/device_stream_id_insert.sql
\ No newline at end of file
diff --git a/synapse/storage/schema/state/full_schemas/54/full.sql b/synapse/storage/schema/state/full_schemas/54/full.sql
deleted file mode 100644
index e47536dbf9..0000000000
--- a/synapse/storage/schema/state/full_schemas/54/full.sql
+++ /dev/null
@@ -1,56 +0,0 @@
---
--- This file is licensed under the Affero General Public License (AGPL) version 3.
---
--- Copyright (C) 2023 New Vector, Ltd
---
--- This program is free software: you can redistribute it and/or modify
--- it under the terms of the GNU Affero General Public License as
--- published by the Free Software Foundation, either version 3 of the
--- License, or (at your option) any later version.
---
--- See the GNU Affero General Public License for more details:
--- <https://www.gnu.org/licenses/agpl-3.0.html>.
---
--- Originally licensed under the Apache License, Version 2.0:
--- <http://www.apache.org/licenses/LICENSE-2.0>.
---
--- [This file includes modifications made by New Vector Limited]
---
---
-/* Copyright 2019 The Matrix.org Foundation C.I.C
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-CREATE TABLE state_groups (
-    id BIGINT PRIMARY KEY,
-    room_id TEXT NOT NULL,
-    event_id TEXT NOT NULL
-);
-
-CREATE TABLE state_groups_state (
-    state_group BIGINT NOT NULL,
-    room_id TEXT NOT NULL,
-    type TEXT NOT NULL,
-    state_key TEXT NOT NULL,
-    event_id TEXT NOT NULL
-);
-
-CREATE TABLE state_group_edges (
-    state_group BIGINT NOT NULL,
-    prev_state_group BIGINT NOT NULL
-);
-
-CREATE INDEX state_group_edges_idx ON state_group_edges (state_group);
-CREATE INDEX state_group_edges_prev_idx ON state_group_edges (prev_state_group);
-CREATE INDEX state_groups_state_type_idx ON state_groups_state (state_group, type, state_key);
diff --git a/synapse/storage/schema/state/full_schemas/54/sequence.sql.postgres b/synapse/storage/schema/state/full_schemas/54/sequence.sql.postgres
deleted file mode 100644
index fcd926c9fb..0000000000
--- a/synapse/storage/schema/state/full_schemas/54/sequence.sql.postgres
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright 2019 The Matrix.org Foundation C.I.C
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-CREATE SEQUENCE state_group_id_seq
-    START WITH 1
-    INCREMENT BY 1
-    NO MINVALUE
-    NO MAXVALUE
-    CACHE 1;