Answer an Android database the way it is actually shaped - #29
Merged
Conversation
Every fixture the suite owned used real types - Chinook's InvoiceDate is a DATETIME - so nothing ever
exercised the shape a Room database has: a date is an INTEGER of epoch milliseconds, a boolean is 0 or
1, an id is a Long past what a double holds, and the app's tables sit beside Room's own bookkeeping and
an FTS table's shadow tables. The first time such a database was tried, it answered questions wrongly
without erring once.
A date comparison was the worst of it. The SQLite guidance said to use date('now','-30 days'), which is
right for a TEXT column and wrong for an INTEGER one, and SQLite compares by storage class, so nothing
matches, nothing errors, and "how many users signed up in the last 7 days" answers zero. Guessing epoch
seconds instead is worse: a milliseconds column is a thousand times larger, so every row matches and the
answer is the whole table. A 7B model answered 0 where the truth was 2; a 30B model answered 5. The
guidance now states which units a column is in, a semantic floor catches a numeric column compared
against a date, and the schema carries the unit itself, decided from an aggregate so no value is read
into the prompt. Both models now answer 2.
Full-text search runs. SQLite parses under the Postgresql grammar, which has no MATCH, so every query a
Room @fts4 entity is queried with was refused as unparseable. MATCH is validated as a comparison and the
statement that runs keeps it verbatim; writes, stacked statements, denied functions and a non-literal
right side are all still refused. Verified on FTS4 and FTS5.
rowid is no longer called an invented column: SQLite gives every table rowid, oid and _rowid_ without
listing them, and FTS tables answer to docid and rank. On a WITHOUT ROWID table the database rejects the
name, which the correction loop can act on.
A database copied without its -wal file said it was empty. Room defaults to WAL, so pulling app.db off a
device and leaving the sidecars behind left SQLite reporting no tables and every question answering "no
such table". It now says which file is missing. The header and the sidecar are read before the database
is opened, because SQLite creates an empty -wal itself as soon as it is.
tools/room-regression.mjs runs the engine against that shape with no model involved, so it gates CI.
Android Studio is where this plugin is mostly installed, so the database it opens is usually SQLite via Room. Four faults sat in that path, none of them reachable by a fixture built on ordinary types. A query over a table with a ByteArray column returned nothing at all. The driver reports those cells as BLOB and implements none of the streaming interface, so reading them threw and the whole result set went with it, reported to the user as "the database didn't accept that query". The bytes read back fine the other way. Opening a file that is not a readable database reopened it about twenty-one thousand times a second until the operation timed out, pegging a core and then reporting something unrelated. The driver signals that case by throwing where it is documented to return false, which read as "stale connection, try again". It now fails once and says that a database pulled from a device needs its -wal and -shm files alongside it - which is also how the sidecar gets picked by mistake in the first place. SELECT rowid was refused as an invented column, and a date comparison against an epoch-milliseconds column answered zero or everything without erring, the same fault fixed in the engine. Full-text search over a Room @fts4 entity ran at all: MATCH is not in the SQL parser's grammar, so every such query had been refused as unparseable. A relationship question about two collections is now answered in prose on MongoDB, as it already was on the SQL engines, rather than returning documents.
Both minor: the engine gains a semantic floor for a numeric column compared against a date, accepts SQLite's MATCH, and recognises the columns SQLite gives every table without listing them; the connector states which units an integer timestamp is in and says when a database was copied without its -wal file. The lockfile moves with them: it records the workspace versions, so leaving it behind fails the frozen install CI runs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three commits: the engine and connector work, the JetBrains plugin, and the version bumps.
Why
JetBrains installs are dominated by Android Studio, so the database this plugin opens is usually SQLite via Room. Every fixture the suite owned used real types - Chinook's
InvoiceDateis aDATETIME- so nothing had ever exercised that shape: dates as INTEGER epoch milliseconds, booleans as 0/1, ids as Longs past what a double holds, BLOB columns, and Room's bookkeeping and FTS shadow tables beside the app's own. The first time such a database was tried it produced six defects, none of which raised an error.rowid-walMATCH@Fts4query refused as unparseableThe date one is the worst: a 7B model answered 0 where the truth was 2, and a 30B model answered 5 - a larger model failing in the opposite direction. Both answer 2 now.
Versions
@asksql/core@asksql/sqliteVS Code stays at 0.7.2 and the browser extension at 0.3.1; neither has a source change.
Behaviour a user can see change
MATCHis accepted, so full-text search runs. Writes, stacked statements, denied functions and a non-literal right side are still refused.SELECT rowidis accepted on SQLite.Verification
pnpm test:room)pnpm verifyThe two new checks are held to roughly 67,000 generated statements across both surfaces, in both directions - a rule that fires on a TEXT column would refuse SQL that is correct.
Not verified: no real device database has been opened, and CI runs on Ubuntu only while Android Studio skews Windows.