Found on main @ 68fd61c, verified by execution.
Repro
const M = createModel({
name: 'PgRow', table: 'pg_rows', primaryKey: 'id', autoIncrement: true,
attributes: {
name: { type: 'string', fillable: true },
n: { type: 'integer', fillable: true },
ok: { type: 'boolean', fillable: true },
},
} as const)
await createTableFromModel(M.getDefinition())Generated DDL:
CREATE TABLE pg_rows (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, n TEXT, ok INTEGER)
-- ^^^^ should be INTEGERboolean maps to INTEGER correctly. integer is the one that falls through to the TEXT default.
Consequence:
await M.create({ name: 'r1', n: 1, ok: false })
const row = await M.query().first()
typeof row.get('n') // 'string' <- '1'
typeof row.get('ok') // 'number'SQLite reports typeof(n) = 'text' for the stored value, so this is the column type, not a hydration issue.
Impact
Anything comparing or arithmetic-ing an integer attribute gets string semantics. '10' < '9' is true under SQLite's text collation, so range filters and ORDER BY on an integer attribute silently return the wrong rows — no error anywhere. id is unaffected because the primary key takes a separate code path.
Note
type: 'integer' is the spelling in the docs and in this library's own model examples, so this is the common path rather than an unusual one.