/** * SQL rendering shared by adapters and the migration runner. Column types are * dialect-neutral in the model; this maps them to each dialect's SQL types and * renders `CREATE TABLE`. (Postgres/MySQL land in later phases; the mappings are * here so the model layer is already portable.) */ import type { ColumnDef, Model } from "./schema.ts"; export type Dialect = "sqlite" | "postgres" | "mysql"; function sqlType(def: ColumnDef, dialect: Dialect): string { if (def.type === "id") { if (dialect === "postgres") return "SERIAL"; if (dialect === "mysql") return "INT AUTO_INCREMENT"; return "INTEGER"; } switch (def.type) { case "int": return "INTEGER"; case "real": return dialect === "mysql" ? "DOUBLE" : "REAL"; case "bool": return dialect === "postgres" ? "BOOLEAN" : "INTEGER"; case "timestamp": return dialect === "sqlite" ? "TEXT" : "TIMESTAMP"; case "json": return dialect === "postgres" ? "JSONB" : "TEXT"; default: return dialect === "mysql" ? "VARCHAR(255)" : "TEXT"; } } function quoteId(id: string, dialect: Dialect): string { return dialect === "mysql" ? `\`${id}\`` : `"${id}"`; } function renderDefault(value: unknown, dialect: Dialect): string { if (value === "now") return "CURRENT_TIMESTAMP"; if (typeof value === "number") return String(value); if (typeof value === "boolean") return dialect === "postgres" ? String(value) : value ? "1" : "0"; return `'${String(value).replace(/'/g, "''")}'`; } /** Render `CREATE TABLE` for a model in the given dialect. */ export function createTableSql(model: Model, dialect: Dialect, ifNotExists = true): string { const cols: string[] = []; for (const [name, column] of Object.entries(model.columns)) { const def = column.def; const parts = [quoteId(name, dialect), sqlType(def, dialect)]; if (def.primaryKey) { parts.push( dialect === "sqlite" && def.type === "id" ? "PRIMARY KEY AUTOINCREMENT" : "PRIMARY KEY", ); } if (def.notNull && !def.primaryKey) parts.push("NOT NULL"); if (def.unique && !def.primaryKey) parts.push("UNIQUE"); if (def.default !== undefined) parts.push(`DEFAULT ${renderDefault(def.default, dialect)}`); if (def.references) { parts.push( `REFERENCES ${quoteId(def.references.table, dialect)}(${quoteId(def.references.column, dialect)})`, ); } cols.push(" " + parts.join(" ")); } const head = `CREATE TABLE ${ifNotExists ? "IF NOT EXISTS " : ""}${quoteId(model.name, dialect)}`; return `${head} (\n${cols.join(",\n")}\n);`; }