Skip to content
This repository has been archived by the owner on Mar 18, 2022. It is now read-only.

Commit

Permalink
feat: add referenced table metadata to NamingStrategy to resolve fore…
Browse files Browse the repository at this point in the history
…ign key name (typeorm#4274)

Feature allows to use foreignkey metadata to generate database foreignkey names.

Closes typeorm#3847 and typeorm#1355
  • Loading branch information
tkvw authored and pleerock committed Sep 13, 2019
1 parent 3abe5b9 commit 0094f61
Show file tree
Hide file tree
Showing 15 changed files with 101 additions and 29 deletions.
8 changes: 4 additions & 4 deletions src/driver/cockroachdb/CockroachQueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner
// rename foreign key constraints
newTable.foreignKeys.forEach(foreignKey => {
// build new constraint name
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(newTable)} RENAME CONSTRAINT "${foreignKey.name}" TO "${newForeignKeyName}"`));
Expand Down Expand Up @@ -702,7 +702,7 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner
// build new constraint name
foreignKey.columnNames.splice(foreignKey.columnNames.indexOf(oldColumn.name), 1);
foreignKey.columnNames.push(newColumn.name);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} RENAME CONSTRAINT "${foreignKey.name}" TO "${newForeignKeyName}"`));
Expand Down Expand Up @@ -1123,7 +1123,7 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner

// new FK may be passed without name. In this case we generate FK name manually.
if (!foreignKey.name)
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames);
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

const up = this.createForeignKeySql(table, foreignKey);
const down = this.dropForeignKeySql(table, foreignKey);
Expand Down Expand Up @@ -1618,7 +1618,7 @@ export class CockroachQueryRunner extends BaseQueryRunner implements QueryRunner
const foreignKeysSql = table.foreignKeys.map(fk => {
const columnNames = fk.columnNames.map(columnName => `"${columnName}"`).join(", ");
if (!fk.name)
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames);
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames, fk.referencedTableName, fk.referencedColumnNames);
const referencedColumnNames = fk.referencedColumnNames.map(columnName => `"${columnName}"`).join(", ");

let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(fk.referencedTableName)} (${referencedColumnNames})`;
Expand Down
8 changes: 4 additions & 4 deletions src/driver/mysql/MysqlQueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
// build new constraint name
const columnNames = foreignKey.columnNames.map(column => `\`${column}\``).join(", ");
const referencedColumnNames = foreignKey.referencedColumnNames.map(column => `\`${column}\``).join(",");
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
let up = `ALTER TABLE ${this.escapePath(newTable)} DROP FOREIGN KEY \`${foreignKey.name}\`, ADD CONSTRAINT \`${newForeignKeyName}\` FOREIGN KEY (${columnNames}) ` +
Expand Down Expand Up @@ -599,7 +599,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
foreignKey.columnNames.push(newColumn.name);
const columnNames = foreignKey.columnNames.map(column => `\`${column}\``).join(", ");
const referencedColumnNames = foreignKey.referencedColumnNames.map(column => `\`${column}\``).join(",");
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
let up = `ALTER TABLE ${this.escapePath(table)} DROP FOREIGN KEY \`${foreignKey.name}\`, ADD CONSTRAINT \`${newForeignKeyName}\` FOREIGN KEY (${columnNames}) ` +
Expand Down Expand Up @@ -1004,7 +1004,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {

// new FK may be passed without name. In this case we generate FK name manually.
if (!foreignKey.name)
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames);
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

const up = this.createForeignKeySql(table, foreignKey);
const down = this.dropForeignKeySql(table, foreignKey);
Expand Down Expand Up @@ -1462,7 +1462,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
const foreignKeysSql = table.foreignKeys.map(fk => {
const columnNames = fk.columnNames.map(columnName => `\`${columnName}\``).join(", ");
if (!fk.name)
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames);
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames, fk.referencedTableName, fk.referencedColumnNames);
const referencedColumnNames = fk.referencedColumnNames.map(columnName => `\`${columnName}\``).join(", ");

let constraint = `CONSTRAINT \`${fk.name}\` FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(fk.referencedTableName)} (${referencedColumnNames})`;
Expand Down
8 changes: 4 additions & 4 deletions src/driver/oracle/OracleQueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
// rename foreign key constraints
newTable.foreignKeys.forEach(foreignKey => {
// build new constraint name
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`ALTER TABLE "${newTable.name}" RENAME CONSTRAINT "${foreignKey.name}" TO "${newForeignKeyName}"`));
Expand Down Expand Up @@ -613,7 +613,7 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
// build new constraint name
foreignKey.columnNames.splice(foreignKey.columnNames.indexOf(oldColumn.name), 1);
foreignKey.columnNames.push(newColumn.name);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`ALTER TABLE "${table.name}" RENAME CONSTRAINT "${foreignKey.name}" TO "${newForeignKeyName}"`));
Expand Down Expand Up @@ -1004,7 +1004,7 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {

// new FK may be passed without name. In this case we generate FK name manually.
if (!foreignKey.name)
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames);
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

const up = this.createForeignKeySql(table, foreignKey);
const down = this.dropForeignKeySql(table, foreignKey);
Expand Down Expand Up @@ -1351,7 +1351,7 @@ export class OracleQueryRunner extends BaseQueryRunner implements QueryRunner {
const foreignKeysSql = table.foreignKeys.map(fk => {
const columnNames = fk.columnNames.map(columnName => `"${columnName}"`).join(", ");
if (!fk.name)
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames);
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames, fk.referencedTableName, fk.referencedColumnNames);
const referencedColumnNames = fk.referencedColumnNames.map(columnName => `"${columnName}"`).join(", ");
let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES "${fk.referencedTableName}" (${referencedColumnNames})`;
if (fk.onDelete && fk.onDelete !== "NO ACTION") // Oracle does not support NO ACTION, but we set NO ACTION by default in EntityMetadata
Expand Down
8 changes: 4 additions & 4 deletions src/driver/postgres/PostgresQueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
// rename foreign key constraints
newTable.foreignKeys.forEach(foreignKey => {
// build new constraint name
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(newTable)} RENAME CONSTRAINT "${foreignKey.name}" TO "${newForeignKeyName}"`));
Expand Down Expand Up @@ -687,7 +687,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
// build new constraint name
foreignKey.columnNames.splice(foreignKey.columnNames.indexOf(oldColumn.name), 1);
foreignKey.columnNames.push(newColumn.name);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} RENAME CONSTRAINT "${foreignKey.name}" TO "${newForeignKeyName}"`));
Expand Down Expand Up @@ -1174,7 +1174,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner

// new FK may be passed without name. In this case we generate FK name manually.
if (!foreignKey.name)
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames);
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

const up = this.createForeignKeySql(table, foreignKey);
const down = this.dropForeignKeySql(table, foreignKey);
Expand Down Expand Up @@ -1702,7 +1702,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
const foreignKeysSql = table.foreignKeys.map(fk => {
const columnNames = fk.columnNames.map(columnName => `"${columnName}"`).join(", ");
if (!fk.name)
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames);
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames, fk.referencedTableName, fk.referencedColumnNames);
const referencedColumnNames = fk.referencedColumnNames.map(columnName => `"${columnName}"`).join(", ");

let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(fk.referencedTableName)} (${referencedColumnNames})`;
Expand Down
8 changes: 4 additions & 4 deletions src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen

// rename foreign key constraints
newTable.foreignKeys.forEach(foreignKey => {
foreignKey.name = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames);
foreignKey.name = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);
});

// rename indices
Expand Down Expand Up @@ -384,7 +384,7 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen
changedTable.findColumnForeignKeys(changedColumnSet.oldColumn).forEach(fk => {
fk.columnNames.splice(fk.columnNames.indexOf(changedColumnSet.oldColumn.name), 1);
fk.columnNames.push(changedColumnSet.newColumn.name);
fk.name = this.connection.namingStrategy.foreignKeyName(changedTable, fk.columnNames);
fk.name = this.connection.namingStrategy.foreignKeyName(changedTable, fk.columnNames, fk.referencedTableName, fk.referencedColumnNames);
});

changedTable.findColumnIndices(changedColumnSet.oldColumn).forEach(index => {
Expand Down Expand Up @@ -848,7 +848,7 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen
const columnNames = ownForeignKeys.map(dbForeignKey => dbForeignKey["from"]);
const referencedColumnNames = ownForeignKeys.map(dbForeignKey => dbForeignKey["to"]);
// build foreign key name, because we can not get it directly.
const fkName = this.connection.namingStrategy.foreignKeyName(table, columnNames);
const fkName = this.connection.namingStrategy.foreignKeyName(table, columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

return new TableForeignKey({
name: fkName,
Expand Down Expand Up @@ -975,7 +975,7 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen
const foreignKeysSql = table.foreignKeys.map(fk => {
const columnNames = fk.columnNames.map(columnName => `"${columnName}"`).join(", ");
if (!fk.name)
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames);
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames, fk.referencedTableName, fk.referencedColumnNames);
const referencedColumnNames = fk.referencedColumnNames.map(columnName => `"${columnName}"`).join(", ");

let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES "${fk.referencedTableName}" (${referencedColumnNames})`;
Expand Down
8 changes: 4 additions & 4 deletions src/driver/sqlserver/SqlServerQueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner
// rename foreign key constraints
newTable.foreignKeys.forEach(foreignKey => {
// build new constraint name
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(newTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`EXEC sp_rename "${this.buildForeignKeyName(foreignKey.name!, schemaName, dbName)}", "${newForeignKeyName}"`));
Expand Down Expand Up @@ -823,7 +823,7 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner
// build new constraint name
foreignKey.columnNames.splice(foreignKey.columnNames.indexOf(oldColumn.name), 1);
foreignKey.columnNames.push(newColumn.name);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames);
const newForeignKeyName = this.connection.namingStrategy.foreignKeyName(clonedTable, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

// build queries
upQueries.push(new Query(`EXEC sp_rename "${this.buildForeignKeyName(foreignKey.name!, schemaName, dbName)}", "${newForeignKeyName}"`));
Expand Down Expand Up @@ -1240,7 +1240,7 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner

// new FK may be passed without name. In this case we generate FK name manually.
if (!foreignKey.name)
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames);
foreignKey.name = this.connection.namingStrategy.foreignKeyName(table.name, foreignKey.columnNames, foreignKey.referencedTableName, foreignKey.referencedColumnNames);

const up = this.createForeignKeySql(table, foreignKey);
const down = this.dropForeignKeySql(table, foreignKey);
Expand Down Expand Up @@ -1816,7 +1816,7 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner
const foreignKeysSql = table.foreignKeys.map(fk => {
const columnNames = fk.columnNames.map(columnName => `"${columnName}"`).join(", ");
if (!fk.name)
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames);
fk.name = this.connection.namingStrategy.foreignKeyName(table.name, fk.columnNames, fk.referencedTableName, fk.referencedColumnNames);
const referencedColumnNames = fk.referencedColumnNames.map(columnName => `"${columnName}"`).join(", ");

let constraint = `CONSTRAINT "${fk.name}" FOREIGN KEY (${columnNames}) REFERENCES ${this.escapePath(fk.referencedTableName)} (${referencedColumnNames})`;
Expand Down
2 changes: 1 addition & 1 deletion src/metadata/ForeignKeyMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class ForeignKeyMetadata {
this.columnNames = this.columns.map(column => column.databaseName);
this.referencedColumnNames = this.referencedColumns.map(column => column.databaseName);
this.referencedTablePath = this.referencedEntityMetadata.tablePath;
this.name = namingStrategy.foreignKeyName(this.entityMetadata.tablePath, this.columnNames);
this.name = namingStrategy.foreignKeyName(this.entityMetadata.tablePath, this.columnNames, this.referencedTablePath, this.referencedColumnNames);
}

}
2 changes: 1 addition & 1 deletion src/naming-strategy/DefaultNamingStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
return "DF_" + RandomGenerator.sha1(key).substr(0, 27);
}

foreignKeyName(tableOrName: Table|string, columnNames: string[]): string {
foreignKeyName(tableOrName: Table|string, columnNames: string[], _referencedTablePath?: string, _referencedColumnNames?: string[]): string {
// sort incoming column names to avoid issue when ["id", "name"] and ["name", "id"] arrays
const clonedColumnNames = [...columnNames];
clonedColumnNames.sort();
Expand Down
2 changes: 1 addition & 1 deletion src/naming-strategy/NamingStrategyInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export interface NamingStrategyInterface {
/**
* Gets the name of the foreign key.
*/
foreignKeyName(tableOrName: Table|string, columnNames: string[]): string;
foreignKeyName(tableOrName: Table|string, columnNames: string[], referencedTablePath?: string, referencedColumnNames?: string[]): string;

/**
* Gets the name of the index - simple and compose index.
Expand Down
Loading

0 comments on commit 0094f61

Please sign in to comment.