Skip to content

Commit

Permalink
fix(core): prefer current schema for loading wild card pivot table en…
Browse files Browse the repository at this point in the history
…tities

Closes #3177
  • Loading branch information
B4nan committed Jun 21, 2022
1 parent 60fc7f6 commit f40cafa
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 0 deletions.
10 changes: 10 additions & 0 deletions packages/knex/src/AbstractSqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
async loadFromPivotTable<T, O>(prop: EntityProperty, owners: Primary<O>[][], where: FilterQuery<T> = {} as FilterQuery<T>, orderBy?: QueryOrderMap<T>[], ctx?: Transaction, options?: FindOptions<T>): Promise<Dictionary<T[]>> {
const pivotProp2 = this.getPivotInverseProperty(prop);
const ownerMeta = this.metadata.find(pivotProp2.type)!;
const pivotMeta = this.metadata.find(prop.pivotEntity)!;
const cond = { [`${prop.pivotEntity}.${pivotProp2.name}`]: { $in: ownerMeta.compositePK ? owners : owners.map(o => o[0]) } };

/* istanbul ignore if */
Expand All @@ -473,6 +474,15 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
qb.limit(options.limit, options.offset);
}

if (prop.targetMeta!.schema !== '*' && pivotMeta.schema === '*' && options?.schema) {
// eslint-disable-next-line dot-notation
qb['finalize']();
// eslint-disable-next-line dot-notation
Object.values(qb['_joins']).forEach(join => {
join.schema = options.schema;
});
}

const items = owners.length ? await this.rethrow(qb.execute('all')) : [];

const map: Dictionary<T[]> = {};
Expand Down
104 changes: 104 additions & 0 deletions tests/features/multiple-schemas/GH3177.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Collection, Entity, ManyToMany, ManyToOne, MikroORM, OneToMany, PrimaryKey, Property, Reference, t } from '@mikro-orm/core';
import { mockLogger } from '../../helpers';

@Entity({ schema: '*' })
class UserAccessProfile {

@PrimaryKey()
id!: number;

// eslint-disable-next-line @typescript-eslint/no-use-before-define
@OneToMany(() => User, user => user.accessProfile)
users = new Collection<User>(this);

// eslint-disable-next-line @typescript-eslint/no-use-before-define
@ManyToMany({ entity: () => Permission, pivotEntity: () => AccessProfilePermission })
permissions = new Collection<Permission>(this);

}

@Entity({ schema: '*' })
class User {

@PrimaryKey()
id!: number;

// eslint-disable-next-line @typescript-eslint/no-use-before-define
@ManyToOne(() => UserAccessProfile)
accessProfile!: UserAccessProfile;

}

@Entity({ schema: 'public' })
class Permission {

@PrimaryKey()
id!: number;

// eslint-disable-next-line @typescript-eslint/no-use-before-define
@ManyToMany({ entity: () => UserAccessProfile, mappedBy: p => p.permissions })
accessProfiles = new Collection<UserAccessProfile>(this);

}

@Entity({ schema: '*' })
class AccessProfilePermission {

@ManyToOne(() => UserAccessProfile, { primary: true })
accessProfile!: UserAccessProfile;

@ManyToOne(() => Permission, { primary: true })
permission!: Permission;

}

let orm: MikroORM;

beforeAll(async () => {
orm = await MikroORM.init({
entities: [User],
dbName: 'mikro_orm_test_3177',
type: 'postgresql',
schema: 'tenant_01',
});
await orm.getSchemaGenerator().ensureDatabase();
await orm.getSchemaGenerator().dropSchema();
await orm.getSchemaGenerator().dropSchema({ schema: 'tenant_01' });
await orm.getSchemaGenerator().updateSchema();
await orm.getSchemaGenerator().updateSchema({ schema: 'tenant_01' });
});

afterAll(async () => {
await orm.close(true);
});

test(`GH issue 3177`, async () => {
const user = new User();
user.id = 1;
user.accessProfile = new UserAccessProfile();
user.accessProfile.permissions.add(new Permission(), new Permission(), new Permission());
orm.em.persist(user);

const mock = mockLogger(orm);

const u1 = await orm.em.findOneOrFail(User, 1, { populate: ['accessProfile'] });
const permissions = await u1.accessProfile.permissions.init();

expect(permissions.getItems()).toHaveLength(3);
orm.em.clear();

const u2 = await orm.em.findOneOrFail(User, 1, { populate: ['accessProfile.permissions'] });
expect(u2.accessProfile.permissions.getItems()).toHaveLength(3);

expect(mock.mock.calls[0][0]).toMatch(`begin`);
expect(mock.mock.calls[1][0]).toMatch(`insert into "tenant_01"."user_access_profile" default values returning "id"`);
expect(mock.mock.calls[2][0]).toMatch(`insert into "tenant_01"."user" ("access_profile_id", "id") values (1, 1) returning "id"`);
expect(mock.mock.calls[3][0]).toMatch(`insert into "public"."permission" ("id") values (default), (default), (default) returning "id"`);
expect(mock.mock.calls[4][0]).toMatch(`insert into "tenant_01"."user_access_profile_permissions" ("user_access_profile_id", "permission_id") values (1, 1), (1, 2), (1, 3) returning "user_access_profile_id", "permission_id"`);
expect(mock.mock.calls[5][0]).toMatch(`commit`);
expect(mock.mock.calls[6][0]).toMatch(`select "u0".* from "tenant_01"."user" as "u0" where "u0"."id" = 1 limit 1`);
expect(mock.mock.calls[7][0]).toMatch(`select "p0".* from "public"."permission" as "p0" where "p0"."id" in (1, 2, 3)`);
expect(mock.mock.calls[8][0]).toMatch(`select "u0".* from "tenant_01"."user" as "u0" where "u0"."id" = 1 limit 1`);
expect(mock.mock.calls[9][0]).toMatch(`select "u0".* from "tenant_01"."user_access_profile" as "u0" where "u0"."id" in (1) order by "u0"."id" asc`);
expect(mock.mock.calls[10][0]).toMatch(`select "p0".*, "u1"."permission_id" as "fk__permission_id", "u1"."user_access_profile_id" as "fk__user_access_profile_id" from "public"."permission" as "p0" left join "tenant_01"."user_access_profile_permissions" as "u1" on "p0"."id" = "u1"."permission_id" where "u1"."user_access_profile_id" in (1)`);
});

0 comments on commit f40cafa

Please sign in to comment.