first commit
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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);`;
|
||||
}
|
||||
Reference in New Issue
Block a user