also looking at this
fix(schema): emit inline FOREIGN KEY on SQLite CREATE TABLE (#1019)
#1020
6 files
+239
-10
| @@ -107,14 +107,16 @@ export class SQLiteDriver implements DialectDriver { | ||
| 107 | 107 | return `CREATE ${kind}INDEX IF NOT EXISTS ${this.quoteIdentifier(idxName)} ON ${this.quoteIdentifier(tableName)} (${columns})${where};` |
| 108 | 108 | } |
| 109 | 109 | |
| 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 '' | |
| 118 | 120 | } |
| 119 | 121 | |
| 120 | 122 | addColumn(tableName: string, column: ColumnPlan): string { |
| @@ -200,6 +202,22 @@ export class SQLiteDriver implements DialectDriver { | ||
| 200 | 202 | parts.push(defaultValue) |
| 201 | 203 | } |
| 202 | 204 | |
| 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 | ||
| 203 | 221 | return parts.join(' ') |
| 204 | 222 | } |
| 205 | 223 | } |