We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
SQLAlchemy Session backend uses wrong column type in `SessionModelMixin`
**Describe the bug**
The SQLAlchem session backend uses `BLOB` as the datatype in the `SessionModelMixin`, which is not compatible with all databases. 



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `starlite/middleware/session/sqlalchemy_backend.py`
Content:
```
1 from abc import ABC, abstractmethod
2 from datetime import datetime, timedelta
3 from typing import TYPE_CHECKING, Any, Generic, Optional, Type, TypeVar, Union, cast
4 
5 import anyio.to_thread
6 import sqlalchemy as sa
7 from pydantic import validator
8 from sqlalchemy.ext.asyncio import AsyncSession as AsyncSASession
9 from sqlalchemy.ext.hybrid import hybrid_property
10 from sqlalchemy.orm import Mapped
11 from sqlalchemy.orm import Session as SASession
12 from sqlalchemy.orm import declarative_mixin, registry
13 
14 from starlite.middleware.session.base import ServerSideBackend, ServerSideSessionConfig
15 from starlite.plugins.sql_alchemy import SQLAlchemyPlugin
16 
17 if TYPE_CHECKING:
18     from sqlalchemy.sql import Select
19     from sqlalchemy.sql.elements import BooleanClauseList
20 
21     from starlite.plugins.sql_alchemy import SQLAlchemyConfig as SQLAlchemyPluginConfig
22 
23 
24 AnySASession = Union[SASession, AsyncSASession]
25 AnySASessionT = TypeVar("AnySASessionT", bound=AnySASession)
26 SessionModelT = TypeVar("SessionModelT", bound="SessionModelMixin")
27 
28 
29 @declarative_mixin
30 class SessionModelMixin:
31     """Mixin for session storage."""
32 
33     session_id: Mapped[str] = sa.Column(sa.String, nullable=False, unique=True, index=True)  # pyright: ignore
34     data: Mapped[bytes] = sa.Column(sa.BLOB, nullable=False)  # pyright: ignore
35     expires: Mapped[datetime] = sa.Column(sa.DateTime, nullable=False)  # pyright: ignore
36 
37     @hybrid_property
38     def expired(self) -> bool:  # pyright: ignore
39         """Boolean indicating if the session has expired."""
40         return datetime.utcnow().replace(tzinfo=None) > self.expires
41 
42     @expired.expression  # type: ignore[no-redef]
43     def expired(cls) -> "BooleanClauseList":  # pylint: disable=no-self-argument
44         """SQL-Expression to check if the session has expired."""
45         return datetime.utcnow().replace(tzinfo=None) > cls.expires  # pyright: ignore
46 
47 
48 class SessionModel(SessionModelMixin):
49     """Session storage model."""
50 
51     __tablename__ = "session"
52     id: Mapped[int] = sa.Column(sa.Integer, primary_key=True)  # pyright: ignore
53 
54 
55 def create_session_model(base: Type[Any], table_name: str = "session") -> Type[SessionModelMixin]:
56     """Dynamically generate a session storage model and register it with the declarative base.
57 
58     Args:
59         base: SQLAlchemy declarative base
60         table_name: Alternative table name
61 
62     Returns:
63         A mapped model subclassing `base` and `SessionModelMixin`
64     """
65 
66     class Model(base, SessionModelMixin):  # type: ignore[valid-type,misc]
67         __tablename__ = table_name
68         id: Mapped[int] = sa.Column(sa.Integer, primary_key=True)  # pyright: ignore
69 
70     return Model
71 
72 
73 def register_session_model(base: Union[registry, Any], model: Type[SessionModelT]) -> Type[SessionModelT]:
74     """Map and register a pre-existing model subclassing `SessionModelMixin` with a declarative base or registry.
75 
76     Args:
77         base: Either a `orm.registry` or `DeclarativeBase`
78         model: SQLAlchemy model to register
79 
80     Returns:
81         A mapped model subclassing `SessionModelMixin`, and registered in `registry`
82     """
83     registry_ = base.registry if not isinstance(base, registry) else base
84     return cast("Type[SessionModelT]", registry_.map_declaratively(model))
85 
86 
87 class BaseSQLAlchemyBackend(Generic[AnySASessionT], ServerSideBackend["SQLAlchemyBackendConfig"], ABC):
88     """Session backend to store data in a database with SQLAlchemy. Works with both sync and async engines.
89 
90     Notes:
91         - Requires `sqlalchemy` which needs to be installed separately, and a configured
92         [SQLAlchemyPlugin][starlite.plugins.sql_alchemy.SQLAlchemyPlugin].
93     """
94 
95     __slots__ = ("_model", "_session_maker")
96 
97     def __init__(self, config: "SQLAlchemyBackendConfig") -> None:
98         """Initialize `BaseSQLAlchemyBackend`.
99 
100         Args:
101             config: An instance of `SQLAlchemyBackendConfig`
102         """
103         super().__init__(config=config)
104         self._model = config.model
105         self._session_maker = cast("SQLAlchemyPluginConfig", config.plugin._config).session_maker
106 
107     def _create_sa_session(self) -> AnySASessionT:
108         return cast("AnySASessionT", self._session_maker())
109 
110     def _select_session_obj(self, session_id: str) -> "Select":
111         return sa.select(self._model).where(self._model.session_id == session_id)
112 
113     def _update_session_expiry(self, session_obj: SessionModelMixin) -> None:
114         session_obj.expires = datetime.utcnow().replace(tzinfo=None) + timedelta(seconds=self.config.max_age)
115 
116     @abstractmethod
117     async def delete_expired(self) -> None:
118         """Delete all expired sessions from the database."""
119 
120 
121 class AsyncSQLAlchemyBackend(BaseSQLAlchemyBackend[AsyncSASession]):
122     """Asynchronous SQLAlchemy backend."""
123 
124     async def _get_session_obj(self, *, sa_session: AsyncSASession, session_id: str) -> Optional[SessionModelMixin]:
125         result = await sa_session.scalars(self._select_session_obj(session_id))
126         return result.one_or_none()
127 
128     async def get(self, session_id: str) -> Optional[bytes]:
129         """Retrieve data associated with `session_id`.
130 
131         Args:
132             session_id: The session-ID
133 
134         Returns:
135             The session data, if existing, otherwise `None`.
136         """
137         async with self._create_sa_session() as sa_session:
138             session_obj = await self._get_session_obj(sa_session=sa_session, session_id=session_id)
139             if session_obj:
140                 if not session_obj.expired:  # type: ignore[truthy-function]
141                     self._update_session_expiry(session_obj)  # type: ignore[unreachable]
142                     await sa_session.commit()
143                     return session_obj.data
144                 await sa_session.delete(session_obj)
145                 await sa_session.commit()
146         return None
147 
148     async def set(self, session_id: str, data: bytes) -> None:
149         """Store `data` under the `session_id` for later retrieval.
150 
151         If there is already data associated with `session_id`, replace
152         it with `data` and reset its expiry time
153 
154         Args:
155             session_id: The session-ID.
156             data: Serialized session data
157 
158         Returns:
159             None
160         """
161         async with self._create_sa_session() as sa_session:
162             session_obj = await self._get_session_obj(sa_session=sa_session, session_id=session_id)
163 
164             if not session_obj:
165                 session_obj = self._model(session_id=session_id)  # type: ignore[call-arg]
166                 sa_session.add(session_obj)
167             session_obj.data = data
168             self._update_session_expiry(session_obj)
169             await sa_session.commit()
170 
171     async def delete(self, session_id: str) -> None:
172         """Delete the data associated with `session_id`. Fails silently if no such session-ID exists.
173 
174         Args:
175             session_id: The session-ID
176 
177         Returns:
178             None
179         """
180         async with self._create_sa_session() as sa_session:
181             await sa_session.execute(sa.delete(self._model).where(self._model.session_id == session_id))
182             await sa_session.commit()
183 
184     async def delete_all(self) -> None:
185         """Delete all session data.
186 
187         Returns:
188             None
189         """
190         async with self._create_sa_session() as sa_session:
191             await sa_session.execute(sa.delete(self._model))
192             await sa_session.commit()
193 
194     async def delete_expired(self) -> None:
195         """Delete all expired session from the database.
196 
197         Returns:
198             None
199         """
200         async with self._create_sa_session() as sa_session:
201             await sa_session.execute(sa.delete(self._model).where(self._model.expired))
202 
203 
204 class SQLAlchemyBackend(BaseSQLAlchemyBackend[SASession]):
205     """Synchronous SQLAlchemy backend."""
206 
207     def _get_session_obj(self, *, sa_session: SASession, session_id: str) -> Optional[SessionModelMixin]:
208         return sa_session.scalars(self._select_session_obj(session_id)).one_or_none()
209 
210     def _get_sync(self, session_id: str) -> Optional[bytes]:
211         sa_session = self._create_sa_session()
212         session_obj = self._get_session_obj(sa_session=sa_session, session_id=session_id)
213 
214         if session_obj:
215             if not session_obj.expired:  # type: ignore[truthy-function]
216                 self._update_session_expiry(session_obj)  # type: ignore[unreachable]
217                 sa_session.commit()
218                 return session_obj.data
219             sa_session.delete(session_obj)
220             sa_session.commit()
221         return None
222 
223     async def get(self, session_id: str) -> Optional[bytes]:
224         """Retrieve data associated with `session_id`.
225 
226         Args:
227             session_id: The session-ID
228 
229         Returns:
230             The session data, if existing, otherwise `None`.
231         """
232         return await anyio.to_thread.run_sync(self._get_sync, session_id)
233 
234     def _set_sync(self, session_id: str, data: bytes) -> None:
235         sa_session = self._create_sa_session()
236         session_obj = self._get_session_obj(sa_session=sa_session, session_id=session_id)
237 
238         if not session_obj:
239             session_obj = self._model(session_id=session_id)  # type: ignore[call-arg]
240             sa_session.add(session_obj)
241         session_obj.data = data
242         self._update_session_expiry(session_obj)
243         sa_session.commit()
244 
245     async def set(self, session_id: str, data: bytes) -> None:
246         """Store `data` under the `session_id` for later retrieval.
247 
248         If there is already data associated with `session_id`, replace
249         it with `data` and reset its expiry time
250 
251         Args:
252             session_id: The session-ID
253             data: Serialized session data
254 
255         Returns:
256             None
257         """
258         return await anyio.to_thread.run_sync(self._set_sync, session_id, data)
259 
260     def _delete_sync(self, session_id: str) -> None:
261         sa_session = self._create_sa_session()
262         sa_session.execute(sa.delete(self._model).where(self._model.session_id == session_id))
263         sa_session.commit()
264 
265     async def delete(self, session_id: str) -> None:
266         """Delete the data associated with `session_id`. Fails silently if no such session-ID exists.
267 
268         Args:
269             session_id: The session-ID
270 
271         Returns:
272             None
273         """
274         return await anyio.to_thread.run_sync(self._delete_sync, session_id)
275 
276     def _delete_all_sync(self) -> None:
277         sa_session = self._create_sa_session()
278 
279         sa_session.execute(sa.delete(self._model))
280         sa_session.commit()
281 
282     async def delete_all(self) -> None:
283         """Delete all session data.
284 
285         Returns:
286             None
287         """
288         await anyio.to_thread.run_sync(self._delete_all_sync)
289 
290     def _delete_expired_sync(self) -> None:
291         sa_session = self._create_sa_session()
292         sa_session.execute(sa.delete(self._model).where(self._model.expired))
293 
294     async def delete_expired(self) -> None:
295         """Delete all expired session from the database.
296 
297         Returns:
298             None
299         """
300         await anyio.to_thread.run_sync(self._delete_expired_sync)
301 
302 
303 class SQLAlchemyBackendConfig(ServerSideSessionConfig):
304     """Configuration for `SQLAlchemyBackend` and `AsyncSQLAlchemyBackend`"""
305 
306     model: Type[SessionModelMixin]
307     plugin: SQLAlchemyPlugin
308 
309     @validator("plugin", always=True)
310     def validate_plugin_config(cls, value: SQLAlchemyPlugin) -> SQLAlchemyPlugin:  # pylint: disable=no-self-argument)
311         """Check if the SQLAlchemyPlugin is configured."""
312         if not (value._config and value._config.session_maker):  # pylint: disable=protected-access
313             raise ValueError("Plugin needs to be configured")
314         return value
315 
316     @property
317     def _backend_class(self) -> Type[Union[SQLAlchemyBackend, AsyncSQLAlchemyBackend]]:  # type: ignore[override]
318         """Return either `SQLAlchemyBackend` or `AsyncSQLAlchemyBackend`, depending on the engine type configured in the
319         `SQLAlchemyPlugin`
320         """
321         if cast("SQLAlchemyPluginConfig", self.plugin._config).use_async_engine:  # pylint: disable=protected-access
322             return AsyncSQLAlchemyBackend
323         return SQLAlchemyBackend
324
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/starlite/middleware/session/sqlalchemy_backend.py b/starlite/middleware/session/sqlalchemy_backend.py
--- a/starlite/middleware/session/sqlalchemy_backend.py
+++ b/starlite/middleware/session/sqlalchemy_backend.py
@@ -31,7 +31,7 @@
     """Mixin for session storage."""
 
     session_id: Mapped[str] = sa.Column(sa.String, nullable=False, unique=True, index=True)  # pyright: ignore
-    data: Mapped[bytes] = sa.Column(sa.BLOB, nullable=False)  # pyright: ignore
+    data: Mapped[bytes] = sa.Column(sa.LargeBinary, nullable=False)  # pyright: ignore
     expires: Mapped[datetime] = sa.Column(sa.DateTime, nullable=False)  # pyright: ignore
 
     @hybrid_property

{"golden_diff": "diff --git a/starlite/middleware/session/sqlalchemy_backend.py b/starlite/middleware/session/sqlalchemy_backend.py\n--- a/starlite/middleware/session/sqlalchemy_backend.py\n+++ b/starlite/middleware/session/sqlalchemy_backend.py\n@@ -31,7 +31,7 @@\n     \"\"\"Mixin for session storage.\"\"\"\n \n     session_id: Mapped[str] = sa.Column(sa.String, nullable=False, unique=True, index=True)  # pyright: ignore\n-    data: Mapped[bytes] = sa.Column(sa.BLOB, nullable=False)  # pyright: ignore\n+    data: Mapped[bytes] = sa.Column(sa.LargeBinary, nullable=False)  # pyright: ignore\n     expires: Mapped[datetime] = sa.Column(sa.DateTime, nullable=False)  # pyright: ignore\n \n     @hybrid_property\n", "issue": "SQLAlchemy Session backend uses wrong column type in `SessionModelMixin`\n**Describe the bug**\r\nThe SQLAlchem session backend uses `BLOB` as the datatype in the `SessionModelMixin`, which is not compatible with all databases. \r\n\r\n\n", "before_files": [{"content": "from abc import ABC, abstractmethod\nfrom datetime import datetime, timedelta\nfrom typing import TYPE_CHECKING, Any, Generic, Optional, Type, TypeVar, Union, cast\n\nimport anyio.to_thread\nimport sqlalchemy as sa\nfrom pydantic import validator\nfrom sqlalchemy.ext.asyncio import AsyncSession as AsyncSASession\nfrom sqlalchemy.ext.hybrid import hybrid_property\nfrom sqlalchemy.orm import Mapped\nfrom sqlalchemy.orm import Session as SASession\nfrom sqlalchemy.orm import declarative_mixin, registry\n\nfrom starlite.middleware.session.base import ServerSideBackend, ServerSideSessionConfig\nfrom starlite.plugins.sql_alchemy import SQLAlchemyPlugin\n\nif TYPE_CHECKING:\n    from sqlalchemy.sql import Select\n    from sqlalchemy.sql.elements import BooleanClauseList\n\n    from starlite.plugins.sql_alchemy import SQLAlchemyConfig as SQLAlchemyPluginConfig\n\n\nAnySASession = Union[SASession, AsyncSASession]\nAnySASessionT = TypeVar(\"AnySASessionT\", bound=AnySASession)\nSessionModelT = TypeVar(\"SessionModelT\", bound=\"SessionModelMixin\")\n\n\n@declarative_mixin\nclass SessionModelMixin:\n    \"\"\"Mixin for session storage.\"\"\"\n\n    session_id: Mapped[str] = sa.Column(sa.String, nullable=False, unique=True, index=True)  # pyright: ignore\n    data: Mapped[bytes] = sa.Column(sa.BLOB, nullable=False)  # pyright: ignore\n    expires: Mapped[datetime] = sa.Column(sa.DateTime, nullable=False)  # pyright: ignore\n\n    @hybrid_property\n    def expired(self) -> bool:  # pyright: ignore\n        \"\"\"Boolean indicating if the session has expired.\"\"\"\n        return datetime.utcnow().replace(tzinfo=None) > self.expires\n\n    @expired.expression  # type: ignore[no-redef]\n    def expired(cls) -> \"BooleanClauseList\":  # pylint: disable=no-self-argument\n        \"\"\"SQL-Expression to check if the session has expired.\"\"\"\n        return datetime.utcnow().replace(tzinfo=None) > cls.expires  # pyright: ignore\n\n\nclass SessionModel(SessionModelMixin):\n    \"\"\"Session storage model.\"\"\"\n\n    __tablename__ = \"session\"\n    id: Mapped[int] = sa.Column(sa.Integer, primary_key=True)  # pyright: ignore\n\n\ndef create_session_model(base: Type[Any], table_name: str = \"session\") -> Type[SessionModelMixin]:\n    \"\"\"Dynamically generate a session storage model and register it with the declarative base.\n\n    Args:\n        base: SQLAlchemy declarative base\n        table_name: Alternative table name\n\n    Returns:\n        A mapped model subclassing `base` and `SessionModelMixin`\n    \"\"\"\n\n    class Model(base, SessionModelMixin):  # type: ignore[valid-type,misc]\n        __tablename__ = table_name\n        id: Mapped[int] = sa.Column(sa.Integer, primary_key=True)  # pyright: ignore\n\n    return Model\n\n\ndef register_session_model(base: Union[registry, Any], model: Type[SessionModelT]) -> Type[SessionModelT]:\n    \"\"\"Map and register a pre-existing model subclassing `SessionModelMixin` with a declarative base or registry.\n\n    Args:\n        base: Either a `orm.registry` or `DeclarativeBase`\n        model: SQLAlchemy model to register\n\n    Returns:\n        A mapped model subclassing `SessionModelMixin`, and registered in `registry`\n    \"\"\"\n    registry_ = base.registry if not isinstance(base, registry) else base\n    return cast(\"Type[SessionModelT]\", registry_.map_declaratively(model))\n\n\nclass BaseSQLAlchemyBackend(Generic[AnySASessionT], ServerSideBackend[\"SQLAlchemyBackendConfig\"], ABC):\n    \"\"\"Session backend to store data in a database with SQLAlchemy. Works with both sync and async engines.\n\n    Notes:\n        - Requires `sqlalchemy` which needs to be installed separately, and a configured\n        [SQLAlchemyPlugin][starlite.plugins.sql_alchemy.SQLAlchemyPlugin].\n    \"\"\"\n\n    __slots__ = (\"_model\", \"_session_maker\")\n\n    def __init__(self, config: \"SQLAlchemyBackendConfig\") -> None:\n        \"\"\"Initialize `BaseSQLAlchemyBackend`.\n\n        Args:\n            config: An instance of `SQLAlchemyBackendConfig`\n        \"\"\"\n        super().__init__(config=config)\n        self._model = config.model\n        self._session_maker = cast(\"SQLAlchemyPluginConfig\", config.plugin._config).session_maker\n\n    def _create_sa_session(self) -> AnySASessionT:\n        return cast(\"AnySASessionT\", self._session_maker())\n\n    def _select_session_obj(self, session_id: str) -> \"Select\":\n        return sa.select(self._model).where(self._model.session_id == session_id)\n\n    def _update_session_expiry(self, session_obj: SessionModelMixin) -> None:\n        session_obj.expires = datetime.utcnow().replace(tzinfo=None) + timedelta(seconds=self.config.max_age)\n\n    @abstractmethod\n    async def delete_expired(self) -> None:\n        \"\"\"Delete all expired sessions from the database.\"\"\"\n\n\nclass AsyncSQLAlchemyBackend(BaseSQLAlchemyBackend[AsyncSASession]):\n    \"\"\"Asynchronous SQLAlchemy backend.\"\"\"\n\n    async def _get_session_obj(self, *, sa_session: AsyncSASession, session_id: str) -> Optional[SessionModelMixin]:\n        result = await sa_session.scalars(self._select_session_obj(session_id))\n        return result.one_or_none()\n\n    async def get(self, session_id: str) -> Optional[bytes]:\n        \"\"\"Retrieve data associated with `session_id`.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            The session data, if existing, otherwise `None`.\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            session_obj = await self._get_session_obj(sa_session=sa_session, session_id=session_id)\n            if session_obj:\n                if not session_obj.expired:  # type: ignore[truthy-function]\n                    self._update_session_expiry(session_obj)  # type: ignore[unreachable]\n                    await sa_session.commit()\n                    return session_obj.data\n                await sa_session.delete(session_obj)\n                await sa_session.commit()\n        return None\n\n    async def set(self, session_id: str, data: bytes) -> None:\n        \"\"\"Store `data` under the `session_id` for later retrieval.\n\n        If there is already data associated with `session_id`, replace\n        it with `data` and reset its expiry time\n\n        Args:\n            session_id: The session-ID.\n            data: Serialized session data\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            session_obj = await self._get_session_obj(sa_session=sa_session, session_id=session_id)\n\n            if not session_obj:\n                session_obj = self._model(session_id=session_id)  # type: ignore[call-arg]\n                sa_session.add(session_obj)\n            session_obj.data = data\n            self._update_session_expiry(session_obj)\n            await sa_session.commit()\n\n    async def delete(self, session_id: str) -> None:\n        \"\"\"Delete the data associated with `session_id`. Fails silently if no such session-ID exists.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            await sa_session.execute(sa.delete(self._model).where(self._model.session_id == session_id))\n            await sa_session.commit()\n\n    async def delete_all(self) -> None:\n        \"\"\"Delete all session data.\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            await sa_session.execute(sa.delete(self._model))\n            await sa_session.commit()\n\n    async def delete_expired(self) -> None:\n        \"\"\"Delete all expired session from the database.\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            await sa_session.execute(sa.delete(self._model).where(self._model.expired))\n\n\nclass SQLAlchemyBackend(BaseSQLAlchemyBackend[SASession]):\n    \"\"\"Synchronous SQLAlchemy backend.\"\"\"\n\n    def _get_session_obj(self, *, sa_session: SASession, session_id: str) -> Optional[SessionModelMixin]:\n        return sa_session.scalars(self._select_session_obj(session_id)).one_or_none()\n\n    def _get_sync(self, session_id: str) -> Optional[bytes]:\n        sa_session = self._create_sa_session()\n        session_obj = self._get_session_obj(sa_session=sa_session, session_id=session_id)\n\n        if session_obj:\n            if not session_obj.expired:  # type: ignore[truthy-function]\n                self._update_session_expiry(session_obj)  # type: ignore[unreachable]\n                sa_session.commit()\n                return session_obj.data\n            sa_session.delete(session_obj)\n            sa_session.commit()\n        return None\n\n    async def get(self, session_id: str) -> Optional[bytes]:\n        \"\"\"Retrieve data associated with `session_id`.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            The session data, if existing, otherwise `None`.\n        \"\"\"\n        return await anyio.to_thread.run_sync(self._get_sync, session_id)\n\n    def _set_sync(self, session_id: str, data: bytes) -> None:\n        sa_session = self._create_sa_session()\n        session_obj = self._get_session_obj(sa_session=sa_session, session_id=session_id)\n\n        if not session_obj:\n            session_obj = self._model(session_id=session_id)  # type: ignore[call-arg]\n            sa_session.add(session_obj)\n        session_obj.data = data\n        self._update_session_expiry(session_obj)\n        sa_session.commit()\n\n    async def set(self, session_id: str, data: bytes) -> None:\n        \"\"\"Store `data` under the `session_id` for later retrieval.\n\n        If there is already data associated with `session_id`, replace\n        it with `data` and reset its expiry time\n\n        Args:\n            session_id: The session-ID\n            data: Serialized session data\n\n        Returns:\n            None\n        \"\"\"\n        return await anyio.to_thread.run_sync(self._set_sync, session_id, data)\n\n    def _delete_sync(self, session_id: str) -> None:\n        sa_session = self._create_sa_session()\n        sa_session.execute(sa.delete(self._model).where(self._model.session_id == session_id))\n        sa_session.commit()\n\n    async def delete(self, session_id: str) -> None:\n        \"\"\"Delete the data associated with `session_id`. Fails silently if no such session-ID exists.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            None\n        \"\"\"\n        return await anyio.to_thread.run_sync(self._delete_sync, session_id)\n\n    def _delete_all_sync(self) -> None:\n        sa_session = self._create_sa_session()\n\n        sa_session.execute(sa.delete(self._model))\n        sa_session.commit()\n\n    async def delete_all(self) -> None:\n        \"\"\"Delete all session data.\n\n        Returns:\n            None\n        \"\"\"\n        await anyio.to_thread.run_sync(self._delete_all_sync)\n\n    def _delete_expired_sync(self) -> None:\n        sa_session = self._create_sa_session()\n        sa_session.execute(sa.delete(self._model).where(self._model.expired))\n\n    async def delete_expired(self) -> None:\n        \"\"\"Delete all expired session from the database.\n\n        Returns:\n            None\n        \"\"\"\n        await anyio.to_thread.run_sync(self._delete_expired_sync)\n\n\nclass SQLAlchemyBackendConfig(ServerSideSessionConfig):\n    \"\"\"Configuration for `SQLAlchemyBackend` and `AsyncSQLAlchemyBackend`\"\"\"\n\n    model: Type[SessionModelMixin]\n    plugin: SQLAlchemyPlugin\n\n    @validator(\"plugin\", always=True)\n    def validate_plugin_config(cls, value: SQLAlchemyPlugin) -> SQLAlchemyPlugin:  # pylint: disable=no-self-argument)\n        \"\"\"Check if the SQLAlchemyPlugin is configured.\"\"\"\n        if not (value._config and value._config.session_maker):  # pylint: disable=protected-access\n            raise ValueError(\"Plugin needs to be configured\")\n        return value\n\n    @property\n    def _backend_class(self) -> Type[Union[SQLAlchemyBackend, AsyncSQLAlchemyBackend]]:  # type: ignore[override]\n        \"\"\"Return either `SQLAlchemyBackend` or `AsyncSQLAlchemyBackend`, depending on the engine type configured in the\n        `SQLAlchemyPlugin`\n        \"\"\"\n        if cast(\"SQLAlchemyPluginConfig\", self.plugin._config).use_async_engine:  # pylint: disable=protected-access\n            return AsyncSQLAlchemyBackend\n        return SQLAlchemyBackend\n", "path": "starlite/middleware/session/sqlalchemy_backend.py"}], "after_files": [{"content": "from abc import ABC, abstractmethod\nfrom datetime import datetime, timedelta\nfrom typing import TYPE_CHECKING, Any, Generic, Optional, Type, TypeVar, Union, cast\n\nimport anyio.to_thread\nimport sqlalchemy as sa\nfrom pydantic import validator\nfrom sqlalchemy.ext.asyncio import AsyncSession as AsyncSASession\nfrom sqlalchemy.ext.hybrid import hybrid_property\nfrom sqlalchemy.orm import Mapped\nfrom sqlalchemy.orm import Session as SASession\nfrom sqlalchemy.orm import declarative_mixin, registry\n\nfrom starlite.middleware.session.base import ServerSideBackend, ServerSideSessionConfig\nfrom starlite.plugins.sql_alchemy import SQLAlchemyPlugin\n\nif TYPE_CHECKING:\n    from sqlalchemy.sql import Select\n    from sqlalchemy.sql.elements import BooleanClauseList\n\n    from starlite.plugins.sql_alchemy import SQLAlchemyConfig as SQLAlchemyPluginConfig\n\n\nAnySASession = Union[SASession, AsyncSASession]\nAnySASessionT = TypeVar(\"AnySASessionT\", bound=AnySASession)\nSessionModelT = TypeVar(\"SessionModelT\", bound=\"SessionModelMixin\")\n\n\n@declarative_mixin\nclass SessionModelMixin:\n    \"\"\"Mixin for session storage.\"\"\"\n\n    session_id: Mapped[str] = sa.Column(sa.String, nullable=False, unique=True, index=True)  # pyright: ignore\n    data: Mapped[bytes] = sa.Column(sa.LargeBinary, nullable=False)  # pyright: ignore\n    expires: Mapped[datetime] = sa.Column(sa.DateTime, nullable=False)  # pyright: ignore\n\n    @hybrid_property\n    def expired(self) -> bool:  # pyright: ignore\n        \"\"\"Boolean indicating if the session has expired.\"\"\"\n        return datetime.utcnow().replace(tzinfo=None) > self.expires\n\n    @expired.expression  # type: ignore[no-redef]\n    def expired(cls) -> \"BooleanClauseList\":  # pylint: disable=no-self-argument\n        \"\"\"SQL-Expression to check if the session has expired.\"\"\"\n        return datetime.utcnow().replace(tzinfo=None) > cls.expires  # pyright: ignore\n\n\nclass SessionModel(SessionModelMixin):\n    \"\"\"Session storage model.\"\"\"\n\n    __tablename__ = \"session\"\n    id: Mapped[int] = sa.Column(sa.Integer, primary_key=True)  # pyright: ignore\n\n\ndef create_session_model(base: Type[Any], table_name: str = \"session\") -> Type[SessionModelMixin]:\n    \"\"\"Dynamically generate a session storage model and register it with the declarative base.\n\n    Args:\n        base: SQLAlchemy declarative base\n        table_name: Alternative table name\n\n    Returns:\n        A mapped model subclassing `base` and `SessionModelMixin`\n    \"\"\"\n\n    class Model(base, SessionModelMixin):  # type: ignore[valid-type,misc]\n        __tablename__ = table_name\n        id: Mapped[int] = sa.Column(sa.Integer, primary_key=True)  # pyright: ignore\n\n    return Model\n\n\ndef register_session_model(base: Union[registry, Any], model: Type[SessionModelT]) -> Type[SessionModelT]:\n    \"\"\"Map and register a pre-existing model subclassing `SessionModelMixin` with a declarative base or registry.\n\n    Args:\n        base: Either a `orm.registry` or `DeclarativeBase`\n        model: SQLAlchemy model to register\n\n    Returns:\n        A mapped model subclassing `SessionModelMixin`, and registered in `registry`\n    \"\"\"\n    registry_ = base.registry if not isinstance(base, registry) else base\n    return cast(\"Type[SessionModelT]\", registry_.map_declaratively(model))\n\n\nclass BaseSQLAlchemyBackend(Generic[AnySASessionT], ServerSideBackend[\"SQLAlchemyBackendConfig\"], ABC):\n    \"\"\"Session backend to store data in a database with SQLAlchemy. Works with both sync and async engines.\n\n    Notes:\n        - Requires `sqlalchemy` which needs to be installed separately, and a configured\n        [SQLAlchemyPlugin][starlite.plugins.sql_alchemy.SQLAlchemyPlugin].\n    \"\"\"\n\n    __slots__ = (\"_model\", \"_session_maker\")\n\n    def __init__(self, config: \"SQLAlchemyBackendConfig\") -> None:\n        \"\"\"Initialize `BaseSQLAlchemyBackend`.\n\n        Args:\n            config: An instance of `SQLAlchemyBackendConfig`\n        \"\"\"\n        super().__init__(config=config)\n        self._model = config.model\n        self._session_maker = cast(\"SQLAlchemyPluginConfig\", config.plugin._config).session_maker\n\n    def _create_sa_session(self) -> AnySASessionT:\n        return cast(\"AnySASessionT\", self._session_maker())\n\n    def _select_session_obj(self, session_id: str) -> \"Select\":\n        return sa.select(self._model).where(self._model.session_id == session_id)\n\n    def _update_session_expiry(self, session_obj: SessionModelMixin) -> None:\n        session_obj.expires = datetime.utcnow().replace(tzinfo=None) + timedelta(seconds=self.config.max_age)\n\n    @abstractmethod\n    async def delete_expired(self) -> None:\n        \"\"\"Delete all expired sessions from the database.\"\"\"\n\n\nclass AsyncSQLAlchemyBackend(BaseSQLAlchemyBackend[AsyncSASession]):\n    \"\"\"Asynchronous SQLAlchemy backend.\"\"\"\n\n    async def _get_session_obj(self, *, sa_session: AsyncSASession, session_id: str) -> Optional[SessionModelMixin]:\n        result = await sa_session.scalars(self._select_session_obj(session_id))\n        return result.one_or_none()\n\n    async def get(self, session_id: str) -> Optional[bytes]:\n        \"\"\"Retrieve data associated with `session_id`.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            The session data, if existing, otherwise `None`.\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            session_obj = await self._get_session_obj(sa_session=sa_session, session_id=session_id)\n            if session_obj:\n                if not session_obj.expired:  # type: ignore[truthy-function]\n                    self._update_session_expiry(session_obj)  # type: ignore[unreachable]\n                    await sa_session.commit()\n                    return session_obj.data\n                await sa_session.delete(session_obj)\n                await sa_session.commit()\n        return None\n\n    async def set(self, session_id: str, data: bytes) -> None:\n        \"\"\"Store `data` under the `session_id` for later retrieval.\n\n        If there is already data associated with `session_id`, replace\n        it with `data` and reset its expiry time\n\n        Args:\n            session_id: The session-ID.\n            data: Serialized session data\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            session_obj = await self._get_session_obj(sa_session=sa_session, session_id=session_id)\n\n            if not session_obj:\n                session_obj = self._model(session_id=session_id)  # type: ignore[call-arg]\n                sa_session.add(session_obj)\n            session_obj.data = data\n            self._update_session_expiry(session_obj)\n            await sa_session.commit()\n\n    async def delete(self, session_id: str) -> None:\n        \"\"\"Delete the data associated with `session_id`. Fails silently if no such session-ID exists.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            await sa_session.execute(sa.delete(self._model).where(self._model.session_id == session_id))\n            await sa_session.commit()\n\n    async def delete_all(self) -> None:\n        \"\"\"Delete all session data.\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            await sa_session.execute(sa.delete(self._model))\n            await sa_session.commit()\n\n    async def delete_expired(self) -> None:\n        \"\"\"Delete all expired session from the database.\n\n        Returns:\n            None\n        \"\"\"\n        async with self._create_sa_session() as sa_session:\n            await sa_session.execute(sa.delete(self._model).where(self._model.expired))\n\n\nclass SQLAlchemyBackend(BaseSQLAlchemyBackend[SASession]):\n    \"\"\"Synchronous SQLAlchemy backend.\"\"\"\n\n    def _get_session_obj(self, *, sa_session: SASession, session_id: str) -> Optional[SessionModelMixin]:\n        return sa_session.scalars(self._select_session_obj(session_id)).one_or_none()\n\n    def _get_sync(self, session_id: str) -> Optional[bytes]:\n        sa_session = self._create_sa_session()\n        session_obj = self._get_session_obj(sa_session=sa_session, session_id=session_id)\n\n        if session_obj:\n            if not session_obj.expired:  # type: ignore[truthy-function]\n                self._update_session_expiry(session_obj)  # type: ignore[unreachable]\n                sa_session.commit()\n                return session_obj.data\n            sa_session.delete(session_obj)\n            sa_session.commit()\n        return None\n\n    async def get(self, session_id: str) -> Optional[bytes]:\n        \"\"\"Retrieve data associated with `session_id`.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            The session data, if existing, otherwise `None`.\n        \"\"\"\n        return await anyio.to_thread.run_sync(self._get_sync, session_id)\n\n    def _set_sync(self, session_id: str, data: bytes) -> None:\n        sa_session = self._create_sa_session()\n        session_obj = self._get_session_obj(sa_session=sa_session, session_id=session_id)\n\n        if not session_obj:\n            session_obj = self._model(session_id=session_id)  # type: ignore[call-arg]\n            sa_session.add(session_obj)\n        session_obj.data = data\n        self._update_session_expiry(session_obj)\n        sa_session.commit()\n\n    async def set(self, session_id: str, data: bytes) -> None:\n        \"\"\"Store `data` under the `session_id` for later retrieval.\n\n        If there is already data associated with `session_id`, replace\n        it with `data` and reset its expiry time\n\n        Args:\n            session_id: The session-ID\n            data: Serialized session data\n\n        Returns:\n            None\n        \"\"\"\n        return await anyio.to_thread.run_sync(self._set_sync, session_id, data)\n\n    def _delete_sync(self, session_id: str) -> None:\n        sa_session = self._create_sa_session()\n        sa_session.execute(sa.delete(self._model).where(self._model.session_id == session_id))\n        sa_session.commit()\n\n    async def delete(self, session_id: str) -> None:\n        \"\"\"Delete the data associated with `session_id`. Fails silently if no such session-ID exists.\n\n        Args:\n            session_id: The session-ID\n\n        Returns:\n            None\n        \"\"\"\n        return await anyio.to_thread.run_sync(self._delete_sync, session_id)\n\n    def _delete_all_sync(self) -> None:\n        sa_session = self._create_sa_session()\n\n        sa_session.execute(sa.delete(self._model))\n        sa_session.commit()\n\n    async def delete_all(self) -> None:\n        \"\"\"Delete all session data.\n\n        Returns:\n            None\n        \"\"\"\n        await anyio.to_thread.run_sync(self._delete_all_sync)\n\n    def _delete_expired_sync(self) -> None:\n        sa_session = self._create_sa_session()\n        sa_session.execute(sa.delete(self._model).where(self._model.expired))\n\n    async def delete_expired(self) -> None:\n        \"\"\"Delete all expired session from the database.\n\n        Returns:\n            None\n        \"\"\"\n        await anyio.to_thread.run_sync(self._delete_expired_sync)\n\n\nclass SQLAlchemyBackendConfig(ServerSideSessionConfig):\n    \"\"\"Configuration for `SQLAlchemyBackend` and `AsyncSQLAlchemyBackend`\"\"\"\n\n    model: Type[SessionModelMixin]\n    plugin: SQLAlchemyPlugin\n\n    @validator(\"plugin\", always=True)\n    def validate_plugin_config(cls, value: SQLAlchemyPlugin) -> SQLAlchemyPlugin:  # pylint: disable=no-self-argument)\n        \"\"\"Check if the SQLAlchemyPlugin is configured.\"\"\"\n        if not (value._config and value._config.session_maker):  # pylint: disable=protected-access\n            raise ValueError(\"Plugin needs to be configured\")\n        return value\n\n    @property\n    def _backend_class(self) -> Type[Union[SQLAlchemyBackend, AsyncSQLAlchemyBackend]]:  # type: ignore[override]\n        \"\"\"Return either `SQLAlchemyBackend` or `AsyncSQLAlchemyBackend`, depending on the engine type configured in the\n        `SQLAlchemyPlugin`\n        \"\"\"\n        if cast(\"SQLAlchemyPluginConfig\", self.plugin._config).use_async_engine:  # pylint: disable=protected-access\n            return AsyncSQLAlchemyBackend\n        return SQLAlchemyBackend\n", "path": "starlite/middleware/session/sqlalchemy_backend.py"}]}