Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/src/baserow/api/admin/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
)
from baserow.api.admin.views import AdminListingView
from baserow.api.decorators import map_exceptions, validate_body
from baserow.api.pagination import PageNumberPaginationWithApproximateCount
from baserow.api.schemas import get_error_schema
from baserow.api.user.registries import member_data_registry
from baserow.api.user.schemas import authenticate_user_schema
Expand All @@ -43,6 +44,7 @@

class UsersAdminView(AdminListingView):
serializer_class = UserAdminResponseSerializer
pagination_class = PageNumberPaginationWithApproximateCount
search_fields = ["id", "username", "first_name"]
sort_field_mapping = {
"id": "id",
Expand Down
3 changes: 2 additions & 1 deletion backend/src/baserow/api/admin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class APIListingView(
APIView, SearchableViewMixin, SortableViewMixin, FilterableViewMixin
):
serializer_class = None
pagination_class = PageNumberPagination
search_fields: List[str] = ["id"]
filters_field_mapping: Dict[str, str] = {}
sort_field_mapping: Dict[str, str] = {}
Expand All @@ -56,7 +57,7 @@ def get(self, request):
queryset = self.apply_sorts_or_default_sort(sorts, queryset)
queryset = self.apply_ids_filter(ids_param, queryset)

paginator = PageNumberPagination(limit_page_size=100)
paginator = self.pagination_class(limit_page_size=100)
page = paginator.paginate_queryset(queryset, request, self)
serializer = self.get_serializer(request, page, many=True)

Expand Down
2 changes: 2 additions & 0 deletions backend/src/baserow/api/admin/workspaces/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from baserow.api.admin.views import AdminListingView, APIListingView
from baserow.api.decorators import map_exceptions
from baserow.api.errors import ERROR_GROUP_DOES_NOT_EXIST
from baserow.api.pagination import PageNumberPaginationWithApproximateCount
from baserow.api.schemas import get_error_schema
from baserow.core.admin.workspaces.exceptions import CannotDeleteATemplateGroupError
from baserow.core.admin.workspaces.handler import WorkspacesAdminHandler
Expand All @@ -27,6 +28,7 @@

class WorkspacesAdminView(AdminListingView):
serializer_class = WorkspacesAdminResponseSerializer
pagination_class = PageNumberPaginationWithApproximateCount
search_fields = ["id", "name"]
sort_field_mapping = {
"id": "id",
Expand Down
24 changes: 24 additions & 0 deletions backend/src/baserow/api/pagination.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from functools import cached_property
from typing import Protocol

from django.core.paginator import Paginator as DjangoPaginator
Expand All @@ -12,6 +13,8 @@
from rest_framework.response import Response
from rest_framework.status import HTTP_400_BAD_REQUEST

from baserow.core.db import get_approximate_row_count


class Pageable(Protocol):
def paginate_queryset(self, queryset, request, view=None):
Expand Down Expand Up @@ -150,3 +153,24 @@ def get_paginated_response_schema(self, schema):
"results": schema,
},
}


class ApproximateCountPaginator(Paginator):
"""
A paginator that uses Postgres EXPLAIN to estimate the total row count
instead of running an expensive COUNT(*) query.
"""

@cached_property
def count(self):
return get_approximate_row_count(self.object_list)


class PageNumberPaginationWithApproximateCount(PageNumberPagination):
"""
Page number pagination that uses an approximate count from Postgres EXPLAIN
instead of COUNT(*). Suitable for large tables like audit logs where an
exact count is not required.
"""

django_paginator_class = ApproximateCountPaginator
13 changes: 9 additions & 4 deletions backend/src/baserow/contrib/database/fields/field_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -7414,12 +7414,17 @@ def create_field_sequence(
cursor.execute(
f"ALTER SEQUENCE {db_column}_seq OWNED BY {db_table}.{db_column};"
)
# Set the sequence to the count of rows in the table, only if there
# is at least one row.
# Use COALESCE(MAX, COUNT) to set the sequence correctly in all
# cases: MAX handles gaps from deleted rows or imported data,
# COUNT is the fallback when the column is all NULLs (e.g. when
# creating a new autonumber field on an existing table).
cursor.execute(
f"""
WITH count AS (SELECT COUNT(*) FROM {db_table})
SELECT setval('{db_column}_seq', count) FROM count WHERE count > 0;
WITH seq_val AS (
SELECT COALESCE(MAX({db_column}), COUNT(*)) AS val
FROM {db_table}
)
SELECT setval('{db_column}_seq', val) FROM seq_val WHERE val > 0;
""" # noqa: S608
)

Expand Down
Loading
Loading