ReviewOS

also looking at this

stacks/bun-query-builder

fix(schema): emit inline FOREIGN KEY on SQLite CREATE TABLE (#1019)

#1020
Merged glennmichael123 wants to merge fix/inline-foreign-keys into main
6 files +239 -10
packages/bun-query-builder/src/drivers/sqlite.tsmodified+26-8
Changes to packages/bun-query-builder/src/drivers/sqlite.ts
@@ -107,14 +107,16 @@ export class SQLiteDriver implements DialectDriver {
107107 return `CREATE ${kind}INDEX IF NOT EXISTS ${this.quoteIdentifier(idxName)} ON ${this.quoteIdentifier(tableName)} (${columns})${where};`
108108 }
109109
110 addForeignKey(tableName: string, columnName: string, refTable: string, refColumn: string, onDelete?: string, onUpdate?: string): string {
111 const fkName = `${tableName}_${columnName}_fk`
112 let sql = `ALTER TABLE ${this.quoteIdentifier(tableName)} ADD CONSTRAINT ${this.quoteIdentifier(fkName)} FOREIGN KEY (${this.quoteIdentifier(columnName)}) REFERENCES ${this.quoteIdentifier(refTable)}(${this.quoteIdentifier(refColumn)})`
113 if (onDelete)
114 sql += ` ON DELETE ${onDelete.toUpperCase()}`
115 if (onUpdate)
116 sql += ` ON UPDATE ${onUpdate.toUpperCase()}`
117 return `${sql};`
110 addForeignKey(_tableName: string, _columnName: string, _refTable: string, _refColumn: string, _onDelete?: string, _onUpdate?: string): string {
111 // SQLite doesn't support `ALTER TABLE … ADD CONSTRAINT FOREIGN
112 // KEY` — it can only declare FKs inline on `CREATE TABLE`, which
113 // `renderColumn` already does. Return an empty string so the
114 // orchestrator (`generateSql` / `generateDiffSql`) skips emitting
115 // an unrunnable ALTER migration file for SQLite.
116 //
117 // Consumers that previously stripped these files from disk after
118 // generation (e.g. stacksjs/stacks#1916) can drop that workaround.
119 return ''
118120 }
119121
120122 addColumn(tableName: string, column: ColumnPlan): string {
@@ -200,6 +202,22 @@ export class SQLiteDriver implements DialectDriver {
200202 parts.push(defaultValue)
201203 }
202204
205 // Inline FK — for SQLite this is the ONLY path that works, since
206 // SQLite doesn't support `ALTER TABLE ADD CONSTRAINT`. The
207 // orchestrator (`generateSql` / `generateDiffSql` in migrations.ts)
208 // skips its post-CREATE `addForeignKey` pass when emitting CREATE
209 // TABLE so we don't duplicate the FK on dialects that accept both
210 // forms. Enforcement still requires `PRAGMA foreign_keys = ON` on
211 // the SQLite connection (off by default — set this in the
212 // consumer's connection bootstrap).
213 if (column.references) {
214 parts.push(`REFERENCES ${this.quoteIdentifier(column.references.table)}(${this.quoteIdentifier(column.references.column)})`)
215 if (column.references.onDelete)
216 parts.push(`ON DELETE ${column.references.onDelete.toUpperCase()}`)
217 if (column.references.onUpdate)
218 parts.push(`ON UPDATE ${column.references.onUpdate.toUpperCase()}`)
219 }
220
203221 return parts.join(' ')
204222 }
205223}