UNPKG

node-pg-migrate

Version:

PostgreSQL database migration management tool for node.js

1,465 lines (1,388 loc) 124 kB
// src/migration.ts import { glob } from "glob"; import { createReadStream, createWriteStream } from "fs"; import { mkdir, readdir } from "fs/promises"; import { basename, extname, join, resolve } from "path"; import { cwd } from "process"; // src/operations/casts/dropCast.ts function dropCast(mOptions) { const _drop = (sourceType, targetType, options = {}) => { const { ifExists = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; return `DROP CAST${ifExistsStr} (${sourceType} AS ${targetType});`; }; return _drop; } // src/operations/casts/createCast.ts function createCast(mOptions) { const _create = (sourceType, targetType, options = {}) => { const { functionName, argumentTypes, inout = false, as } = options; let conversion = ""; if (functionName) { const args = argumentTypes || [sourceType]; conversion = ` WITH FUNCTION ${mOptions.literal(functionName)}(${args.join(", ")})`; } else if (inout) { conversion = " WITH INOUT"; } else { conversion = " WITHOUT FUNCTION"; } const implicit = as ? ` AS ${as}` : ""; return `CREATE CAST (${sourceType} AS ${targetType})${conversion}${implicit};`; }; _create.reverse = dropCast(mOptions); return _create; } // src/utils/decamelize.ts var REPLACEMENT = "$1_$2"; function decamelize(text) { if (text.length < 2) { return text.toLowerCase(); } const decamelized = text.replace( new RegExp("([\\p{Lowercase_Letter}\\d])(\\p{Uppercase_Letter})", "gu"), REPLACEMENT ); return decamelized.replace( new RegExp("(\\p{Uppercase_Letter})(\\p{Uppercase_Letter}\\p{Lowercase_Letter}+)", "gu"), REPLACEMENT ).toLowerCase(); } // src/utils/identity.ts function identity(v) { return v; } // src/utils/quote.ts function quote(str) { return `"${str}"`; } // src/utils/createSchemalize.ts function createSchemalize(options) { const { shouldDecamelize, shouldQuote } = options; const transform = [ shouldDecamelize ? decamelize : identity, shouldQuote ? quote : identity ].reduce((acc, fn) => fn === identity ? acc : (str) => acc(fn(str))); return (value) => { if (typeof value === "object") { const { schema, name } = value; return (schema ? `${transform(schema)}.` : "") + transform(name); } return transform(value); }; } // src/utils/createTransformer.ts function createTransformer(literal) { return (statement, mapping = {}) => Object.keys(mapping).reduce((str, param) => { const val = mapping?.[param]; return str.replace( new RegExp(`{${param}}`, "g"), val === void 0 ? "" : typeof val === "string" || typeof val === "object" && val !== null && "name" in val ? literal(val) : String(escapeValue(val)).replace(/\$/g, "$$$$") ); }, statement); } // src/utils/escapeValue.ts function escapeValue(val) { if (val === null) { return "NULL"; } if (typeof val === "boolean") { return val.toString(); } if (typeof val === "string") { let dollars; const ids = new StringIdGenerator(); let index; do { index = ids.next(); dollars = `$pg${index}$`; } while (val.includes(dollars)); return `${dollars}${val}${dollars}`; } if (typeof val === "number") { return val; } if (Array.isArray(val)) { const arrayStr = val.map(escapeValue).join(",").replace(/ARRAY/g, ""); return `ARRAY[${arrayStr}]`; } if (isPgLiteral(val)) { return val.value; } return ""; } // src/utils/formatLines.ts function formatLines(lines, replace = " ", separator = ",") { return lines.map((line) => line.replace(/(?:\r\n|\r|\n)+/g, " ")).join(`${separator} `).replace(/^/gm, replace); } // src/utils/formatParams.ts function formatParam(mOptions) { return (param) => { const { mode, name, type, default: defaultValue } = applyType(param, mOptions.typeShorthands); const options = []; if (mode) { options.push(mode); } if (name) { options.push(mOptions.literal(name)); } if (type) { options.push(type); } if (defaultValue) { options.push(`DEFAULT ${escapeValue(defaultValue)}`); } return options.join(" "); }; } function formatParams(params, mOptions) { return `(${params.map(formatParam(mOptions)).join(", ")})`; } // src/utils/toArray.ts function toArray(item) { return Array.isArray(item) ? [...item] : [item]; } // src/utils/formatPartitionColumns.ts function formatPartitionColumn(column, literal) { if (typeof column === "string") { return literal(column); } let formatted = literal(column.name); if (column.collate) { formatted += ` COLLATE ${column.collate}`; } if (column.opclass) { formatted += ` ${column.opclass}`; } return formatted; } function formatPartitionColumns(partition, literal) { const columns = toArray(partition.columns); return columns.map((col) => formatPartitionColumn(col, literal)).join(", "); } // src/utils/getMigrationTableSchema.ts function getMigrationTableSchema(options) { return options.migrationsSchema === void 0 ? getSchemas(options.schema)[0] : options.migrationsSchema; } // src/utils/getSchemas.ts function getSchemas(schema) { const schemas = toArray(schema).filter( (s) => typeof s === "string" && s.length > 0 ); return schemas.length > 0 ? schemas : ["public"]; } // src/utils/intersection.ts function intersection(list1, list2) { return list1.filter((element) => list2.includes(element)); } // src/utils/makeComment.ts function makeComment(object, name, text = null) { const literal = escapeValue(text); return `COMMENT ON ${object} ${name} IS ${literal};`; } // src/utils/PgLiteral.ts var PgLiteral = class _PgLiteral { /** * Creates a new `PgLiteral` instance. * * @param str The string value. * @returns The new `PgLiteral` instance. */ static create(str) { return new _PgLiteral(str); } /** * Indicates that this object is a `PgLiteral`. */ literal = true; /** * Value of the literal. */ value; /** * Creates a new `PgLiteral` instance. * * @param value The string value. */ constructor(value) { this.value = value; } /** * Returns the string value. * * @returns The string value. */ toString() { return this.value; } }; function isPgLiteral(val) { return val instanceof PgLiteral || typeof val === "object" && val !== null && "literal" in val && val.literal === true; } // src/utils/StringIdGenerator.ts var StringIdGenerator = class { chars; ids = [0]; constructor(chars = "abcdefghijklmnopqrstuvwxyz") { this.chars = chars; } next() { const idsChars = this.ids.map((id) => this.chars[id]); this.increment(); return idsChars.join(""); } increment() { for (let i = this.ids.length - 1; i >= 0; i -= 1) { this.ids[i] += 1; if (this.ids[i] < this.chars.length) { return; } this.ids[i] = 0; } this.ids.unshift(0); } }; // src/utils/types.ts var TYPE_ADAPTERS = Object.freeze({ int: "integer", string: "text", float: "real", double: "double precision", datetime: "timestamp", bool: "boolean" }); var DEFAULT_TYPE_SHORTHANDS = Object.freeze({ id: { type: "serial", primaryKey: true } // convenience type for serial primary keys }); function applyTypeAdapters(type) { return type in TYPE_ADAPTERS ? TYPE_ADAPTERS[type] : type; } function toType(type) { return typeof type === "string" ? { type } : type; } function removeType({ // eslint-disable-next-line @typescript-eslint/no-unused-vars type, ...rest }) { return rest; } function applyType(type, extendingTypeShorthands = {}) { const typeShorthands = { ...DEFAULT_TYPE_SHORTHANDS, ...extendingTypeShorthands }; const options = toType(type); let ext = null; const types = [options.type]; while (typeShorthands[types[types.length - 1]]) { ext = { ...toType(typeShorthands[types[types.length - 1]]), ...ext === null ? {} : removeType(ext) }; if (types.includes(ext.type)) { throw new Error( `Shorthands contain cyclic dependency: ${types.join(", ")}, ${ext.type}` ); } else { types.push(ext.type); } } return { ...ext, ...options, type: applyTypeAdapters(ext?.type ?? options.type) }; } // src/operations/domains/alterDomain.ts function alterDomain(mOptions) { const _alter = (domainName, options) => { const { default: defaultValue, notNull, allowNull = false, check, constraintName } = options; const actions = []; if (defaultValue === null) { actions.push("DROP DEFAULT"); } else if (defaultValue !== void 0) { actions.push(`SET DEFAULT ${escapeValue(defaultValue)}`); } if (notNull) { actions.push("SET NOT NULL"); } else if (notNull === false || allowNull) { actions.push("DROP NOT NULL"); } if (check) { actions.push( `${constraintName ? `CONSTRAINT ${mOptions.literal(constraintName)} ` : ""}CHECK (${check})` ); } return `${actions.map((action) => `ALTER DOMAIN ${mOptions.literal(domainName)} ${action}`).join(";\n")};`; }; return _alter; } // src/operations/domains/dropDomain.ts function dropDomain(mOptions) { const _drop = (domainName, options = {}) => { const { ifExists = false, cascade = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; const domainNameStr = mOptions.literal(domainName); return `DROP DOMAIN${ifExistsStr} ${domainNameStr}${cascadeStr};`; }; return _drop; } // src/operations/domains/createDomain.ts function createDomain(mOptions) { const _create = (domainName, type, options = {}) => { const { default: defaultValue, collation, notNull = false, check, constraintName } = options; const constraints = []; if (collation) { constraints.push(`COLLATE ${collation}`); } if (defaultValue !== void 0) { constraints.push(`DEFAULT ${escapeValue(defaultValue)}`); } if (notNull && check) { throw new Error(`"notNull" and "check" can't be specified together`); } else if (notNull || check) { if (constraintName) { constraints.push(`CONSTRAINT ${mOptions.literal(constraintName)}`); } if (notNull) { constraints.push("NOT NULL"); } else if (check) { constraints.push(`CHECK (${check})`); } } const constraintsStr = constraints.length > 0 ? ` ${constraints.join(" ")}` : ""; const typeStr = applyType(type, mOptions.typeShorthands).type; const domainNameStr = mOptions.literal(domainName); return `CREATE DOMAIN ${domainNameStr} AS ${typeStr}${constraintsStr};`; }; _create.reverse = (domainName, type, options) => dropDomain(mOptions)(domainName, options); return _create; } // src/operations/domains/renameDomain.ts function renameDomain(mOptions) { const _rename = (domainName, newDomainName) => { const domainNameStr = mOptions.literal(domainName); const newDomainNameStr = mOptions.literal(newDomainName); return `ALTER DOMAIN ${domainNameStr} RENAME TO ${newDomainNameStr};`; }; _rename.reverse = (domainName, newDomainName) => _rename(newDomainName, domainName); return _rename; } // src/operations/extensions/dropExtension.ts function dropExtension(mOptions) { const _drop = (_extensions, options = {}) => { const { ifExists = false, cascade = false } = options; const extensions = toArray(_extensions); const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; return extensions.map((extension) => { const extensionStr = mOptions.literal(extension); return `DROP EXTENSION${ifExistsStr} ${extensionStr}${cascadeStr};`; }); }; return _drop; } // src/operations/extensions/createExtension.ts function createExtension(mOptions) { const _create = (_extensions, options = {}) => { const { ifNotExists = false, schema } = options; const extensions = toArray(_extensions); const ifNotExistsStr = ifNotExists ? " IF NOT EXISTS" : ""; const schemaStr = schema ? ` SCHEMA ${mOptions.literal(schema)}` : ""; return extensions.map((extension) => { const extensionStr = mOptions.literal(extension); return `CREATE EXTENSION${ifNotExistsStr} ${extensionStr}${schemaStr};`; }); }; _create.reverse = dropExtension(mOptions); return _create; } // src/operations/functions/dropFunction.ts function dropFunction(mOptions) { const _drop = (functionName, functionParams = [], options = {}) => { const { ifExists = false, cascade = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; const paramsStr = formatParams(functionParams, mOptions); const functionNameStr = mOptions.literal(functionName); return `DROP FUNCTION${ifExistsStr} ${functionNameStr}${paramsStr}${cascadeStr};`; }; return _drop; } // src/operations/functions/createFunction.ts function createFunction(mOptions) { const _create = (functionName, functionParams = [], functionOptions, definition) => { const { replace = false, returns = "void", language, window = false, behavior = "VOLATILE", security = "INVOKER", onNull = false, parallel, set } = functionOptions; const options = []; if (behavior) { options.push(behavior); } if (language) { options.push(`LANGUAGE ${language}`); } else { throw new Error( `Language for function ${functionName} have to be specified` ); } if (security !== "INVOKER") { options.push(`SECURITY ${security}`); } if (window) { options.push("WINDOW"); } if (onNull) { options.push("RETURNS NULL ON NULL INPUT"); } if (parallel) { options.push(`PARALLEL ${parallel}`); } if (set) { for (const { configurationParameter, value } of set) { if (value === "FROM CURRENT") { options.push( `SET ${mOptions.literal(configurationParameter)} FROM CURRENT` ); } else { options.push( `SET ${mOptions.literal(configurationParameter)} TO ${value}` ); } } } const replaceStr = replace ? " OR REPLACE" : ""; const paramsStr = formatParams(functionParams, mOptions); const functionNameStr = mOptions.literal(functionName); return `CREATE${replaceStr} FUNCTION ${functionNameStr}${paramsStr} RETURNS ${returns} AS ${escapeValue(definition)} ${options.join("\n ")};`; }; _create.reverse = dropFunction(mOptions); return _create; } // src/operations/functions/renameFunction.ts function renameFunction(mOptions) { const _rename = (oldFunctionName, functionParams = [], newFunctionName) => { const paramsStr = formatParams(functionParams, mOptions); const oldFunctionNameStr = mOptions.literal(oldFunctionName); const newFunctionNameStr = mOptions.literal(newFunctionName); return `ALTER FUNCTION ${oldFunctionNameStr}${paramsStr} RENAME TO ${newFunctionNameStr};`; }; _rename.reverse = (oldFunctionName, functionParams, newFunctionName) => _rename(newFunctionName, functionParams, oldFunctionName); return _rename; } // src/operations/grants/shared.ts function isAllTablesOptions(options) { return "schema" in options; } function asRolesStr(roles, mOptions) { return toArray(roles).map((role) => role === "PUBLIC" ? role : mOptions.literal(role)).join(", "); } function asTablesStr(options, mOptions) { return isAllTablesOptions(options) ? `ALL TABLES IN SCHEMA ${mOptions.literal(options.schema)}` : toArray(options.tables).map(mOptions.literal).join(", "); } // src/operations/grants/revokeOnSchemas.ts function revokeOnSchemas(mOptions) { const _revokeOnSchemas = (options) => { const { privileges, schemas, roles, onlyGrantOption = false, cascade = false } = options; const rolesStr = asRolesStr(roles, mOptions); const schemasStr = toArray(schemas).map(mOptions.literal).join(", "); const privilegesStr = toArray(privileges).map(String).join(", "); const onlyGrantOptionStr = onlyGrantOption ? " GRANT OPTION FOR" : ""; const cascadeStr = cascade ? " CASCADE" : ""; return `REVOKE${onlyGrantOptionStr} ${privilegesStr} ON SCHEMA ${schemasStr} FROM ${rolesStr}${cascadeStr};`; }; return _revokeOnSchemas; } // src/operations/grants/grantOnSchemas.ts function grantOnSchemas(mOptions) { const _grantOnSchemas = (options) => { const { privileges, schemas, roles, withGrantOption = false } = options; const rolesStr = asRolesStr(roles, mOptions); const schemasStr = toArray(schemas).map(mOptions.literal).join(", "); const privilegesStr = toArray(privileges).map(String).join(", "); const withGrantOptionStr = withGrantOption ? " WITH GRANT OPTION" : ""; return `GRANT ${privilegesStr} ON SCHEMA ${schemasStr} TO ${rolesStr}${withGrantOptionStr};`; }; _grantOnSchemas.reverse = revokeOnSchemas(mOptions); return _grantOnSchemas; } // src/operations/grants/revokeOnTables.ts function revokeOnTables(mOptions) { const _revokeOnTables = (options) => { const { privileges, roles, onlyGrantOption = false, cascade = false } = options; const rolesStr = asRolesStr(roles, mOptions); const privilegesStr = toArray(privileges).map(String).join(", "); const tablesStr = asTablesStr(options, mOptions); const onlyGrantOptionStr = onlyGrantOption ? " GRANT OPTION FOR" : ""; const cascadeStr = cascade ? " CASCADE" : ""; return `REVOKE${onlyGrantOptionStr} ${privilegesStr} ON ${tablesStr} FROM ${rolesStr}${cascadeStr};`; }; return _revokeOnTables; } // src/operations/grants/grantOnTables.ts function grantOnTables(mOptions) { const _grantOnTables = (options) => { const { privileges, roles, withGrantOption = false } = options; const rolesStr = asRolesStr(roles, mOptions); const privilegesStr = toArray(privileges).map(String).join(", "); const tablesStr = asTablesStr(options, mOptions); const withGrantOptionStr = withGrantOption ? " WITH GRANT OPTION" : ""; return `GRANT ${privilegesStr} ON ${tablesStr} TO ${rolesStr}${withGrantOptionStr};`; }; _grantOnTables.reverse = revokeOnTables(mOptions); return _grantOnTables; } // src/operations/grants/revokeRoles.ts function revokeRoles(mOptions) { const _revokeRoles = (roles, rolesFrom, options = {}) => { const { onlyAdminOption = false, cascade = false } = options; const rolesStr = toArray(roles).map(mOptions.literal).join(", "); const rolesToStr = toArray(rolesFrom).map(mOptions.literal).join(", "); const onlyAdminOptionStr = onlyAdminOption ? " ADMIN OPTION FOR" : ""; const cascadeStr = cascade ? " CASCADE" : ""; return `REVOKE${onlyAdminOptionStr} ${rolesStr} FROM ${rolesToStr}${cascadeStr};`; }; return _revokeRoles; } // src/operations/grants/grantRoles.ts function grantRoles(mOptions) { const _grantRoles = (rolesFrom, rolesTo, options = {}) => { const { withAdminOption = false } = options; const rolesFromStr = toArray(rolesFrom).map(mOptions.literal).join(", "); const rolesToStr = toArray(rolesTo).map(mOptions.literal).join(", "); const withAdminOptionStr = withAdminOption ? " WITH ADMIN OPTION" : ""; return `GRANT ${rolesFromStr} TO ${rolesToStr}${withAdminOptionStr};`; }; _grantRoles.reverse = revokeRoles(mOptions); return _grantRoles; } // src/operations/indexes/shared.ts function generateIndexName(table, columns, options, schemalize) { if (options.name) { return typeof table === "object" ? { schema: table.schema, name: options.name } : options.name; } const cols = columns.map((col) => schemalize(typeof col === "string" ? col : col.name)).join("_"); const uniq = "unique" in options && options.unique ? "_unique" : ""; return typeof table === "object" ? { schema: table.schema, name: `${table.name}_${cols}${uniq}_index` } : `${table}_${cols}${uniq}_index`; } function generateColumnString(column, mOptions) { const name = mOptions.schemalize(column); const isSpecial = /[ ().]/.test(name); return isSpecial ? name : mOptions.literal(name); } function generateColumnsString(columns, mOptions) { return columns.map( (column) => typeof column === "string" ? generateColumnString(column, mOptions) : [ generateColumnString(column.name, mOptions), column.opclass ? mOptions.literal(column.opclass) : void 0, column.sort ].filter((s) => typeof s === "string" && s !== "").join(" ") ).join(", "); } // src/operations/indexes/dropIndex.ts function dropIndex(mOptions) { const _drop = (tableName, rawColumns, options = {}) => { const { concurrently = false, ifExists = false, cascade = false } = options; const columns = toArray(rawColumns); const concurrentlyStr = concurrently ? " CONCURRENTLY" : ""; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const indexName = generateIndexName( tableName, columns, options, mOptions.schemalize ); const cascadeStr = cascade ? " CASCADE" : ""; const indexNameStr = mOptions.literal(indexName); return `DROP INDEX${concurrentlyStr}${ifExistsStr} ${indexNameStr}${cascadeStr};`; }; return _drop; } // src/operations/indexes/createIndex.ts function createIndex(mOptions) { const _create = (tableName, rawColumns, options = {}) => { const { unique = false, concurrently = false, ifNotExists = false, method, where, include } = options; const columns = toArray(rawColumns); const indexName = generateIndexName( typeof tableName === "object" ? tableName.name : tableName, columns, options, mOptions.schemalize ); const columnsString = generateColumnsString(columns, mOptions); const uniqueStr = unique ? " UNIQUE" : ""; const concurrentlyStr = concurrently ? " CONCURRENTLY" : ""; const ifNotExistsStr = ifNotExists ? " IF NOT EXISTS" : ""; const methodStr = method ? ` USING ${method}` : ""; const whereStr = where ? ` WHERE ${where}` : ""; const includeStr = include ? ` INCLUDE (${toArray(include).map(mOptions.literal).join(", ")})` : ""; const indexNameStr = mOptions.literal(indexName); const tableNameStr = mOptions.literal(tableName); return `CREATE${uniqueStr} INDEX${concurrentlyStr}${ifNotExistsStr} ${indexNameStr} ON ${tableNameStr}${methodStr} (${columnsString})${includeStr}${whereStr};`; }; _create.reverse = dropIndex(mOptions); return _create; } // src/operations/materializedViews/shared.ts function dataClause(data) { return data === void 0 ? "" : ` WITH${data ? "" : " NO"} DATA`; } function storageParameterStr(storageParameters) { return (key) => { const value = storageParameters[key] === true ? "" : ` = ${storageParameters[key]}`; return `${key}${value}`; }; } // src/operations/materializedViews/alterMaterializedView.ts function alterMaterializedView(mOptions) { const _alter = (viewName, options) => { const { cluster, extension, storageParameters = {} } = options; const clauses = []; if (cluster !== void 0) { if (cluster) { clauses.push(`CLUSTER ON ${mOptions.literal(cluster)}`); } else { clauses.push("SET WITHOUT CLUSTER"); } } if (extension) { clauses.push(`DEPENDS ON EXTENSION ${mOptions.literal(extension)}`); } const withOptions = Object.keys(storageParameters).filter((key) => storageParameters[key] !== null).map(storageParameterStr(storageParameters)).join(", "); if (withOptions) { clauses.push(`SET (${withOptions})`); } const resetOptions = Object.keys(storageParameters).filter((key) => storageParameters[key] === null).join(", "); if (resetOptions) { clauses.push(`RESET (${resetOptions})`); } const clausesStr = formatLines(clauses); const viewNameStr = mOptions.literal(viewName); return `ALTER MATERIALIZED VIEW ${viewNameStr} ${clausesStr};`; }; return _alter; } // src/operations/materializedViews/dropMaterializedView.ts function dropMaterializedView(mOptions) { const _drop = (viewName, options = {}) => { const { ifExists = false, cascade = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; const viewNameStr = mOptions.literal(viewName); return `DROP MATERIALIZED VIEW${ifExistsStr} ${viewNameStr}${cascadeStr};`; }; return _drop; } // src/operations/materializedViews/createMaterializedView.ts function createMaterializedView(mOptions) { const _create = (viewName, options, definition) => { const { ifNotExists = false, columns = [], tablespace, storageParameters = {}, data } = options; const columnNames = toArray(columns).map(mOptions.literal).join(", "); const withOptions = Object.keys(storageParameters).map(storageParameterStr(storageParameters)).join(", "); const ifNotExistsStr = ifNotExists ? " IF NOT EXISTS" : ""; const columnsStr = columnNames ? `(${columnNames})` : ""; const withOptionsStr = withOptions ? ` WITH (${withOptions})` : ""; const tablespaceStr = tablespace ? ` TABLESPACE ${mOptions.literal(tablespace)}` : ""; const dataStr = dataClause(data); const viewNameStr = mOptions.literal(viewName); return `CREATE MATERIALIZED VIEW${ifNotExistsStr} ${viewNameStr}${columnsStr}${withOptionsStr}${tablespaceStr} AS ${definition}${dataStr};`; }; _create.reverse = dropMaterializedView(mOptions); return _create; } // src/operations/materializedViews/refreshMaterializedView.ts function refreshMaterializedView(mOptions) { const _refresh = (viewName, options = {}) => { const { concurrently = false, data } = options; const concurrentlyStr = concurrently ? " CONCURRENTLY" : ""; const dataStr = dataClause(data); const viewNameStr = mOptions.literal(viewName); return `REFRESH MATERIALIZED VIEW${concurrentlyStr} ${viewNameStr}${dataStr};`; }; _refresh.reverse = _refresh; return _refresh; } // src/operations/materializedViews/renameMaterializedView.ts function renameMaterializedView(mOptions) { const _rename = (viewName, newViewName) => { const viewNameStr = mOptions.literal(viewName); const newViewNameStr = mOptions.literal(newViewName); return `ALTER MATERIALIZED VIEW ${viewNameStr} RENAME TO ${newViewNameStr};`; }; _rename.reverse = (viewName, newViewName) => _rename(newViewName, viewName); return _rename; } // src/operations/materializedViews/renameMaterializedViewColumn.ts function renameMaterializedViewColumn(mOptions) { const _rename = (viewName, columnName, newColumnName) => { const viewNameStr = mOptions.literal(viewName); const columnNameStr = mOptions.literal(columnName); const newColumnNameStr = mOptions.literal(newColumnName); return `ALTER MATERIALIZED VIEW ${viewNameStr} RENAME COLUMN ${columnNameStr} TO ${newColumnNameStr};`; }; _rename.reverse = (viewName, columnName, newColumnName) => _rename(viewName, newColumnName, columnName); return _rename; } // src/operations/operators/shared.ts function operatorMap(mOptions) { return ({ type, number, name, params = [] }) => { const nameStr = mOptions.literal(name); if (String(type).toLowerCase() === "operator") { if (params.length > 2) { throw new Error("Operator can't have more than 2 parameters"); } const paramsStr = params.length > 0 ? formatParams(params, mOptions) : ""; return `OPERATOR ${number} ${nameStr}${paramsStr}`; } if (String(type).toLowerCase() === "function") { const paramsStr = formatParams(params, mOptions); return `FUNCTION ${number} ${nameStr}${paramsStr}`; } throw new Error('Operator "type" must be either "function" or "operator"'); }; } // src/operations/operators/removeFromOperatorFamily.ts var removeFromOperatorFamily = (mOptions) => { const method = (operatorFamilyName, indexMethod, operatorList) => { const operatorFamilyNameStr = mOptions.literal(operatorFamilyName); const operatorListStr = operatorList.map(operatorMap(mOptions)).join(",\n "); return `ALTER OPERATOR FAMILY ${operatorFamilyNameStr} USING ${indexMethod} DROP ${operatorListStr};`; }; return method; }; // src/operations/operators/addToOperatorFamily.ts var addToOperatorFamily = (mOptions) => { const method = (operatorFamilyName, indexMethod, operatorList) => { const operatorFamilyNameStr = mOptions.literal(operatorFamilyName); const operatorListStr = operatorList.map(operatorMap(mOptions)).join(",\n "); return `ALTER OPERATOR FAMILY ${operatorFamilyNameStr} USING ${indexMethod} ADD ${operatorListStr};`; }; method.reverse = removeFromOperatorFamily(mOptions); return method; }; // src/operations/operators/dropOperator.ts function dropOperator(mOptions) { const _drop = (operatorName, options = {}) => { const { left = "none", right = "none", ifExists = false, cascade = false } = options; const operatorNameStr = mOptions.schemalize(operatorName); const leftStr = mOptions.literal(left); const rightStr = mOptions.literal(right); const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; return `DROP OPERATOR${ifExistsStr} ${operatorNameStr}(${leftStr}, ${rightStr})${cascadeStr};`; }; return _drop; } // src/operations/operators/createOperator.ts function createOperator(mOptions) { const _create = (operatorName, options) => { const { procedure, left, right, commutator, negator, restrict, join: join2, hashes = false, merges = false } = options; const defs = []; defs.push(`PROCEDURE = ${mOptions.literal(procedure)}`); if (left) { defs.push(`LEFTARG = ${mOptions.literal(left)}`); } if (right) { defs.push(`RIGHTARG = ${mOptions.literal(right)}`); } if (commutator) { defs.push(`COMMUTATOR = ${mOptions.schemalize(commutator)}`); } if (negator) { defs.push(`NEGATOR = ${mOptions.schemalize(negator)}`); } if (restrict) { defs.push(`RESTRICT = ${mOptions.literal(restrict)}`); } if (join2) { defs.push(`JOIN = ${mOptions.literal(join2)}`); } if (hashes) { defs.push("HASHES"); } if (merges) { defs.push("MERGES"); } const operatorNameStr = mOptions.schemalize(operatorName); return `CREATE OPERATOR ${operatorNameStr} (${defs.join(", ")});`; }; _create.reverse = dropOperator(mOptions); return _create; } // src/operations/operators/dropOperatorClass.ts function dropOperatorClass(mOptions) { const _drop = (operatorClassName, indexMethod, options = {}) => { const { ifExists = false, cascade = false } = options; const operatorClassNameStr = mOptions.literal(operatorClassName); const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; return `DROP OPERATOR CLASS${ifExistsStr} ${operatorClassNameStr} USING ${indexMethod}${cascadeStr};`; }; return _drop; } // src/operations/operators/createOperatorClass.ts function createOperatorClass(mOptions) { const _create = (operatorClassName, type, indexMethod, operatorList, options) => { const { default: isDefault, family } = options; const operatorClassNameStr = mOptions.literal(operatorClassName); const defaultStr = isDefault ? " DEFAULT" : ""; const typeStr = mOptions.literal(applyType(type).type); const indexMethodStr = mOptions.literal(indexMethod); const familyStr = family ? ` FAMILY ${family}` : ""; const operatorListStr = operatorList.map(operatorMap(mOptions)).join(",\n "); return `CREATE OPERATOR CLASS ${operatorClassNameStr}${defaultStr} FOR TYPE ${typeStr} USING ${indexMethodStr}${familyStr} AS ${operatorListStr};`; }; _create.reverse = (operatorClassName, type, indexMethod, operatorList, options) => dropOperatorClass(mOptions)(operatorClassName, indexMethod, options); return _create; } // src/operations/operators/dropOperatorFamily.ts function dropOperatorFamily(mOptions) { const _drop = (operatorFamilyName, indexMethod, options = {}) => { const { ifExists = false, cascade = false } = options; const operatorFamilyNameStr = mOptions.literal(operatorFamilyName); const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; return `DROP OPERATOR FAMILY${ifExistsStr} ${operatorFamilyNameStr} USING ${indexMethod}${cascadeStr};`; }; return _drop; } // src/operations/operators/createOperatorFamily.ts function createOperatorFamily(mOptions) { const _create = (operatorFamilyName, indexMethod) => { const operatorFamilyNameStr = mOptions.literal(operatorFamilyName); return `CREATE OPERATOR FAMILY ${operatorFamilyNameStr} USING ${indexMethod};`; }; _create.reverse = dropOperatorFamily(mOptions); return _create; } // src/operations/operators/renameOperatorClass.ts function renameOperatorClass(mOptions) { const _rename = (oldOperatorClassName, indexMethod, newOperatorClassName) => { const oldOperatorClassNameStr = mOptions.literal(oldOperatorClassName); const newOperatorClassNameStr = mOptions.literal(newOperatorClassName); return `ALTER OPERATOR CLASS ${oldOperatorClassNameStr} USING ${indexMethod} RENAME TO ${newOperatorClassNameStr};`; }; _rename.reverse = (oldOperatorClassName, indexMethod, newOperatorClassName) => _rename(newOperatorClassName, indexMethod, oldOperatorClassName); return _rename; } // src/operations/operators/renameOperatorFamily.ts function renameOperatorFamily(mOptions) { const _rename = (oldOperatorFamilyName, indexMethod, newOperatorFamilyName) => { const oldOperatorFamilyNameStr = mOptions.literal(oldOperatorFamilyName); const newOperatorFamilyNameStr = mOptions.literal(newOperatorFamilyName); return `ALTER OPERATOR FAMILY ${oldOperatorFamilyNameStr} USING ${indexMethod} RENAME TO ${newOperatorFamilyNameStr};`; }; _rename.reverse = (oldOperatorFamilyName, indexMethod, newOperatorFamilyName) => _rename(newOperatorFamilyName, indexMethod, oldOperatorFamilyName); return _rename; } // src/operations/policies/shared.ts function makeClauses({ role, using, check }) { const roles = toArray(role).join(", "); const clauses = []; if (roles) { clauses.push(`TO ${roles}`); } if (using) { clauses.push(`USING (${using})`); } if (check) { clauses.push(`WITH CHECK (${check})`); } return clauses; } // src/operations/policies/alterPolicy.ts function alterPolicy(mOptions) { const _alter = (tableName, policyName, options = {}) => { const clausesStr = makeClauses(options).join(" "); const policyNameStr = mOptions.literal(policyName); const tableNameStr = mOptions.literal(tableName); return `ALTER POLICY ${policyNameStr} ON ${tableNameStr} ${clausesStr};`; }; return _alter; } // src/operations/policies/dropPolicy.ts function dropPolicy(mOptions) { const _drop = (tableName, policyName, options = {}) => { const { ifExists = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const policyNameStr = mOptions.literal(policyName); const tableNameStr = mOptions.literal(tableName); return `DROP POLICY${ifExistsStr} ${policyNameStr} ON ${tableNameStr};`; }; return _drop; } // src/operations/policies/createPolicy.ts function createPolicy(mOptions) { const _create = (tableName, policyName, options = {}) => { const { role = "PUBLIC", command = "ALL" } = options; const createOptions = { ...options, role }; const clauses = [`FOR ${command}`, ...makeClauses(createOptions)]; const clausesStr = clauses.join(" "); const policyNameStr = mOptions.literal(policyName); const tableNameStr = mOptions.literal(tableName); return `CREATE POLICY ${policyNameStr} ON ${tableNameStr} ${clausesStr};`; }; _create.reverse = dropPolicy(mOptions); return _create; } // src/operations/policies/renamePolicy.ts function renamePolicy(mOptions) { const _rename = (tableName, policyName, newPolicyName) => { const policyNameStr = mOptions.literal(policyName); const newPolicyNameStr = mOptions.literal(newPolicyName); const tableNameStr = mOptions.literal(tableName); return `ALTER POLICY ${policyNameStr} ON ${tableNameStr} RENAME TO ${newPolicyNameStr};`; }; _rename.reverse = (tableName, policyName, newPolicyName) => _rename(tableName, newPolicyName, policyName); return _rename; } // src/operations/roles/shared.ts function formatRoleOptions(roleOptions = {}) { const options = []; if (roleOptions.superuser !== void 0) { options.push(roleOptions.superuser ? "SUPERUSER" : "NOSUPERUSER"); } if (roleOptions.createdb !== void 0) { options.push(roleOptions.createdb ? "CREATEDB" : "NOCREATEDB"); } if (roleOptions.createrole !== void 0) { options.push(roleOptions.createrole ? "CREATEROLE" : "NOCREATEROLE"); } if (roleOptions.inherit !== void 0) { options.push(roleOptions.inherit ? "INHERIT" : "NOINHERIT"); } if (roleOptions.login !== void 0) { options.push(roleOptions.login ? "LOGIN" : "NOLOGIN"); } if (roleOptions.replication !== void 0) { options.push(roleOptions.replication ? "REPLICATION" : "NOREPLICATION"); } if (roleOptions.bypassrls !== void 0) { options.push(roleOptions.bypassrls ? "BYPASSRLS" : "NOBYPASSRLS"); } if (roleOptions.limit) { options.push(`CONNECTION LIMIT ${Number(roleOptions.limit)}`); } if (roleOptions.password !== void 0) { const encrypted = roleOptions.encrypted === false ? "UNENCRYPTED" : "ENCRYPTED"; options.push(`${encrypted} PASSWORD ${escapeValue(roleOptions.password)}`); } if (roleOptions.valid !== void 0) { const valid = roleOptions.valid ? escapeValue(roleOptions.valid) : "'infinity'"; options.push(`VALID UNTIL ${valid}`); } if (roleOptions.inRole) { const inRole = toArray(roleOptions.inRole).join(", "); options.push(`IN ROLE ${inRole}`); } if (roleOptions.role) { const role = toArray(roleOptions.role).join(", "); options.push(`ROLE ${role}`); } if (roleOptions.admin) { const admin = toArray(roleOptions.admin).join(", "); options.push(`ADMIN ${admin}`); } return options.join(" "); } // src/operations/roles/alterRole.ts function alterRole(mOptions) { const _alter = (roleName, roleOptions = {}) => { const options = formatRoleOptions(roleOptions); return options ? `ALTER ROLE ${mOptions.literal(roleName)} WITH ${options};` : ""; }; return _alter; } // src/operations/roles/dropRole.ts function dropRole(mOptions) { const _drop = (roleName, options = {}) => { const { ifExists = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const roleNameStr = mOptions.literal(roleName); return `DROP ROLE${ifExistsStr} ${roleNameStr};`; }; return _drop; } // src/operations/roles/createRole.ts function createRole(mOptions) { const _create = (roleName, roleOptions = {}) => { const options = formatRoleOptions({ ...roleOptions, superuser: roleOptions.superuser || false, createdb: roleOptions.createdb || false, createrole: roleOptions.createrole || false, inherit: roleOptions.inherit !== false, login: roleOptions.login || false, replication: roleOptions.replication || false }); const optionsStr = options ? ` WITH ${options}` : ""; return `CREATE ROLE ${mOptions.literal(roleName)}${optionsStr};`; }; _create.reverse = dropRole(mOptions); return _create; } // src/operations/roles/renameRole.ts function renameRole(mOptions) { const _rename = (oldRoleName, newRoleName) => { const oldRoleNameStr = mOptions.literal(oldRoleName); const newRoleNameStr = mOptions.literal(newRoleName); return `ALTER ROLE ${oldRoleNameStr} RENAME TO ${newRoleNameStr};`; }; _rename.reverse = (oldRoleName, newRoleName) => _rename(newRoleName, oldRoleName); return _rename; } // src/operations/schemas/dropSchema.ts function dropSchema(mOptions) { const _drop = (schemaName, options = {}) => { const { ifExists = false, cascade = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; const schemaNameStr = mOptions.literal(schemaName); return `DROP SCHEMA${ifExistsStr} ${schemaNameStr}${cascadeStr};`; }; return _drop; } // src/operations/schemas/createSchema.ts function createSchema(mOptions) { const _create = (schemaName, options = {}) => { const { ifNotExists = false, authorization } = options; const ifNotExistsStr = ifNotExists ? " IF NOT EXISTS" : ""; const schemaNameStr = mOptions.literal(schemaName); const authorizationStr = authorization ? ` AUTHORIZATION ${authorization}` : ""; return `CREATE SCHEMA${ifNotExistsStr} ${schemaNameStr}${authorizationStr};`; }; _create.reverse = dropSchema(mOptions); return _create; } // src/operations/schemas/renameSchema.ts function renameSchema(mOptions) { const _rename = (schemaName, newSchemaName) => { const schemaNameStr = mOptions.literal(schemaName); const newSchemaNameStr = mOptions.literal(newSchemaName); return `ALTER SCHEMA ${schemaNameStr} RENAME TO ${newSchemaNameStr};`; }; _rename.reverse = (schemaName, newSchemaName) => _rename(newSchemaName, schemaName); return _rename; } // src/operations/sequences/shared.ts function parseSequenceOptions(typeShorthands, options) { const { type, increment, minvalue, maxvalue, start, cache, cycle, owner } = options; const clauses = []; if (type) { clauses.push(`AS ${applyType(type, typeShorthands).type}`); } if (increment) { clauses.push(`INCREMENT BY ${increment}`); } if (minvalue) { clauses.push(`MINVALUE ${minvalue}`); } else if (minvalue === null || minvalue === false) { clauses.push("NO MINVALUE"); } if (maxvalue) { clauses.push(`MAXVALUE ${maxvalue}`); } else if (maxvalue === null || maxvalue === false) { clauses.push("NO MAXVALUE"); } if (start) { clauses.push(`START WITH ${start}`); } if (cache) { clauses.push(`CACHE ${cache}`); } if (cycle) { clauses.push("CYCLE"); } else if (cycle === false) { clauses.push("NO CYCLE"); } if (owner) { clauses.push(`OWNED BY ${owner}`); } else if (owner === null || owner === false) { clauses.push("OWNED BY NONE"); } return clauses; } // src/operations/sequences/alterSequence.ts function alterSequence(mOptions) { return (sequenceName, options) => { const { restart } = options; const clauses = parseSequenceOptions(mOptions.typeShorthands, options); if (restart) { if (restart === true) { clauses.push("RESTART"); } else { clauses.push(`RESTART WITH ${restart}`); } } return `ALTER SEQUENCE ${mOptions.literal(sequenceName)} ${clauses.join("\n ")};`; }; } // src/operations/sequences/dropSequence.ts function dropSequence(mOptions) { const _drop = (sequenceName, options = {}) => { const { ifExists = false, cascade = false } = options; const ifExistsStr = ifExists ? " IF EXISTS" : ""; const cascadeStr = cascade ? " CASCADE" : ""; const sequenceNameStr = mOptions.literal(sequenceName); return `DROP SEQUENCE${ifExistsStr} ${sequenceNameStr}${cascadeStr};`; }; return _drop; } // src/operations/sequences/createSequence.ts function createSequence(mOptions) { const _create = (sequenceName, options = {}) => { const { temporary = false, ifNotExists = false } = options; const temporaryStr = temporary ? " TEMPORARY" : ""; const ifNotExistsStr = ifNotExists ? " IF NOT EXISTS" : ""; const sequenceNameStr = mOptions.literal(sequenceName); const clausesStr = parseSequenceOptions( mOptions.typeShorthands, options ).join("\n "); return `CREATE${temporaryStr} SEQUENCE${ifNotExistsStr} ${sequenceNameStr} ${clausesStr};`; }; _create.reverse = dropSequence(mOptions); return _create; } // src/operations/sequences/renameSequence.ts function renameSequence(mOptions) { const _rename = (sequenceName, newSequenceName) => { const sequenceNameStr = mOptions.literal(sequenceName); const newSequenceNameStr = mOptions.literal(newSequenceName); return `ALTER SEQUENCE ${sequenceNameStr} RENAME TO ${newSequenceNameStr};`; }; _rename.reverse = (sequenceName, newSequenceName) => _rename(newSequenceName, sequenceName); return _rename; } // src/operations/sql.ts function sql(mOptions) { const t = createTransformer(mOptions.literal); return (sqlStr, args) => { let statement = t(sqlStr, args); if (statement.lastIndexOf(";") !== statement.length - 1) { statement += ";"; } return statement; }; } // src/operations/tables/dropColumns.ts function dropColumns(mOptions) { const _drop = (tableName, columns, options = {}) => { const { ifExists = false, cascade = false } = options; if (typeof columns === "string") { columns = [columns]; } else if (!Array.isArray(columns) && typeof columns === "object") { columns = Object.keys(columns); } const ifExistsStr = ifExists ? "IF EXISTS " : ""; const cascadeStr = cascade ? " CASCADE" : ""; const lines = columns.map(mOptions.literal).map((column) => `DROP ${ifExistsStr}${column}${cascadeStr}`); return `ALTER TABLE ${mOptions.literal(tableName)} ${formatLines(lines)};`; }; return _drop; } // src/operations/tables/shared.ts 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}`; }), constr