From 071846ee77ae453a3e26a244ea8c2f0966ab6942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Thu, 19 May 2022 23:39:06 +0200 Subject: [PATCH] fix(core): fix aliasing of formula properties in complex conditions (#3130) --- packages/knex/src/query/ObjectCriteriaNode.ts | 3 +- tests/EntityManager.mysql.test.ts | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/packages/knex/src/query/ObjectCriteriaNode.ts b/packages/knex/src/query/ObjectCriteriaNode.ts index 7beaa852538b..42aa34fac3c3 100644 --- a/packages/knex/src/query/ObjectCriteriaNode.ts +++ b/packages/knex/src/query/ObjectCriteriaNode.ts @@ -26,7 +26,8 @@ export class ObjectCriteriaNode extends CriteriaNode { const payload = childNode.process(qb, this.prop ? alias : ownerAlias); const operator = Utils.isOperator(field); const customExpression = ObjectCriteriaNode.isCustomExpression(field); - const virtual = childNode.prop?.persist === false; + // we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators + const virtual = childNode.prop?.persist === false && !childNode.prop?.formula; // if key is missing, we are inside group operator and we need to prefix with alias const primaryKey = this.key && this.metadata.find(this.entityName)!.primaryKeys.includes(field); diff --git a/tests/EntityManager.mysql.test.ts b/tests/EntityManager.mysql.test.ts index 3988cdca4dff..e0a672030b91 100644 --- a/tests/EntityManager.mysql.test.ts +++ b/tests/EntityManager.mysql.test.ts @@ -2207,6 +2207,52 @@ describe('EntityManagerMySql', () => { expect(mock.mock.calls[1][0]).toMatch('select `f0`.*, (select 123) as `random`, (select 456) as `lazy_random` from `foo_bar2` as `f0` where `f0`.`id` = ? limit ?'); }); + test('search by formulas (gh #3048)', async () => { + const god = new Author2('God', 'hello@heaven.god'); + const bible = new Book2('Bible', god); + bible.price = 1000; + god.favouriteBook = bible; + await orm.em.persistAndFlush(bible); + + const mock = mockLogger(orm, ['query']); + + const b = await orm.em.fork().findOneOrFail(Book2, { priceTaxed: '1190.0000' }); + expect(b.price).toBe('1000.00'); + expect(b.priceTaxed).toBe('1190.0000'); + expect(mock.mock.calls[0][0]).toMatch('select `b0`.`uuid_pk`, `b0`.`created_at`, `b0`.`title`, `b0`.`price`, `b0`.`double`, `b0`.`meta`, `b0`.`author_id`, `b0`.`publisher_id`, `b0`.price * 1.19 as `price_taxed`, `t1`.`id` as `test_id` ' + + 'from `book2` as `b0` ' + + 'left join `test2` as `t1` on `b0`.`uuid_pk` = `t1`.`book_uuid_pk` ' + + 'where `b0`.`author_id` is not null and `b0`.price * 1.19 = ? limit ?'); + + const a1 = await orm.em.fork().find(Author2, { $or: [{ favouriteBook: { priceTaxed: '1190.0000' } }] }, { populate: ['books'] }); + expect(a1[0].books[0].price).toBe('1000.00'); + expect(a1[0].books[0].priceTaxed).toBe('1190.0000'); + expect(mock.mock.calls[1][0]).toMatch('select `a0`.*, `a2`.`author_id` as `address_author_id` ' + + 'from `author2` as `a0` ' + + 'left join `book2` as `b1` on `a0`.`favourite_book_uuid_pk` = `b1`.`uuid_pk` ' + + 'left join `address2` as `a2` on `a0`.`id` = `a2`.`author_id` ' + + 'where `b1`.price * 1.19 = ?'); + expect(mock.mock.calls[2][0]).toMatch('select `b0`.`uuid_pk`, `b0`.`created_at`, `b0`.`title`, `b0`.`price`, `b0`.`double`, `b0`.`meta`, `b0`.`author_id`, `b0`.`publisher_id`, `b0`.price * 1.19 as `price_taxed`, `t1`.`id` as `test_id` ' + + 'from `book2` as `b0` ' + + 'left join `test2` as `t1` on `b0`.`uuid_pk` = `t1`.`book_uuid_pk` ' + + 'where `b0`.`author_id` is not null and `b0`.`author_id` in (?) ' + + 'order by `b0`.`title` asc, `b0`.`author_id` asc'); + + const a2 = await orm.em.fork().find(Author2, { favouriteBook: { $or: [{ priceTaxed: '1190.0000' }] } }, { populate: ['books'] }); + expect(a2[0].books[0].price).toBe('1000.00'); + expect(a2[0].books[0].priceTaxed).toBe('1190.0000'); + expect(mock.mock.calls[3][0]).toMatch('select `a0`.*, `a2`.`author_id` as `address_author_id` ' + + 'from `author2` as `a0` ' + + 'left join `book2` as `b1` on `a0`.`favourite_book_uuid_pk` = `b1`.`uuid_pk` ' + + 'left join `address2` as `a2` on `a0`.`id` = `a2`.`author_id` ' + + 'where `b1`.price * 1.19 = ?'); + expect(mock.mock.calls[4][0]).toMatch('select `b0`.`uuid_pk`, `b0`.`created_at`, `b0`.`title`, `b0`.`price`, `b0`.`double`, `b0`.`meta`, `b0`.`author_id`, `b0`.`publisher_id`, `b0`.price * 1.19 as `price_taxed`, `t1`.`id` as `test_id` ' + + 'from `book2` as `b0` ' + + 'left join `test2` as `t1` on `b0`.`uuid_pk` = `t1`.`book_uuid_pk` ' + + 'where `b0`.`author_id` is not null and `b0`.`author_id` in (?) ' + + 'order by `b0`.`title` asc, `b0`.`author_id` asc'); + }); + test('refreshing already loaded entity', async () => { const god = new Author2('God', 'hello@heaven.god'); new Book2('Bible 1', god);