UNPKG

node-pg-migrate

Version:

PostgreSQL database migration management tool for node.js

234 lines (233 loc) 7.64 kB
import { applyType, escapeValue, makeComment, toArray } from "../../utils.js"; import { parseSequenceOptions } from "../sequences.js"; function parseReferences(options, literal) { const { references, match, onDelete, onUpdate } = options; const clauses = []; clauses.push( typeof references === "string" && (references.startsWith('"') || references.endsWith(")")) ? `REFERENCES ${references}` : `REFERENCES ${literal(references)}` ); if (match) { clauses.push(`MATCH ${match}`); } if (onDelete) { clauses.push(`ON DELETE ${onDelete}`); } if (onUpdate) { clauses.push(`ON UPDATE ${onUpdate}`); } return clauses.join(" "); } function parseDeferrable(options) { return `DEFERRABLE INITIALLY ${options.deferred ? "DEFERRED" : "IMMEDIATE"}`; } function parseColumns(tableName, columns, mOptions) { const extendingTypeShorthands = mOptions.typeShorthands; let columnsWithOptions = Object.keys(columns).reduce( (previous, column) => ({ ...previous, [column]: applyType(columns[column], extendingTypeShorthands) }), {} ); const primaryColumns = Object.entries(columnsWithOptions).filter(([, { primaryKey }]) => Boolean(primaryKey)).map(([columnName]) => columnName); const multiplePrimaryColumns = primaryColumns.length > 1; if (multiplePrimaryColumns) { columnsWithOptions = Object.fromEntries( Object.entries(columnsWithOptions).map(([columnName, options]) => [ columnName, { ...options, primaryKey: false } ]) ); } const comments = Object.entries(columnsWithOptions).map(([columnName, { comment }]) => { return comment !== void 0 && makeComment( "COLUMN", `${mOptions.literal(tableName)}.${mOptions.literal(columnName)}`, comment ); }).filter((comment) => Boolean(comment)); return { columns: Object.entries(columnsWithOptions).map(([columnName, options]) => { const { type, collation, default: defaultValue, unique, primaryKey, notNull, check, references, referencesConstraintName, referencesConstraintComment, deferrable, expressionGenerated } = options; const sequenceGenerated = options.sequenceGenerated; const constraints = []; if (collation) { constraints.push(`COLLATE ${collation}`); } if (defaultValue !== void 0) { constraints.push(`DEFAULT ${escapeValue(defaultValue)}`); } if (unique) { constraints.push("UNIQUE"); } if (primaryKey) { constraints.push("PRIMARY KEY"); } if (notNull) { constraints.push("NOT NULL"); } if (check) { constraints.push(`CHECK (${check})`); } if (references) { const name = referencesConstraintName || (referencesConstraintComment ? `${tableName}_fk_${columnName}` : ""); const constraintName = name ? `CONSTRAINT ${mOptions.literal(name)} ` : ""; constraints.push( `${constraintName}${parseReferences(options, mOptions.literal)}` ); if (referencesConstraintComment) { comments.push( makeComment( `CONSTRAINT ${mOptions.literal(name)} ON`, mOptions.literal(tableName), referencesConstraintComment ) ); } } if (deferrable) { constraints.push(parseDeferrable(options)); } if (sequenceGenerated) { const sequenceOptions = parseSequenceOptions( extendingTypeShorthands, sequenceGenerated ).join(" "); constraints.push( `GENERATED ${sequenceGenerated.precedence} AS IDENTITY${sequenceOptions ? ` (${sequenceOptions})` : ""}` ); } if (expressionGenerated) { constraints.push(`GENERATED ALWAYS AS (${expressionGenerated}) STORED`); } const constraintsStr = constraints.length > 0 ? ` ${constraints.join(" ")}` : ""; const sType = typeof type === "object" ? mOptions.literal(type) : type; return `${mOptions.literal(columnName)} ${sType}${constraintsStr}`; }), constraints: multiplePrimaryColumns ? { primaryKey: primaryColumns } : {}, comments }; } function parseConstraints(table, options, optionName, literal) { const { check, unique, primaryKey, foreignKeys, exclude, deferrable, comment } = options; const tableName = typeof table === "object" ? table.name : table; let constraints = []; const comments = []; if (check) { if (Array.isArray(check)) { for (const [i, ch] of check.entries()) { const name = literal(optionName || `${tableName}_chck_${i + 1}`); constraints.push(`CONSTRAINT ${name} CHECK (${ch})`); } } else { const name = literal(optionName || `${tableName}_chck`); constraints.push(`CONSTRAINT ${name} CHECK (${check})`); } } if (unique) { const uniqueArray = toArray(unique); const isArrayOfArrays = uniqueArray.some( (uniqueSet) => Array.isArray(uniqueSet) ); for (const uniqueSet of isArrayOfArrays ? uniqueArray : [uniqueArray]) { const cols = toArray(uniqueSet); const name = literal(optionName || `${tableName}_uniq_${cols.join("_")}`); constraints.push( `CONSTRAINT ${name} UNIQUE (${cols.map(literal).join(", ")})` ); } } if (primaryKey) { const name = literal(optionName || `${tableName}_pkey`); const key = toArray(primaryKey).map(literal).join(", "); constraints.push(`CONSTRAINT ${name} PRIMARY KEY (${key})`); } if (foreignKeys) { for (const fk of toArray(foreignKeys)) { const { columns, referencesConstraintName, referencesConstraintComment } = fk; const cols = toArray(columns); const name = literal( referencesConstraintName || optionName || `${tableName}_fk_${cols.join("_")}` ); const key = cols.map(literal).join(", "); const referencesStr = parseReferences(fk, literal); constraints.push( `CONSTRAINT ${name} FOREIGN KEY (${key}) ${referencesStr}` ); if (referencesConstraintComment) { comments.push( makeComment( `CONSTRAINT ${name} ON`, literal(table), referencesConstraintComment ) ); } } } if (exclude) { const name = literal(optionName || `${tableName}_excl`); constraints.push(`CONSTRAINT ${name} EXCLUDE ${exclude}`); } if (deferrable) { constraints = constraints.map( (constraint) => `${constraint} ${parseDeferrable(options)}` ); } if (comment) { if (!optionName) { throw new Error("cannot comment on unspecified constraints"); } comments.push( makeComment( `CONSTRAINT ${literal(optionName)} ON`, literal(table), comment ) ); } return { constraints, comments }; } function parseLike(like, literal) { const formatOptions = (name, options2) => toArray(options2).filter((option) => option !== void 0).map((option) => ` ${name} ${option}`).join(""); const table = typeof like === "string" || !("table" in like) ? like : like.table; const options = typeof like === "string" || !("options" in like) || like.options === void 0 ? "" : [ formatOptions("INCLUDING", like.options.including), formatOptions("EXCLUDING", like.options.excluding) ].join(""); return `LIKE ${literal(table)}${options}`; } export { parseColumns, parseConstraints, parseDeferrable, parseLike, parseReferences };