tspace-mysql
Version:
Tspace MySQL is a promise-based ORM for Node.js, designed with modern TypeScript and providing type safety for schema databases.
243 lines • 8.69 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AlterTable = void 0;
/**
* Builder class for altering an existing database table.
*
* This class collects schema modification operations (primary key,
* unique constraints, indexes, etc.) and executes them sequentially
* when {@link run} is called.
*
* @template M - The model type extending Model.
*/
class AlterTable {
model;
table;
queries = [];
/**
* Creates a new AlterTable builder.
*
* @param {new () => M} model - The model constructor used to resolve the table.
*/
constructor(model) {
this.model = new model().disabledRetry();
this.table = this.model.getTableName();
}
/**
* Dumps debugging information from the underlying model.
*
* @returns {this} Returns the current builder instance for chaining.
*/
dd() {
this.model.dd();
return this;
}
/**
* Adds a primary key constraint to the table.
*
* @template C
* @param {C} columns - Column names that will form the primary key.
* @returns {this} Returns the current builder instance for chaining.
*/
addPrimary(columns) {
this.queries.push(() => {
return this.model.addPrimaryKey({ columns, table: this.table });
});
return this;
}
/**
* Drops the primary key constraint from the table.
*
* @returns {this} Returns the current builder instance for chaining.
*/
dropPrimary() {
this.queries.push(() => {
return this.model.dropPrimaryKey({ table: this.table });
});
return this;
}
/**
* Adds a unique constraint to the table.
*
* If no name is provided, a default constraint name will be generated
* in the format: `ux_{table}_{column1_column2...}`.
*
* @template C
* @param {C} columns - Column names included in the unique constraint.
* @param {string} [name=""] - Optional unique constraint name.
* @returns {this} Returns the current builder instance for chaining.
*/
addUnique(columns, name = "") {
const n = name || `ux_${this.table}_${columns.join("_")}`;
this.queries.push(() => {
return this.model.addUnique({ columns, name: n, table: this.table });
});
return this;
}
/**
* Drops a unique constraint from the table.
*
* @param {string} name - The name of the unique constraint to drop.
* @returns {this} Returns the current builder instance for chaining.
*/
dropUnique(name) {
this.queries.push(() => {
return this.model.dropUnique({ name, table: this.table });
});
return this;
}
/**
* Adds an index to the table.
*
* If no name is provided, a default index name will be generated
* in the format: `idx_{table}_{column1_column2...}`.
*
* @template C
* @param {C} columns - Column names included in the index.
* @param {string} [name=""] - Optional index name.
* @returns {this} Returns the current builder instance for chaining.
*/
addIndex(columns, name = "") {
const n = name || `idx_${this.table}_${columns.join("_")}`;
this.queries.push(() => {
return this.model.addIndex({ columns, name: n, table: this.table });
});
return this;
}
/**
* Drops an index from the table.
*
* @param {string} name - The name of the index to drop.
* @returns {this} Returns the current builder instance for chaining.
*/
dropIndex(name) {
this.queries.push(() => {
return this.model.dropIndex({ name, table: this.table });
});
return this;
}
/**
* Adds a foreign key constraint to the table.
*
* This method defines a relationship between a column in the current table
* and a referenced column in another table. If no constraint name is provided,
* a deterministic name will be automatically generated based on the table
* and column names.
*
* The generated constraint name follows the format:
* `fk_{table(column)}_{referencedTable(referencedColumn)}`.
* If the generated name exceeds the database identifier length limit,
* a shortened version with a hash suffix will be used.
*
* @param {Object} params - Foreign key configuration.
* @param {string} params.column - The column in the current table that will act as the foreign key.
* @param {string} [params.constraint] - Optional constraint of the foreign key constraint.
* @param {string} [params.references] - The referenced column in the foreign table. Defaults to `"id"`.
* @param {(new () => Model) | string} params.on - The referenced table. This can be a Model constructor or a table name string.
* @param {"CASCADE" | "NO ACTION" | "RESTRICT" | "SET NULL"} params.onDelete - Action executed when the referenced row is deleted.
* @param {"CASCADE" | "NO ACTION" | "RESTRICT" | "SET NULL"} params.onUpdate - Action executed when the referenced row is updated.
*
* @returns {this} Returns the current builder instance for method chaining.
*
* @example
* await Schema
* .alterTable(User)
* .addForeignKey({
* references: {
* on : User
* },
* onDelete: 'CASCADE',
* onUpdate: 'CASCADE'
* })
* .run();
*/
addForeignKey({ column, name, references, onDelete, onUpdate }) {
const onReference = typeof references.on === "string" ? references.on : new references.on();
const tableRef = typeof onReference === "string"
? onReference
: onReference.getTableName();
const generateConstraintName = ({ modelTable, key, foreignTable, foreignKey }) => {
const MAX_LENGTH = 64;
const baseName = [
"fk",
`${modelTable}(${key})`,
`${foreignTable}(${foreignKey})`,
].join("_");
if (baseName.length <= MAX_LENGTH) {
return `\`${baseName}\``;
}
const hash = Buffer.from(baseName).toString("base64").slice(0, 8);
const shortParts = [
"fk",
`${modelTable.slice(0, 16)}(${key.slice(0, 16)})`,
`${foreignTable.slice(0, 16)}(${foreignKey.slice(0, 16)})`,
hash,
];
const shortName = shortParts.join("_").slice(0, MAX_LENGTH);
return `\`${shortName}\``;
};
const constraintName = generateConstraintName({
modelTable: this.model.getTableName(),
key: column,
foreignTable: tableRef,
foreignKey: references.column ?? "id",
}).replace(/`/g, "");
this.queries.push(() => {
return this.model.addFK({
table: this.table,
tableRef,
key: column,
constraint: name ?? constraintName,
foreign: {
references: references.column ?? "id",
onDelete: onDelete ?? "CASCADE",
onUpdate: onUpdate ?? "CASCADE"
}
});
});
return this;
}
/**
* Drops a foreign key constraint from the table.
*
* The specified foreign key constraint will be removed from the current table.
*
* @param {string} constraint - The name of the foreign key constraint to drop.
*
* @returns {this} Returns the current builder instance for method chaining.
*
* @example
* alterTable(User)
* .dropForeignKey("fk_users_role_id")
* .run();
*/
dropForeignKey(constraint) {
this.queries.push(() => {
return this.model.dropFK({ constraint, table: this.table });
});
return this;
}
/**
* Executes all queued schema modification queries sequentially.
*
* If a query fails, the error will be logged but execution will
* continue with the remaining queries.
*
* @async
* @returns {Promise<void>}
*/
async run() {
for (const query of this.queries) {
try {
await query();
}
catch (err) {
console.log(`\n\x1b[31mERROR: Failed to Alter table "${this.table}" caused by "${err.message}"\x1b[0m`);
}
}
}
}
exports.AlterTable = AlterTable;
;
exports.default = AlterTable;
//# sourceMappingURL=AlterTable.js.map