Skip to content

fix(sqlite): delete-all cleared the wrong schema's table - #545

Merged
jeremydmiller merged 1 commit into
JasperFx:masterfrom
jakobt:jta/fix-sqlite-delete-all-schema
Sep 2, 2026
Merged

jeremydmiller merged 1 commit into
JasperFx:masterfrom
jakobt:jta/fix-sqlite-delete-all-schema

Conversation

@jakobt

@jakobt jakobt commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

GenerateDeleteAllSql built every statement from the table name alone and threw the schema away, so each one went out unqualified and SQLite resolved it by search order

GenerateDeleteAllSql built every statement from the table name alone and
threw the schema away, so each one went out unqualified and SQLite
resolved it by search order -- temp first, then main, then attached
databases. Clearing main.records while a temp.records existed emptied
temp and left main untouched, and a table in an attached database was
addressed wrongly every time, with no error either way.

The sqlite_sequence reset had the defect twice over: it hit whichever
sqlite_sequence the same search found, and it matched AUTOINCREMENT marks
by bare table name, so clearing aux.records reseeded the identity counter
of main.records -- silently corrupting identity allocation in a database
the caller never named. It now resets each schema's own sqlite_sequence
with only that schema's tables.

Both statements now name their schema explicitly, including main. The
rest of Weasel.Sqlite leaves main unqualified, because DDL creates where
it is pointed and the shorter form reads better. This method's whole job
is targeting the right table for a destructive statement, and there the
unqualified form is exactly the shape a temp table intercepts -- so main,
the schema nearly every caller uses, would otherwise keep the bug.

Behaviour change worth noting on upgrade: clearing a schema that holds no
AUTOINCREMENT table used to emit an unqualified DELETE that found some
other schema's sqlite_sequence -- wrong, but quiet. It now emits
<schema>.sqlite_sequence and throws "no such table" instead. A loud
failure beats a silent wrong answer, but it is a change. Weasel has never
guarded this; that guard is its own fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXHTRGQTNfn7rb9eJPfq4z
@jeremydmiller
jeremydmiller merged commit 6c64333 into JasperFx:master Sep 2, 2026
18 checks passed
@jeremydmiller jeremydmiller mentioned this pull request Sep 2, 2026
jeremydmiller added a commit that referenced this pull request Sep 2, 2026
Clears 9.28's known upgrade break, makes the SQLite table rebuild safe against a
database that has foreign keys, and adds two capabilities.

- #542 closes #538. AssertPatchingIsValid no longer refuses a delta that can rebuild in
  place, which is what made a numeric or decimal SQLite column throw on 9.28 under every
  AutoCreate except All. CreateOrUpdate permits a rebuild; CreateOnly still refuses.
- #539. The SQLite rebuild runs the way SQLite documents it -- enforcement suspended, one
  transaction, foreign_key_check before the commit -- instead of four autocommitting
  statements that wedged the database when the table was referenced. Also fixes an
  AUTOINCREMENT table reissuing a used id, and a view breaking the rename.
- #540. Mutually referencing tables can be created from scratch. A migration holds back
  only the keys whose target it creates later, so a schema that never had the problem
  generates byte-for-byte identical DDL.
- #543 closes #541. FullTextIndexDefinition can index a pre-built tsvector, making
  setweight() weighting reachable for JasperFx/marten#5298.
- #545. SQLite delete-all names the schema instead of letting SQLite resolve by search
  order, which put temp first.

Minor rather than patch: #543 and #540 add API, and #539 and #542 change what an existing
migration does.

Ships with one known issue, #546: on SQLite, resetting identity against a schema that has
no AUTOINCREMENT table fails with "no such table: <schema>.sqlite_sequence". The
single-schema form of this predates 9.29.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 2, 2026
@jeremydmiller

Copy link
Copy Markdown
Member

Merged into 9.29.0 unchanged — the schema qualification is right, and the qualify helper's comment about temp coming first in the search order is what put me onto the same bug hiding in #539's new sqlite_sequence carry-over. That one is fixed too.

One consequence worth recording, which is why I opened #546 rather than holding this PR.

Naming the schema removes the accidental fallback to another schema's copy, so a reset against a schema with no AUTOINCREMENT table now fails where it previously succeeded:

no such table: temp.sqlite_sequence

sqlite_sequence only exists in a database once something in it has been declared AUTOINCREMENT. Before, the single unqualified statement resolved to whichever copy existed anywhere in the search path — wrongly, which is exactly what this PR fixes, but without throwing.

Two ages of the same bug, and I want to be clear this PR only introduces the second:

  • single-schema — predates this PR. The old unqualified statement failed identically when no schema had the table. Verified: old and new forms both error.
  • multi-schema — new here, and the honest price of the fix being correct.

I couldn't fix it inside this PR. Three dead ends, all measured on 3.51.0:

  • No in-SQL guard. A sqlite_master subquery doesn't help — resolution happens at prepare, before any row is read.
  • GenerateDeleteAllSql has no connection to probe with, and the signature lives on the abstract Migrator shared by all five providers.
  • DatabaseCleaner caches the string and runs it as one command, and Microsoft.Data.Sqlite prepares a multi-statement CommandText one statement at a time — so the table DELETEs ahead of it have already run. The operation half-applies, then throws, which is the part that made me want it tracked properly.

#546 has three candidate designs; a connection-aware virtual overload defaulting to the existing sync method looks right to me. Documented as a known issue in the 9.29 guide.

@jakobt
jakobt deleted the jta/fix-sqlite-delete-all-schema branch September 2, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants