Skip to content
Draft
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
7 changes: 5 additions & 2 deletions sqlit/domains/query/ui/mixins/query_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@

from .query_constants import MAX_COLUMN_CONTENT_WIDTH, MAX_RENDER_ROWS

RESULTS_RENDER_CHUNK_SIZE = 200
# Preserve the quick initial paint for medium and large result sets, then use
# larger continuation batches to avoid repeated Arrow table rebuilds and renders.
RESULTS_RENDER_INCREMENTAL_THRESHOLD = 200
RESULTS_RENDER_CHUNK_SIZE = 2_000
RESULTS_RENDER_INITIAL_ROWS = 20


Expand Down Expand Up @@ -333,7 +336,7 @@ async def _display_query_results(
self._cancel_results_render()
render_token = getattr(self, "_results_render_token", 0)
row_limit = min(len(rows), MAX_RENDER_ROWS)
if row_limit > RESULTS_RENDER_CHUNK_SIZE:
if row_limit > RESULTS_RENDER_INCREMENTAL_THRESHOLD:
self._render_results_table_incremental(
columns,
rows,
Expand Down
52 changes: 52 additions & 0 deletions tests/ui/test_results_incremental_rendering.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,63 @@

import pytest

from sqlit.domains.query.ui.mixins.query_results import RESULTS_RENDER_CHUNK_SIZE
from sqlit.domains.shell.app.main import SSMSTUI
from sqlit.shared.ui.widgets_tables import SqlitDataTable

from .mocks import MockConnectionStore, MockSettingsStore, build_test_services, create_test_connection


@pytest.mark.asyncio
async def test_incremental_rendering_uses_large_batches(monkeypatch):
"""Large results should minimize table updates after the fast first paint."""
connections = [create_test_connection("test-db", "sqlite")]
services = build_test_services(
connection_store=MockConnectionStore(connections),
settings_store=MockSettingsStore({"theme": "tokyo-night"}),
)
app = SSMSTUI(services=services)
columns = ["id", "value"]
rows = [(index, f"value-{index}") for index in range(5_000)]
batch_sizes: list[int] = []
initial_row_counts: list[int] = []
original_add_rows = SqlitDataTable.add_rows
original_replace = app._replace_results_table_with_table

def _record_add_rows(self, batch):
batch = list(batch)
batch_sizes.append(len(batch))
return original_add_rows(self, batch)

def _record_replace(self, table):
initial_row_counts.append(table.row_count)
return original_replace(table)

monkeypatch.setattr(SqlitDataTable, "add_rows", _record_add_rows)
app._replace_results_table_with_table = MethodType(_record_replace, app)

async with app.run_test(size=(120, 40)) as pilot:
await pilot.pause()
await app._display_query_results(
columns=columns,
rows=rows,
row_count=len(rows),
truncated=False,
elapsed_ms=0,
)

assert initial_row_counts == [20]

await pilot.pause()

assert app.results_table.row_count == len(rows)
assert batch_sizes == [
RESULTS_RENDER_CHUNK_SIZE,
RESULTS_RENDER_CHUNK_SIZE,
len(rows) - 20 - (2 * RESULTS_RENDER_CHUNK_SIZE),
]


@pytest.mark.asyncio
async def test_incremental_rendering_decimal_scale_mismatch():
"""Incremental rendering should not drop rows when Decimal scale changes mid-stream."""
Expand Down
Loading