From 432217c66b0d678c1c5d6cfe2534c8ecfbcc75fe Mon Sep 17 00:00:00 2001 From: siaka-Akash <109946032+siaka-Akash@users.noreply.github.com> Date: Thu, 2 Nov 2023 18:27:04 +0530 Subject: [PATCH] Fix sqlGeneration for filters without aliases in relational queries (#2406) --- .../tests/projection/testFilters.pure | 28 ++++++++ .../pureToSQLQuery/pureToSQLQuery.pure | 5 +- .../mapping/innerJoin/testIsolation.pure | 14 +++- .../mapping/innerJoin/testIsolationSetUp.pure | 43 +++++++++++-- .../mapping/join/advancedRelationalSetUp.pure | 64 ++++++++++++++++++- 5 files changed, 145 insertions(+), 9 deletions(-) diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/functions/tests/projection/testFilters.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/functions/tests/projection/testFilters.pure index 05e294b03b3..d317a10f9e4 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/functions/tests/projection/testFilters.pure +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/functions/tests/projection/testFilters.pure @@ -90,3 +90,31 @@ function <> meta::relational::tests::projection::filter::isolation::t assertEquals(['Firm X, UK', 'Firm X, Europe', 'Firm X, Europe', 'Firm X, Europe', 'Firm A, Europe', 'Firm B, Europe', 'Firm C, Europe'],$tds.rows->map(r|$r.values->makeString(', '))); assertEquals('select "root".LEGALNAME as "legalName", case when ("firmpersonbridgetable_0".ADDRESSID = 1 or "addresstable_0".ID = 1) then \'UK\' else \'Europe\' end as "addressName" from firmTable as "root" left outer join (select "firmpersonbridgetable_1".FIRM_ID as FIRM_ID, "persontable_0".ADDRESSID as ADDRESSID from firmPersonBridgeTable as "firmpersonbridgetable_1" inner join personTable as "persontable_0" on ("persontable_0".ID = "firmpersonbridgetable_1".PERSON_ID)) as "firmpersonbridgetable_0" on ("root".ID = "firmpersonbridgetable_0".FIRM_ID) left outer join addressTable as "addresstable_0" on ("addresstable_0".ID = "root".ADDRESSID)', $result->sqlRemoveFormatting()); } + +// Filters having no table alias like 'Smith' = 'Smith' +function <> meta::relational::tests::projection::filter::isolation::testIsolationOfFiltersWithoutAlias():Boolean[1] +{ + let result1 = execute(|Firm.all()->project([f|$f.employeeByLastName('Smith').address.name, f | $f.employeeByLastName('Smith').firstName],['address', 'employeeFirstName']), meta::relational::tests::mapping::join::model::mapping::MappingWithLiteral, testRuntime(), meta::relational::extension::relationalExtensions()); + let result2 = execute(|Firm.all()->project([f|$f.employeeByLastName('Roberts').address.name, f | $f.employeeByLastName('Roberts').firstName],['address', 'employeeFirstName']), meta::relational::tests::mapping::join::model::mapping::MappingWithLiteral, testRuntime(), meta::relational::extension::relationalExtensions()); + let result3 = execute(|Firm.all()->project([f|$f.employeeByLastName('Roberts').address.name, f | $f.employeeByLastName('Smith').firstName],['address', 'employeeFirstName']), meta::relational::tests::mapping::join::model::mapping::MappingWithLiteral, testRuntime(), meta::relational::extension::relationalExtensions()); + + let tds1 = $result1.values->at(0); + assertEquals(['Hoboken, Peter', 'New York, John', 'New York, John', 'New York, Anthony', 'San Fransisco, Fabrice', 'Hong Kong, Oliver', 'New York, David'], $tds1.rows->map(r|$r.values->makeString(', '))); + assertEquals('select "addresstable_0".NAME as "address", "persontable_0".FIRSTNAME as "employeeFirstName" from firmTable as "root" left outer join personTable as "persontable_0" on ("root".ID = "persontable_0".FIRMID and \'Smith\' = \'Smith\') left outer join addressTable as "addresstable_0" on ("addresstable_0".ID = "persontable_0".ADDRESSID)', $result1->sqlRemoveFormatting()); + + let tds2 = $result2.values->at(0); + assertEquals(['TDSNull, TDSNull', 'TDSNull, TDSNull', 'TDSNull, TDSNull', 'TDSNull, TDSNull'], $tds2.rows->map(r|$r.values->makeString(', '))); + assertEquals('select "addresstable_0".NAME as "address", "persontable_0".FIRSTNAME as "employeeFirstName" from firmTable as "root" left outer join personTable as "persontable_0" on ("root".ID = "persontable_0".FIRMID and \'Smith\' = \'Roberts\') left outer join addressTable as "addresstable_0" on ("addresstable_0".ID = "persontable_0".ADDRESSID)', $result2->sqlRemoveFormatting()); + + let tds3 = $result3.values->at(0); + assertEquals(['TDSNull, Peter', 'TDSNull, John', 'TDSNull, John', 'TDSNull, Anthony', 'TDSNull, Fabrice', 'TDSNull, Oliver', 'TDSNull, David'], $tds3.rows->map(r|$r.values->makeString(', '))); + assertEquals('select "addresstable_0".NAME as "address", "persontable_1".FIRSTNAME as "employeeFirstName" from firmTable as "root" left outer join personTable as "persontable_0" on ("root".ID = "persontable_0".FIRMID and \'Smith\' = \'Roberts\') left outer join addressTable as "addresstable_0" on ("addresstable_0".ID = "persontable_0".ADDRESSID) left outer join personTable as "persontable_1" on ("root".ID = "persontable_1".FIRMID and \'Smith\' = \'Smith\')', $result3->sqlRemoveFormatting()); +} + +function <> meta::relational::tests::projection::filter::lessThanEqual::testIsolationOfFiltersWithoutAliasWithChainedJoins():Boolean[1] +{ + let result = execute(|Trade.all()->project([t | $t.id, t| $t.product.synonymByType(ProductSynonymType.CUSIP).name, t| $t.product.synonymByType(ProductSynonymType.ISIN).name], ['tradeId','SynonyName1', 'SynonymName2']), meta::relational::tests::mapping::join::model::mapping::MappingWithLiteral, testRuntime(), meta::relational::extension::relationalExtensions()); + let tds = $result.values->at(0); + assertEquals(['1, CUSIP1, TDSNull', '1, ISIN1, TDSNull', '2, CUSIP1, TDSNull', '2, ISIN1, TDSNull', '3, CUSIP2, TDSNull', '3, ISIN2, TDSNull', '4, CUSIP2, TDSNull', '4, ISIN2, TDSNull', '5, CUSIP2, TDSNull', '5, ISIN2, TDSNull', '6, CUSIP3, TDSNull', '6, ISIN3, TDSNull', '7, CUSIP3, TDSNull', '7, ISIN3, TDSNull', '8, CUSIP3, TDSNull', '8, ISIN3, TDSNull', '9, CUSIP3, TDSNull', '9, ISIN3, TDSNull', '10, CUSIP3, TDSNull', '10, ISIN3, TDSNull', '11, TDSNull, TDSNull'], $tds.rows->map(r|$r.values->makeString(', '))); + assertEquals('select "root".ID as "tradeId", "synonymtable_0".NAME as "SynonyName1", "synonymtable_2".NAME as "SynonymName2" from tradeTable as "root" left outer join productSchema.productTable as "producttable_0" on ("root".prodId = "producttable_0".ID) left outer join (select "synonymtable_1".PRODID as PRODID, "synonymtable_1".NAME as NAME from productSchema.synonymTable as "synonymtable_1" where \'CUSIP\' = \'CUSIP\') as "synonymtable_0" on ("synonymtable_0".PRODID = "producttable_0".ID) left outer join (select "synonymtable_1".PRODID as PRODID, "synonymtable_1".NAME as NAME from productSchema.synonymTable as "synonymtable_1" where \'CUSIP\' = \'ISIN\') as "synonymtable_2" on ("synonymtable_2".PRODID = "producttable_0".ID)', $result->sqlRemoveFormatting()); +} \ No newline at end of file diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/pureToSQLQuery/pureToSQLQuery.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/pureToSQLQuery/pureToSQLQuery.pure index c638d3eac5c..89a0d1341aa 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/pureToSQLQuery/pureToSQLQuery.pure +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/pureToSQLQuery/pureToSQLQuery.pure @@ -5872,7 +5872,10 @@ function meta::relational::functions::pureToSqlQuery::findBestNodeToIsolate(sele { let joinThreads = ^List(values=$select.data)->buildThreads(); - let aliasesWithConstraints = $select.savedFilteringOperation.second->extractTableAliasColumns().alias->removeDuplicates(); + //For filters having aliases like a.x = 'y' fetch aliases from relationalOperationalElement and for filters without aliases like 'x' = 'x' fetch aliases from the treeNode. + let aliasesWithConstraints = $select.savedFilteringOperation->map(x | let aliasFromFilters = $x.second->extractTableAliasColumns().alias; + if($aliasFromFilters->isNotEmpty(), |$aliasFromFilters, | $x.first.alias); + )->removeDuplicates(); let filterThreadsWithAllConditions = $joinThreads->filter(thread| $thread->matchAllAliases($aliasesWithConstraints)); diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolation.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolation.pure index 42d7b405c64..eedf74d4616 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolation.pure +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolation.pure @@ -21,13 +21,21 @@ function <> meta::relational::tests::mapping::innerjoin::iso } function <> meta::relational::tests::mapping::innerjoin::isolation::testIsolationOfInnerJoins():Any[*] { - let func = {| Car.all()->filter(c|$c.org.publicAncestor.name != 'GSorg2' - && $c.org.publicAncestor.name->contains('GSorg') - && $c.org.publicAncestor.name->contains('toReturn') + let func = {| Car.all()->filter(c|$c.org.privateAncestor.name != 'GSorg2' + && $c.org.privateAncestor.name->contains('GSorg') + && $c.org.privateAncestor.name->contains('toReturn') )->project([col(c|$c.id, 'Id'), col(c|$c.type, 'type')])}; let result = execute($func,AutoMapping,autoMobileRuntime(), meta::relational::extension::relationalExtensions()); assertEquals([ 3, 'Mercedes3'],$result.values.rows->map(r|$r.values)); assertSize($result.values.rows, 1); assertEquals('select "root".vehicleId as "Id", "root".type as "type" from AutomobileTable as "root" left outer join AutomobileTable as "automobiletable_1" on ("root".vehicleId = "automobiletable_1".vehicleId and "root".orgId < 100) left outer join (select "autoancestor_1".childId as childId, "autoancestor_2".orgName as orgName from AutoAncestor as "autoancestor_1" left outer join (select "autoancestor_2".parentId as parentId, "automobiletable_2".orgName as orgName from AutoAncestor as "autoancestor_2" inner join AutomobileTable as "automobiletable_2" on ("autoancestor_2".parentId = "automobiletable_2".orgId and case when "automobiletable_2".orgtype = \'public\' then \'N\' else \'Y\' end = \'Y\')) as "autoancestor_2" on ("autoancestor_1".parentId = "autoancestor_2".parentId)) as "autoancestor_0" on ("automobiletable_1".orgId = "autoancestor_0".childId) left outer join (select "autoancestor_4".childId as childId, "automobiletable_3".orgName as orgName from AutoAncestor as "autoancestor_4" inner join AutomobileTable as "automobiletable_3" on ("autoancestor_4".parentId = "automobiletable_3".orgId) where case when "automobiletable_3".orgtype = \'public\' then \'N\' else \'Y\' end = \'Y\') as "autoancestor_3" on ("automobiletable_1".orgId = "autoancestor_3".childId) where ((("autoancestor_3".orgName <> \'GSorg2\' OR "autoancestor_3".orgName is null) and ("autoancestor_0".orgName is not null and "autoancestor_3".orgName like \'%GSorg%\')) and ("autoancestor_0".orgName is not null and "autoancestor_3".orgName like \'%toReturn%\'))',$result->sqlRemoveFormatting()); +} +function <> meta::relational::tests::mapping::innerjoin::isolation::testIsolationForFiltersWithoutAliasAndInnerJoins():Any[*] +{ + let func = {| Car.all()->project([col(c|$c.org.privateAncestor.name, 'privateAncestorName'), col(c|$c.org.publicAncestor.name, 'publicAncestorName')])}; + let result = execute($func,MappingWithConstant,autoMobileRuntime(), meta::relational::extension::relationalExtensions()); + let tds = $result.values->at(0); + assertEquals(['GSorg4, TDSNull', 'GSorg3, TDSNull', 'GSorgtoReturn, TDSNull', 'TDSNull, TDSNull'], $tds.rows->map(r|$r.values->makeString(', '))); + assertEquals('select "autoancestor_0".orgName as "privateAncestorName", "autoancestor_2".orgName as "publicAncestorName" from AutomobileTable as "root" left outer join AutomobileTable as "automobiletable_1" on ("root".vehicleId = "automobiletable_1".vehicleId and "root".orgId < 100) left outer join (select "autoancestor_1".childId as childId, "automobiletable_2".orgName as orgName from AutoAncestor as "autoancestor_1" inner join AutomobileTable as "automobiletable_2" on ("autoancestor_1".parentId = "automobiletable_2".orgId) where \'Y\' = \'Y\') as "autoancestor_0" on ("automobiletable_1".orgId = "autoancestor_0".childId) left outer join (select "autoancestor_1".childId as childId, "automobiletable_2".orgName as orgName from AutoAncestor as "autoancestor_1" inner join AutomobileTable as "automobiletable_2" on ("autoancestor_1".parentId = "automobiletable_2".orgId) where \'Y\' = \'N\') as "autoancestor_2" on ("automobiletable_1".orgId = "autoancestor_2".childId)',$result->sqlRemoveFormatting()); } \ No newline at end of file diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolationSetUp.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolationSetUp.pure index cb1c8d6ba7e..55a270578c8 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolationSetUp.pure +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/innerJoin/testIsolationSetUp.pure @@ -40,12 +40,17 @@ Class meta::relational::tests::mapping::innerjoin::isolation::AutomobileUnion isPrivate: String[0..1]; ancestors: AutomobileUnion[0..*]; children: AutomobileUnion[0..*]; - publicAncestor() + privateAncestor() { $this.ancestors->filter(a|$a.isPrivate =='Y')->toOne() }:AutomobileUnion[1]; - publicAncestor2() + publicAncestor() + { + $this.ancestors->filter(a|$a.isPrivate =='N')->toOne() + }:AutomobileUnion[1]; + + privateAncestor2() { $this.ancestors->filter(a|$a.isPrivate =='Y').name=='haha' || ($this.ancestors->filter(a|$a.isPrivate=='Y').name=='ahah') }: Boolean[1]; @@ -63,8 +68,8 @@ function meta::relational::tests::mapping::innerjoin::isolation::initAutomobileD executeInDb('Create Table AutomobileTable (vehicleId INT, type VARCHAR(20),orgId INT, orgName VARCHAR(40),orgtype VARCHAR(40));', $connection); executeInDb('insert into AutomobileTable (vehicleId, type, orgId,orgName,orgType) values (1, \'Mercedes1\',17,\'GSorgtoReturn\' ,\'private\');', $connection); executeInDb('insert into AutomobileTable (vehicleId, type, orgId,orgName,orgType) values (2, \'Mercedes2\',18,\'GSorgtoReturn2\' ,\'private\');', $connection); - executeInDb('insert into AutomobileTable (vehicleId, type, orgId,orgName,orgType) values (3, \'Mercedes3\',19,\'GSorg3\' ,\'public3\');', $connection); - executeInDb('insert into AutomobileTable (vehicleId, type, orgId,orgName,orgType) values (4, \'Mercedes4\',20,\'GSorg4\' ,\'public4\');', $connection); + executeInDb('insert into AutomobileTable (vehicleId, type, orgId,orgName,orgType) values (3, \'Mercedes3\',19,\'GSorg3\' ,\'public\');', $connection); + executeInDb('insert into AutomobileTable (vehicleId, type, orgId,orgName,orgType) values (4, \'Mercedes4\',20,\'GSorg4\' ,\'public\');', $connection); executeInDb('Drop table if exists AutoAncestor;', $connection); executeInDb('Create Table AutoAncestor (childId INT ,parentId INT);',$connection); @@ -118,6 +123,36 @@ Mapping meta::relational::tests::mapping::innerjoin::isolation::AutoMapping ) } +) + +###Mapping +import meta::relational::tests::mapping::innerjoin::isolation::*; +Mapping meta::relational::tests::mapping::innerjoin::isolation::MappingWithConstant +( + Car :Relational + { + scope([AutomobileDb]) + ( + type: AutomobileTable.type, + id: AutomobileTable.vehicleId, + org( + name: AutomobileTable.orgName, + isPrivate: 'Y', + ancestors: @ Auto_Auto > @Auto_Ancestor > (INNER)@AncestorAuto + ) + ) + } + + AutomobileUnion:Relational + { + scope([AutomobileDb]) + ( + name: AutomobileTable.orgName, + isPrivate: 'Y', + ancestors: @Auto_Ancestor > (INNER)@AncestorAuto + ) + } + ) ###Relational Database meta::relational::tests::mapping::innerjoin::isolation::AutomobileDb diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/join/advancedRelationalSetUp.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/join/advancedRelationalSetUp.pure index 11dd534456a..67c193dc1b0 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/join/advancedRelationalSetUp.pure +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/tests/mapping/join/advancedRelationalSetUp.pure @@ -385,6 +385,68 @@ Mapping meta::relational::tests::mapping::join::model::mapping::JoinSchemaBWithS } ) +###Mapping +import meta::relational::tests::model::simple::*; +import meta::relational::tests::*; +Mapping meta::relational::tests::mapping::join::model::mapping::MappingWithLiteral +( + Person : Relational + { + scope([dbInc]) + ( + firstName : personTable.FIRSTNAME, + age : personTable.AGE + ), + scope([dbInc]default.personTable) + ( + lastName : 'Smith' + ), + firm : [dbInc]@Firm_Person, + address : [dbInc]@Address_Person, + locations : [dbInc]@Person_Location, + manager : [dbInc]@Person_Manager + } + + Firm : Relational + { + ~mainTable [dbInc] firmTable + legalName : [dbInc]firmTable.LEGALNAME, + employees : [dbInc]@Firm_Person, + address : [dbInc]@Address_Firm + } + + Address : Relational + { + name : [dbInc]addressTable.NAME, + street : [dbInc]addressTable.STREET, + comments : [dbInc]addressTable.COMMENTS + } + + Product : Relational + { + name : [db]productSchema.productTable.NAME, + synonyms : [db]@Product_Synonym + } + + ProductSynonymType: EnumerationMapping SynonymEnum + { + CUSIP: 'CUSIP', + ISIN: 'ISIN' + } + + Synonym : Relational + { + name : [db]productSchema.synonymTable.NAME, + type : EnumerationMapping SynonymEnum: 'CUSIP' + } + + Trade : Relational + { + id : [db]tradeTable.ID, + product : [db]@Trade_Product + } +) + ###Pure import meta::relational::tests::mapping::join::model::store::*; import meta::pure::profiles::*; @@ -494,4 +556,4 @@ function meta::relational::tests::mapping::join::model::store::createTablesAndFi executeInDb('insert into schemaB.PersonTable (id, firstName, LastName) values (4, \'Anthonye\', \'Anthony B\');', $connection); executeInDb('insert into schemaB.PersonTable (id, firstName, LastName) values (5, \'Oliver\', \'Oliver B\');', $connection); true; -} +} \ No newline at end of file