Skip to content

Commit

Permalink
feat(schema): add ability to ignore specific column changes
Browse files Browse the repository at this point in the history
```ts
@Property({
  columnType: 'timestamp',
  extra: 'VIRTUAL GENERATED',
  ignoreSchemaChanges: ['type', 'extra'],
})
changingField!: Date;
```

This is useful for situations such as mikro-orm#1904, where `knex` is unable to
properly diff the column.
  • Loading branch information
PenguinToast committed Sep 14, 2022
1 parent 0af0d58 commit a99cef5
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/core/src/decorators/Property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export type PropertyOptions<T> = {
comment?: string;
/** mysql only */
extra?: string;
ignoreSchemaChanges?: ('type' | 'extra')[];
};

export interface ReferenceOptions<T, O> extends PropertyOptions<O> {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ export interface EntityProperty<T = any> {
extra?: string;
userDefined?: boolean;
optional?: boolean; // for ts-morph
ignoreSchemaChanges?: ('type' | 'extra')[];
}

export class EntityMetadata<T = any> {
Expand Down
1 change: 1 addition & 0 deletions packages/knex/src/schema/DatabaseTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export class DatabaseTable {
enumItems: prop.items?.every(Utils.isString) ? prop.items as string[] : undefined,
comment: prop.comment,
extra: prop.extra,
ignoreSchemaChanges: prop.ignoreSchemaChanges,
};
this.columns[field].unsigned ||= this.columns[field].autoincrement;
const defaultValue = this.platform.getSchemaHelper()!.normalizeDefaultValue(prop.defaultRaw!, prop.length);
Expand Down
16 changes: 14 additions & 2 deletions packages/knex/src/schema/SchemaComparator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,13 @@ export class SchemaComparator {
}
};

if (columnType1 !== columnType2) {
if (
columnType1 !== columnType2 &&
!(
column1.ignoreSchemaChanges?.includes('type') ||
column2.ignoreSchemaChanges?.includes('type')
)
) {
log(`'type' changed for column ${tableName}.${column1.name}`, { columnType1, columnType2 });
changedProperties.add('type');
}
Expand Down Expand Up @@ -443,7 +449,13 @@ export class SchemaComparator {
changedProperties.add('enumItems');
}

if ((column1.extra || '').toLowerCase() !== (column2.extra || '').toLowerCase()) {
if (
(column1.extra || '').toLowerCase() !== (column2.extra || '').toLowerCase() &&
!(
column1.ignoreSchemaChanges?.includes('extra') ||
column2.ignoreSchemaChanges?.includes('extra')
)
) {
log(`'extra' changed for column ${tableName}.${column1.name}`, { column1, column2 });
changedProperties.add('extra');
}
Expand Down
1 change: 1 addition & 0 deletions packages/knex/src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface Column {
unique?: boolean;
/** mysql only */
extra?: string;
ignoreSchemaChanges?: ('type' | 'extra')[];
}

export interface ForeignKey {
Expand Down
114 changes: 114 additions & 0 deletions tests/features/schema-generator/GH1904.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { Entity, MikroORM, PrimaryKey, Property } from '@mikro-orm/core';
import type { MySqlDriver } from '@mikro-orm/mysql';

@Entity({ tableName: 'book' })
export class Book1 {

@PrimaryKey()
id!: number;

@Property({ columnType: 'int' })
changingField!: number;

}

@Entity({ tableName: 'book' })
export class Book2 {

@PrimaryKey()
id!: number;

@Property({ columnType: 'timestamp', ignoreSchemaChanges: ['type'] })
changingField!: Date;

}

@Entity({ tableName: 'book' })
export class Book3 {

@PrimaryKey()
id!: number;

@Property({
columnType: 'int',
extra: 'VIRTUAL GENERATED',
ignoreSchemaChanges: ['extra'],
})
changingField!: number;

}

@Entity({ tableName: 'book' })
export class Book4 {

@PrimaryKey()
id!: number;

@Property({
columnType: 'timestamp',
extra: 'VIRTUAL GENERATED',
ignoreSchemaChanges: ['extra', 'type'],
})
changingField!: Date;

}

@Entity({ tableName: 'book' })
export class Book5 {

@PrimaryKey()
id!: number;

@Property({ columnType: 'timestamp' })
changingField!: Date;

}

describe('ignore specific schema changes (GH 1904)', () => {
let orm: MikroORM<MySqlDriver>;

beforeEach(async () => {
orm = await MikroORM.init({
entities: [Book1],
dbName: `mikro_orm_test_gh_1904`,
type: 'mysql',
port: 3308,
});
await orm.schema.refreshDatabase();
});

afterEach(() => orm.close(true));

test('schema generator respects ignoreSchemaChanges for `type`', async () => {
const diff0 = await orm.schema.getUpdateSchemaSQL({ wrap: false });
expect(diff0).toBe('');
await orm.discoverEntity(Book2);
orm.getMetadata().reset('Book1');
const diff1 = await orm.schema.getUpdateSchemaSQL({ wrap: false });
expect(diff1).toBe('');

// Once we remove ignoreSchemaChanges, we should see a diff again.
await orm.discoverEntity(Book5);
orm.getMetadata().reset('Book2');
const diff2 = await orm.schema.getUpdateSchemaSQL({ wrap: false });
expect(diff2).toBe('alter table `book` modify `changing_field` timestamp not null;\n\n');
});

test('schema generator respects ignoreSchemaChanges for `extra`', async () => {
const diff0 = await orm.schema.getUpdateSchemaSQL({ wrap: false });
expect(diff0).toBe('');
await orm.discoverEntity(Book3);
orm.getMetadata().reset('Book1');
const diff1 = await orm.schema.getUpdateSchemaSQL({ wrap: false });
expect(diff1).toBe('');
});

test('schema generator respects ignoreSchemaChanges for `extra` and `type`', async () => {
const diff0 = await orm.schema.getUpdateSchemaSQL({ wrap: false });
expect(diff0).toBe('');
await orm.discoverEntity(Book4);
orm.getMetadata().reset('Book1');
const diff1 = await orm.schema.getUpdateSchemaSQL({ wrap: false });
expect(diff1).toBe('');
});
});

0 comments on commit a99cef5

Please sign in to comment.