Remove describe.only
from spec file as this was stopping all other
spec files from running.
Closes #7284
1 | 10 |
import {MysqlDriver} from "../driver/mysql/MysqlDriver"; |
2 | 10 |
import {ColumnMetadata} from "../metadata/ColumnMetadata"; |
3 | 10 |
import {UniqueMetadata} from "../metadata/UniqueMetadata"; |
4 | 10 |
import {ForeignKeyMetadata} from "../metadata/ForeignKeyMetadata"; |
5 |
import {RelationMetadata} from "../metadata/RelationMetadata"; |
|
6 |
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs"; |
|
7 |
import {Connection} from "../connection/Connection"; |
|
8 | 10 |
import {OracleDriver} from "../driver/oracle/OracleDriver"; |
9 | 10 |
import {AuroraDataApiDriver} from "../driver/aurora-data-api/AuroraDataApiDriver"; |
10 |
|
|
11 |
/**
|
|
12 |
* Builds join column for the many-to-one and one-to-one owner relations.
|
|
13 |
*
|
|
14 |
* Cases it should cover:
|
|
15 |
* 1. when join column is set with custom name and without referenced column name
|
|
16 |
* we need automatically set referenced column name - primary ids by default
|
|
17 |
* @JoinColumn({ name: "custom_name" })
|
|
18 |
*
|
|
19 |
* 2. when join column is set with only referenced column name
|
|
20 |
* we need automatically set join column name - relation name + referenced column name
|
|
21 |
* @JoinColumn({ referencedColumnName: "title" })
|
|
22 |
*
|
|
23 |
* 3. when join column is set without both referenced column name and join column name
|
|
24 |
* we need to automatically set both of them
|
|
25 |
* @JoinColumn()
|
|
26 |
*
|
|
27 |
* 4. when join column is not set at all (as in case of @ManyToOne relation)
|
|
28 |
* we need to create join column for it with proper referenced column name and join column name
|
|
29 |
*
|
|
30 |
* 5. when multiple join columns set none of referencedColumnName and name can be optional
|
|
31 |
* both options are required
|
|
32 |
* @JoinColumn([
|
|
33 |
* { name: "category_title", referencedColumnName: "type" },
|
|
34 |
* { name: "category_title", referencedColumnName: "name" },
|
|
35 |
* ])
|
|
36 |
*
|
|
37 |
* Since for many-to-one relations having JoinColumn decorator is not required,
|
|
38 |
* we need to go thought each many-to-one relation without join column decorator set
|
|
39 |
* and create join column metadata args for them.
|
|
40 |
*/
|
|
41 | 10 |
export class RelationJoinColumnBuilder { |
42 |
|
|
43 |
// -------------------------------------------------------------------------
|
|
44 |
// Constructor
|
|
45 |
// -------------------------------------------------------------------------
|
|
46 |
|
|
47 | 10 |
constructor(private connection: Connection) { |
48 |
}
|
|
49 |
|
|
50 |
// -------------------------------------------------------------------------
|
|
51 |
// Public Methods
|
|
52 |
// -------------------------------------------------------------------------
|
|
53 |
|
|
54 |
/**
|
|
55 |
* Builds a foreign key of the many-to-one or one-to-one owner relations.
|
|
56 |
*/
|
|
57 | 10 |
build(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata): { |
58 |
foreignKey: ForeignKeyMetadata|undefined, |
|
59 |
uniqueConstraint: UniqueMetadata|undefined, |
|
60 |
} { |
|
61 | 10 |
const referencedColumns = this.collectReferencedColumns(joinColumns, relation); |
62 | 10 |
if (!referencedColumns.length) |
63 | 10 |
return { foreignKey: undefined, uniqueConstraint: undefined }; // this case is possible only for one-to-one non owning side |
64 |
|
|
65 | 10 |
const columns = this.collectColumns(joinColumns, relation, referencedColumns); |
66 | 10 |
const foreignKey = new ForeignKeyMetadata({ |
67 |
entityMetadata: relation.entityMetadata, |
|
68 |
referencedEntityMetadata: relation.inverseEntityMetadata, |
|
69 |
namingStrategy: this.connection.namingStrategy, |
|
70 |
columns: columns, |
|
71 |
referencedColumns: referencedColumns, |
|
72 |
onDelete: relation.onDelete, |
|
73 |
onUpdate: relation.onUpdate, |
|
74 |
deferrable: relation.deferrable, |
|
75 |
});
|
|
76 |
|
|
77 |
// Oracle does not allow both primary and unique constraints on the same column
|
|
78 | 10 |
if (this.connection.driver instanceof OracleDriver && columns.every(column => column.isPrimary)) |
79 | 2 |
return { foreignKey, uniqueConstraint: undefined }; |
80 |
|
|
81 |
// CockroachDB requires UNIQUE constraints on referenced columns
|
|
82 | 10 |
if (referencedColumns.length > 0 && relation.isOneToOne) { |
83 | 10 |
const uniqueConstraint = new UniqueMetadata({ |
84 |
entityMetadata: relation.entityMetadata, |
|
85 |
columns: foreignKey.columns, |
|
86 |
args: { |
|
87 | 10 |
name: this.connection.namingStrategy.relationConstraintName(relation.entityMetadata.tablePath, foreignKey.columns.map(c => c.databaseName)), |
88 |
target: relation.entityMetadata.target, |
|
89 |
}
|
|
90 |
});
|
|
91 | 10 |
uniqueConstraint.build(this.connection.namingStrategy); |
92 | 10 |
return {foreignKey, uniqueConstraint}; |
93 |
}
|
|
94 |
|
|
95 | 10 |
return { foreignKey, uniqueConstraint: undefined }; |
96 |
}
|
|
97 |
// -------------------------------------------------------------------------
|
|
98 |
// Protected Methods
|
|
99 |
// -------------------------------------------------------------------------
|
|
100 |
|
|
101 |
/**
|
|
102 |
* Collects referenced columns from the given join column args.
|
|
103 |
*/
|
|
104 | 10 |
protected collectReferencedColumns(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata): ColumnMetadata[] { |
105 | 10 |
const hasAnyReferencedColumnName = joinColumns.find(joinColumnArgs => !!joinColumnArgs.referencedColumnName); |
106 | 10 |
const manyToOneWithoutJoinColumn = joinColumns.length === 0 && relation.isManyToOne; |
107 | 10 |
const hasJoinColumnWithoutAnyReferencedColumnName = joinColumns.length > 0 && !hasAnyReferencedColumnName; |
108 |
|
|
109 | 10 |
if (manyToOneWithoutJoinColumn || hasJoinColumnWithoutAnyReferencedColumnName) { // covers case3 and case1 |
110 | 10 |
return relation.inverseEntityMetadata.primaryColumns; |
111 |
|
|
112 |
} else { // cases with referenced columns defined |
|
113 | 10 |
return joinColumns.map(joinColumn => { |
114 | 10 |
const referencedColumn = relation.inverseEntityMetadata.ownColumns.find(column => column.propertyName === joinColumn.referencedColumnName); // todo: can we also search in relations? |
115 | 10 |
if (!referencedColumn) |
116 |
throw new Error(`Referenced column ${joinColumn.referencedColumnName} was not found in entity ${relation.inverseEntityMetadata.name}`); |
|
117 |
|
|
118 | 10 |
return referencedColumn; |
119 |
});
|
|
120 |
}
|
|
121 |
}
|
|
122 |
|
|
123 |
/**
|
|
124 |
* Collects columns from the given join column args.
|
|
125 |
*/
|
|
126 | 10 |
private collectColumns(joinColumns: JoinColumnMetadataArgs[], relation: RelationMetadata, referencedColumns: ColumnMetadata[]): ColumnMetadata[] { |
127 | 10 |
return referencedColumns.map(referencedColumn => { |
128 |
|
|
129 |
// in the case if relation has join column with only name set we need this check
|
|
130 | 10 |
const joinColumnMetadataArg = joinColumns.find(joinColumn => { |
131 | 10 |
return (!joinColumn.referencedColumnName || joinColumn.referencedColumnName === referencedColumn.propertyName) && |
132 | 10 |
!!joinColumn.name; |
133 |
});
|
|
134 | 10 |
const joinColumnName = joinColumnMetadataArg ? joinColumnMetadataArg.name : this.connection.namingStrategy.joinColumnName(relation.propertyName, referencedColumn.propertyName); |
135 |
|
|
136 | 10 |
let relationalColumn = relation.entityMetadata.ownColumns.find(column => column.databaseName === joinColumnName); |
137 | 10 |
if (!relationalColumn) { |
138 | 10 |
relationalColumn = new ColumnMetadata({ |
139 |
connection: this.connection, |
|
140 |
entityMetadata: relation.entityMetadata, |
|
141 |
args: { |
|
142 |
target: "", |
|
143 |
mode: "virtual", |
|
144 |
propertyName: relation.propertyName, |
|
145 |
options: { |
|
146 |
name: joinColumnName, |
|
147 |
type: referencedColumn.type, |
|
148 | 10 |
length: !referencedColumn.length |
149 | 10 |
&& (this.connection.driver instanceof MysqlDriver || this.connection.driver instanceof AuroraDataApiDriver) |
150 | 10 |
&& (referencedColumn.generationStrategy === "uuid" || referencedColumn.type === "uuid") |
151 | 6 |
? "36" |
152 | 10 |
: referencedColumn.length, // fix https://github.com/typeorm/typeorm/issues/3604 |
153 |
width: referencedColumn.width, |
|
154 |
charset: referencedColumn.charset, |
|
155 |
collation: referencedColumn.collation, |
|
156 |
precision: referencedColumn.precision, |
|
157 |
scale: referencedColumn.scale, |
|
158 |
zerofill: referencedColumn.zerofill, |
|
159 |
unsigned: referencedColumn.unsigned, |
|
160 |
comment: referencedColumn.comment, |
|
161 |
primary: relation.isPrimary, |
|
162 |
nullable: relation.isNullable |
|
163 |
}
|
|
164 |
}
|
|
165 |
});
|
|
166 | 10 |
relation.entityMetadata.registerColumn(relationalColumn); |
167 |
}
|
|
168 | 10 |
relationalColumn.referencedColumn = referencedColumn; // its important to set it here because we need to set referenced column for user defined join column |
169 | 10 |
relationalColumn.type = referencedColumn.type; // also since types of relational column and join column must be equal we override user defined column type |
170 | 10 |
relationalColumn.relationMetadata = relation; |
171 | 10 |
relationalColumn.build(this.connection); |
172 | 10 |
return relationalColumn; |
173 |
});
|
|
174 |
}
|
|
175 | 10 |
}
|
Read our documentation on viewing source code .