Skip to content

Commit

Permalink
fix(mysql): fix reloading of database defaults for complex composite PKs
Browse files Browse the repository at this point in the history
Closes #3965
  • Loading branch information
B4nan committed Jan 24, 2023
1 parent c89b4af commit d36af00
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 109 deletions.
15 changes: 12 additions & 3 deletions packages/core/src/unit-of-work/ChangeSetPersister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,14 +329,23 @@ export class ChangeSetPersister {
return;
}

reloadProps.unshift(...meta.getPrimaryProps());
const pk = Utils.getPrimaryKeyHash(meta.primaryKeys);
const pks = changeSets.map(cs => helper(cs.entity).getPrimaryKey(true));
const pks = changeSets.map(cs => {
const val = helper(cs.entity).getPrimaryKey(true);

if (Utils.isPlainObject(val)) {
return Utils.getCompositeKeyValue(val, meta, false, this.platform);
}

return val;
});
options = this.propagateSchemaFromMetadata(meta, options, {
fields: reloadProps.map(prop => prop.fieldNames[0]),
fields: Utils.unique(reloadProps.map(prop => prop.name)),
});
const data = await this.driver.find<T>(meta.name!, { [pk]: { $in: pks } } as FilterQuery<T>, options);
const map = new Map<string, Dictionary>();
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform), item));
data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));

for (const changeSet of changeSets) {
const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
Expand Down
8 changes: 6 additions & 2 deletions packages/core/src/utils/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,12 @@ export class Utils {
}) as Primary<T>;
}

static getCompositeKeyHash<T>(data: EntityData<T>, meta: EntityMetadata<T>, convertCustomTypes = false, platform?: Platform): string {
const pks = this.getCompositeKeyValue(data, meta, convertCustomTypes, platform);
static getCompositeKeyHash<T>(data: EntityData<T>, meta: EntityMetadata<T>, convertCustomTypes = false, platform?: Platform, flat = false): string {
let pks = this.getCompositeKeyValue(data, meta, convertCustomTypes, platform);

if (flat) {
pks = Utils.flatten(pks as unknown[][]) as Primary<T>;
}

return Utils.getPrimaryKeyHash(pks as string[]);
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/utils/clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export function clone<T>(parent: T, respectCustomCloneMethod = true): T {
const symbol = symbols[i];
const descriptor = Object.getOwnPropertyDescriptor(parent, symbol);

/* istanbul ignore next */
if (descriptor && !descriptor.enumerable) {
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/knex/src/AbstractSqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ export abstract class AbstractSqlDriver<Connection extends AbstractSqlConnection
}

if (prop.fieldNames) {
ret.push(prop.fieldNames[0]);
ret.push(...prop.fieldNames);
}
}

Expand Down
5 changes: 2 additions & 3 deletions tests/features/optimistic-lock/GH3440.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Entity, MikroORM, PrimaryKey, Property, Type, ValidationError } from '@mikro-orm/core';
import { Entity, PrimaryKey, Property, Type, ValidationError } from '@mikro-orm/core';
import { MikroORM } from '@mikro-orm/mysql';
import { mockLogger } from '../../helpers';
import { MySqlDriver } from '@mikro-orm/mysql';

export function toBinaryUuid(uuid: string): Buffer {
const buf = Buffer.from(uuid.replace(/-/g, ''), 'hex');
Expand Down Expand Up @@ -65,7 +65,6 @@ beforeAll(async () => {
orm = await MikroORM.init({
entities: [Couch],
dbName: 'mikro_orm_test_3440',
driver: MySqlDriver,
port: 3308,
});
await orm.schema.refreshDatabase();
Expand Down
100 changes: 0 additions & 100 deletions tests/issues/GH3440.test.ts

This file was deleted.

104 changes: 104 additions & 0 deletions tests/issues/GH3965.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Collection, Entity, Property, ManyToOne, OneToMany, PrimaryKey, PrimaryKeyType, Cascade, Ref } from '@mikro-orm/core';
import { MikroORM } from '@mikro-orm/mysql';
import { randomUUID } from 'crypto';

@Entity()
export class Category {

@PrimaryKey({
length: 36,
})
id!: string;

@Property({
defaultRaw: 'CURRENT_TIMESTAMP',
})
createdAt?: Date;

@OneToMany(() => Article, attr => attr.category, {
cascade: [Cascade.ALL],
})
articles = new Collection<Article>(this);

}

@Entity()
export class Article {

@PrimaryKey({
length: 36,
})
id!: string;

@Property({
defaultRaw: 'CURRENT_TIMESTAMP',
})
createdAt?: Date;

@ManyToOne(() => Category, {
primary: true,
ref: true,
})
category!: Ref<Category>;

[PrimaryKeyType]?: [string, string];

@OneToMany(() => ArticleAttribute, attr => attr.article, {
cascade: [Cascade.ALL],
})
attributes = new Collection<ArticleAttribute>(this);

}

@Entity()
export class ArticleAttribute {

@PrimaryKey({ length: 36 })
id!: string;

@Property({
defaultRaw: 'CURRENT_TIMESTAMP',
})
createdAt?: Date;

@ManyToOne(() => Article, {
primary: true,
ref: true,
})
article!: Ref<Article>;

[PrimaryKeyType]?: [string, [string, string]];

}

let orm: MikroORM;

beforeAll(async () => {
orm = await MikroORM.init({
dbName: 'mikro_orm_3965',
entities: [Article],
port: 3308,
});
await orm.schema.refreshDatabase();
});

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

test('3965', async () => {
const category = new Category();
category.id = randomUUID();

const article = new Article();
article.id = randomUUID();

category.articles.add(article);

const articleAttribute = new ArticleAttribute();
articleAttribute.id = randomUUID();

article.attributes.add(articleAttribute);
expect(category.createdAt).toBeUndefined();
await orm.em.persistAndFlush(category);
expect(category.createdAt).toBeDefined();
expect(category.createdAt).toBeInstanceOf(Date);
});

0 comments on commit d36af00

Please sign in to comment.