ReviewOS

stacks/bun-query-builder

createTableFromModel generates a TEXT column for `type: 'integer'`, so integers read back as strings

#1094
Closed glennmichael123 opened this 22 days ago · 0 comments
22 days ago

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 INTEGER

boolean 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.

Sign in to comment on this issue.