node-pg-migrate-custom
Version:
Postgresql database migration management tool for node.js
358 lines (357 loc) • 17.8 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.addConstraint = exports.dropConstraint = exports.renameConstraint = exports.renameColumn = exports.renameTable = exports.alterColumn = exports.addColumns = exports.dropColumns = exports.alterTable = exports.createTable = exports.dropTable = void 0;
const lodash_1 = __importDefault(require("lodash"));
const utils_1 = require("../utils");
const sequences_1 = require("./sequences");
const 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(' ');
};
const parseDeferrable = (options) => `DEFERRABLE INITIALLY ${options.deferred ? 'DEFERRED' : 'IMMEDIATE'}`;
const parseColumns = (tableName, columns, mOptions) => {
const extendingTypeShorthands = mOptions.typeShorthands;
let columnsWithOptions = lodash_1.default.mapValues(columns, (column) => utils_1.applyType(column, extendingTypeShorthands));
const primaryColumns = lodash_1.default.chain(columnsWithOptions)
.map((options, columnName) => (options.primaryKey ? columnName : null))
.filter((columnName) => Boolean(columnName))
.value();
const multiplePrimaryColumns = primaryColumns.length > 1;
if (multiplePrimaryColumns) {
columnsWithOptions = lodash_1.default.mapValues(columnsWithOptions, (options) => (Object.assign(Object.assign({}, options), { primaryKey: false })));
}
const comments = lodash_1.default.chain(columnsWithOptions)
.map((options, columnName) => typeof options.comment !== 'undefined' &&
utils_1.makeComment('COLUMN', `${mOptions.literal(tableName)}.${mOptions.literal(columnName)}`, options.comment))
.filter((comment) => Boolean(comment))
.value();
return {
columns: lodash_1.default.map(columnsWithOptions, (options, columnName) => {
var _a;
const { type, collation, default: defaultValue, unique, primaryKey, notNull, check, references, referencesConstraintName, referencesConstraintComment, deferrable, expressionGenerated, } = options;
const sequenceGenerated = (_a = options.sequenceGenerated) !== null && _a !== void 0 ? _a : options.generated;
const constraints = [];
if (collation) {
constraints.push(`COLLATE ${collation}`);
}
if (defaultValue !== undefined) {
constraints.push(`DEFAULT ${utils_1.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(utils_1.makeComment(`CONSTRAINT ${mOptions.literal(name)} ON`, mOptions.literal(tableName), referencesConstraintComment));
}
}
if (deferrable) {
constraints.push(parseDeferrable(options));
}
if (sequenceGenerated) {
const sequenceOptions = sequences_1.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 ? ` ${constraints.join(' ')}` : '';
const sType = typeof type === 'object' ? mOptions.literal(type) : type;
return `${mOptions.literal(columnName)} ${sType}${constraintsStr}`;
}),
constraints: multiplePrimaryColumns ? { primaryKey: primaryColumns } : {},
comments,
};
};
const 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 (lodash_1.default.isArray(check)) {
check.forEach((ch, i) => {
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 = lodash_1.default.isArray(unique) ? unique : [unique];
const isArrayOfArrays = uniqueArray.some((uniqueSet) => lodash_1.default.isArray(uniqueSet));
(isArrayOfArrays ? uniqueArray : [uniqueArray]).forEach((uniqueSet) => {
const cols = lodash_1.default.isArray(uniqueSet) ? uniqueSet : [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 = (lodash_1.default.isArray(primaryKey) ? primaryKey : [primaryKey]).map(literal).join(', ');
constraints.push(`CONSTRAINT ${name} PRIMARY KEY (${key})`);
}
if (foreignKeys) {
;
(lodash_1.default.isArray(foreignKeys) ? foreignKeys : [foreignKeys]).forEach((fk) => {
const { columns, referencesConstraintName, referencesConstraintComment } = fk;
const cols = lodash_1.default.isArray(columns) ? columns : [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(utils_1.makeComment(`CONSTRAINT ${name} ON`, literal(tableName), 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(utils_1.makeComment(`CONSTRAINT ${literal(optionName)} ON`, literal(tableName), comment));
}
return {
constraints,
comments,
};
};
const parseLike = (like, literal) => {
const formatOptions = (name, options) => (lodash_1.default.isArray(options) ? options : [options])
.filter((option) => option !== undefined)
.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 === undefined
? ''
: [formatOptions('INCLUDING', like.options.including), formatOptions('EXCLUDING', like.options.excluding)].join('');
return `LIKE ${literal(table)}${options}`;
};
function dropTable(mOptions) {
const _drop = (tableName, options = {}) => {
const { ifExists, cascade } = options;
const ifExistsStr = ifExists ? ' IF EXISTS' : '';
const cascadeStr = cascade ? ' CASCADE' : '';
const tableNameStr = mOptions.literal(tableName);
return `DROP TABLE${ifExistsStr} ${tableNameStr}${cascadeStr};`;
};
return _drop;
}
exports.dropTable = dropTable;
function createTable(mOptions) {
const _create = (tableName, columns, options = {}) => {
const { temporary, ifNotExists, inherits, like, constraints: optionsConstraints = {}, comment } = options;
const { columns: columnLines, constraints: crossColumnConstraints, comments: columnComments = [] } = parseColumns(tableName, columns, mOptions);
const dupes = lodash_1.default.intersection(Object.keys(optionsConstraints), Object.keys(crossColumnConstraints));
if (dupes.length > 0) {
const dupesStr = dupes.join(', ');
throw new Error(`There is duplicate constraint definition in table and columns options: ${dupesStr}`);
}
const constraints = Object.assign(Object.assign({}, optionsConstraints), crossColumnConstraints);
const { constraints: constraintLines, comments: constraintComments } = parseConstraints(tableName, constraints, '', mOptions.literal);
const tableDefinition = [...columnLines, ...constraintLines].concat(like ? [parseLike(like, mOptions.literal)] : []);
const temporaryStr = temporary ? ' TEMPORARY' : '';
const ifNotExistsStr = ifNotExists ? ' IF NOT EXISTS' : '';
const inheritsStr = inherits ? ` INHERITS (${mOptions.literal(inherits)})` : '';
const tableNameStr = mOptions.literal(tableName);
const createTableQuery = `CREATE${temporaryStr} TABLE${ifNotExistsStr} ${tableNameStr} (
${utils_1.formatLines(tableDefinition)}
)${inheritsStr};`;
const comments = [...columnComments, ...constraintComments];
if (typeof comment !== 'undefined') {
comments.push(utils_1.makeComment('TABLE', mOptions.literal(tableName), comment));
}
return `${createTableQuery}${comments.length > 0 ? `\n${comments.join('\n')}` : ''}`;
};
_create.reverse = dropTable(mOptions);
return _create;
}
exports.createTable = createTable;
function alterTable(mOptions) {
const _alter = (tableName, options) => {
const alterDefinition = [];
if (options.levelSecurity) {
alterDefinition.push(`${options.levelSecurity} ROW LEVEL SECURITY`);
}
return `ALTER TABLE ${mOptions.literal(tableName)}
${utils_1.formatLines(alterDefinition)};`;
};
return _alter;
}
exports.alterTable = alterTable;
function dropColumns(mOptions) {
const _drop = (tableName, columns, options = {}) => {
const { ifExists, cascade } = options;
if (typeof columns === 'string') {
columns = [columns];
}
else if (!lodash_1.default.isArray(columns) && typeof columns === 'object') {
columns = lodash_1.default.keys(columns);
}
const columnsStr = utils_1.formatLines(columns.map(mOptions.literal), ` DROP ${ifExists ? ' IF EXISTS' : ''}`, `${cascade ? ' CASCADE' : ''},`);
return `ALTER TABLE ${mOptions.literal(tableName)}
${columnsStr};`;
};
return _drop;
}
exports.dropColumns = dropColumns;
function addColumns(mOptions) {
const _add = (tableName, columns, options = {}) => {
const { ifNotExists } = options;
const { columns: columnLines, comments: columnComments = [] } = parseColumns(tableName, columns, mOptions);
const columnsStr = utils_1.formatLines(columnLines, ` ADD ${ifNotExists ? 'IF NOT EXISTS ' : ''}`);
const tableNameStr = mOptions.literal(tableName);
const alterTableQuery = `ALTER TABLE ${tableNameStr}\n${columnsStr};`;
const columnCommentsStr = columnComments.length > 0 ? `\n${columnComments.join('\n')}` : '';
return `${alterTableQuery}${columnCommentsStr}`;
};
_add.reverse = dropColumns(mOptions);
return _add;
}
exports.addColumns = addColumns;
function alterColumn(mOptions) {
return (tableName, columnName, options) => {
var _a;
const { default: defaultValue, type, collation, using, notNull, allowNull, comment } = options;
const sequenceGenerated = (_a = options.sequenceGenerated) !== null && _a !== void 0 ? _a : options.generated;
const actions = [];
if (defaultValue === null) {
actions.push('DROP DEFAULT');
}
else if (defaultValue !== undefined) {
actions.push(`SET DEFAULT ${utils_1.escapeValue(defaultValue)}`);
}
if (type) {
const typeStr = utils_1.applyTypeAdapters(type);
const collationStr = collation ? ` COLLATE ${collation}` : '';
const usingStr = using ? ` USING ${using}` : '';
actions.push(`SET DATA TYPE ${typeStr}${collationStr}${usingStr}`);
}
if (notNull) {
actions.push('SET NOT NULL');
}
else if (notNull === false || allowNull) {
actions.push('DROP NOT NULL');
}
if (sequenceGenerated !== undefined) {
if (!sequenceGenerated) {
actions.push('DROP IDENTITY');
}
else {
const sequenceOptions = sequences_1.parseSequenceOptions(mOptions.typeShorthands, sequenceGenerated).join(' ');
actions.push(`SET GENERATED ${sequenceGenerated.precedence} AS IDENTITY${sequenceOptions ? ` (${sequenceOptions})` : ''}`);
}
}
const queries = [];
if (actions.length > 0) {
const columnsStr = utils_1.formatLines(actions, ` ALTER ${mOptions.literal(columnName)} `);
queries.push(`ALTER TABLE ${mOptions.literal(tableName)}\n${columnsStr};`);
}
if (typeof comment !== 'undefined') {
queries.push(utils_1.makeComment('COLUMN', `${mOptions.literal(tableName)}.${mOptions.literal(columnName)}`, comment));
}
return queries.join('\n');
};
}
exports.alterColumn = alterColumn;
function renameTable(mOptions) {
const _rename = (tableName, newName) => {
const tableNameStr = mOptions.literal(tableName);
const newNameStr = mOptions.literal(newName);
return `ALTER TABLE ${tableNameStr} RENAME TO ${newNameStr};`;
};
_rename.reverse = (tableName, newName) => _rename(newName, tableName);
return _rename;
}
exports.renameTable = renameTable;
function renameColumn(mOptions) {
const _rename = (tableName, columnName, newName) => {
const tableNameStr = mOptions.literal(tableName);
const columnNameStr = mOptions.literal(columnName);
const newNameStr = mOptions.literal(newName);
return `ALTER TABLE ${tableNameStr} RENAME ${columnNameStr} TO ${newNameStr};`;
};
_rename.reverse = (tableName, columnName, newName) => _rename(tableName, newName, columnName);
return _rename;
}
exports.renameColumn = renameColumn;
function renameConstraint(mOptions) {
const _rename = (tableName, constraintName, newName) => {
const tableNameStr = mOptions.literal(tableName);
const constraintNameStr = mOptions.literal(constraintName);
const newNameStr = mOptions.literal(newName);
return `ALTER TABLE ${tableNameStr} RENAME CONSTRAINT ${constraintNameStr} TO ${newNameStr};`;
};
_rename.reverse = (tableName, constraintName, newName) => _rename(tableName, newName, constraintName);
return _rename;
}
exports.renameConstraint = renameConstraint;
function dropConstraint(mOptions) {
const _drop = (tableName, constraintName, options = {}) => {
const { ifExists, cascade } = options;
const ifExistsStr = ifExists ? ' IF EXISTS' : '';
const cascadeStr = cascade ? ' CASCADE' : '';
const tableNameStr = mOptions.literal(tableName);
const constraintNameStr = mOptions.literal(constraintName);
return `ALTER TABLE ${tableNameStr} DROP CONSTRAINT${ifExistsStr} ${constraintNameStr}${cascadeStr};`;
};
return _drop;
}
exports.dropConstraint = dropConstraint;
function addConstraint(mOptions) {
const _add = (tableName, constraintName, expression) => {
const { constraints, comments } = typeof expression === 'string'
? {
constraints: [`${constraintName ? `CONSTRAINT ${mOptions.literal(constraintName)} ` : ''}${expression}`],
comments: [],
}
: parseConstraints(tableName, expression, constraintName, mOptions.literal);
const constraintStr = utils_1.formatLines(constraints, ' ADD ');
return [`ALTER TABLE ${mOptions.literal(tableName)}\n${constraintStr};`, ...comments].join('\n');
};
_add.reverse = (tableName, constraintName, options) => {
if (constraintName === null) {
throw new Error(`Impossible to automatically infer down migration for addConstraint without naming constraint`);
}
return dropConstraint(mOptions)(tableName, constraintName, options);
};
return _add;
}
exports.addConstraint = addConstraint;
;