What happens to Expo SQLite pagination when rows are added or deleted between LIMIT/OFFSET pages?
0 reputation · 09 Sept 2020, 09:08 UTC
0 reputation · 09 Sept 2020, 09:08 UTC
Goal: Use Expo‑SQLite’s executeSql with LIMIT and OFFSET to paginate a local table while another process may insert or delete rows.
Constraint: The module forwards raw SQL to SQLite without providing snapshot isolation, so OFFSET values are calculated on the live table state.
Uncertainty: It is unclear whether concurrent modifications cause page boundaries to shift, leading to duplicate or missing rows, and whether any built‑in mechanism can mitigate this.
Does Expo‑SQLite expose any isolation level or transaction mode that creates a consistent snapshot for the duration of a paginated query?
Can a developer safely wrap each page fetch in a transaction to prevent shifting offsets, or does that merely serialize writes without solving the issue?
Which strategy—such as keyset pagination, row versioning, or application‑level locking—best guarantees stable page boundaries under concurrent changes?
35275 reputation · 09 Sept 2020, 09:37 UTC
Expo‑SQLite forwards the SQL string you give it straight to the underlying SQLite engine. The engine runs each query against the current, live state of the table – there is no built‑in snapshot isolation or read‑committed mode that would freeze the data for the duration of a multi‑page walk.
Wrapping a single executeSql call in db.transaction only guarantees that the write you perform inside that transaction is atomic. It does not create a snapshot for subsequent reads. If you perform a paginated read in one transaction and then delete/insert in another, the offsets will still shift.
Instead of relying on OFFSET, use a stable, monotonically increasing column (usually an auto‑increment id or the SQLite rowid) to keep track of where you are:
SELECT * FROM myTable
WHERE id > :lastId
ORDER BY id ASC
LIMIT :pageSize;
Because the WHERE id > :lastId clause is evaluated against the current table state, rows added or removed before lastId never affect the next page. Rows inserted after lastId will appear on the next page, and deletions after lastId simply shorten the result set – no gaps or duplicates occur.
id and insert 100 rows.LIMIT/OFFSET.db.transaction and repeat – you will notice no difference in offset behavior.Expo‑SQLite does not provide snapshot isolation for LIMIT/OFFSET pagination. If your app can tolerate gaps or duplicates, you can use it as a quick solution, but for a reliable user experience, switch to keyset pagination or enforce a write lock while paginating.
Missing diagnostic detail: Does your table already have a stable, increasing column you can use for keyset pagination? If not, adding one is the simplest fix.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.