mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
fix: fixed all known enum issues (#7419)
* fix #5371 * fix #6471; fix: `enumName` changes not handled; fix: `enumName` does not handle table schema; * fixed falling test; * added test for #7217 * fix #6047, #7283; * fix #5871 * added support for `enumName` in `joinColumns` (#5729) * fix #5478 * fixed falling test; updated `postgres-enum` test; * added column `array` property change detection (#5882); updated `postgres-enum` test; * fix #5275 * added validation for `enum` property (#2233) * fix #5648 * improved missing "enum" or "enumName" properties validation; * fix #4897, #6376 * lint fix; * fixed falling tests; * fixed falling tests; * removed .only * fix #6115
This commit is contained in:
parent
2fa6231e59
commit
724d80bf1a
@ -534,6 +534,10 @@ export class AuroraDataApiDriver implements Driver {
|
||||
normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
|
||||
const defaultValue = columnMetadata.default;
|
||||
|
||||
if (defaultValue === null) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if ((columnMetadata.type === "enum" || columnMetadata.type === "simple-enum") && defaultValue !== undefined) {
|
||||
return `'${defaultValue}'`;
|
||||
}
|
||||
|
||||
@ -1333,7 +1333,7 @@ export class AuroraDataApiQueryRunner extends BaseQueryRunner implements QueryRu
|
||||
|
||||
if (tableColumn.type === "enum" || tableColumn.type === "simple-enum") {
|
||||
const colType = dbColumn["COLUMN_TYPE"];
|
||||
const items = colType.substring(colType.indexOf("(") + 1, colType.indexOf(")")).split(",");
|
||||
const items = colType.substring(colType.indexOf("(") + 1, colType.lastIndexOf(")")).split(",");
|
||||
tableColumn.enum = (items as string[]).map(item => {
|
||||
return item.substring(1, item.length - 1);
|
||||
});
|
||||
|
||||
@ -582,15 +582,20 @@ export class MysqlDriver implements Driver {
|
||||
normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
|
||||
const defaultValue = columnMetadata.default;
|
||||
|
||||
if ((columnMetadata.type === "enum" || columnMetadata.type === "simple-enum") && defaultValue !== undefined) {
|
||||
if (defaultValue === null) {
|
||||
return undefined
|
||||
|
||||
} else if (
|
||||
(columnMetadata.type === "enum"
|
||||
|| columnMetadata.type === "simple-enum"
|
||||
|| typeof defaultValue === "string")
|
||||
&& defaultValue !== undefined) {
|
||||
return `'${defaultValue}'`;
|
||||
}
|
||||
|
||||
if ((columnMetadata.type === "set") && defaultValue !== undefined) {
|
||||
} else if ((columnMetadata.type === "set") && defaultValue !== undefined) {
|
||||
return `'${DateUtils.simpleArrayToString(defaultValue)}'`;
|
||||
}
|
||||
|
||||
if (typeof defaultValue === "number") {
|
||||
} else if (typeof defaultValue === "number") {
|
||||
return `'${defaultValue.toFixed(columnMetadata.scale)}'`;
|
||||
|
||||
} else if (typeof defaultValue === "boolean") {
|
||||
@ -599,12 +604,6 @@ export class MysqlDriver implements Driver {
|
||||
} else if (typeof defaultValue === "function") {
|
||||
return defaultValue();
|
||||
|
||||
} else if (typeof defaultValue === "string") {
|
||||
return `'${defaultValue}'`;
|
||||
|
||||
} else if (defaultValue === null) {
|
||||
return undefined;
|
||||
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@ -1529,7 +1529,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
|
||||
|
||||
if (tableColumn.type === "enum" || tableColumn.type === "simple-enum" || tableColumn.type === "set") {
|
||||
const colType = dbColumn["COLUMN_TYPE"];
|
||||
const items = colType.substring(colType.indexOf("(") + 1, colType.indexOf(")")).split(",");
|
||||
const items = colType.substring(colType.indexOf("(") + 1, colType.lastIndexOf(")")).split(",");
|
||||
tableColumn.enum = (items as string[]).map(item => {
|
||||
return item.substring(1, item.length - 1);
|
||||
});
|
||||
@ -1861,7 +1861,7 @@ export class MysqlQueryRunner extends BaseQueryRunner implements QueryRunner {
|
||||
const isMariaDb = this.driver.options.type === "mariadb";
|
||||
if (isMariaDb && column.asExpression && (column.generatedType || "VIRTUAL") === "VIRTUAL") {
|
||||
// do nothing - MariaDB does not support NULL/NOT NULL expressions for VIRTUAL columns
|
||||
} else {
|
||||
} else {
|
||||
if (!column.isNullable)
|
||||
c += " NOT NULL";
|
||||
if (column.isNullable)
|
||||
|
||||
@ -586,21 +586,30 @@ export class PostgresDriver implements Driver {
|
||||
|
||||
} else if (columnMetadata.type === "enum" || columnMetadata.type === "simple-enum" ) {
|
||||
if (columnMetadata.isArray) {
|
||||
if (value === "{}") return [];
|
||||
|
||||
// manually convert enum array to array of values (pg does not support, see https://github.com/brianc/node-pg-types/issues/56)
|
||||
value = value !== "{}" ? (value as string).substr(1, (value as string).length - 2).split(",") : [];
|
||||
// convert to number if that exists in poosible enum options
|
||||
value = (value as string).substr(1, (value as string).length - 2).split(",").map(val => {
|
||||
// replace double quotes from the beginning and from the end
|
||||
if (val.startsWith(`"`) && val.endsWith(`"`)) val = val.slice(1, -1);
|
||||
// replace double escaped backslash to single escaped e.g. \\\\ -> \\
|
||||
val = val.replace(/(\\\\)/g, "\\")
|
||||
// replace escaped double quotes to non-escaped e.g. \"asd\" -> "asd"
|
||||
return val.replace(/(\\")/g, '"')
|
||||
});
|
||||
|
||||
// convert to number if that exists in possible enum options
|
||||
value = value.map((val: string) => {
|
||||
return !isNaN(+val) && columnMetadata.enum!.indexOf(parseInt(val)) >= 0 ? parseInt(val) : val;
|
||||
});
|
||||
} else {
|
||||
// convert to number if that exists in poosible enum options
|
||||
// convert to number if that exists in possible enum options
|
||||
value = !isNaN(+value) && columnMetadata.enum!.indexOf(parseInt(value)) >= 0 ? parseInt(value) : value;
|
||||
}
|
||||
}
|
||||
|
||||
if (columnMetadata.transformer)
|
||||
value = ApplyValueTransformers.transformFrom(columnMetadata.transformer, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -719,18 +728,22 @@ export class PostgresDriver implements Driver {
|
||||
/**
|
||||
* Normalizes "default" value of the column.
|
||||
*/
|
||||
normalizeDefault(columnMetadata: ColumnMetadata): string {
|
||||
normalizeDefault(columnMetadata: ColumnMetadata): string | undefined {
|
||||
const defaultValue = columnMetadata.default;
|
||||
if (columnMetadata.isArray && Array.isArray(defaultValue)) {
|
||||
|
||||
if (defaultValue === null) {
|
||||
return undefined;
|
||||
|
||||
} else if (columnMetadata.isArray && Array.isArray(defaultValue)) {
|
||||
return `'{${defaultValue.map((val: string) => `${val}`).join(",")}}'`;
|
||||
}
|
||||
|
||||
if ((columnMetadata.type === "enum" || columnMetadata.type === "simple-enum")
|
||||
&& defaultValue !== undefined) {
|
||||
return `'${defaultValue}'`;
|
||||
}
|
||||
|
||||
if (typeof defaultValue === "number") {
|
||||
} else if (
|
||||
(columnMetadata.type === "enum"
|
||||
|| columnMetadata.type === "simple-enum"
|
||||
|| typeof defaultValue === "number"
|
||||
|| typeof defaultValue === "string")
|
||||
&& defaultValue !== undefined
|
||||
) {
|
||||
return `'${defaultValue}'`;
|
||||
|
||||
} else if (typeof defaultValue === "boolean") {
|
||||
@ -739,10 +752,7 @@ export class PostgresDriver implements Driver {
|
||||
} else if (typeof defaultValue === "function") {
|
||||
return defaultValue();
|
||||
|
||||
} else if (typeof defaultValue === "string") {
|
||||
return `'${defaultValue}'`;
|
||||
|
||||
} else if (typeof defaultValue === "object" && defaultValue !== null) {
|
||||
} else if (typeof defaultValue === "object") {
|
||||
return `'${JSON.stringify(defaultValue)}'`;
|
||||
|
||||
} else {
|
||||
@ -867,6 +877,7 @@ export class PostgresDriver implements Driver {
|
||||
const isColumnChanged = tableColumn.name !== columnMetadata.databaseName
|
||||
|| tableColumn.type !== this.normalizeType(columnMetadata)
|
||||
|| tableColumn.length !== columnMetadata.length
|
||||
|| tableColumn.isArray !== columnMetadata.isArray
|
||||
|| tableColumn.precision !== columnMetadata.precision
|
||||
|| (columnMetadata.scale !== undefined && tableColumn.scale !== columnMetadata.scale)
|
||||
|| tableColumn.comment !== columnMetadata.comment
|
||||
@ -874,6 +885,7 @@ export class PostgresDriver implements Driver {
|
||||
|| tableColumn.isPrimary !== columnMetadata.isPrimary
|
||||
|| tableColumn.isNullable !== columnMetadata.isNullable
|
||||
|| tableColumn.isUnique !== this.normalizeIsUnique(columnMetadata)
|
||||
|| tableColumn.enumName !== columnMetadata.enumName
|
||||
|| (tableColumn.enum && columnMetadata.enum && !OrmUtils.isArraysEqual(tableColumn.enum, columnMetadata.enum.map(val => val + ""))) // enums in postgres are always strings
|
||||
|| tableColumn.isGenerated !== columnMetadata.isGenerated
|
||||
|| (tableColumn.spatialFeatureType || "").toLowerCase() !== (columnMetadata.spatialFeatureType || "").toLowerCase()
|
||||
@ -885,9 +897,11 @@ export class PostgresDriver implements Driver {
|
||||
// console.log("name:", tableColumn.name, columnMetadata.databaseName);
|
||||
// console.log("type:", tableColumn.type, this.normalizeType(columnMetadata));
|
||||
// console.log("length:", tableColumn.length, columnMetadata.length);
|
||||
// console.log("isArray:", tableColumn.isArray, columnMetadata.isArray);
|
||||
// console.log("precision:", tableColumn.precision, columnMetadata.precision);
|
||||
// console.log("scale:", tableColumn.scale, columnMetadata.scale);
|
||||
// console.log("comment:", tableColumn.comment, columnMetadata.comment);
|
||||
// console.log("enumName:", tableColumn.enumName, columnMetadata.enumName);
|
||||
// console.log("enum:", tableColumn.enum && columnMetadata.enum && !OrmUtils.isArraysEqual(tableColumn.enum, columnMetadata.enum.map(val => val + "")));
|
||||
// console.log("onUpdate:", tableColumn.onUpdate, columnMetadata.onUpdate);
|
||||
// console.log("isPrimary:", tableColumn.isPrimary, columnMetadata.isPrimary);
|
||||
|
||||
@ -349,17 +349,20 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
const downQueries: Query[] = [];
|
||||
|
||||
// if table have column with ENUM type, we must create this type in postgres.
|
||||
await Promise.all(table.columns
|
||||
.filter(column => column.type === "enum" || column.type === "simple-enum")
|
||||
.map(async column => {
|
||||
const hasEnum = await this.hasEnumType(table, column);
|
||||
// TODO: Should also check if values of existing type matches expected ones
|
||||
if (!hasEnum) {
|
||||
upQueries.push(this.createEnumTypeSql(table, column));
|
||||
downQueries.push(this.dropEnumTypeSql(table, column));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}));
|
||||
const enumColumns = table.columns.filter(column => column.type === "enum" || column.type === "simple-enum")
|
||||
const createdEnumTypes: string[] = []
|
||||
for (const column of enumColumns) {
|
||||
// TODO: Should also check if values of existing type matches expected ones
|
||||
const hasEnum = await this.hasEnumType(table, column);
|
||||
const enumName = this.buildEnumName(table, column)
|
||||
|
||||
// if enum with the same "enumName" is defined more then once, me must prevent double creation
|
||||
if (!hasEnum && createdEnumTypes.indexOf(enumName) === -1) {
|
||||
createdEnumTypes.push(enumName)
|
||||
upQueries.push(this.createEnumTypeSql(table, column, enumName));
|
||||
downQueries.push(this.dropEnumTypeSql(table, column, enumName));
|
||||
}
|
||||
}
|
||||
|
||||
upQueries.push(this.createTableSql(table, createForeignKeys));
|
||||
downQueries.push(this.dropTableSql(table));
|
||||
@ -629,6 +632,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
let clonedTable = table.clone();
|
||||
const upQueries: Query[] = [];
|
||||
const downQueries: Query[] = [];
|
||||
let defaultValueChanged = false
|
||||
|
||||
const oldColumn = oldTableColumnOrName instanceof TableColumn
|
||||
? oldTableColumnOrName
|
||||
@ -636,7 +640,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
if (!oldColumn)
|
||||
throw new Error(`Column "${oldTableColumnOrName}" was not found in the "${table.name}" table.`);
|
||||
|
||||
if (oldColumn.type !== newColumn.type || oldColumn.length !== newColumn.length) {
|
||||
if (oldColumn.type !== newColumn.type || oldColumn.length !== newColumn.length || newColumn.isArray !== oldColumn.isArray) {
|
||||
// To avoid data conversion, we just recreate column
|
||||
await this.dropColumn(table, oldColumn);
|
||||
await this.addColumn(table, newColumn);
|
||||
@ -753,45 +757,58 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
if (
|
||||
(newColumn.type === "enum" || newColumn.type === "simple-enum")
|
||||
&& (oldColumn.type === "enum" || oldColumn.type === "simple-enum")
|
||||
&& !OrmUtils.isArraysEqual(newColumn.enum!, oldColumn.enum!)
|
||||
&& (!OrmUtils.isArraysEqual(newColumn.enum!, oldColumn.enum!) || newColumn.enumName !== oldColumn.enumName)
|
||||
) {
|
||||
const enumName = this.buildEnumName(table, newColumn);
|
||||
const arraySuffix = newColumn.isArray ? "[]" : "";
|
||||
const oldEnumName = this.buildEnumName(table, newColumn, true, false, true);
|
||||
const oldEnumNameWithoutSchema = this.buildEnumName(table, newColumn, false, false, true);
|
||||
const enumTypeBeforeColumnChange = await this.getEnumTypeName(table, oldColumn);
|
||||
|
||||
// "public"."new_enum"
|
||||
const newEnumName = this.buildEnumName(table, newColumn);
|
||||
|
||||
// "public"."old_enum"
|
||||
const oldEnumName = this.buildEnumName(table, oldColumn);
|
||||
|
||||
// "old_enum"
|
||||
const oldEnumNameWithoutSchema = this.buildEnumName(table, oldColumn, false);
|
||||
|
||||
//"public"."old_enum_old"
|
||||
const oldEnumNameWithSchema_old = this.buildEnumName(table, oldColumn, true, false, true);
|
||||
|
||||
//"old_enum_old"
|
||||
const oldEnumNameWithoutSchema_old = this.buildEnumName(table, oldColumn, false, false, true);
|
||||
|
||||
// rename old ENUM
|
||||
upQueries.push(new Query(`ALTER TYPE "${enumTypeBeforeColumnChange.enumTypeSchema}"."${enumTypeBeforeColumnChange.enumTypeName}" RENAME TO ${oldEnumNameWithoutSchema}`));
|
||||
downQueries.push(new Query(`ALTER TYPE ${oldEnumName} RENAME TO "${enumTypeBeforeColumnChange.enumTypeName}"`));
|
||||
upQueries.push(new Query(`ALTER TYPE ${oldEnumName} RENAME TO ${oldEnumNameWithoutSchema_old}`));
|
||||
downQueries.push(new Query(`ALTER TYPE ${oldEnumNameWithSchema_old} RENAME TO ${oldEnumNameWithoutSchema}`));
|
||||
|
||||
// create new ENUM
|
||||
upQueries.push(this.createEnumTypeSql(table, newColumn));
|
||||
downQueries.push(this.dropEnumTypeSql(table, oldColumn));
|
||||
upQueries.push(this.createEnumTypeSql(table, newColumn, newEnumName));
|
||||
downQueries.push(this.dropEnumTypeSql(table, newColumn, newEnumName));
|
||||
|
||||
// if column have default value, we must drop it to avoid issues with type casting
|
||||
if (newColumn.default !== null && newColumn.default !== undefined) {
|
||||
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`));
|
||||
downQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${newColumn.default}`));
|
||||
if (oldColumn.default !== null && oldColumn.default !== undefined) {
|
||||
// mark default as changed to prevent double update
|
||||
defaultValueChanged = true
|
||||
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${oldColumn.name}" DROP DEFAULT`));
|
||||
downQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${oldColumn.name}" SET DEFAULT ${oldColumn.default}`));
|
||||
}
|
||||
|
||||
// build column types
|
||||
const upType = `${enumName}${arraySuffix} USING "${newColumn.name}"::"text"::${enumName}${arraySuffix}`;
|
||||
const downType = `${oldEnumName}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumName}${arraySuffix}`;
|
||||
const upType = `${newEnumName}${arraySuffix} USING "${newColumn.name}"::"text"::${newEnumName}${arraySuffix}`;
|
||||
const downType = `${oldEnumNameWithSchema_old}${arraySuffix} USING "${newColumn.name}"::"text"::${oldEnumNameWithSchema_old}${arraySuffix}`;
|
||||
|
||||
// update column to use new type
|
||||
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${newColumn.name}" TYPE ${upType}`));
|
||||
downQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${newColumn.name}" TYPE ${downType}`));
|
||||
|
||||
// if column have default value and we dropped it before, we must bring it back
|
||||
// restore column default or create new one
|
||||
if (newColumn.default !== null && newColumn.default !== undefined) {
|
||||
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${newColumn.default}`));
|
||||
downQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${newColumn.name}" DROP DEFAULT`));
|
||||
}
|
||||
|
||||
// remove old ENUM
|
||||
upQueries.push(this.dropEnumTypeSql(table, newColumn, oldEnumName));
|
||||
downQueries.push(this.createEnumTypeSql(table, oldColumn, oldEnumName));
|
||||
upQueries.push(this.dropEnumTypeSql(table, oldColumn, oldEnumNameWithSchema_old));
|
||||
downQueries.push(this.createEnumTypeSql(table, oldColumn, oldEnumNameWithSchema_old));
|
||||
}
|
||||
|
||||
if (oldColumn.isNullable !== newColumn.isNullable) {
|
||||
@ -885,7 +902,8 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
}
|
||||
}
|
||||
|
||||
if (newColumn.default !== oldColumn.default) {
|
||||
// the default might have changed when the enum changed
|
||||
if (newColumn.default !== oldColumn.default && !defaultValueChanged) {
|
||||
if (newColumn.default !== null && newColumn.default !== undefined) {
|
||||
upQueries.push(new Query(`ALTER TABLE ${this.escapePath(table)} ALTER COLUMN "${newColumn.name}" SET DEFAULT ${newColumn.default}`));
|
||||
|
||||
@ -1567,11 +1585,17 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
}
|
||||
|
||||
if (tableColumn.type.indexOf("enum") !== -1) {
|
||||
// check if `enumName` is specified by user
|
||||
const { enumTypeName } = await this.getEnumTypeName(table, tableColumn)
|
||||
const builtEnumName = this.buildEnumName(table, tableColumn, false, true)
|
||||
if (builtEnumName !== enumTypeName)
|
||||
tableColumn.enumName = enumTypeName
|
||||
|
||||
tableColumn.type = "enum";
|
||||
const sql = `SELECT "e"."enumlabel" AS "value" FROM "pg_enum" "e" ` +
|
||||
`INNER JOIN "pg_type" "t" ON "t"."oid" = "e"."enumtypid" ` +
|
||||
`INNER JOIN "pg_namespace" "n" ON "n"."oid" = "t"."typnamespace" ` +
|
||||
`WHERE "n"."nspname" = '${dbTable["table_schema"]}' AND "t"."typname" = '${this.buildEnumName(table, tableColumn.name, false, true)}'`;
|
||||
`WHERE "n"."nspname" = '${dbTable["table_schema"]}' AND "t"."typname" = '${this.buildEnumName(table, tableColumn, false, true)}'`;
|
||||
const results: ObjectLiteral[] = await this.query(sql);
|
||||
tableColumn.enum = results.map(result => result["value"]);
|
||||
}
|
||||
@ -1938,8 +1962,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
* Builds create ENUM type sql.
|
||||
*/
|
||||
protected createEnumTypeSql(table: Table, column: TableColumn, enumName?: string): Query {
|
||||
if (!enumName)
|
||||
enumName = this.buildEnumName(table, column);
|
||||
if (!enumName) enumName = this.buildEnumName(table, column);
|
||||
const enumValues = column.enum!.map(value => `'${value.replace("'", "''")}'`).join(", ");
|
||||
return new Query(`CREATE TYPE ${enumName} AS ENUM(${enumValues})`);
|
||||
}
|
||||
@ -1948,8 +1971,7 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
* Builds create ENUM type sql.
|
||||
*/
|
||||
protected dropEnumTypeSql(table: Table, column: TableColumn, enumName?: string): Query {
|
||||
if (!enumName)
|
||||
enumName = this.buildEnumName(table, column);
|
||||
if (!enumName) enumName = this.buildEnumName(table, column);
|
||||
return new Query(`DROP TYPE ${enumName}`);
|
||||
}
|
||||
|
||||
@ -2089,20 +2111,12 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
/**
|
||||
* Builds ENUM type name from given table and column.
|
||||
*/
|
||||
protected buildEnumName(table: Table, columnOrName: TableColumn|string, withSchema: boolean = true, disableEscape?: boolean, toOld?: boolean): string {
|
||||
/**
|
||||
* If enumName is specified in column options then use it instead
|
||||
*/
|
||||
if (columnOrName instanceof TableColumn && columnOrName.enumName) {
|
||||
let enumName = columnOrName.enumName;
|
||||
if (toOld)
|
||||
enumName = enumName + "_old";
|
||||
return disableEscape ? enumName : `"${enumName}"`;
|
||||
}
|
||||
const columnName = columnOrName instanceof TableColumn ? columnOrName.name : columnOrName;
|
||||
protected buildEnumName(table: Table, column: TableColumn, withSchema: boolean = true, disableEscape?: boolean, toOld?: boolean): string {
|
||||
const schema = table.name.indexOf(".") === -1 ? this.driver.options.schema : table.name.split(".")[0];
|
||||
const tableName = table.name.indexOf(".") === -1 ? table.name : table.name.split(".")[1];
|
||||
let enumName = schema && withSchema ? `${schema}.${tableName}_${columnName.toLowerCase()}_enum` : `${tableName}_${columnName.toLowerCase()}_enum`;
|
||||
let enumName = column.enumName ? column.enumName : `${tableName}_${column.name.toLowerCase()}_enum`;
|
||||
if (schema && withSchema)
|
||||
enumName = `${schema}.${enumName}`
|
||||
if (toOld)
|
||||
enumName = enumName + "_old";
|
||||
return enumName.split(".").map(i => {
|
||||
@ -2120,9 +2134,19 @@ export class PostgresQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
}
|
||||
const result = await this.query(`SELECT "udt_schema", "udt_name" ` +
|
||||
`FROM "information_schema"."columns" WHERE "table_schema" = '${schema}' AND "table_name" = '${name}' AND "column_name"='${column.name}'`);
|
||||
|
||||
// docs: https://www.postgresql.org/docs/current/xtypes.html
|
||||
// When you define a new base type, PostgreSQL automatically provides support for arrays of that type.
|
||||
// The array type typically has the same name as the base type with the underscore character (_) prepended.
|
||||
// ----
|
||||
// so, we must remove this underscore character from enum type name
|
||||
let udtName = result[0]["udt_name"]
|
||||
if (udtName.indexOf("_") === 0) {
|
||||
udtName = udtName.substr(1, udtName.length)
|
||||
}
|
||||
return {
|
||||
enumTypeSchema: result[0]["udt_schema"],
|
||||
enumTypeName: result[0]["udt_name"]
|
||||
enumTypeName: udtName
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -565,7 +565,7 @@ export abstract class AbstractSqliteDriver implements Driver {
|
||||
// console.log("precision:", tableColumn.precision, columnMetadata.precision);
|
||||
// console.log("scale:", tableColumn.scale, columnMetadata.scale);
|
||||
// console.log("comment:", tableColumn.comment, columnMetadata.comment);
|
||||
// console.log("default:", tableColumn.default, columnMetadata.default);
|
||||
// console.log("default:", this.normalizeDefault(columnMetadata), columnMetadata.default);
|
||||
// console.log("isPrimary:", tableColumn.isPrimary, columnMetadata.isPrimary);
|
||||
// console.log("isNullable:", tableColumn.isNullable, columnMetadata.isNullable);
|
||||
// console.log("isUnique:", tableColumn.isUnique, this.normalizeIsUnique(columnMetadata));
|
||||
|
||||
@ -846,7 +846,6 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen
|
||||
const enumMatch = sql.match(new RegExp("\"(" + tableColumn.name + ")\" varchar CHECK\\s*\\(\\s*\\1\\s+IN\\s*\\(('[^']+'(?:\\s*,\\s*'[^']+')+)\\s*\\)\\s*\\)"));
|
||||
if (enumMatch) {
|
||||
// This is an enum
|
||||
tableColumn.type = "simple-enum";
|
||||
tableColumn.enum = enumMatch[2].substr(1, enumMatch[2].length - 2).split("','");
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,20 +602,38 @@ export class SqlServerDriver implements Driver {
|
||||
if (!tableColumn)
|
||||
return false; // we don't need new columns, we only need exist and changed
|
||||
|
||||
return tableColumn.name !== columnMetadata.databaseName
|
||||
const isColumnChanged = tableColumn.name !== columnMetadata.databaseName
|
||||
|| tableColumn.type !== this.normalizeType(columnMetadata)
|
||||
|| tableColumn.length !== columnMetadata.length
|
||||
|| tableColumn.precision !== columnMetadata.precision
|
||||
|| tableColumn.scale !== columnMetadata.scale
|
||||
// || tableColumn.comment !== columnMetadata.comment || // todo
|
||||
|| (!tableColumn.isGenerated && this.lowerDefaultValueIfNessesary(this.normalizeDefault(columnMetadata)) !== this.lowerDefaultValueIfNessesary(tableColumn.default)) // we included check for generated here, because generated columns already can have default values
|
||||
|| tableColumn.isGenerated !== columnMetadata.isGenerated
|
||||
|| (!tableColumn.isGenerated && this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata)) !== this.lowerDefaultValueIfNecessary(tableColumn.default)) // we included check for generated here, because generated columns already can have default values
|
||||
|| tableColumn.isPrimary !== columnMetadata.isPrimary
|
||||
|| tableColumn.isNullable !== columnMetadata.isNullable
|
||||
|| tableColumn.isUnique !== this.normalizeIsUnique(columnMetadata)
|
||||
|| tableColumn.isGenerated !== columnMetadata.isGenerated;
|
||||
|| tableColumn.isUnique !== this.normalizeIsUnique(columnMetadata);
|
||||
|
||||
// DEBUG SECTION
|
||||
// if (isColumnChanged) {
|
||||
// console.log("table:", columnMetadata.entityMetadata.tableName);
|
||||
// console.log("name:", tableColumn.name, columnMetadata.databaseName);
|
||||
// console.log("type:", tableColumn.type, this.normalizeType(columnMetadata));
|
||||
// console.log("length:", tableColumn.length, columnMetadata.length);
|
||||
// console.log("precision:", tableColumn.precision, columnMetadata.precision);
|
||||
// console.log("scale:", tableColumn.scale, columnMetadata.scale);
|
||||
// console.log("isGenerated:", tableColumn.isGenerated, columnMetadata.isGenerated);
|
||||
// console.log("isGenerated 2:", !tableColumn.isGenerated && this.lowerDefaultValueIfNecessary(this.normalizeDefault(columnMetadata)) !== this.lowerDefaultValueIfNecessary(tableColumn.default));
|
||||
// console.log("isPrimary:", tableColumn.isPrimary, columnMetadata.isPrimary);
|
||||
// console.log("isNullable:", tableColumn.isNullable, columnMetadata.isNullable);
|
||||
// console.log("isUnique:", tableColumn.isUnique, this.normalizeIsUnique(columnMetadata));
|
||||
// console.log("==========================================");
|
||||
// }
|
||||
|
||||
return isColumnChanged
|
||||
});
|
||||
}
|
||||
private lowerDefaultValueIfNessesary(value: string | undefined) {
|
||||
private lowerDefaultValueIfNecessary(value: string | undefined) {
|
||||
// SqlServer saves function calls in default value as lowercase https://github.com/typeorm/typeorm/issues/2733
|
||||
if (!value) {
|
||||
return value;
|
||||
|
||||
@ -1710,11 +1710,10 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
// Check if this is an enum
|
||||
const columnCheckConstraints = columnConstraints.filter(constraint => constraint["CONSTRAINT_TYPE"] === "CHECK");
|
||||
if (columnCheckConstraints.length) {
|
||||
const isEnumRegexp = new RegExp("^\\(\\[" + tableColumn.name + "\\]='[^']+'(?: OR \\[" + tableColumn.name + "\\]='[^']+')*\\)$");
|
||||
// const isEnumRegexp = new RegExp("^\\(\\[" + tableColumn.name + "\\]='[^']+'(?: OR \\[" + tableColumn.name + "\\]='[^']+')*\\)$");
|
||||
for (const checkConstraint of columnCheckConstraints) {
|
||||
if (isEnumRegexp.test(checkConstraint["definition"])) {
|
||||
if (this.isEnumCheckConstraint(checkConstraint["CONSTRAINT_NAME"])) {
|
||||
// This is an enum constraint, make column into an enum
|
||||
tableColumn.type = "simple-enum";
|
||||
tableColumn.enum = [];
|
||||
const enumValueRegexp = new RegExp("\\[" + tableColumn.name + "\\]='([^']+)'", "g");
|
||||
let result;
|
||||
@ -1775,13 +1774,15 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
&& dbConstraint["CONSTRAINT_TYPE"] === "CHECK";
|
||||
}), dbConstraint => dbConstraint["CONSTRAINT_NAME"]);
|
||||
|
||||
table.checks = tableCheckConstraints.map(constraint => {
|
||||
const checks = dbConstraints.filter(dbC => dbC["CONSTRAINT_NAME"] === constraint["CONSTRAINT_NAME"]);
|
||||
return new TableCheck({
|
||||
name: constraint["CONSTRAINT_NAME"],
|
||||
columnNames: checks.map(c => c["COLUMN_NAME"]),
|
||||
expression: constraint["definition"]
|
||||
});
|
||||
table.checks = tableCheckConstraints
|
||||
.filter(constraint => !this.isEnumCheckConstraint(constraint["CONSTRAINT_NAME"]))
|
||||
.map(constraint => {
|
||||
const checks = dbConstraints.filter(dbC => dbC["CONSTRAINT_NAME"] === constraint["CONSTRAINT_NAME"]);
|
||||
return new TableCheck({
|
||||
name: constraint["CONSTRAINT_NAME"],
|
||||
columnNames: checks.map(c => c["COLUMN_NAME"]),
|
||||
expression: constraint["definition"]
|
||||
});
|
||||
});
|
||||
|
||||
// find foreign key constraints of table, group them by constraint name and build TableForeignKey.
|
||||
@ -2125,8 +2126,11 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
protected buildCreateColumnSql(table: Table, column: TableColumn, skipIdentity: boolean, createDefault: boolean) {
|
||||
let c = `"${column.name}" ${this.connection.driver.createFullType(column)}`;
|
||||
|
||||
if (column.enum)
|
||||
c += " CHECK( " + column.name + " IN (" + column.enum.map(val => "'" + val + "'").join(",") + ") )";
|
||||
if (column.enum) {
|
||||
const expression = column.name + " IN (" + column.enum.map(val => "'" + val + "'").join(",") + ")";
|
||||
const checkName = this.connection.namingStrategy.checkConstraintName(table, expression, true)
|
||||
c += ` CONSTRAINT ${checkName} CHECK(${expression})`;
|
||||
}
|
||||
|
||||
if (column.collation)
|
||||
c += " COLLATE " + column.collation;
|
||||
@ -2151,6 +2155,10 @@ export class SqlServerQueryRunner extends BaseQueryRunner implements QueryRunner
|
||||
return c;
|
||||
}
|
||||
|
||||
protected isEnumCheckConstraint(name: string): boolean {
|
||||
return name.indexOf("CHK_") !== -1 && name.indexOf("_ENUM") !== -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts MssqlParameter into real mssql parameter type.
|
||||
*/
|
||||
|
||||
@ -87,6 +87,8 @@ export class EntityMetadataValidator {
|
||||
throw new DataTypeNotSupportedError(column, normalizedColumn, driver.options.type);
|
||||
if (column.length && driver.withLengthColumnTypes.indexOf(normalizedColumn) === -1)
|
||||
throw new Error(`Column ${column.propertyName} of Entity ${entityMetadata.name} does not support length property.`);
|
||||
if (column.type === "enum" && !column.enum && !column.enumName)
|
||||
throw new Error(`Column "${column.propertyName}" of Entity "${entityMetadata.name}" is defined as enum, but missing "enum" or "enumName" properties.`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -83,6 +83,8 @@ export class JunctionEntityMetadataBuilder {
|
||||
collation: referencedColumn.collation,
|
||||
zerofill: referencedColumn.zerofill,
|
||||
unsigned: referencedColumn.zerofill ? true : referencedColumn.unsigned,
|
||||
enum: referencedColumn.enum,
|
||||
enumName: referencedColumn.enumName,
|
||||
nullable: false,
|
||||
primary: true,
|
||||
}
|
||||
@ -121,6 +123,8 @@ export class JunctionEntityMetadataBuilder {
|
||||
collation: inverseReferencedColumn.collation,
|
||||
zerofill: inverseReferencedColumn.zerofill,
|
||||
unsigned: inverseReferencedColumn.zerofill ? true : inverseReferencedColumn.unsigned,
|
||||
enum: inverseReferencedColumn.enum,
|
||||
enumName: inverseReferencedColumn.enumName,
|
||||
name: columnName,
|
||||
nullable: false,
|
||||
primary: true,
|
||||
|
||||
@ -159,8 +159,10 @@ export class RelationJoinColumnBuilder {
|
||||
zerofill: referencedColumn.zerofill,
|
||||
unsigned: referencedColumn.unsigned,
|
||||
comment: referencedColumn.comment,
|
||||
enum: referencedColumn.enum,
|
||||
enumName: referencedColumn.enumName,
|
||||
primary: relation.isPrimary,
|
||||
nullable: relation.isNullable
|
||||
nullable: relation.isNullable,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -29,7 +29,7 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
|
||||
columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string {
|
||||
const name = customName || propertyName;
|
||||
|
||||
|
||||
if (embeddedPrefixes.length)
|
||||
return camelCase(embeddedPrefixes.join("_")) + titleCase(name);
|
||||
|
||||
@ -103,11 +103,12 @@ export class DefaultNamingStrategy implements NamingStrategyInterface {
|
||||
return "IDX_" + RandomGenerator.sha1(key).substr(0, 26);
|
||||
}
|
||||
|
||||
checkConstraintName(tableOrName: Table|string, expression: string): string {
|
||||
checkConstraintName(tableOrName: Table|string, expression: string, isEnum?: boolean): string {
|
||||
const tableName = tableOrName instanceof Table ? tableOrName.name : tableOrName;
|
||||
const replacedTableName = tableName.replace(".", "_");
|
||||
const key = `${replacedTableName}_${expression}`;
|
||||
return "CHK_" + RandomGenerator.sha1(key).substr(0, 26);
|
||||
const name = "CHK_" + RandomGenerator.sha1(key).substr(0, 26);
|
||||
return isEnum ? `${name}_ENUM` : name;
|
||||
}
|
||||
|
||||
exclusionConstraintName(tableOrName: Table|string, expression: string): string {
|
||||
|
||||
@ -69,8 +69,13 @@ export interface NamingStrategyInterface {
|
||||
|
||||
/**
|
||||
* Gets the name of the check constraint.
|
||||
*
|
||||
* "isEnum" parameter is used to indicate if this check constraint used
|
||||
* to handle "simple-enum" type for databases that are not supporting "enum"
|
||||
* type out of the box. If "true", constraint is ignored during CHECK constraints
|
||||
* synchronization.
|
||||
*/
|
||||
checkConstraintName(tableOrName: Table|string, expression: string): string;
|
||||
checkConstraintName(tableOrName: Table|string, expression: string, isEnum?: boolean): string;
|
||||
|
||||
/**
|
||||
* Gets the name of the exclusion constraint.
|
||||
|
||||
@ -155,11 +155,11 @@ describe("database schema > column types > mssql", () => { // https://github.com
|
||||
table!.findColumnByName("geometry1")!.type.should.be.equal("geometry");
|
||||
table!.findColumnByName("simpleArray")!.type.should.be.equal("ntext");
|
||||
table!.findColumnByName("simpleJson")!.type.should.be.equal("ntext");
|
||||
table!.findColumnByName("simpleEnum")!.type.should.be.equal("simple-enum");
|
||||
table!.findColumnByName("simpleEnum")!.type.should.be.equal("nvarchar");
|
||||
table!.findColumnByName("simpleEnum")!.enum![0].should.be.equal("A");
|
||||
table!.findColumnByName("simpleEnum")!.enum![1].should.be.equal("B");
|
||||
table!.findColumnByName("simpleEnum")!.enum![2].should.be.equal("C");
|
||||
table!.findColumnByName("simpleClassEnum1")!.type.should.be.equal("simple-enum");
|
||||
table!.findColumnByName("simpleClassEnum1")!.type.should.be.equal("nvarchar");
|
||||
table!.findColumnByName("simpleClassEnum1")!.enum![0].should.be.equal("apple");
|
||||
table!.findColumnByName("simpleClassEnum1")!.enum![1].should.be.equal("pineapple");
|
||||
table!.findColumnByName("simpleClassEnum1")!.enum![2].should.be.equal("banana");
|
||||
|
||||
@ -11,9 +11,12 @@ export class Post {
|
||||
@Column("enum", { enum: ["A", "B", "C"] })
|
||||
enum: string;
|
||||
|
||||
@Column("enum", { enum: ["A", "B", "C"], array: true })
|
||||
enumArray: string[];
|
||||
|
||||
@Column("simple-enum", { enum: ["A", "B", "C"] })
|
||||
simpleEnum: string;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,15 +26,19 @@ describe("database schema > column types > postgres-enum", () => {
|
||||
|
||||
const post = new Post();
|
||||
post.enum = "A";
|
||||
post.enumArray = ["A", "B"];
|
||||
post.simpleEnum = "A";
|
||||
post.name = "Post #1";
|
||||
await postRepository.save(post);
|
||||
|
||||
const loadedPost = (await postRepository.findOne(1))!;
|
||||
loadedPost.enum.should.be.equal(post.enum);
|
||||
loadedPost.enumArray.should.be.deep.equal(post.enumArray);
|
||||
loadedPost.simpleEnum.should.be.equal(post.simpleEnum);
|
||||
|
||||
table!.findColumnByName("enum")!.type.should.be.equal("enum");
|
||||
table!.findColumnByName("enumArray")!.type.should.be.equal("enum");
|
||||
table!.findColumnByName("enumArray")!.isArray.should.be.true;
|
||||
table!.findColumnByName("simpleEnum")!.type.should.be.equal("enum");
|
||||
})));
|
||||
|
||||
@ -170,7 +174,30 @@ describe("database schema > column types > postgres-enum", () => {
|
||||
await queryRunner.release();
|
||||
})));
|
||||
|
||||
it("should change ENUM column and revert change", () => Promise.all(connections.map(async connection => {
|
||||
it("should change ENUM array column in to non-array and revert change", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
let table = await queryRunner.getTable("post");
|
||||
let enumColumn = table!.findColumnByName("enumArray")!;
|
||||
let changedColumn = enumColumn.clone();
|
||||
changedColumn.isArray = false;
|
||||
|
||||
await queryRunner.changeColumn(table!, enumColumn, changedColumn);
|
||||
|
||||
table = await queryRunner.getTable("post");
|
||||
changedColumn = table!.findColumnByName("enumArray")!;
|
||||
changedColumn.isArray.should.be.false;
|
||||
|
||||
await queryRunner.executeMemoryDownSql();
|
||||
|
||||
table = await queryRunner.getTable("post");
|
||||
enumColumn = table!.findColumnByName("enumArray")!;
|
||||
enumColumn.isArray.should.be.true;
|
||||
|
||||
await queryRunner.release();
|
||||
})));
|
||||
|
||||
it("should change ENUM value and revert change", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
let table = await queryRunner.getTable("post");
|
||||
@ -191,6 +218,94 @@ describe("database schema > column types > postgres-enum", () => {
|
||||
await queryRunner.release();
|
||||
})));
|
||||
|
||||
it("should change `enumName` and revert change", () => Promise.all(connections.map(async connection => {
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
|
||||
// add `enumName`
|
||||
let table = await queryRunner.getTable("post");
|
||||
const column = table!.findColumnByName("enum")!;
|
||||
const newColumn = column.clone();
|
||||
newColumn.enumName = "PostTypeEnum"
|
||||
|
||||
// change column
|
||||
await queryRunner.changeColumn(table!, column, newColumn)
|
||||
|
||||
// check if `enumName` changed
|
||||
table = await queryRunner.getTable("post");
|
||||
let changedColumn = table!.findColumnByName("enum")!;
|
||||
expect(changedColumn.enumName).to.equal("PostTypeEnum");
|
||||
|
||||
// revert changes
|
||||
await queryRunner.executeMemoryDownSql()
|
||||
|
||||
// check if `enumName` reverted
|
||||
table = await queryRunner.getTable("post");
|
||||
changedColumn = table!.findColumnByName("enum")!;
|
||||
expect(changedColumn.enumName).to.undefined;
|
||||
|
||||
await queryRunner.release();
|
||||
})));
|
||||
|
||||
it("should not create new type if same `enumName` is used more than once", () => Promise.all(connections.map(async connection => {
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
|
||||
const table = new Table({
|
||||
name: "my_table",
|
||||
columns: [
|
||||
{
|
||||
name: "enum1",
|
||||
type: "enum",
|
||||
enum: ["Apple", "Banana", "Cherry"],
|
||||
enumName: "Fruits"
|
||||
},
|
||||
{
|
||||
name: "enum2",
|
||||
type: "enum",
|
||||
enum: ["Apple", "Banana", "Cherry"],
|
||||
enumName: "Fruits"
|
||||
},
|
||||
{
|
||||
name: "enum3",
|
||||
type: "enum",
|
||||
enumName: "Fruits"
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
await queryRunner.createTable(table)
|
||||
|
||||
// revert changes
|
||||
await queryRunner.executeMemoryDownSql()
|
||||
|
||||
await queryRunner.release();
|
||||
})));
|
||||
|
||||
it("should change both ENUM value and ENUM name and revert change", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
let table = await queryRunner.getTable("post");
|
||||
const enumColumn = table!.findColumnByName("enum")!;
|
||||
const changedColumn = enumColumn.clone();
|
||||
changedColumn.enum = ["C", "D", "E"];
|
||||
changedColumn.enumName = "my_enum_type";
|
||||
|
||||
await queryRunner.changeColumn(table!, enumColumn, changedColumn);
|
||||
|
||||
table = await queryRunner.getTable("post");
|
||||
const columnAfterChange = table!.findColumnByName("enum")!
|
||||
columnAfterChange.enum!.should.be.eql(["C", "D", "E"]);
|
||||
columnAfterChange.enumName!.should.be.eql("my_enum_type");
|
||||
|
||||
await queryRunner.executeMemoryDownSql();
|
||||
|
||||
table = await queryRunner.getTable("post");
|
||||
const columnAfterRevert = table!.findColumnByName("enum")!
|
||||
columnAfterRevert.enum!.should.be.eql(["A", "B", "C"]);
|
||||
expect(columnAfterRevert.enumName).to.undefined
|
||||
|
||||
await queryRunner.release();
|
||||
})));
|
||||
|
||||
it("should rename ENUM when column renamed and revert rename", () => Promise.all(connections.map(async connection => {
|
||||
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
|
||||
@ -128,11 +128,11 @@ describe("database schema > column types > sqlite", () => {
|
||||
table!.findColumnByName("datetime")!.type.should.be.equal("datetime");
|
||||
table!.findColumnByName("simpleArray")!.type.should.be.equal("text");
|
||||
table!.findColumnByName("simpleJson")!.type.should.be.equal("text");
|
||||
table!.findColumnByName("simpleEnum")!.type.should.be.equal("simple-enum");
|
||||
table!.findColumnByName("simpleEnum")!.type.should.be.equal("varchar");
|
||||
table!.findColumnByName("simpleEnum")!.enum![0].should.be.equal("A");
|
||||
table!.findColumnByName("simpleEnum")!.enum![1].should.be.equal("B");
|
||||
table!.findColumnByName("simpleEnum")!.enum![2].should.be.equal("C");
|
||||
table!.findColumnByName("simpleClassEnum1")!.type.should.be.equal("simple-enum");
|
||||
table!.findColumnByName("simpleClassEnum1")!.type.should.be.equal("varchar");
|
||||
table!.findColumnByName("simpleClassEnum1")!.enum![0].should.be.equal("apple");
|
||||
table!.findColumnByName("simpleClassEnum1")!.enum![1].should.be.equal("pineapple");
|
||||
table!.findColumnByName("simpleClassEnum1")!.enum![2].should.be.equal("banana");
|
||||
|
||||
@ -86,4 +86,12 @@ export class EnumEntity {
|
||||
})
|
||||
enumWithoutdefault: StringEnum;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: StringEnum,
|
||||
nullable: true,
|
||||
default: null,
|
||||
})
|
||||
nullableDefaultEnum: StringEnum;
|
||||
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ describe("database schema > enums", () => {
|
||||
before(async () => {
|
||||
connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
enabledDrivers: ["postgres", "mysql"]
|
||||
enabledDrivers: ["postgres", "mysql", "mariadb"]
|
||||
});
|
||||
});
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
@ -60,4 +60,14 @@ describe("database schema > enums", () => {
|
||||
|
||||
})));
|
||||
|
||||
it("should not generate queries when no model changes", () => Promise.all(connections.map(async connection => {
|
||||
await connection.driver.createSchemaBuilder().build();
|
||||
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
|
||||
sqlInMemory.upQueries.length.should.be.equal(0);
|
||||
sqlInMemory.downQueries.length.should.be.equal(0);
|
||||
|
||||
})));
|
||||
|
||||
});
|
||||
|
||||
33
test/github-issues/4897/entity/SomeEntity.ts
Normal file
33
test/github-issues/4897/entity/SomeEntity.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {Column, PrimaryGeneratedColumn} from "../../../../src";
|
||||
import {Entity} from "../../../../src";
|
||||
|
||||
export type UserRoleType = 'user' | 'admin';
|
||||
export const userRoles = {
|
||||
USER: 'user' as UserRoleType,
|
||||
ADMIN: 'admin' as UserRoleType,
|
||||
}
|
||||
|
||||
export enum UserRoles {
|
||||
USER = 'user',
|
||||
ADMIN = 'admin'
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class SomeEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({
|
||||
type: "simple-enum",
|
||||
enum: Object.values(userRoles),
|
||||
default: userRoles.USER,
|
||||
})
|
||||
test: UserRoleType;
|
||||
|
||||
@Column({
|
||||
type: "simple-enum",
|
||||
enum: UserRoles,
|
||||
default: UserRoles.USER,
|
||||
})
|
||||
test2: UserRoles;
|
||||
}
|
||||
30
test/github-issues/4897/issue-4897.ts
Normal file
30
test/github-issues/4897/issue-4897.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import "reflect-metadata";
|
||||
import {Connection} from "../../../src";
|
||||
import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils";
|
||||
import {SomeEntity} from "./entity/SomeEntity";
|
||||
|
||||
describe("github issues > #4897 [MSSQL] Enum column definition removes and recreates constraint overwritting existing data", () => {
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
migrations: [],
|
||||
enabledDrivers: ["mssql", "sqlite"],
|
||||
schemaCreate: false,
|
||||
dropSchema: true,
|
||||
entities: [SomeEntity],
|
||||
}));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should recognize model changes", () => Promise.all(connections.map(async connection => {
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.greaterThan(0);
|
||||
sqlInMemory.downQueries.length.should.be.greaterThan(0);
|
||||
})));
|
||||
|
||||
it("should not generate queries when no model changes", () => Promise.all(connections.map(async connection => {
|
||||
await connection.driver.createSchemaBuilder().build();
|
||||
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.equal(0);
|
||||
sqlInMemory.downQueries.length.should.be.equal(0);
|
||||
})));
|
||||
});
|
||||
23
test/github-issues/5275/entity/UserEntity.ts
Normal file
23
test/github-issues/5275/entity/UserEntity.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {Column, Entity, PrimaryColumn} from "../../../../src";
|
||||
|
||||
export enum Role {
|
||||
GuildMaster = "Guild Master",
|
||||
Officer = "Officer",
|
||||
Boss = 'BOSS "LEVEL 80"',
|
||||
Warrior = "Knight\\Rogue",
|
||||
Number = 1,
|
||||
PlayerAlt = "Player Alt"
|
||||
}
|
||||
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: "enum", enum: Role, default: Role.GuildMaster })
|
||||
role: Role;
|
||||
|
||||
@Column({ type: "enum", enum: Role, default: [Role.GuildMaster], array: true })
|
||||
roles: Role[];
|
||||
}
|
||||
59
test/github-issues/5275/issue-5275.ts
Normal file
59
test/github-issues/5275/issue-5275.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import "reflect-metadata";
|
||||
import { Connection } from "../../../src";
|
||||
import {
|
||||
closeTestingConnections,
|
||||
createTestingConnections,
|
||||
reloadTestingDatabases,
|
||||
} from "../../utils/test-utils";
|
||||
import {Role, User} from "./entity/UserEntity";
|
||||
|
||||
describe("github issues > #5275 Enums with spaces are not converted properly.", () => {
|
||||
let connections: Connection[];
|
||||
before(
|
||||
async () =>
|
||||
(connections = await createTestingConnections({
|
||||
entities: [User],
|
||||
schemaCreate: true,
|
||||
dropSchema: true,
|
||||
enabledDrivers: ["postgres"],
|
||||
}))
|
||||
);
|
||||
|
||||
beforeEach(() => reloadTestingDatabases(connections));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should correctly parse enums of strings with spaces", () => Promise.all(connections.map(async connection => {
|
||||
const userRepository = connection.getRepository(User);
|
||||
await userRepository.save({
|
||||
id: 1,
|
||||
roles: [
|
||||
Role.GuildMaster,
|
||||
Role.Officer,
|
||||
Role.Boss,
|
||||
Role.Warrior,
|
||||
Role.Number,
|
||||
Role.PlayerAlt,
|
||||
],
|
||||
});
|
||||
const user = await userRepository.findOneOrFail(1);
|
||||
user.roles.should.deep.equal(["Guild Master", "Officer", 'BOSS "LEVEL 80"', "Knight\\Rogue", 1, "Player Alt"]);
|
||||
})));
|
||||
|
||||
it("should correctly parse non-array enums with spaces", () => Promise.all(connections.map(async connection => {
|
||||
const userRepository = connection.getRepository(User);
|
||||
await userRepository.save([
|
||||
{ id: 1 },
|
||||
{ id: 2, role: Role.Boss },
|
||||
{ id: 3, role: Role.Warrior }
|
||||
]);
|
||||
|
||||
const user1 = await userRepository.findOneOrFail(1);
|
||||
user1.role.should.equal("Guild Master");
|
||||
|
||||
const user2 = await userRepository.findOneOrFail(2);
|
||||
user2.role.should.equal('BOSS "LEVEL 80"');
|
||||
|
||||
const user3 = await userRepository.findOneOrFail(3);
|
||||
user3.role.should.equal("Knight\\Rogue");
|
||||
})));
|
||||
});
|
||||
16
test/github-issues/5478/entity/UserEntity.ts
Normal file
16
test/github-issues/5478/entity/UserEntity.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {Column, PrimaryGeneratedColumn} from "../../../../src";
|
||||
import {Entity} from "../../../../src";
|
||||
|
||||
enum UserType {
|
||||
ADMIN = "ADMIN",
|
||||
USER = "USER",
|
||||
}
|
||||
|
||||
@Entity("user")
|
||||
export class UserEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: "enum", enum: UserType })
|
||||
userType: UserType;
|
||||
}
|
||||
45
test/github-issues/5478/issue-5478.ts
Normal file
45
test/github-issues/5478/issue-5478.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import "reflect-metadata";
|
||||
import {Connection} from "../../../src";
|
||||
import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils";
|
||||
import {expect} from "chai";
|
||||
import {UserEntity} from "./entity/UserEntity";
|
||||
|
||||
describe("github issues > #5478 Setting enumName doesn't change how migrations get generated", () => {
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
migrations: [],
|
||||
enabledDrivers: ["postgres"],
|
||||
schemaCreate: true,
|
||||
dropSchema: true,
|
||||
entities: [UserEntity],
|
||||
}));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should correctly rename enum", () => Promise.all(connections.map(async connection => {
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
|
||||
// add `enumName`
|
||||
let table = await queryRunner.getTable("user");
|
||||
const column = table!.findColumnByName("userType")!;
|
||||
const newColumn = column.clone();
|
||||
newColumn.enumName = "UserTypeEnum"
|
||||
|
||||
// change column
|
||||
await queryRunner.changeColumn(table!, column, newColumn)
|
||||
|
||||
// check if `enumName` changed
|
||||
table = await queryRunner.getTable("user");
|
||||
let changedColumn = table!.findColumnByName("userType")!;
|
||||
expect(changedColumn.enumName).to.equal("UserTypeEnum");
|
||||
|
||||
// revert changes
|
||||
await queryRunner.executeMemoryDownSql()
|
||||
|
||||
// check if `enumName` reverted
|
||||
table = await queryRunner.getTable("user");
|
||||
changedColumn = table!.findColumnByName("userType")!;
|
||||
expect(changedColumn.enumName).to.undefined;
|
||||
|
||||
await queryRunner.release();
|
||||
})));
|
||||
});
|
||||
16
test/github-issues/5871/entity/SomeEntity.ts
Normal file
16
test/github-issues/5871/entity/SomeEntity.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {Column, PrimaryGeneratedColumn} from "../../../../src";
|
||||
import {Entity} from "../../../../src";
|
||||
|
||||
enum Test {
|
||||
TEST1 = 'testing (brackets)',
|
||||
TEST2 = 'testing (brackers too)',
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class SomeEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: "enum", enum: Test })
|
||||
test: Test;
|
||||
}
|
||||
30
test/github-issues/5871/issue-5871.ts
Normal file
30
test/github-issues/5871/issue-5871.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import "reflect-metadata";
|
||||
import {Connection} from "../../../src";
|
||||
import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils";
|
||||
import {SomeEntity} from "./entity/SomeEntity";
|
||||
|
||||
describe("github issues > #5871 Migration generate does not play well with mysql enum with parentheses in the enum value", () => {
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
migrations: [],
|
||||
enabledDrivers: ["mysql"],
|
||||
schemaCreate: false,
|
||||
dropSchema: true,
|
||||
entities: [SomeEntity],
|
||||
}));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should recognize model changes", () => Promise.all(connections.map(async connection => {
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.greaterThan(0);
|
||||
sqlInMemory.downQueries.length.should.be.greaterThan(0);
|
||||
})));
|
||||
|
||||
it("should not generate queries when no model changes", () => Promise.all(connections.map(async connection => {
|
||||
await connection.driver.createSchemaBuilder().build();
|
||||
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.equal(0);
|
||||
sqlInMemory.downQueries.length.should.be.equal(0);
|
||||
})));
|
||||
});
|
||||
29
test/github-issues/6115/entity/v1/MetricEntity.ts
Normal file
29
test/github-issues/6115/entity/v1/MetricEntity.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Column, PrimaryGeneratedColumn } from "../../../../../src";
|
||||
import { Entity } from "../../../../../src";
|
||||
|
||||
export enum Operator {
|
||||
LT = "lt",
|
||||
LE = "le",
|
||||
EQ = "eq",
|
||||
NE = "ne",
|
||||
GE = "ge",
|
||||
GT = "gt"
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class Metric {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ type: "enum", enum: Operator, default: Operator.EQ })
|
||||
defaultOperator!: string;
|
||||
|
||||
@Column({ type: "enum", enum: Operator })
|
||||
defaultOperator2!: string;
|
||||
|
||||
@Column({ type: "enum", enum: Operator, default: Operator.EQ })
|
||||
defaultOperator3!: string;
|
||||
|
||||
@Column({ type: "enum", enum: Operator, default: Operator.EQ })
|
||||
defaultOperator4!: string;
|
||||
}
|
||||
29
test/github-issues/6115/entity/v2/MetricEntity.ts
Normal file
29
test/github-issues/6115/entity/v2/MetricEntity.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Column, PrimaryGeneratedColumn } from "../../../../../src";
|
||||
import { Entity } from "../../../../../src";
|
||||
|
||||
export enum Operator {
|
||||
LT = "lessthan",
|
||||
LE = "lessequal",
|
||||
EQ = "equal",
|
||||
NE = "notequal",
|
||||
GE = "greaterequal",
|
||||
GT = "greaterthan"
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class Metric {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ type: "enum", enum: Operator, default: Operator.EQ })
|
||||
defaultOperator!: string;
|
||||
|
||||
@Column({ type: "enum", enum: Operator, default: Operator.EQ })
|
||||
defaultOperator2!: string;
|
||||
|
||||
@Column({ type: "enum", enum: Operator })
|
||||
defaultOperator3!: string;
|
||||
|
||||
@Column({ type: "enum", enum: Operator, default: Operator.GT })
|
||||
defaultOperator4!: string;
|
||||
}
|
||||
87
test/github-issues/6115/issue-6115.ts
Normal file
87
test/github-issues/6115/issue-6115.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import "reflect-metadata";
|
||||
import {Connection, createConnection} from "../../../src";
|
||||
import {closeTestingConnections, createTestingConnections, setupSingleTestingConnection} from "../../utils/test-utils";
|
||||
import {fail} from "assert";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe("github issues > #6115 Down migration for enums with defaults are wrong", () => {
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
enabledDrivers: ["postgres"],
|
||||
entities: [__dirname + "/entity/v1/*{.js,.ts}"],
|
||||
dropSchema: true,
|
||||
schemaCreate: true
|
||||
}));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should change schema when enum definition changes", () => Promise.all(connections.map(async _connection => {
|
||||
const options = setupSingleTestingConnection(
|
||||
_connection.options.type,
|
||||
{
|
||||
name: `${_connection.name}-v2`,
|
||||
entities: [__dirname + "/entity/v2/*{.js,.ts}"],
|
||||
dropSchema: false,
|
||||
schemaCreate: false
|
||||
}
|
||||
);
|
||||
if (!options) {
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
|
||||
const connection = await createConnection(options);
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
|
||||
const sqlInMemory = await connection.driver
|
||||
.createSchemaBuilder()
|
||||
.log();
|
||||
|
||||
const upQueries = sqlInMemory.upQueries.map(
|
||||
query => query.query
|
||||
);
|
||||
const downQueries = sqlInMemory.downQueries.map(
|
||||
query => query.query
|
||||
);
|
||||
|
||||
// update entity
|
||||
for (const query of upQueries) {
|
||||
await connection.query(query)
|
||||
}
|
||||
|
||||
let table = await queryRunner.getTable("metric");
|
||||
let defaultOperator = table!.findColumnByName("defaultOperator");
|
||||
expect(defaultOperator!.enum).to.deep.equal(["lessthan", "lessequal", "equal", "notequal", "greaterequal", "greaterthan"]);
|
||||
expect(defaultOperator!.default).to.equal(`'equal'`);
|
||||
|
||||
let defaultOperator2 = table!.findColumnByName("defaultOperator2");
|
||||
expect(defaultOperator2!.default).to.equal(`'equal'`);
|
||||
|
||||
let defaultOperator3 = table!.findColumnByName("defaultOperator3");
|
||||
expect(defaultOperator3!.default).to.be.undefined
|
||||
|
||||
let defaultOperator4 = table!.findColumnByName("defaultOperator4");
|
||||
expect(defaultOperator4!.default).to.equal(`'greaterthan'`);
|
||||
|
||||
// revert update
|
||||
for (const query of downQueries.reverse()) {
|
||||
await connection.query(query)
|
||||
}
|
||||
|
||||
table = await queryRunner.getTable("metric");
|
||||
defaultOperator = table!.findColumnByName("defaultOperator");
|
||||
expect(defaultOperator!.enum).to.deep.equal(["lt", "le", "eq", "ne", "ge", "gt"]);
|
||||
expect(defaultOperator!.default).to.equal(`'eq'`);
|
||||
|
||||
defaultOperator2 = table!.findColumnByName("defaultOperator2");
|
||||
expect(defaultOperator2!.default).to.be.undefined
|
||||
|
||||
defaultOperator3 = table!.findColumnByName("defaultOperator3");
|
||||
expect(defaultOperator3!.default).to.equal(`'eq'`);
|
||||
|
||||
defaultOperator4 = table!.findColumnByName("defaultOperator4");
|
||||
expect(defaultOperator4!.default).to.equal(`'eq'`);
|
||||
|
||||
await queryRunner.release();
|
||||
await connection.close();
|
||||
})));
|
||||
});
|
||||
35
test/github-issues/6471/entity/SomeEntity.ts
Normal file
35
test/github-issues/6471/entity/SomeEntity.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {Column, Unique, PrimaryGeneratedColumn} from "../../../../src";
|
||||
import {Entity} from "../../../../src";
|
||||
|
||||
export enum CreationMechanism {
|
||||
SOURCE_A = 'SOURCE_A',
|
||||
SOURCE_B = 'SOURCE_B',
|
||||
SOURCE_C = 'SOURCE_C',
|
||||
SOURCE_D = 'SOURCE_D'
|
||||
}
|
||||
|
||||
@Entity({ name: 'some_entity' })
|
||||
@Unique([
|
||||
'field1',
|
||||
'field2',
|
||||
])
|
||||
export class SomeEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
field1: string;
|
||||
|
||||
@Column()
|
||||
field2: string;
|
||||
|
||||
@Column({
|
||||
type : 'enum',
|
||||
enumName : 'creation_mechanism_enum',
|
||||
enum : CreationMechanism,
|
||||
})
|
||||
creationMechanism: CreationMechanism;
|
||||
|
||||
@Column({ nullable: false, default: () => 'now()' })
|
||||
createdAt: Date;
|
||||
}
|
||||
40
test/github-issues/6471/issue-6471.ts
Normal file
40
test/github-issues/6471/issue-6471.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import "reflect-metadata";
|
||||
import {Connection} from "../../../src";
|
||||
import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils";
|
||||
import {SomeEntity} from "./entity/SomeEntity";
|
||||
|
||||
describe("github issues > #6471 Postgres enum is recreated in every new generated migration", () => {
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
migrations: [],
|
||||
enabledDrivers: ["postgres"],
|
||||
schemaCreate: false,
|
||||
dropSchema: true,
|
||||
entities: [SomeEntity],
|
||||
}));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should recognize model changes", () => Promise.all(connections.map(async connection => {
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.greaterThan(0);
|
||||
sqlInMemory.downQueries.length.should.be.greaterThan(0);
|
||||
})));
|
||||
|
||||
it("should not generate queries when no model changes", () => Promise.all(connections.map(async connection => {
|
||||
await connection.driver.createSchemaBuilder().build();
|
||||
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.equal(0);
|
||||
sqlInMemory.downQueries.length.should.be.equal(0);
|
||||
})));
|
||||
|
||||
it("should handle `enumName` change", () => Promise.all(connections.map(async connection => {
|
||||
const entityMetadata = connection.getMetadata(SomeEntity)
|
||||
const columnMetadata = entityMetadata.columns.find(column => column.databaseName === "creationMechanism")
|
||||
columnMetadata!.enumName = "changed_enum_name"
|
||||
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.greaterThan(0);
|
||||
sqlInMemory.downQueries.length.should.be.greaterThan(0);
|
||||
})));
|
||||
});
|
||||
23
test/github-issues/7217/entity/UserEntity.ts
Normal file
23
test/github-issues/7217/entity/UserEntity.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {Column, Entity, PrimaryGeneratedColumn} from "../../../../src";
|
||||
|
||||
export enum UserRole {
|
||||
PLAYER = 'PLAYER',
|
||||
FULL_GAME = 'FULL_GAME',
|
||||
SUPERVISOR = 'SUPERVISOR',
|
||||
REPORTS = 'REPORTS',
|
||||
ADMIN = 'ADMIN',
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: UserRole,
|
||||
array: true,
|
||||
default: [UserRole.PLAYER],
|
||||
})
|
||||
roles: UserRole[];
|
||||
}
|
||||
34
test/github-issues/7217/issue-7217.ts
Normal file
34
test/github-issues/7217/issue-7217.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import "reflect-metadata";
|
||||
import {Connection} from "../../../src";
|
||||
import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils";
|
||||
import {User} from "./entity/UserEntity";
|
||||
|
||||
describe("github issues > #7217 Modifying enum fails migration if the enum is used in an array column", () => {
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
migrations: [],
|
||||
enabledDrivers: ["postgres"],
|
||||
schemaCreate: false,
|
||||
dropSchema: true,
|
||||
entities: [User],
|
||||
}));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should not generate queries when no model changes", () => Promise.all(connections.map(async connection => {
|
||||
await connection.driver.createSchemaBuilder().build();
|
||||
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.equal(0);
|
||||
sqlInMemory.downQueries.length.should.be.equal(0);
|
||||
})));
|
||||
|
||||
it("should correctly change enum", () => Promise.all(connections.map(async connection => {
|
||||
const metadata = connection.getMetadata(User)
|
||||
const columnMetadata = metadata.columns.find(column => column.databaseName === "roles")
|
||||
columnMetadata!.enum = ["PLAYER", "FULL_GAME"]
|
||||
|
||||
const sqlInMemory = await connection.driver.createSchemaBuilder().log();
|
||||
sqlInMemory.upQueries.length.should.be.greaterThan(0);
|
||||
sqlInMemory.downQueries.length.should.be.greaterThan(0);
|
||||
})));
|
||||
});
|
||||
15
test/github-issues/7283/entity/AccessEvent.ts
Normal file
15
test/github-issues/7283/entity/AccessEvent.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {BaseEntity, Entity, JoinTable, ManyToMany, ManyToOne, PrimaryColumn} from "../../../../src";
|
||||
import {Employee} from "./Employee";
|
||||
|
||||
@Entity()
|
||||
export class AccessEvent extends BaseEntity {
|
||||
@PrimaryColumn({ type: 'varchar', length: 128 })
|
||||
id!: string
|
||||
|
||||
@ManyToOne(() => Employee, (employee) => employee.accessEvents)
|
||||
employee!: Employee
|
||||
|
||||
@ManyToMany(() => Employee)
|
||||
@JoinTable()
|
||||
employees: Employee[];
|
||||
}
|
||||
16
test/github-issues/7283/entity/Employee.ts
Normal file
16
test/github-issues/7283/entity/Employee.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {BaseEntity, Entity, OneToMany, PrimaryColumn} from "../../../../src";
|
||||
import {AccessEvent} from "./AccessEvent";
|
||||
|
||||
enum Providers {
|
||||
MS_GRAPH = 'msGraph',
|
||||
ATLASSIAN = 'atlassian'
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class Employee extends BaseEntity {
|
||||
@PrimaryColumn({ type: 'enum', enum: Providers, enumName: "providerEnum" })
|
||||
provider!: Providers
|
||||
|
||||
@OneToMany(() => AccessEvent, (accessEvent) => accessEvent.employee)
|
||||
accessEvents!: AccessEvent[]
|
||||
}
|
||||
35
test/github-issues/7283/issue-7283.ts
Normal file
35
test/github-issues/7283/issue-7283.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import "reflect-metadata";
|
||||
import {Connection} from "../../../src";
|
||||
import {createTestingConnections, closeTestingConnections} from "../../utils/test-utils";
|
||||
import {AccessEvent} from "./entity/AccessEvent";
|
||||
import {Employee} from "./entity/Employee";
|
||||
import {expect} from "chai";
|
||||
|
||||
describe("github issues > #7283 Generating Migration on ManyToOne/OneToMany + Primary enum column results in missing enum type in migration output", () => {
|
||||
let connections: Connection[];
|
||||
before(async () => connections = await createTestingConnections({
|
||||
migrations: [],
|
||||
enabledDrivers: ["mysql", "mariadb", "postgres"],
|
||||
schemaCreate: false,
|
||||
dropSchema: true,
|
||||
entities: [AccessEvent, Employee],
|
||||
}));
|
||||
after(() => closeTestingConnections(connections));
|
||||
|
||||
it("should create tables with enum primary column", () => Promise.all(connections.map(async connection => {
|
||||
await connection.driver.createSchemaBuilder().build();
|
||||
const queryRunner = connection.createQueryRunner();
|
||||
|
||||
// ManyToOne
|
||||
const table = await queryRunner.getTable("access_event");
|
||||
const column = table!.findColumnByName("employeeProvider");
|
||||
expect(column!.enum).to.deep.equal([ "msGraph", "atlassian" ]);
|
||||
|
||||
// ManyToMany
|
||||
const table2 = await queryRunner.getTable("access_event_employees_employee");
|
||||
const column2 = table2!.findColumnByName("employeeProvider");
|
||||
expect(column2!.enum).to.deep.equal([ "msGraph", "atlassian" ]);
|
||||
|
||||
await queryRunner.release();
|
||||
})));
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user