Skip to content

Upstream quirk: libsql /dump does not escape single quotes in sqlite_schema INSERTs for virtual tables (FTS5) — cold-store reaper works around it #1391

Closed

Description

@dezren39

Summary

Turso/libsql GET /dump output cannot always be replayed by a stock SQLite parser. When a database contains a virtual table whose CREATE statement itself contains single quotes — the common case is an FTS5 external-content table, e.g. content='chunks' — the dump registers the virtual table inside a PRAGMA writable_schema=ON; ... OFF; block with a statement like:

INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql) VALUES('table','chunks_fts','chunks_fts',0,'CREATE VIRTUAL TABLE chunks_fts
      USING fts5(text, content='chunks', content_rowid='id')');

The embedded SQL text is emitted verbatim: its single quotes are not doubled (''), so the string literal terminates early and every stock SQLite parser fails with near "chunks": syntax error.

This is an upstream libsql dump-generation bug (the same class of bug stock sqlite3 .dump avoids by escaping). It is filed here only to document the quirk and the workaround; nothing is filed upstream.

Impact

Found live 2026-10-27 on the atlas database: pkgs/cold-store-reaper/scripts/export-turso.sh downloads /dump and rebuilds it with sqlite3 -bail, which hard-fails on this statement — so any Turso database with a quoted virtual-table definition (FTS5 content= tables in practice) can never be filed into the cold store.

Workaround (this repo)

export-turso.sh runs the downloaded dump through a streaming repair filter before the rebuild. The filter passes every line through byte-identical except INSERT INTO sqlite_schema(...) statements inside a writable_schema block whose final SQL literal is verifiably mis-escaped (a lone, undoubled single quote in the literal body): for those it rewrites the literal with proper '' doubling so the statement parses and registers the virtual table exactly as authored. Correctly-escaped statements and all other lines pass through unchanged.

Notes

  • Detection is parse-based and conservative: a statement is only rewritten when its final literal cannot be a valid SQL string literal as emitted.
  • The truncation guard (dump must end in COMMIT;) applies to the filtered stream.

Acceptance criteria

  • pkgs/cold-store-reaper/scripts/export-turso.sh (or a helper it calls) contains the streaming repair filter the body describes: only INSERT INTO sqlite_schema(...) statements inside a writable_schema block whose final SQL literal is verifiably mis-escaped are rewritten, with '' doubling; every other line passes byte-identical
  • A fixture dump containing an FTS5 external-content virtual table (quoted content= definition) rebuilds cleanly through the export path with sqlite3 -bail
  • Correctly-escaped dumps are unchanged by the filter (byte-identical output), and the COMMIT; truncation guard still applies to the filtered stream
  • A test/fixture covers both the mis-escaped and correctly-escaped cases so the workaround cannot silently rot

Activity

  1. developing-today commented on Oct 27, 2026

    @developing-today
    Contributor
  2. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    Intake review — fleet monitor (2026-10-27)

    Still live — and the body overstates the current state. The issue says the workaround already runs in export-turso.sh, but a code search of current main for writable_schema / sqlite_schema returns no hits anywhere in the repo (checked 2026-10-27, local clone + GitHub code search): the repair filter is described, not landed. So the remaining work is implementation + fixture test, not just documentation.

    Related: the cold-store turso hardening line of work (PR #1383, GB-class export hardening, merged today) touches the same script — coordinate so the filter lands on top of it, not against it.

    Risks: a non-conservative filter could corrupt valid dumps; the parse-based, lone-quote-only detection in the body is the right constraint — keep it.

    Suggested approach: implement the filter as a small separately-testable script invoked by export-turso.sh, with the two fixtures above.

  3. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/normalOrdinary priority (the default)
    effort/sSmall: expected under 2 hours of work
    source/humanFiled directly by a human
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 27, 2026
  4. added a commit that references this issue on Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingeffort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)meta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)source/humanFiled directly by a humanstatus:done

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions