|
| 1 | +from doc._examples.async_sqlite_storage import AsyncSQLiteStorage |
| 2 | +from scim2_server.storage import SearchPage |
| 3 | + |
| 4 | + |
| 5 | +class AsyncCursorSQLiteStorage(AsyncSQLiteStorage): |
| 6 | + supports_cursors = True |
| 7 | + |
| 8 | + async def search(self, resource_types, search_request, *, position=None): |
| 9 | + types = {resource_type.name: resource_type for resource_type in resource_types} |
| 10 | + [(total,)] = await self._select(types, "COUNT(*)") |
| 11 | + if search_request.cursor is None: |
| 12 | + return await self._search_index(types, total, search_request) |
| 13 | + return await self._search_cursor(types, total, search_request, position) |
| 14 | + |
| 15 | + async def _search_index(self, types, total, search_request): |
| 16 | + count = search_request.count |
| 17 | + page = [-1 if count is None else count, search_request.start_index_0] |
| 18 | + rows = await self._select(types, "*", "ORDER BY id LIMIT ? OFFSET ?", page) |
| 19 | + return SearchPage(total, self._to_resources(types, rows)) |
| 20 | + |
| 21 | + async def _search_cursor(self, types, total, search_request, position): |
| 22 | + count = search_request.count |
| 23 | + limit = -1 if count is None else count + 1 |
| 24 | + if position is None or "after" in position: |
| 25 | + after = "" if position is None else position["after"] |
| 26 | + clause = "AND id > ? ORDER BY id LIMIT ?" |
| 27 | + rows = await self._select(types, "*", clause, [after, limit]) |
| 28 | + resources = self._to_resources(types, rows[:count]) |
| 29 | + more_after, more_before = len(rows) > len(resources), position is not None |
| 30 | + else: |
| 31 | + clause = "AND id < ? ORDER BY id DESC LIMIT ?" |
| 32 | + rows = await self._select(types, "*", clause, [position["before"], limit]) |
| 33 | + resources = self._to_resources(types, rows[:count][::-1]) |
| 34 | + more_after, more_before = True, len(rows) > len(resources) |
| 35 | + |
| 36 | + result = SearchPage(total, resources) |
| 37 | + if resources and more_after: |
| 38 | + result.next = {"after": resources[-1].id} |
| 39 | + if resources and more_before: |
| 40 | + result.previous = {"before": resources[0].id} |
| 41 | + return result |
| 42 | + |
| 43 | + async def _select(self, types, columns, clause="", parameters=()): |
| 44 | + connection = await self.connect() |
| 45 | + among = ", ".join("?" * len(types)) |
| 46 | + cursor = await connection.execute( |
| 47 | + f"SELECT {columns} FROM resources WHERE resource_type IN ({among}) {clause}", |
| 48 | + [*types, *parameters], |
| 49 | + ) |
| 50 | + return list(await cursor.fetchall()) |
| 51 | + |
| 52 | + def _to_resources(self, types, rows): |
| 53 | + return [self.row_to_resource(types[row["resource_type"]], row) for row in rows] |
0 commit comments