Skip to content

Commit

Permalink
feat(schema): add support for timestamp columns in mysql
Browse files Browse the repository at this point in the history
```ts
@Property({
  columnType: 'timestamp',
  defaultRaw: 'current_timestamp',
  extra: 'on update current_timestamp',
})
updatedAt!: Date;
```

Closes #2386
  • Loading branch information
B4nan committed Nov 18, 2021
1 parent 3bdb15d commit a224ec9
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 6 deletions.
2 changes: 2 additions & 0 deletions packages/core/src/decorators/Property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export type PropertyOptions<T> = {
serializer?: (value: any) => any;
serializedName?: string;
comment?: string;
/** mysql only */
extra?: string;
};

export interface ReferenceOptions<T, O> extends PropertyOptions<O> {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/metadata/EntitySchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
const tableName = this._meta.collection ?? this._meta.tableName;

if (tableName?.includes('.') && !this._meta.schema) {
this._meta.schema = tableName.substr(0, tableName.indexOf('.'));
this._meta.collection = tableName.substr(tableName.indexOf('.') + 1);
this._meta.schema = tableName.substring(0, tableName.indexOf('.'));
this._meta.collection = tableName.substring(tableName.indexOf('.') + 1);
}

this.initProperties();
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ export interface EntityProperty<T extends AnyEntity<T> = any> {
serializer?: (value: any) => any;
serializedName?: string;
comment?: string;
/** mysql only */
extra?: string;
userDefined?: boolean;
}

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 @@ -83,6 +83,7 @@ export class DatabaseTable {
default: prop.defaultRaw,
enumItems: prop.items?.every(Utils.isString) ? prop.items as string[] : undefined,
comment: prop.comment,
extra: prop.extra,
};
this.columns[field].unsigned ||= this.columns[field].autoincrement;
const defaultValue = this.platform.getSchemaHelper()!.normalizeDefaultValue(prop.defaultRaw!, prop.length);
Expand Down
4 changes: 4 additions & 0 deletions packages/knex/src/schema/SchemaComparator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@ export class SchemaComparator {
changedProperties.add('enumItems');
}

if ((column1.extra || '').toLowerCase() !== (column2.extra || '').toLowerCase()) {
changedProperties.add('extra');
}

return changedProperties;
}

Expand Down
2 changes: 2 additions & 0 deletions packages/knex/src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export interface Column {
enumItems?: string[];
primary?: boolean;
unique?: boolean;
/** mysql only */
extra?: string;
}

export interface ForeignKey {
Expand Down
1 change: 1 addition & 0 deletions packages/mysql-base/src/MySqlPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class MySqlPlatform extends AbstractSqlPlatform {
const normalizedType = this.extractSimpleType(type);
const map = {
int: 'integer',
timestamp: 'datetime',
};

return super.getMappedType(map[normalizedType] ?? type);
Expand Down
11 changes: 7 additions & 4 deletions packages/mysql-base/src/MySqlSchemaHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ export class MySqlSchemaHelper extends SchemaHelper {
}

configureColumnDefault(column: Column, col: Knex.ColumnBuilder, knex: Knex, changedProperties?: Set<string>) {
if (changedProperties) {
col.defaultTo(column.default == null ? null : knex.raw(column.default));
} else if (column.default !== undefined) {
col.defaultTo(column.default == null ? null : knex.raw(column.default));
if (changedProperties || column.default !== undefined) {
if (column.default == null) {
col.defaultTo(null);
} else {
col.defaultTo(knex.raw(column.default + (column.extra ? ' ' + column.extra : '')));
}
}

return col;
Expand Down Expand Up @@ -145,6 +147,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
precision: col.numeric_precision,
scale: col.numeric_scale,
comment: col.column_comment,
extra: col.extra.replace('auto_increment', ''),
});
});
}
Expand Down
80 changes: 80 additions & 0 deletions tests/features/schema-generator/GH2386.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
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: 'timestamp', defaultRaw: `current_timestamp` })
createdAt!: Date;

@Property({ columnType: 'timestamp', defaultRaw: `current_timestamp`, extra: `on update current_timestamp` })
updatedAt!: Date;

}

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

@PrimaryKey()
id!: number;

@Property({ columnType: 'timestamp', defaultRaw: `current_timestamp` })
createdAt!: Date;

@Property({ columnType: 'timestamp', defaultRaw: `current_timestamp` })
updatedAt!: Date;

}

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

@PrimaryKey()
id!: number;

@Property({ columnType: 'timestamp', defaultRaw: `current_timestamp` })
createdAt!: Date;

@Property({ columnType: 'timestamp', defaultRaw: `current_timestamp`, extra: `on update current_timestamp` })
updatedAt!: Date;

}

describe('changing column in mysql (GH 2386)', () => {

let orm: MikroORM<MySqlDriver>;

beforeAll(async () => {
orm = await MikroORM.init({
entities: [Book1],
dbName: `mikro_orm_test_gh_2386`,
type: 'mysql',
port: 3307,
});
await orm.getSchemaGenerator().ensureDatabase();
await orm.getSchemaGenerator().dropSchema();
await orm.getSchemaGenerator().createSchema();
});

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

test('schema generator respect indexes on FKs on column update', async () => {
const diff0 = await orm.getSchemaGenerator().getUpdateSchemaSQL({ wrap: false });
expect(diff0).toBe('');
await orm.discoverEntity(Book2);
orm.getMetadata().reset('Book1');
const diff1 = await orm.getSchemaGenerator().getUpdateSchemaSQL({ wrap: false });
expect(diff1).toBe('alter table `book` modify `updated_at` timestamp not null default current_timestamp;\n\n');
await orm.getSchemaGenerator().execute(diff1);

await orm.discoverEntity(Book3);
orm.getMetadata().reset('Book2');
const diff3 = await orm.getSchemaGenerator().getUpdateSchemaSQL({ wrap: false });
expect(diff3).toBe('alter table `book` modify `updated_at` timestamp not null default current_timestamp on update current_timestamp;\n\n');
await orm.getSchemaGenerator().execute(diff3);
});

});

0 comments on commit a224ec9

Please sign in to comment.