From be5304da1013df3d26ac1bc933341690f27044bb Mon Sep 17 00:00:00 2001 From: Haroon Sherjan Date: Tue, 21 Nov 2023 09:37:59 -0500 Subject: [PATCH] Localize XStoreAssociations based on Runtime Locality --- .../resources/core/pure/mapping/XStore.pure | 6 +- .../pure/router/deprecated/deprecated.pure | 2 +- .../router/extension/router_extension.pure | 1 + .../core/pure/router/store/cluster.pure | 4 +- .../core/pure/router/store/routing.pure | 81 +++-- .../core/pure/store/storeContract.pure | 2 + .../relational/contract/storeContract.pure | 8 +- .../relational/modelJoins/modelJoins.pure | 118 ++++++++ .../testModelJoinsToRelationalJoins.pure | 279 ++++++++++++++++++ .../core_service/service/extension.pure | 5 +- 10 files changed, 478 insertions(+), 28 deletions(-) create mode 100644 legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/modelJoins.pure create mode 100644 legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/testModelJoinsToRelationalJoins.pure diff --git a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/mapping/XStore.pure b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/mapping/XStore.pure index 2b198c0bf4d..357c5d4d94e 100644 --- a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/mapping/XStore.pure +++ b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/mapping/XStore.pure @@ -124,10 +124,10 @@ function meta::pure::mapping::xStore::performXStoreQuery(o:Any[1], p:PropertyMap multiplicity = PureOne, values = $returnType.rawType ), - routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($objectInfo.static.mapping, $objectInfo.static.runtime) + routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($objectInfo.static.mapping, $objectInfo.static.runtime, $extensions) ) ), - routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($objectInfo.static.mapping, $objectInfo.static.runtime) + routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($objectInfo.static.mapping, $objectInfo.static.runtime, $extensions) ), ^FunctionRoutedValueSpecification ( @@ -143,7 +143,7 @@ function meta::pure::mapping::xStore::performXStoreQuery(o:Any[1], p:PropertyMap ) ] ), - routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($objectInfo.static.mapping, $objectInfo.static.runtime) + routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($objectInfo.static.mapping, $objectInfo.static.runtime, $extensions) )->evaluateAndDeactivate(); print(if(!$debug.debug, |'', diff --git a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/deprecated/deprecated.pure b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/deprecated/deprecated.pure index e8595f8cab3..d43c1e6f542 100644 --- a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/deprecated/deprecated.pure +++ b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/deprecated/deprecated.pure @@ -25,5 +25,5 @@ function <> meta::pure::router::routeFunction(f:FunctionDefiniti function <> meta::pure::router::routeFunction(f:FunctionDefinition[1], mapping:Mapping[1], runtime:Runtime[1], exeCtx: ExecutionContext[1], extensions:meta::pure::extension::Extension[*], debug:DebugContext[1]):FunctionDefinition[1] { - routeFunction($f, getRoutingStrategyFromMappingAndRuntime($mapping, $runtime), $exeCtx, [], $extensions, $debug) + routeFunction($f, getRoutingStrategyFromMappingAndRuntime($mapping, $runtime, $extensions), $exeCtx, [], $extensions, $debug) } diff --git a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/extension/router_extension.pure b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/extension/router_extension.pure index 95be391df8b..62ccfd245ec 100644 --- a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/extension/router_extension.pure +++ b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/extension/router_extension.pure @@ -26,6 +26,7 @@ Class meta::pure::router::extension::RouterExtension shouldStopPreeval : Function<{Any[*] -> Boolean[1]}>[0..1]; routeFunctionExpressions : PairBoolean[1]}>, Function<{Function[1], FunctionExpression[1], RoutingState[1], ExecutionContext[1], Map[1], Map>[1], meta::pure::extension::Extension[*], DebugContext[1]->RoutingState[1]}>>[*]; connectionEquality : Function<{Connection[1] -> Function<{Nil[1]->Boolean[1]}>[*]}>[0..1]; + processModelJoinsInExecution : Function<{Mapping[1], Runtime[1] -> Function<{Nil[1]->Mapping[1]}>[*]}>[0..1]; splitGraphFetchTreeForPropertyUnion : Boolean[1] = false; // Execution traceQueryInfoBuilder : Function<{FunctionDefinition[1], Mapping[1], meta::core::runtime::Runtime[1] -> String[1]}>[0..1]; diff --git a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/cluster.pure b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/cluster.pure index b341f47866a..3e4ea492dfe 100644 --- a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/cluster.pure +++ b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/cluster.pure @@ -704,10 +704,10 @@ function meta::pure::router::clustering::clusteredGetAll(set: SetImplementation[ ( genericType = ^GenericType(rawType=Class, typeArguments=$sourceType), multiplicity = PureOne, values = $sourceType.rawType ), - routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($mapping, $runtime) + routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($mapping, $runtime, $extensions) ) ), - routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($mapping, $runtime) + routingStrategy = meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime($mapping, $runtime, $extensions) )->evaluateAndDeactivate(); let clustered = list($newExpr)->doCluster(^LambdaFunction<{->Any[1]}>(expressionSequence=^InstanceValue(values='', genericType=^GenericType(rawType=String), multiplicity=PureOne)), diff --git a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/routing.pure b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/routing.pure index bb57daf24cc..e672be248a4 100644 --- a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/routing.pure +++ b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/router/store/routing.pure @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import meta::relational::metamodel::*; +import meta::pure::extension::*; import meta::pure::graphFetch::*; import meta::pure::mapping::*; import meta::pure::router::builder::*; @@ -29,16 +31,67 @@ import meta::pure::runtime::*; // Store Mapping Routing Strategy Builder Helper // ========================================================================================= -function meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime(mapping:Mapping[1], runtime:Runtime[1]):StoreMappingRoutingStrategy[1] +function meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime(mapping:Mapping[1], runtime:Runtime[1], extensions: Extension[*]):StoreMappingRoutingStrategy[1] { - ^StoreMappingRoutingStrategy(mapping = $mapping, - runtime = $runtime, - setsByDepth = ^Map(), - classMappingsByClass = $mapping->buildClassMappingsByClassMap(), - processClass = processClass_Class_1__InstanceValue_1__RoutingState_1__ExecutionContext_1__DebugContext_1__RoutingState_1_, - processProperty = routeFunctionExpressionProperty_Property_1__FunctionExpression_1__RoutingState_1__ExecutionContext_1__Map_1__Map_1__Extension_MANY__DebugContext_1__RoutingState_1_, - wrapValueSpec = wrapValueSpecification_ValueSpecification_1__RoutingStrategy_1__String_1__ExecutionContext_1__Extension_MANY__DebugContext_1__ExtendedRoutedValueSpecification_1_, - toString = {strategy:RoutingStrategy[1] | $strategy->cast(@StoreMappingRoutingStrategy).sets->size()->toString()}); + getRoutingStrategyFromMappingAndRuntime([],$mapping, $runtime, $extensions); +} + +function meta::pure::router::store::routing::getRoutingStrategyFromMappingAndRuntime(routingStrategy: RoutingStrategy[0..1], mapping:Mapping[1], runtime:Runtime[1], extensions: Extension[*]):StoreMappingRoutingStrategy[1] +{ + let modifiedRuntime = $runtime; + let modifiedMapping = modifyMappingBasedOnRuntimeLocality($mapping, $modifiedRuntime, $extensions); + let currentStoreMappingRoutingStrategy = $routingStrategy->filter(x | $x->instanceOf(StoreMappingRoutingStrategy))->cast(@StoreMappingRoutingStrategy); + if($currentStoreMappingRoutingStrategy->isEmpty(), + | ^StoreMappingRoutingStrategy(mapping = $modifiedMapping, + runtime = $modifiedRuntime, + setsByDepth = ^Map(), + classMappingsByClass = $modifiedMapping->buildClassMappingsByClassMap(), + processClass = processClass_Class_1__InstanceValue_1__RoutingState_1__ExecutionContext_1__DebugContext_1__RoutingState_1_, + processProperty = routeFunctionExpressionProperty_Property_1__FunctionExpression_1__RoutingState_1__ExecutionContext_1__Map_1__Map_1__Extension_MANY__DebugContext_1__RoutingState_1_, + wrapValueSpec = wrapValueSpecification_ValueSpecification_1__RoutingStrategy_1__String_1__ExecutionContext_1__Extension_MANY__DebugContext_1__ExtendedRoutedValueSpecification_1_, + toString = {strategy:RoutingStrategy[1] | $strategy->cast(@StoreMappingRoutingStrategy).sets->size()->toString()} + );, + | let newStoreMappingRoutingStrategy = $currentStoreMappingRoutingStrategy->toOne(); + ^$newStoreMappingRoutingStrategy(mapping = $modifiedMapping, runtime = $modifiedRuntime, classMappingsByClass = $modifiedMapping->buildClassMappingsByClassMap(), setsByDepth = ^Map()); + ); +} + +function <> meta::pure::router::store::routing::modifyMappingBasedOnRuntimeLocality(mapping:Mapping[1], runtime:meta::core::runtime::Runtime[1], extensions: meta::pure::extension::Extension[*]):Mapping[1] +{ + let allMappings = $mapping->getIncludedMappingsRecursively(); + let classMappings = $allMappings.classMappings; + let associationMappings = $allMappings.associationMappings->map(associationMap|$associationMap->match( + [ + x:meta::pure::mapping::xStore::XStoreAssociationImplementation[1]| + let mappingIds = $x.propertyMappings.sourceSetImplementationId->concatenate($x.propertyMappings.targetSetImplementationId); + let inScopeClassMappings = $classMappings->filter(x|$x.id->in($mappingIds))->distinct(); + let inScopeStores = $inScopeClassMappings->map(setImpl | meta::pure::router::clustering::storeContractForSetImplementation($setImpl, $mapping, $extensions).second); + if( + $runtime.connectionStores->size() >= 1, + | let connection = $inScopeStores->map(s|$runtime->connectionByElement($s))->distinct(); + if( + $connection->size() == 1, + | $connection->toOne()->match( + $extensions.availableStores.localizeXStoreAssociation + ->map(e|$e->eval($x, $inScopeClassMappings, $inScopeStores)) + ->concatenate([a:Connection[1] | $x;])->toOneMany() + );, + | $x + );, + | $x + );, + + a:AssociationImplementation[1]|$a + ] + )); + let newMapping = ^Mapping(name=$mapping.name, includes=[], associationMappings=$associationMappings); + let includedMappings = $allMappings->map(map|^$map(associationMappings=[]))->map(map|^MappingInclude(owner=$newMapping,included=$map)); + $newMapping->mutateAdd('includes', $includedMappings); +} + +function <> meta::pure::router::store::routing::getIncludedMappingsRecursively(m : Mapping[1]): Mapping[*] +{ + $m->concatenate(if($m.includes->isEmpty(), |[], |$m.includes.included->map(i|$i->getIncludedMappingsRecursively()))); } function <> meta::pure::router::store::routing::processClass(c:Class[1], i:InstanceValue[1], s:RoutingState[1], executionContext:ExecutionContext[1], debug:DebugContext[1]):RoutingState[1] @@ -230,10 +283,7 @@ function meta::pure::router::store::routing::specializedFunctionExpressionRoute let fromExecutionContext = if($resolvedParameters->size() == 3, | $resolvedParameters->at(2)->cast(@ExecutionContext), | $executionContext); let currentRoutingStrategy = $state.routingStrategy; - let newRoutingStrategy = if($currentRoutingStrategy->instanceOf(StoreMappingRoutingStrategy), - | let storeMapStrategy = $currentRoutingStrategy->cast(@StoreMappingRoutingStrategy); - ^$storeMapStrategy(mapping = $fromMapping, runtime = $fromRuntime, classMappingsByClass = $fromMapping->buildClassMappingsByClassMap(), setsByDepth = ^Map());, - | getRoutingStrategyFromMappingAndRuntime($fromMapping, $fromRuntime)); + let newRoutingStrategy = getRoutingStrategyFromMappingAndRuntime($currentRoutingStrategy, $fromMapping, $fromRuntime, $extensions); let newState = ^$state(routingStrategy = $newRoutingStrategy); let processedFunction = routeFunctionExpression($fe.parametersValues->at(0)->cast(@FunctionExpression), $newState, $fromExecutionContext, $vars, $inScopeVars, $extensions, $debug); @@ -283,10 +333,7 @@ function meta::pure::router::store::routing::specializedFunctionExpressionRoute let runtime = $prevalFunc.parametersValues->at(3)->cast(@InstanceValue).values->toOne()->cast(@Runtime); let currentRoutingStrategy = $state.routingStrategy; - let newRoutingStrategy = if($currentRoutingStrategy->instanceOf(StoreMappingRoutingStrategy), - | let storeMapStrategy = $currentRoutingStrategy->cast(@StoreMappingRoutingStrategy); - ^$storeMapStrategy(mapping = $mapping, runtime = $runtime, classMappingsByClass = $mapping->buildClassMappingsByClassMap(), setsByDepth = ^Map());, - | getRoutingStrategyFromMappingAndRuntime($mapping, $runtime)); + let newRoutingStrategy = getRoutingStrategyFromMappingAndRuntime($currentRoutingStrategy, $mapping, $runtime, $extensions); let processedFirstParam = processCollection($state, $fe.parametersValues->at(0), $executionContext, $vars, $inScopeVars, {x:Any[1] | true}, $extensions, $debug)->toOne(); let processedSave = ^$fe(parametersValues = $processedFirstParam.value->cast(@ValueSpecification)->concatenate($fe.parametersValues->tail())); diff --git a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/store/storeContract.pure b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/store/storeContract.pure index cd79361bd1a..948c20fe06e 100644 --- a/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/store/storeContract.pure +++ b/legend-engine-pure/legend-engine-pure-code/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/store/storeContract.pure @@ -7,6 +7,7 @@ import meta::pure::store::*; import meta::core::runtime::*; import meta::pure::runtime::*; import meta::pure::executionPlan::*; +import meta::pure::mapping::xStore::*; Class meta::pure::store::StoreContract extends meta::pure::router::extension::RouterExtension, meta::pure::executionPlan::extension::ExecutionPlanExtension { @@ -16,6 +17,7 @@ Class meta::pure::store::StoreContract extends meta::pure::router::extension::Ro supports : Function<{FunctionExpression[1]->Boolean[1]}>[0..1]; supportsNativeJoinAmongElements : Boolean[1]; isPropertyAutoMapped : Function<{AbstractProperty[1], InstanceSetImplementation[1]->Boolean[1]}>[0..1]; + localizeXStoreAssociation : Function<{XStoreAssociationImplementation[1], SetImplementation[*], Store[*] -> Function<{Nil[1]->AssociationImplementation[1]}>[*]}>[0..1]; // ------------------------------------------------------------------------------------------ Routing // ExecutionPlan Generation ------------------------------------------------------------------------- diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/contract/storeContract.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/contract/storeContract.pure index bd9037fccfc..b7b974aba39 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/contract/storeContract.pure +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/contract/storeContract.pure @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +import meta::pure::mapping::xStore::*; import meta::relational::contract::*; import meta::pure::router::metamodel::*; import meta::pure::router::systemMapping::tests::*; @@ -43,7 +44,12 @@ function meta::relational::contract::relationalStoreContract():StoreContract[1] planExecution = meta::relational::contract::planExecution_StoreQuery_1__RoutedValueSpecification_$0_1$__Mapping_$0_1$__Runtime_$0_1$__ExecutionContext_1__Extension_MANY__DebugContext_1__ExecutionNode_1_, planGraphFetchExecution = meta::relational::contract::planGraphFetchExecution_StoreMappingLocalGraphFetchExecutionNodeGenerationInput_1__LocalGraphFetchExecutionNode_1_, planCrossGraphFetchExecution = meta::relational::contract::planCrossGraphFetchExecution_StoreMappingCrossLocalGraphFetchExecutionNodeGenerationInput_1__LocalGraphFetchExecutionNode_1_, - + localizeXStoreAssociation = { + x : XStoreAssociationImplementation[1], setImpl : SetImplementation[*], stores : Store[*] | + [ + d: DatabaseConnection[1]| meta::external::store::relational::modelJoins::localizeXStoreAssociation($x, $setImpl, $stores); + ] + }, connectionEquality = { b : Connection [1] | [ d: RelationalDatabaseConnection[1]| diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/modelJoins.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/modelJoins.pure new file mode 100644 index 00000000000..b62caf16180 --- /dev/null +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/modelJoins.pure @@ -0,0 +1,118 @@ +import meta::pure::store::*; +import meta::pure::executionPlan::*; +import meta::pure::extension::*; +import meta::relational::extension::*; +import meta::alloy::objectReference::*; +import meta::external::store::relational::runtime::*; +import meta::relational::runtime::*; +import meta::relational::functions::pureToSqlQuery::union::*; +import meta::relational::functions::pureToSqlQuery::metamodel::*; +import meta::relational::functions::pureToSqlQuery::*; +import meta::pure::metamodel::valuespecification::*; +import meta::relational::mapping::*; +import meta::relational::metamodel::*; +import meta::relational::metamodel::operation::*; +import meta::relational::metamodel::relation::*; +import meta::relational::metamodel::join::*; +import meta::relational::functions::pureToSqlQuery::*; +import meta::relational::functions::pureToSqlQuery::relationalmappingspecification::*; +import meta::pure::mapping::*; +import meta::pure::metamodel::path::*; +import meta::pure::metamodel::serialization::grammar::*; +import meta::pure::milestoning::*; +import meta::pure::tds::*; +import meta::relational::functions::*; +import meta::relational::milestoning::*; +import meta::pure::router::clustering::*; +import meta::pure::router::printer::*; +import meta::pure::router::routing::*; +import meta::pure::router::store::embedded::*; +import meta::pure::router::store::metamodel::*; +import meta::pure::router::store::metamodel::clustering::*; +import meta::pure::router::utils::*; +import meta::relational::extension::*; +import meta::pure::mapping::xStore::*; +import meta::external::store::relational::modelJoins::*; + +function meta::external::store::relational::modelJoins::localizeXStoreAssociation(xstoreAssociation: XStoreAssociationImplementation[1], setImpl:SetImplementation[*], stores:Store[*]):AssociationImplementation[1] +{ + let database = ^Database(includes=$stores->cast(@Database)); + let relationalPropertyMappings = $xstoreAssociation.propertyMappings->cast(@XStorePropertyMapping)->map(xstorePropertyMapping | transformXStorePropertyIntoRelationalProperty($xstorePropertyMapping, $setImpl, $database)); + ^RelationalAssociationImplementation(association=$xstoreAssociation.association,parent=$xstoreAssociation.parent,id=$xstoreAssociation.id, stores=$database, propertyMappings=$relationalPropertyMappings); +} + +function <> meta::external::store::relational::modelJoins::transformXStorePropertyIntoRelationalProperty(xstoreProperty: meta::pure::mapping::xStore::XStorePropertyMapping[1], setImpl:SetImplementation[*], database:Database[1]): RelationalPropertyMapping[1] +{ + let expressionSequence = $xstoreProperty.crossExpression.expressionSequence->cast(@SimpleFunctionExpression)->toOne()->evaluateAndDeactivate(); + let sourceId = $xstoreProperty.sourceSetImplementationId->toOne(); + let targetId = $xstoreProperty.targetSetImplementationId->toOne(); + let classMappings = $setImpl->cast(@RootRelationalInstanceSetImplementation); + let sourceMainTableAlias = $classMappings->filter(c| $c.id == $sourceId)->toOne().mainTableAlias; + + let join = transformExpressionSequenceIntoJoin($expressionSequence, $sourceId, $targetId, $classMappings); + + let joinTreeNode = ^JoinTreeNode( + joinName=$join.name, + database=$join.database->toOne(), + alias=$join.target->toOne(), + join=$join + ); + + let ro = ^RelationalOperationElementWithJoin(joinTreeNode=$joinTreeNode); + let r = ^RelationalPropertyMapping(sourceSetImplementationId=$sourceId,property=$xstoreProperty.property->toOne(),relationalOperationElement=$ro,targetSetImplementationId=$targetId); +} + +function <> meta::external::store::relational::modelJoins::getTableAliasColumn(propertyImplementations:SimpleFunctionExpression[*], thisOrThat:String[1], id:String[1], classMappings:RootRelationalInstanceSetImplementation[*]):TableAliasColumn[1] +{ + let propertyImplementation = $propertyImplementations->filter(p|$p.parametersValues->cast(@VariableExpression).name->toOne()==$thisOrThat).func->toOne(); + let classMapping = $classMappings->filter(c| $c.id == $id)->toOne(); + let relationalPropertyMapping = $classMapping.propertyMappings->filter(p|$p.property==$propertyImplementation)->cast(@RelationalPropertyMapping)->toOne(); + let tableAliasColumn = $relationalPropertyMapping.relationalOperationElement->cast(@TableAliasColumn)->toOne(); +} + +function <> meta::external::store::relational::modelJoins::transformExpressionSequenceIntoJoin(expressionSequence: SimpleFunctionExpression[1], sourceId:String[1], targetId:String[1], classMappings:RootRelationalInstanceSetImplementation[*]): Join[1] +{ + let functionOperator = $expressionSequence.functionName->toOne(); + let joinName = $sourceId + '_' + $targetId + '_GeneratedRelationalJoin'; + let sourceMainTableAlias = $classMappings->filter(c| $c.id == $sourceId)->toOne().mainTableAlias->map(alias|^$alias(name=$alias.relation->cast(@Table).name)); + let targetMainTableAlias = $classMappings->filter(c| $c.id == $targetId)->toOne().mainTableAlias->map(alias|^$alias(name=$alias.relation->cast(@Table).name)); + let sourceDatabase = $sourceMainTableAlias.database->toOne(); + let targetDatabase = $targetMainTableAlias.database->toOne(); + let aggregatedDatabase = ^Database(includes=[$sourceDatabase, $targetDatabase]); + + assertContains(['equal', 'not', 'and', 'or'], $functionOperator, 'Failed to translate XStore Property into Relational Property because function operator is not in standard list'); + + let join = if( + $functionOperator=='equal' || $functionOperator=='not', + | let propertyImplementations = $expressionSequence.parametersValues->cast(@SimpleFunctionExpression)->evaluateAndDeactivate(); + + let sourceTableAliasColumn = getTableAliasColumn($propertyImplementations, 'this', $sourceId, $classMappings); + let sourceTableAlias = $sourceTableAliasColumn.alias->cast(@TableAlias)->map(alias|^$alias(name=$alias.relation->cast(@Table).name)); + + let targetTableAliasColumn = getTableAliasColumn($propertyImplementations, 'that', $targetId, $classMappings); + let targetTableAlias = $targetTableAliasColumn.alias->cast(@TableAlias)->map(alias|^$alias(name=$alias.relation->cast(@Table).name)); + + let parameters = $sourceTableAliasColumn->concatenate($targetTableAliasColumn); + let operation = ^DynaFunction(name=$functionOperator, parameters=$parameters); + ^Join( + name=$joinName, + operation=$operation, + target=$targetTableAlias, + database=$aggregatedDatabase, + aliases = [^Pair(first=$targetTableAlias,second=$sourceTableAlias), ^Pair(first=$sourceTableAlias,second=$targetTableAlias)] + );, + | let childJoins = $expressionSequence.parametersValues->map(p | + transformExpressionSequenceIntoJoin($p->cast(@SimpleFunctionExpression)->evaluateAndDeactivate(), $sourceId, $targetId, $classMappings) + ); + let operation = ^DynaFunction(name=$functionOperator, parameters=$childJoins.operation->evaluateAndDeactivate()); + ^Join( + name=$joinName, + operation=$operation, + target=$targetMainTableAlias, + database=$aggregatedDatabase, + aliases = [^Pair(first=$targetMainTableAlias,second=$sourceMainTableAlias), ^Pair(first=$sourceMainTableAlias,second=$targetMainTableAlias)]->concatenate($childJoins.aliases) + ); + ); + +} + diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/testModelJoinsToRelationalJoins.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/testModelJoinsToRelationalJoins.pure new file mode 100644 index 00000000000..f731237120b --- /dev/null +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/src/main/resources/core_relational/relational/modelJoins/testModelJoinsToRelationalJoins.pure @@ -0,0 +1,279 @@ +###Pure +import meta::pure::mapping::*; +import meta::pure::graphFetch::execution::*; +import meta::pure::executionPlan::*; +import meta::relational::metamodel::execute::*; +import meta::relational::mapping::*; +import meta::relational::runtime::*; +import meta::pure::alloy::connections::*; +import meta::external::store::relational::runtime::*; +import meta::core::runtime::*; +import meta::external::store::relational::modelJoins::test::*; +import meta::pure::alloy::connections::alloy::specification::*; + +function <> meta::external::store::relational::modelJoins::test::setUp():Boolean[1] +{ + let connection = meta::external::store::relational::tests::testRuntime(LocalTradesDatabase).connectionStores.connection->toOne()->cast(@DatabaseConnection); + + executeInDb('Drop table if exists LegalEntity;', $connection); + executeInDb('Create Table LegalEntity (ENTITY_ID INT, name VARCHAR(32));', $connection); + executeInDb('insert into LegalEntity (ENTITY_ID, name) values (1, \'Firm X\');', $connection); + executeInDb('insert into LegalEntity (ENTITY_ID, name) values (2, \'Firm A\');', $connection); + + executeInDb('Drop table if exists Trade;', $connection); + executeInDb('Create Table Trade(id INT, ENTITY_NAME_FK VARCHAR(32), value INT, ENTITY_ID_FK INT);', $connection); + executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (1, \'Firm X\', 8, 1);', $connection); + executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (2, \'Firm A\', 9, 2);', $connection); + executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (3, \'Firm X\', 10, 1);', $connection); + executeInDb('insert into Trade (id, ENTITY_NAME_FK, value, ENTITY_ID_FK) values (4, \'Firm X\', 11, 1);', $connection); + true; +} + +function meta::external::store::relational::modelJoins::test::getConnection():meta::core::runtime::Connection[1] +{ + ^RelationalDatabaseConnection( + type = DatabaseType.H2, + datasourceSpecification = ^meta::pure::alloy::connections::alloy::specification::LocalH2DatasourceSpecification(), + authenticationStrategy = ^meta::pure::alloy::connections::alloy::authentication::DefaultH2AuthenticationStrategy() + ); +} + +function meta::external::store::relational::modelJoins::test::getXStoreRuntime():Runtime[1] +{ + let c = getConnection(); + let xstoreruntime = ^meta::core::runtime::Runtime( + connectionStores=[ + ^ConnectionStore(element=XStoreTradesDatabase, connection=$c), + ^ConnectionStore(element=EntityDatabase, connection=$c) + ] + ); +} + +function meta::external::store::relational::modelJoins::test::getLocalRuntime():Runtime[1] +{ + let localruntime = ^meta::core::runtime::Runtime( + connectionStores=[ + ^ConnectionStore(element=LocalTradesDatabase, connection=getConnection()) + ] + ); +} + +function <> meta::external::store::relational::modelJoins::test::testPersonToFirmUsingProject():Boolean[1] +{ + let query = {|Trade.all()->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);}; + let xstoreResult = executionPlan($query, XStoreTradesMapping, getXStoreRuntime(), meta::relational::extension::relationalExtensions()); + let localResult = executionPlan($query, LocalTradesMapping, getLocalRuntime(),meta::relational::extension::relationalExtensions()); + assertEquals( + $xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery, + $localResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery + ); + assertEquals('select "root".value as "Value", "legalentity_0".name as "Client/Name" from Trades.Trade as "root" left outer join Entity.LegalEntity as "legalentity_0" on ("root".ENTITY_ID_FK = "legalentity_0".ENTITY_ID and "root".ENTITY_NAME_FK = "legalentity_0".name)',$xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery); + // assertSameElements(['Anthony Firm X', 'David Firm C', 'Fabrice Firm A', 'John Firm X', 'John Firm X', 'Oliver Firm B', 'Peter Firm X'], $result.values->at(0).rows->map(r|$r.getString('name')+' '+$r.getString('firm')->toString())); +} + +function <> meta::external::store::relational::modelJoins::test::testPersonToFirmUsingFromProject():Boolean[1] +{ + let xstoreResult = executionPlan({|Trade.all()->from(XStoreTradesMapping, getXStoreRuntime())->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);}, meta::relational::extension::relationalExtensions()); + let localResult = executionPlan({|Trade.all()->from(LocalTradesMapping, getLocalRuntime())->project([x|$x.value,x|$x.client.name],['Value','Client/Name']);}, meta::relational::extension::relationalExtensions()); + assertEquals( + $xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery, + $localResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery + ); + assertEquals('select "root".value as "Value", "legalentity_0".name as "Client/Name" from Trades.Trade as "root" left outer join Entity.LegalEntity as "legalentity_0" on ("root".ENTITY_ID_FK = "legalentity_0".ENTITY_ID and "root".ENTITY_NAME_FK = "legalentity_0".name)',$xstoreResult.rootExecutionNode.executionNodes->at(0)->cast(@SQLExecutionNode).sqlQuery); + // assertSameElements(['Anthony Firm X', 'David Firm C', 'Fabrice Firm A', 'John Firm X', 'John Firm X', 'Oliver Firm B', 'Peter Firm X'], $result.values->at(0).rows->map(r|$r.getString('name')+' '+$r.getString('firm')->toString())); +} + +function <> meta::external::store::relational::modelJoins::test::testPersonToFirmGraphUsingFetch():Boolean[1] +{ + let tree = #{ + Trade { + value, + client { + name + } + } + }#; + let query = {|Trade.all()->graphFetch($tree)->serialize($tree);}; + let xstoreResult = executionPlan($query, XStoreTradesMapping, getXStoreRuntime(), meta::relational::extension::relationalExtensions()); + let localResult = executionPlan($query, LocalTradesMapping, getLocalRuntime(), meta::relational::extension::relationalExtensions()); + false; +} + +function <> meta::external::store::relational::modelJoins::test::testPersonToFirmUsingFromGraphFetch():Boolean[1] +{ + let tree = #{ + Trade { + value, + client { + name + } + } + }#; + let xstoreResult = executionPlan({|Trade.all()->from(XStoreTradesMapping, getXStoreRuntime())->graphFetch($tree)->serialize($tree);}, meta::relational::extension::relationalExtensions()); + let localResult = executionPlan({|Trade.all()->from(LocalTradesMapping, getLocalRuntime())->graphFetch($tree)->serialize($tree);}, meta::relational::extension::relationalExtensions()); + false; +} + +###Pure +Class meta::external::store::relational::modelJoins::test::LegalEntity +{ + entityId: String[1]; + name: String[1]; +} + +Class meta::external::store::relational::modelJoins::test::Trade +{ + id: String[1]; + value: Integer[1]; +} + +Class meta::external::store::relational::modelJoins::test::OtherEntity +{ + idPlusName: String[1]; +} + +Association meta::external::store::relational::modelJoins::test::Trade_LegalEntity +{ + client: meta::external::store::relational::modelJoins::test::LegalEntity[1]; + trades: meta::external::store::relational::modelJoins::test::Trade[*]; +} + +Association meta::external::store::relational::modelJoins::test::Trade_OtherEntity +{ + otherClient: meta::external::store::relational::modelJoins::test::OtherEntity[1]; + trades: meta::external::store::relational::modelJoins::test::Trade[*]; +} + + +###Relational +Database meta::external::store::relational::modelJoins::test::EntityDatabase +( + Schema Entity + ( + Table LegalEntity + ( + ENTITY_ID VARCHAR(32) PRIMARY KEY, + name VARCHAR(32) NOT NULL + ) + ) +) + +###Relational +Database meta::external::store::relational::modelJoins::test::LocalTradesDatabase +( + include meta::external::store::relational::modelJoins::test::EntityDatabase + Schema Trades + ( + Table Trade + ( + id VARCHAR(32) PRIMARY KEY, + value INTEGER NOT NULL, + ENTITY_ID_FK VARCHAR(32) NOT NULL, + ENTITY_NAME_FK VARCHAR(32) NOT NULL + ) + ) + + Join Entity_Trade( + Trades.Trade.ENTITY_ID_FK = Entity.LegalEntity.ENTITY_ID and Trades.Trade.ENTITY_NAME_FK = Entity.LegalEntity.name + ) +) + + +###Relational +Database meta::external::store::relational::modelJoins::test::XStoreTradesDatabase +( + Schema Trades + ( + Table Trade + ( + id VARCHAR(32) PRIMARY KEY, + value INTEGER NOT NULL, + ENTITY_ID_FK VARCHAR(32) NOT NULL, + ENTITY_NAME_FK VARCHAR(32) NOT NULL + ) + ) +) + + +###Mapping +import meta::external::store::relational::modelJoins::test::*; + +Mapping meta::external::store::relational::modelJoins::test::LegalEntityMapping +( + meta::external::store::relational::modelJoins::test::LegalEntity[legal_entity]: Relational + { + ~primaryKey + ( + [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity.ENTITY_ID + ) + ~mainTable [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity + entityId: [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity.ENTITY_ID, + name: [meta::external::store::relational::modelJoins::test::EntityDatabase]Entity.LegalEntity.name + } + + OtherEntity[otherEntity]: Pure + { + ~src LegalEntity + idPlusName: toString($src.entityId)+$src.name + } +) + +###Mapping +import meta::external::store::relational::modelJoins::test::*; + +Mapping meta::external::store::relational::modelJoins::test::LocalTradesMapping +( + include meta::external::store::relational::modelJoins::test::LegalEntityMapping + + Trade[trade]: Relational + { + ~primaryKey + ( + [LocalTradesDatabase]Trades.Trade.id + ) + ~mainTable [LocalTradesDatabase]Trades.Trade + id: [LocalTradesDatabase]Trades.Trade.id, + value: [LocalTradesDatabase]Trades.Trade.value, + +entityIdFk: String[1]: [LocalTradesDatabase]Trades.Trade.ENTITY_ID_FK, + +entityNameFk: String[1]: [LocalTradesDatabase]Trades.Trade.ENTITY_NAME_FK + } + + meta::external::store::relational::modelJoins::test::Trade_LegalEntity[trade_legal]: Relational + { + AssociationMapping + ( + client[trade, legal_entity]: [LocalTradesDatabase]@Entity_Trade, + trades[legal_entity, trade]: [LocalTradesDatabase]@Entity_Trade + ) + } +) + + +###Mapping +import meta::external::store::relational::modelJoins::test::*; + +Mapping meta::external::store::relational::modelJoins::test::XStoreTradesMapping +( + include meta::external::store::relational::modelJoins::test::LegalEntityMapping + + Trade[trade]: Relational + { + ~primaryKey + ( + [XStoreTradesDatabase]Trades.Trade.id + ) + ~mainTable [XStoreTradesDatabase]Trades.Trade + id: [XStoreTradesDatabase]Trades.Trade.id, + value: [XStoreTradesDatabase]Trades.Trade.value, + +entityIdFk: String[1]: [XStoreTradesDatabase]Trades.Trade.ENTITY_ID_FK, + +entityNameFk: String[1]: [XStoreTradesDatabase]Trades.Trade.ENTITY_NAME_FK + } + + meta::external::store::relational::modelJoins::test::Trade_LegalEntity: XStore + { + client[trade, legal_entity]: $this.entityIdFk == + $that.entityId && $this.entityNameFk == $that.name, + trades[legal_entity, trade]: $this.entityId == + $that.entityIdFk && $this.name == $that.entityNameFk + } +) diff --git a/legend-engine-xts-service/legend-engine-language-pure-dsl-service-pure/src/main/resources/core_service/service/extension.pure b/legend-engine-xts-service/legend-engine-language-pure-dsl-service-pure/src/main/resources/core_service/service/extension.pure index c5070ac09e3..497ccf0c7bb 100644 --- a/legend-engine-xts-service/legend-engine-language-pure-dsl-service-pure/src/main/resources/core_service/service/extension.pure +++ b/legend-engine-xts-service/legend-engine-language-pure-dsl-service-pure/src/main/resources/core_service/service/extension.pure @@ -34,10 +34,7 @@ function meta::legend::service::serviceExtension() : Extension[1] let fromRuntime = $fromSingleExecParams.runtime; let currentRoutingStrategy = $state.routingStrategy; - let newRoutingStrategy = if($currentRoutingStrategy->instanceOf(StoreMappingRoutingStrategy), - | let storeMapStrategy = $currentRoutingStrategy->cast(@StoreMappingRoutingStrategy); - ^$storeMapStrategy(mapping = $fromMapping, runtime = $fromRuntime, classMappingsByClass = $fromMapping->buildClassMappingsByClassMap(), setsByDepth = ^Map());, - | getRoutingStrategyFromMappingAndRuntime($fromMapping, $fromRuntime)); + let newRoutingStrategy = getRoutingStrategyFromMappingAndRuntime($currentRoutingStrategy, $fromMapping, $fromRuntime, $extensions); let newState = ^$state(routingStrategy = $newRoutingStrategy); let processedFunction = routeFunctionExpressionFunctionDefinition($f, $fe, $newState, $executionContext, $vars, $inScopeVars, $extensions, $debug);