UNPKG

@lucidcms/postgres-adapter

Version:

The official Postgres adapter for Lucid CMS

204 lines (198 loc) 7.42 kB
import { DatabaseAdapter } from "@lucidcms/core"; import { ParseJSONResultsPlugin, sql } from "kysely"; import { PostgresJSDialect } from "kysely-postgres-js"; import postgres from "postgres"; import { jsonArrayFrom } from "kysely/helpers/postgres"; //#region src/utils/format-default-value.ts const formatDefaultValue = (type, defaultValue) => { if (defaultValue === null) return null; const withoutTypeCast = defaultValue.split("::")[0]; if (withoutTypeCast === void 0) return null; if (withoutTypeCast.toLowerCase() === "now()") return "NOW()"; if (type === "jsonb") try { if (withoutTypeCast.startsWith("'") && withoutTypeCast.endsWith("'")) return JSON.parse(withoutTypeCast.slice(1, -1)); return JSON.parse(withoutTypeCast); } catch (e) { return null; } if (withoutTypeCast.startsWith("'") && withoutTypeCast.endsWith("'")) return withoutTypeCast.slice(1, -1); if (withoutTypeCast.includes("nextval(")) return null; if (withoutTypeCast.toLowerCase() === "true") return true; if (withoutTypeCast.toLowerCase() === "false") return false; if (/^-?\d+(\.\d+)?$/.test(withoutTypeCast)) return type === "integer" || type === "bigint" ? Number.parseInt(withoutTypeCast, 10) : Number.parseFloat(withoutTypeCast); return defaultValue; }; var format_default_value_default = formatDefaultValue; //#endregion //#region src/utils/format-on-delete.ts const formatOnDelete = (value) => { return value?.toLowerCase() ?? "no action"; }; var format_on_delete_default = formatOnDelete; //#endregion //#region src/utils/format-on-update.ts const formatOnUpdate = (value) => { return value?.toLowerCase() ?? "no action"; }; var format_on_update_default = formatOnUpdate; //#endregion //#region src/utils/format-type.ts const formatType = (type, defaultValue) => { if (type.includes("timestamp")) return "timestamp"; if (type.includes("character")) return "text"; if (type === "integer" && defaultValue?.includes("nextval(")) return "serial"; return type; }; var format_type_default = formatType; //#endregion //#region src/index.ts var PostgresAdapter = class extends DatabaseAdapter { constructor(url, options) { super({ adapter: "postgres", dialect: new PostgresJSDialect({ postgres: postgres(url, options) }), plugins: [new ParseJSONResultsPlugin()] }); } async initialise() { await sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`.execute(this.client); await sql`SET timezone = 'UTC'`.execute(this.client); } get jsonArrayFrom() { return jsonArrayFrom; } get config() { return { support: { alterColumn: true, multipleAlterTables: true, autoIncrement: false, boolean: true }, dataTypes: { primary: "serial", integer: "integer", boolean: "boolean", json: "jsonb", text: "text", timestamp: "timestamp", char: (length) => `char(${length})`, varchar: (length) => length ? `varchar(${length})` : "varchar" }, defaults: { timestamp: { now: "NOW()" }, boolean: { true: true, false: false } }, fuzzOperator: "%" }; } async inferSchema(tx) { const res = await sql` WITH table_columns AS ( SELECT c.table_name, c.column_name AS name, c.data_type AS type, c.is_nullable = 'NO' AS notnull, c.column_default AS dflt_value, CASE WHEN pk.constraint_name IS NOT NULL THEN true ELSE false END AS pk, CASE WHEN uc.constraint_name IS NOT NULL THEN true ELSE false END AS is_unique FROM information_schema.columns c LEFT JOIN ( SELECT kcu.table_name, kcu.column_name, tc.constraint_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type = 'PRIMARY KEY' ) pk ON c.table_name = pk.table_name AND c.column_name = pk.column_name LEFT JOIN ( SELECT kcu.table_name, kcu.column_name, tc.constraint_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type = 'UNIQUE' ) uc ON c.table_name = uc.table_name AND c.column_name = uc.column_name WHERE c.table_name LIKE 'lucid_%' ), foreign_keys AS ( SELECT kcu.table_name, kcu.column_name, ccu.table_name AS referenced_table, ccu.column_name AS referenced_column, rc.update_rule AS on_update, rc.delete_rule AS on_delete FROM information_schema.key_column_usage kcu JOIN information_schema.referential_constraints rc ON kcu.constraint_name = rc.constraint_name JOIN information_schema.constraint_column_usage ccu ON rc.unique_constraint_name = ccu.constraint_name WHERE kcu.table_name LIKE 'lucid_%' ) SELECT tc.*, fk.referenced_table AS fk_table, fk.referenced_column AS fk_column, fk.on_update AS fk_on_update, fk.on_delete AS fk_on_delete FROM table_columns tc LEFT JOIN foreign_keys fk ON tc.table_name = fk.table_name AND tc.name = fk.column_name `.execute(tx ?? this.client); const tableMap = /* @__PURE__ */ new Map(); for (const row of res.rows) { let table = tableMap.get(row.table_name); if (!table) { table = { name: row.table_name, columns: [] }; tableMap.set(row.table_name, table); } table.columns.push({ name: row.name, type: format_type_default(row.type, row.dflt_value), nullable: !row.notnull, default: format_default_value_default(format_type_default(row.type, row.dflt_value), row.dflt_value), primary: row.pk, unique: row.is_unique, foreignKey: row.fk_table && row.fk_column ? { table: row.fk_table, column: row.fk_column, onUpdate: format_on_update_default(row.fk_on_update), onDelete: format_on_delete_default(row.fk_on_delete) } : void 0 }); } return Array.from(tableMap.values()); } formatDefaultValue(type, value) { if (type === "text" && typeof value === "string") return sql.raw(`'${value}'`); if (type === "timestamp" && typeof value === "string") return sql.raw(value); if ((type === "json" || type === "jsonb") && typeof value === "object" && value !== null) return sql.raw(`'${JSON.stringify(value)}'`); if (type === "boolean") { if (value) return true; return false; } if (typeof value === "number") return value; return value; } }; var src_default = PostgresAdapter; //#endregion export { src_default as default }; //# sourceMappingURL=index.js.map