diff --git a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/pom.xml b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/pom.xml index 1d791173f14..f602def85a0 100644 --- a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/pom.xml +++ b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/pom.xml @@ -294,6 +294,10 @@ org.finos.legend.engine legend-engine-pure-runtime-java-extension-compiled-functions-json + + org.finos.legend.engine + legend-engine-pure-runtime-java-extension-compiled-functions-relation + org.finos.legend.pure @@ -330,6 +334,11 @@ legend-engine-pure-runtime-java-extension-shared-functions-json + + org.finos.legend.engine + legend-engine-pure-runtime-java-extension-shared-functions-relation + + org.finos.legend.pure legend-pure-runtime-java-extension-compiled-dsl-diagram diff --git a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/extensions/extension.pure b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/extensions/extension.pure index a57eaa65f34..00663a24865 100644 --- a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/extensions/extension.pure +++ b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/extensions/extension.pure @@ -73,6 +73,8 @@ Class meta::pure::extension::Extension tdsSchema_resolveSchemaImpl : Function<{FunctionExpression[1], Map>[1], Extension[*] -> Pair, Function<{->SchemaState[1]}>>[*]}>[0..1]; //--------------------------------------------------------------------------------------------------------------------------------------- + tdsToRelation: meta::pure::tds::toRelation::TdsToRelationExtension[0..1]; + // testedBy navigates the Database structure. Should probably delete -------------------------------------------------------------------- testExtension_testedBy : Function<{ReferenceUsage[1], Extension[*] -> Function<{TestedByResult[1]->TestedByResult[1]}>[*]}>[0..1]; //--------------------------------------------------------------------------------------------------------------------------------------- diff --git a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/protocol/vX_X_X/transfers/helpers.pure b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/protocol/vX_X_X/transfers/helpers.pure new file mode 100644 index 00000000000..9af20f282cb --- /dev/null +++ b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/protocol/vX_X_X/transfers/helpers.pure @@ -0,0 +1,179 @@ +// Copyright 2025 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import meta::pure::extension::*; +import meta::protocols::pure::vX_X_X::transformation::helpers::*; + + +function meta::protocols::pure::vX_X_X::transformation::helpers::var(name:String[1]): meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::Variable[1] +{ + var($name, [], []) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::var(name:String[1], multiplicity:Multiplicity[0..1], genericType:meta::protocols::pure::vX_X_X::metamodel::m3::type::generics::GenericType[0..1]): meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::Variable[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::Variable( + _type = 'var', + name = $name, + genericType = $genericType, + multiplicity = if ($multiplicity->isNotEmpty(), | $multiplicity->toOne()->multiplicity(), | []) + ) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::multiplicity(value:Any[*]):meta::protocols::pure::vX_X_X::metamodel::m3::multiplicity::Multiplicity[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::m3::multiplicity::Multiplicity(lowerBound = $value->size(), upperBound = $value->size()) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::lambda(expressionSequence:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::Lambda[1] +{ + lambda($expressionSequence, []) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::lambda(expressionSequence:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*], parameters: meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::Variable[*]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::Lambda[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::Lambda( + _type = 'lambda', + parameters = $parameters, + body = $expressionSequence + ) +} + + +function meta::protocols::pure::vX_X_X::transformation::helpers::collection(values:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::Collection[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::Collection(_type = 'collection', + values = $values, + multiplicity = ^meta::protocols::pure::vX_X_X::metamodel::m3::multiplicity::Multiplicity(lowerBound=$values->size(), upperBound=$values->size()) + ) +} + + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpecArrayInstance(names:String[*]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::ClassInstance[1] +{ + createColSpecArrayInstance(createColSpecArray($names)) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpecArrayInstance(colSpecs:meta::protocols::pure::vX_X_X::metamodel::relation::ColSpec[*]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::ClassInstance[1] +{ + createColSpecArrayInstance($colSpecs->createColSpecArray()) +} + + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpecArrayInstance(colSpecs:meta::protocols::pure::vX_X_X::metamodel::relation::ColSpecArray[1]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::ClassInstance[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::ClassInstance( + _type = 'classInstance', + type = 'colSpecArray', + value = $colSpecs + ) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpecInstance(colSpec:meta::protocols::pure::vX_X_X::metamodel::relation::ColSpec[1]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::ClassInstance[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::ClassInstance( + _type = 'classInstance', + type = 'colSpec', + value = $colSpec + ) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpec(name:String[1]):meta::protocols::pure::vX_X_X::metamodel::relation::ColSpec[1] +{ + createColSpec($name, [], []); +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpec(name:String[1], func1:meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::Lambda[0..1]):meta::protocols::pure::vX_X_X::metamodel::relation::ColSpec[1] +{ + createColSpec($name, $func1, []); +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpec(name:String[1], func1:meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::Lambda[0..1], func2: meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::Lambda[0..1]):meta::protocols::pure::vX_X_X::metamodel::relation::ColSpec[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::relation::ColSpec( + name = $name, + function1 = $func1, + function2 = $func2 + ) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpecArray(colspecs: meta::protocols::pure::vX_X_X::metamodel::relation::ColSpec[*]):meta::protocols::pure::vX_X_X::metamodel::relation::ColSpecArray[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::relation::ColSpecArray( + colSpecs = $colspecs + ) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpecInstance(name:String[1]):meta::protocols::pure::vX_X_X::metamodel::valueSpecification::ClassInstance[1] +{ + createColSpecInstance(createColSpec($name, [], [])) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::createColSpecArray(names:String[*]):meta::protocols::pure::vX_X_X::metamodel::relation::ColSpecArray[1] +{ + createColSpecArray($names->map(n | $n->createColSpec([], []))) +} + + +function meta::protocols::pure::vX_X_X::transformation::helpers::appliedFunction(f:Function[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedFunction[1] +{ + appliedFunction($f, []); +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::appliedFunction(f:Function[1], parameters:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedFunction[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedFunction( + _type = 'func', + function = $f.functionName->toOne(), + fControl = $f.name, + parameters = $parameters + ) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::property(name:String[1], parameters:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedProperty[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedProperty( + _type = 'property', + property = $name, + parameters = $parameters + ) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::processExtractEnumValue(enum:Enum[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + processExtractEnumValue($enum->type()->cast(@Enumeration), $enum->toString()); +} + + +function meta::protocols::pure::vX_X_X::transformation::helpers::processExtractEnumValue(enumeration:Enumeration[1], value:String[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + processExtractEnumValue($enumeration, ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::CString(_type = 'string', value = $value)) +} + +function meta::protocols::pure::vX_X_X::transformation::helpers::processExtractEnumValue(enumeration:Enumeration[1], value:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + ^meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedProperty( + _type = 'property', + property = $value->match([ + c:meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::CString[1] | $c.value + ]), + parameters = transformAny($enumeration, []) + ) +} + + +function meta::protocols::pure::vX_X_X::transformation::helpers::transformAny(value:Any[1], extensions:Extension[*]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + meta::protocols::pure::vX_X_X::transformation::fromPureGraph::valueSpecification::transformAny($value, [], ^Map>(), PureOne, $extensions); +} diff --git a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/tds/relation/tdsToRelation.pure b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/tds/relation/tdsToRelation.pure new file mode 100644 index 00000000000..a3e86a27fca --- /dev/null +++ b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/tds/relation/tdsToRelation.pure @@ -0,0 +1,481 @@ +// Copyright 2025 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import meta::protocols::pure::vX_X_X::transformation::helpers::*; +import meta::protocols::pure::vX_X_X::metamodel::relation::*; +import meta::pure::functions::math::olap::*; +import meta::protocols::pure::vX_X_X::metamodel::valueSpecification::*; +import meta::pure::extension::*; +import meta::pure::tds::toRelation::*; +import meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::*; +import meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::*; + +Class meta::pure::tds::toRelation::TdsToRelationExtension {} + +Class meta::pure::tds::toRelation::TdsToRelationExtension_V_X_X extends TdsToRelationExtension { + + transfers: Function<{AppliedFunction[1], Extension[*] -> meta::pure::functions::collection::Pair, FunctionDefinition<{->AppliedFunction[1]}>>[*]}>[1]; +} + +function meta::pure::tds::toRelation::transform(l:LambdaFunction[1], extensions:Extension[*]):Lambda[1] +{ + let transformed = $l->meta::protocols::pure::vX_X_X::transformation::fromPureGraph::transformLambda($extensions); + ^$transformed(body = $transformed.body->map(b | $b->transform($extensions))); +} + +function meta::pure::tds::toRelation::transform(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1], extensions:Extension[*]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + $v->match([ + a:AppliedFunction[1] | $a->transform($extensions), + a:AppliedProperty[1] | + if ($a.property->in(TDSRow.qualifiedProperties.name) && $a.parameters->size() == 2 && $a.parameters->at(1)->instanceOf(CString), + | property($a.parameters->at(1)->cast(@CString).value, $a.parameters->at(0)), + | $a), + l:Lambda[1] | ^$l(parameters = $l.parameters->map(p | $p->transform($extensions)->cast(@Variable)), body = $l.body->map(b | $b->transform($extensions))), + c:Collection[1] | ^$c(values = $c.values->map(v | $v->transform($extensions))), + v:Variable[1] | var($v.name), + v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] | $v + ]) +} + +function meta::pure::tds::toRelation::transform(a:AppliedFunction[1], extensions:Extension[*]):AppliedFunction[1] +{ + [ + pair(project_T_MANY__ColumnSpecification_MANY__TabularDataSet_1_->cast(@Function), + | + let columns = $a.parameters->at(1)->colsToColSpecArrayInstance($extensions); + + appliedFunction(project_C_MANY__FuncColSpecArray_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $columns]); + ), + pair(project_K_MANY__Function_MANY__String_MANY__TabularDataSet_1_->cast(@Function), + | + let lambdas = $a.parameters->at(1)->fromCollection()->cast(@Lambda); + let names = $a.parameters->at(2)->fromCollection()->cast(@CString).value; + + let columns = $lambdas->zip($names) + ->map(p | + createColSpec($p.second, $p.first, []) + )->createColSpecArrayInstance(); + + appliedFunction(project_C_MANY__FuncColSpecArray_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $columns]); + ), + //TODO activate when relation inference is fixed on project + // pair(project_TabularDataSet_1__ColumnSpecification_MANY__TabularDataSet_1_->cast(@Function), + // | + // let columns = $a.parameters->at(1)->colsToColSpecArrayInstance($extensions); + + // appliedFunction(project_Relation_1__FuncColSpecArray_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $columns]); + // ), + pair(groupBy_K_MANY__Function_MANY__AggregateValue_MANY__String_MANY__TabularDataSet_1_->cast(@Function), + | + let groupByColumns = $a.parameters->at(1)->fromCollection()->cast(@Lambda); + let aggs = $a.parameters->at(2)->fromCollection()->cast(@ClassInstance).value->cast(@meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::AggregateValue); + let names = $a.parameters->at(3)->fromCollection()->cast(@CString).value; + + let projects = $groupByColumns->concatenate($aggs.mapFn)->zip($names) + ->map(p | createColSpec($p.second, $p.first, [])) + ->createColSpecArrayInstance(); + + let groupBys = $names->slice(0, $groupByColumns->size()) + ->map(n | createColSpec($n, [], [])) + ->createColSpecArrayInstance(); + + let aggregations = $names->slice($groupByColumns->size(), $names->size())->zip($aggs) + ->map(p | + let agg = $p.second.aggregateFn->transform($extensions)->cast(@Lambda); + createColSpec($p.first, lambda(property($p.first, $p.second.mapFn.parameters), $p.second.mapFn.parameters), $agg); + )->createColSpecArrayInstance(); + + + appliedFunction(groupBy_Relation_1__ColSpecArray_1__AggColSpecArray_1__Relation_1_, [ + appliedFunction(project_C_MANY__FuncColSpecArray_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $projects]), + $groupBys, + $aggregations + ]); + ), + pair(groupBy_TabularDataSet_1__String_MANY__AggregateValue_MANY__TabularDataSet_1_->cast(@Function), + | + let groupBys = $a.parameters->at(1)->fromCollection()->cast(@CString).value->createColSpecArrayInstance(); + let aggs = $a.parameters->at(2)->fromCollection()->map(a | + $a->match([ + c:ClassInstance[1] | + let agg = $c.value->cast(@meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TDSAggregateValue); + createColSpec($agg.name, $agg.mapFn->transform($extensions)->cast(@Lambda), $agg.aggregateFn->transform($extensions)->cast(@Lambda));, + a:AppliedFunction[1] | + let name = $a.parameters->at(0)->cast(@CString).value; + let map = $a.parameters->at(1)->transform($extensions)->cast(@Lambda); + let agg = $a.parameters->at(2)->transform($extensions)->cast(@Lambda); + createColSpec($name, $map, $agg); + ]) + )->createColSpecArrayInstance(); + + appliedFunction(groupBy_Relation_1__ColSpecArray_1__AggColSpecArray_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $groupBys, $aggs]); + ), + pair(sort_TabularDataSet_1__SortInformation_MANY__TabularDataSet_1_->cast(@Function), + | + let items = $a.parameters->at(1)->fromCollection()->map(p | $p->match([ + f:AppliedFunction[1] | + let column = $f.parameters->at(0)->cast(@CString).value->createColSpecInstance(); + [ + pair(asc_String_1__SortInformation_1_.name->toOne(), + | appliedFunction(ascending_ColSpec_1__SortInfo_1_, $column)), + pair(desc_String_1__SortInformation_1_.name->toOne(), + | appliedFunction(descending_ColSpec_1__SortInfo_1_, $column)) + ]->getValue($f.fControl->toOne())->eval();, + s:ClassInstance[1] | + let si = $s.value->cast(@TDSSortInformation); + let column = createColSpecInstance($si.column); + [ + pair(SortDirection.ASC.name, + | appliedFunction(ascending_ColSpec_1__SortInfo_1_, $column)), + pair(SortDirection.DESC.name, + | appliedFunction(descending_ColSpec_1__SortInfo_1_, $column)) + ]->getValue($si.direction)->eval(); + ]))->toCollection(false); + + appliedFunction(sort_Relation_1__SortInfo_MANY__Relation_1_, [$a.parameters->at(0)->transform($extensions), $items]); + ), + pair(filter_TabularDataSet_1__Function_1__TabularDataSet_1_->cast(@Function), + | + let func = $a.parameters->at(1)->transform($extensions)->cast(@Lambda); + + appliedFunction(filter_Relation_1__Function_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $func]); + ), + pair(extend_TabularDataSet_1__BasicColumnSpecification_MANY__TabularDataSet_1_->cast(@Function), + | + let columns = $a.parameters->at(1)->colsToColSpecArrayInstance($extensions); + appliedFunction(extend_Relation_1__FuncColSpecArray_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $columns]); + ), + pair(renameColumns_TabularDataSet_1__Pair_MANY__TabularDataSet_1_->cast(@Function), + | + let renames = $a.parameters->at(1)->fromCollection()->map(p | + $p->match([ + c:ClassInstance[1] | + let pair = $c.value->cast(@meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::Pair); + pair($pair.first->cast(@CString).value, $pair.second->cast(@CString).value);, + a:AppliedFunction[1] | + pair($a.parameters->at(0)->cast(@CString).value, $a.parameters->at(1)->cast(@CString).value) + ]) + ); + + $renames->fold({p, acc | + appliedFunction(rename_Relation_1__ColSpec_1__ColSpec_1__Relation_1_, [$acc, createColSpecInstance($p.first), createColSpecInstance($p.second)]) + }, $a.parameters->at(0)->transform($extensions)->cast(@AppliedFunction)); + ), + pair(renameColumn_TabularDataSet_1__String_1__String_1__TabularDataSet_1_->cast(@Function), + | + let from = $a.parameters->at(1)->cast(@CString).value->createColSpecInstance(); + let to = $a.parameters->at(2)->cast(@CString).value->createColSpecInstance(); + + appliedFunction(rename_Relation_1__ColSpec_1__ColSpec_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $from, $to]); + ), + pair(restrict_TabularDataSet_1__String_MANY__TabularDataSet_1_->cast(@Function), + | + let columns = $a.parameters->at(1)->fromCollection()->cast(@CString).value->createColSpecArrayInstance(); + + appliedFunction(select_Relation_1__ColSpecArray_1__Relation_1_, [$a.parameters->at(0)->transform($extensions), $columns]); + ), + pair(restrictDistinct_TabularDataSet_1__String_MANY__TabularDataSet_1_->cast(@Function), + | + let columns = $a.parameters->at(1)->fromCollection()->cast(@CString).value->createColSpecArrayInstance(); + + let args = [$a.parameters->at(0)->transform($extensions), $columns]; + appliedFunction(distinct_Relation_1__Relation_1_, appliedFunction(select_Relation_1__ColSpecArray_1__Relation_1_, [$args])); + ), + pair(olapGroupBy_TabularDataSet_1__String_MANY__SortInformation_$0_1$__OlapOperation_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupByStringSortOpString($a, $extensions) + ), + pair(olapGroupBy_TabularDataSet_1__String_MANY__SortInformation_$0_1$__FunctionDefinition_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupByStringSortOpString($a, $extensions) + ), + pair(olapGroupBy_TabularDataSet_1__String_MANY__OlapOperation_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupByStringOpString($a, $extensions) + ), + pair(olapGroupBy_TabularDataSet_1__OlapOperation_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupByOpString($a, $extensions) + ), + pair(olapGroupBy_TabularDataSet_1__SortInformation_$0_1$__OlapOperation_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupBySortOpString($a, $extensions) + ), + pair(olapGroupBy_TabularDataSet_1__String_MANY__FunctionDefinition_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupByStringOpString($a, $extensions) + ), + pair(olapGroupBy_TabularDataSet_1__FunctionDefinition_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupByOpString($a, $extensions) + ), + pair(olapGroupBy_TabularDataSet_1__SortInformation_$0_1$__FunctionDefinition_1__String_1__TabularDataSet_1_->cast(@Function), + | transformOlapGroupBySortOpString($a, $extensions) + ) + ]->concatenate([ + swap(drop_TabularDataSet_1__Integer_1__TabularDataSet_1_, drop_Relation_1__Integer_1__Relation_1_, $a, $extensions), + swap(slice_TabularDataSet_1__Integer_1__Integer_1__TabularDataSet_1_, slice_Relation_1__Integer_1__Integer_1__Relation_1_, $a, $extensions), + swap(limit_TabularDataSet_1__Integer_1__TabularDataSet_1_, limit_Relation_1__Integer_1__Relation_1_, $a, $extensions), + swap(take_TabularDataSet_1__Integer_1__TabularDataSet_1_, limit_Relation_1__Integer_1__Relation_1_, $a, $extensions), + swap(distinct_TabularDataSet_1__TabularDataSet_1_, distinct_Relation_1__Relation_1_, $a, $extensions), + swap(concatenate_TabularDataSet_1__TabularDataSet_1__TabularDataSet_1_, concatenate_Relation_1__Relation_1__Relation_1_, $a, $extensions) + ])->concatenate(getExtensions($a, $extensions))->getValue(resolveFControlFunction($a, $extensions), | ^$a(parameters = $a.parameters->map(p | $p->transform($extensions))))->eval(); +} + +function meta::pure::tds::toRelation::getExtensions(a:AppliedFunction[1], extensions:Extension[*]):meta::pure::functions::collection::Pair, FunctionDefinition<{->AppliedFunction[1]}>>[*] +{ + $extensions.tdsToRelation + ->filter(t | $t->instanceOf(TdsToRelationExtension_V_X_X)) + ->cast(@TdsToRelationExtension_V_X_X).transfers->map(t | $t->eval($a, $extensions)) +} + + + +function meta::pure::tds::toRelation::swap(f:Function[1], o:Function[1], a:AppliedFunction[1], extensions:Extension[*]):meta::pure::functions::collection::Pair, FunctionDefinition<{->AppliedFunction[1]}>>[1] +{ + pair($f, | appliedFunction($o, $a.parameters->map(p | $p->transform($extensions)))) +} + +function meta::pure::tds::toRelation::colsToColSpecArrayInstance(params:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*], extensions:Extension[*]):ClassInstance[1] +{ + $params->fromCollection()->map(i | $i->match([ + c:ClassInstance[1] | + let col = $c.value->cast(@TDSColumnInformation); + createColSpec($col.name, $col.columnFn, []);, + f:AppliedFunction[1] | + createColSpec($f.parameters->at(1)->cast(@CString).value, $f.parameters->at(0)->transform($extensions)->cast(@Lambda), []) + ]))->createColSpecArrayInstance(); +} + +function meta::pure::tds::toRelation::fromCollection(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*] +{ + $v->match([ + c:Collection[1] | $c.values, + v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*] | $v + ]) +} + +function meta::pure::tds::toRelation::toCollection(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*]):Collection[1] +{ + toCollection($v, true)->cast(@Collection); +} + +function meta::pure::tds::toRelation::toCollection(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*], force:Boolean[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + $v->match([ + c:Collection[1] | $c, + c:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*] + | if($c->size() == 1 && !$force, | $c->toOne(), | collection($c)) + ]) +} + +function meta::pure::tds::toRelation::transformOlapGroupBySortOpString(a:AppliedFunction[1], extensions:Extension[*]):AppliedFunction[1] +{ + let name = $a.parameters->at(3)->cast(@CString).value; + let agg = $a.parameters->at(2)->transformOlapFunction($name, $extensions)->createColSpecArrayInstance(); + let sort = $a.parameters->at(1)->transformSortInfo()->toCollection(false); + let func = $a.parameters->at(2)->getOlapExtendFunction(); + + let over = appliedFunction(over_SortInfo_MANY___Window_1_, [$sort]); + + appliedFunction($func, [$a.parameters->at(0)->transform($extensions), $over, $agg]); +} + +function meta::pure::tds::toRelation::transformOlapGroupByOpString(a:AppliedFunction[1], extensions:Extension[*]):AppliedFunction[1] +{ + let name = $a.parameters->at(2)->cast(@CString).value; + let agg = $a.parameters->at(1)->transformOlapFunction($name, $extensions)->createColSpecArrayInstance(); + let func = $a.parameters->at(1)->getOlapExtendFunction(); + + let over = appliedFunction(over_String_MANY__SortInfo_MANY__Frame_$0_1$___Window_1_, [collection([]), collection([]), collection([])]); + + appliedFunction($func, [$a.parameters->at(0)->transform($extensions), $over, $agg]); +} + +function meta::pure::tds::toRelation::transformOlapGroupByStringOpString(a:AppliedFunction[1], extensions:Extension[*]):AppliedFunction[1] +{ + let columns = $a.parameters->at(1)->fromCollection()->cast(@CString).value->createColSpecArrayInstance(); + let name = $a.parameters->at(3)->cast(@CString).value; + let agg = $a.parameters->at(2)->transformOlapFunction($name, $extensions)->createColSpecArrayInstance(); + let func = $a.parameters->at(2)->getOlapExtendFunction(); + + let over = appliedFunction(over_ColSpecArray_1___Window_1_, [$columns]); + + appliedFunction($func, [$a.parameters->at(0)->transform($extensions), $over, $agg]); +} +function meta::pure::tds::toRelation::transformOlapGroupByStringSortOpString(a:AppliedFunction[1], extensions:Extension[*]):AppliedFunction[1] +{ + let columns = $a.parameters->at(1)->fromCollection()->cast(@CString).value->createColSpecArrayInstance(); + let sort = $a.parameters->at(2)->transformSortInfo()->toCollection(false); + let name = $a.parameters->at(4)->cast(@CString).value; + let agg = $a.parameters->at(3)->transformOlapFunction($name, $extensions)->createColSpecArrayInstance(); + let func = $a.parameters->at(3)->getOlapExtendFunction(); + + let over = appliedFunction(over_ColSpecArray_1__SortInfo_MANY___Window_1_, [$columns, $sort]); + + appliedFunction($func, [$a.parameters->at(0)->transform($extensions), $over, $agg]); +} + +function meta::pure::tds::toRelation::transformOlapFunction(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1], name:String[1], extensions:Extension[*]):ColSpec[1] +{ + $v->match([ + c:ClassInstance[1] | + $c.value->match([ + r:meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TdsOlapRank[1] | + let var = $r.function.parameters->at(0).name; + + assert($r.function.body->size() == 1 && $r.function.body->toOne()->instanceOf(AppliedFunction), 'olap func must be a simple lambda'); + + let func = [ + pair(rowNumber_Any_MANY__Map_1_.name->toOne(), | appliedFunction(rowNumber_Relation_1__T_1__Integer_1_, [var('p'), var('r')])), + pair(denseRank_Any_MANY__Map_1_.name->toOne(), | appliedFunction(denseRank_Relation_1___Window_1__T_1__Integer_1_, [var('p'), var('w'), var('r')])), + pair(rank_Any_MANY__Map_1_.name->toOne(), | appliedFunction(rank_Relation_1___Window_1__T_1__Integer_1_, [var('p'), var('w'), var('r')])) + ]->getValue($r.function.body->toOne()->cast(@AppliedFunction).fControl->toOne())->eval(); + + let aggregation = lambda($func, [var('p'), var('w'), var('r')]); + createColSpec($name, $aggregation);, + a:meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TdsOlapAggregation[1] | + let mapFn = lambda(property($a.columnName, var('r')), [var('p'), var('w'), var('r')]); + let aggregation = $a.function->transform($extensions)->cast(@Lambda); + createColSpec($name, $mapFn, $aggregation); + ]), + a:AppliedFunction[1] | + [ + pair(func_String_1__FunctionDefinition_1__TdsOlapAggregation_1_.name->toOne(), | + ^ClassInstance(_type='classInstance', + value = ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TdsOlapAggregation(function = $a.parameters->at(1)->cast(@Lambda), columnName = $a.parameters->at(0)->cast(@CString).value), + type= 'tdsOlapAggregation') + ), + pair(func_FunctionDefinition_1__TdsOlapRank_1_.name->toOne(), | + ^ClassInstance(_type='classInstance', + value = ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TdsOlapRank(function = $a.parameters->at(0)->cast(@Lambda)), + type= 'tdsOlapRank') + ) + ]->getValue($a.fControl->toOne())->eval()->transformOlapFunction($name, $extensions), + l:Lambda[1] | + ^ClassInstance(_type='classInstance', + value = ^meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TdsOlapRank(function = $l), + type= 'tdsOlapRank')->transformOlapFunction($name, $extensions) + ]); +} + +function meta::pure::tds::toRelation::getOlapExtendFunction(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1]):Function[1] +{ + + $v->match([ + c:ClassInstance[1] | + $c.value->match([ + r:meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TdsOlapRank[1] | extend_Relation_1___Window_1__FuncColSpecArray_1__Relation_1_, + a:meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::TdsOlapAggregation[1] | extend_Relation_1___Window_1__AggColSpecArray_1__Relation_1_ + ]), + l:Lambda[1] | extend_Relation_1___Window_1__FuncColSpecArray_1__Relation_1_ + ]); +} + +function meta::pure::tds::toRelation::transformJoinColsToFunc(left:String[*], right:String[*]):Lambda[1] +{ + let func = $left->zip($right)->map(v | + let lc = property($v.first, var('x')); + let rc = property($v.second, var('y')); + + appliedFunction(equal_Any_MANY__Any_MANY__Boolean_1_, [$lc, $rc]); + )->potentiallyCombineToLogical(true); + + lambda($func, [var('x'), var('y')]); +} + +function meta::pure::tds::toRelation::transformSortInfo(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1]):AppliedFunction[0..1] +{ + $v->match([ + f:AppliedFunction[1] | + let column = $f.parameters->at(0)->cast(@CString).value->createColSpecInstance(); + [ + pair(asc_String_1__SortInformation_1_.name->toOne(), + | appliedFunction(ascending_ColSpec_1__SortInfo_1_, $column)), + pair(desc_String_1__SortInformation_1_.name->toOne(), + | appliedFunction(descending_ColSpec_1__SortInfo_1_, $column)) + ]->getValue($f.fControl->toOne())->eval();, + s:ClassInstance[1] | + let si = $s.value->cast(@TDSSortInformation); + let column = createColSpecInstance($si.column); + [ + pair('ASC', + | appliedFunction(ascending_ColSpec_1__SortInfo_1_, $column)), + pair('DESC', + | appliedFunction(descending_ColSpec_1__SortInfo_1_, $column)) + ]->getValue($si.direction)->eval();, + v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[0..1] | [] + ]) +} + +function meta::pure::tds::toRelation::transformJoinKind(v:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + $v->match([ + a:AppliedProperty[1] | meta::relational::metamodel::join::JoinType->extractEnumValue($a.property); + ])->getJoinKind()->transformJoinKind(); +} + +function meta::pure::tds::toRelation::transformJoinKind(joinKind:meta::pure::functions::relation::JoinKind[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + processExtractEnumValue(meta::pure::functions::relation::JoinKind, $joinKind.name); +} + +function meta::pure::tds::toRelation::getJoinKind(joinType:meta::relational::metamodel::join::JoinType[1]):meta::pure::functions::relation::JoinKind[1] +{ + [ + pair(meta::relational::metamodel::join::JoinType.LEFT_OUTER, | meta::pure::functions::relation::JoinKind.LEFT), + pair(meta::relational::metamodel::join::JoinType.INNER, | meta::pure::functions::relation::JoinKind.INNER) + ]->getValue($joinType, | fail('unsupported join type ' + $joinType.name); meta::pure::functions::relation::JoinKind.INNER;)->eval(); +} + + +function <> meta::pure::tds::toRelation::potentiallyCombineToLogical(expressions:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[*], and:Boolean[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + + $expressions->match([ + e:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] | $e, + e:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[2..*] | combineToLogical($e, $and), + e:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[0..1] | fail('must be at least 1 expression'); $expressions->toOne(); + ]) +} + +function <> meta::pure::tds::toRelation::combineToLogical(expressions:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[2..*], and:Boolean[1]):meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::ValueSpecification[1] +{ + let func = if ($and, | and_Boolean_1__Boolean_1__Boolean_1_, | or_Boolean_1__Boolean_1__Boolean_1_); + + $expressions->slice(2, $expressions->size())->fold({item, acc | + appliedFunction($func, [$item, $acc]); + }, + appliedFunction($func, [$expressions->at(0), $expressions->at(1)]) + ); +} + +function meta::pure::tds::toRelation::getValue(pairs : meta::pure::functions::collection::Pair[*], key : X[1]) : Y[1] +{ + let r = newMap($pairs)->get($key); + assert($r->isNotEmpty(), | 'No value found for ' + $key->makeString() + if($pairs->size() < 15 && ($key->instanceOf(String) || $key->instanceOf(Enumeration)), |', expected one of ' + $pairs.first->map(x|$x->makeString())->sort()->joinStrings('[', ',', ']'), |'')); + $r->toOne(); +} + +function meta::pure::tds::toRelation::getValue(pairs : meta::pure::functions::collection::Pair[*], key : X[1], defaultValue : Y[1]) : Y[1] +{ + let r = newMap($pairs)->get($key); + if ($r->isEmpty(), + | $defaultValue, + | $r->toOne(); + ); +} + +function meta::pure::tds::toRelation::resolveFControlFunction(a:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedFunction[1], extensions:meta::pure::extension::Extension[*]):Function[1] +{ + assert($a.fControl->isNotEmpty(), 'fControl required to calculate type'); + + let state = meta::protocols::pure::vX_X_X::transformation::fromPureGraph::toPureGrammar::defaultAlloyToPureState($extensions); //todo plug in extensions + + $a->meta::protocols::pure::vX_X_X::transformation::fromPureGraph::toPureGrammar::resolveFControlFunction($state.funcs); +} diff --git a/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/tds/relation/testTdsToRelation.pure b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/tds/relation/testTdsToRelation.pure new file mode 100644 index 00000000000..5eae6c1c5ab --- /dev/null +++ b/legend-engine-core/legend-engine-core-pure/legend-engine-pure-code-compiled-core/src/main/resources/core/pure/tds/relation/testTdsToRelation.pure @@ -0,0 +1,435 @@ +// Copyright 2025 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import meta::pure::extension::*; +import meta::relational::metamodel::join::*; +import meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::*; +import meta::pure::tds::toRelation::*; + +Class meta::pure::tds::toRelation::TestClass +{ + string:String[1]; + integer:Integer[1]; + number:Number[1]; + float:Float[1]; + bool:Boolean[1]; +} + + +function <> meta::pure::tds::toRelation::testProjectMulti():Boolean[1] +{ + test( + [ + {| TestClass.all()->project( + [ + col(x | $x.string, 'str'), + col(x | $x.integer, 'int') + ] + ) + }, + {| TestClass.all()->project( + [ + x | $x.string, + x | $x.integer + ], [ + 'str', + 'int' + ] + ) + } + ], + {| + TestClass.all()->project(~[ + str: x | $x.string, + int: x | $x.integer + ]) + } + ); +} + +function <> meta::pure::tds::toRelation::testProjectSingle():Boolean[1] +{ + test( + [ + {| TestClass.all()->project(col(x | $x.string, 'str'))}, + {| TestClass.all()->project(x | $x.string, 'str')} + ], + {| TestClass.all()->project(~[str: x | $x.string])} + ); +} + +//TODO activate when relation inference is fixed on project +// function <> meta::pure::tds::toRelation::testTDSProjectSingle():Boolean[1] +// { +// test( +// {| TestClass.all() +// ->project(col(x | $x.string, 'str')) +// ->project(col(x:TDSRow[1] | $x.getString('str')->toOne() + 'abc', 'newCol'))}, +// {| TestClass.all() +// ->project(~[str: x | $x.string]) +// ->project(~[newCol : x | $x.str->toOne() + 'abc'])} +// ); +// } + +// function <> meta::pure::tds::toRelation::testTDSProjectMulti():Boolean[1] +// { +// test( +// {| TestClass.all() +// ->project([col(x | $x.string, 'str'), col(x | $x.integer, 'int')]) +// ->project([ +// col(x:TDSRow[1] | $x.getString('str')->toOne() + 'abc', 'newCol'), +// col(x:TDSRow[1] | $x.getString('str')->toOne() + 'def', 'newCol2') +// ])}, +// {| TestClass.all() +// ->project(~[str: x | $x.string, int: x | $x.integer]) +// ->project(~[ +// newCol : x | $x.str->toOne() + 'abc', +// newCol2 : x | $x.str->toOne() + 'def' +// ]) +// } +// ); +// } + + +function <> meta::pure::tds::toRelation::testExtendSingle():Boolean[1] +{ + test( + {| TestClass.all() + ->project(col(x | $x.string, 'str')) + ->extend( + col(x:TDSRow[1] | $x.getString('str')->toOne() + 'abc', 'col') + ) + }, + {| TestClass.all() + ->project(~[str: x | $x.string]) + ->extend(~[ + col: x | $x.str->toOne() + 'abc' + ]) + } + ); +} + +function <> meta::pure::tds::toRelation::testExtendMulti():Boolean[1] +{ + test( + {| TestClass.all() + ->project(col(x | $x.string, 'str')) + ->extend([ + col(x:TDSRow[1] | $x.getString('str')->toOne() + 'abc', 'col1'), + col(x:TDSRow[1] | $x.getString('str')->toOne() + 'def', 'col2') + ]) + }, + {| TestClass.all() + ->project(~[str: x | $x.string]) + ->extend(~[ + col1: x | $x.str->toOne() + 'abc', + col2: x | $x.str->toOne() + 'def' + ]) + } + ); +} + +function <> meta::pure::tds::toRelation::testGroupAllMulti():Boolean[1] +{ + test( + {| TestClass.all()->groupBy( + [ + x | $x.string, + x | $x.bool + ], + [ + agg(x | $x.integer, y | $y->sum()), + agg(x | $x.float, y | $y->count()) + ], + [ + 'str', 'boolean', 'sum', 'count' + ] + ) + }, + {| + TestClass.all()->project(~[ + str : x | $x.string, + boolean : x | $x.bool, + sum : x | $x.integer, + count : x | $x.float + ])->groupBy( + ~[str, boolean], + ~[ + sum : x | $x.sum : y | $y->sum(), + count : x | $x.count : y | $y->count() + ] + ) + } + ); +} + +function <> meta::pure::tds::toRelation::testGroupBySingle():Boolean[1] +{ + test( + {| TestClass.all()->groupBy(x | $x.string, agg(x | $x.integer, y | $y->sum()), ['str', 'sum'])}, + {| TestClass.all()->project(~[str : x | $x.string, sum : x | $x.integer])->groupBy(~[str], ~[sum : x | $x.sum : y | $y->sum()])} + ); +} + +function <> meta::pure::tds::toRelation::testTDSGroupBy():Boolean[1] +{ + test( + {| TestClass.all() + ->project([ + col(x | $x.string, 'str'), + col(x | $x.integer, 'int'), + col(x | $x.float, 'float') + ]) + ->groupBy( + ['str', 'float'], + [ + agg('sum', row | $row.getInteger('int'), y | $y->sum()), + agg('max', row | $row.getInteger('int'), y | $y->max()) + ]) + }, + {| TestClass.all() + ->project(~[ + str: x | $x.string, + int: x | $x.integer, + float: x | $x.float + ]) + ->groupBy( + ~[str, float], + ~[ + sum : row | $row.int : y | $y->sum(), + max : row | $row.int : y | $y->max() + ] + ) + } + ); +} + + +function <> meta::pure::tds::toRelation::testSortSingle():Boolean[1] +{ + test( + [ + {| TestClass.all()->project(col(x | $x.string, 'str'))->sort(asc('str'))}, + {| TestClass.all()->project(col(x | $x.string, 'str'))->sort(^SortInformation(column = 'str', direction = SortDirection.ASC))} + ], + {| TestClass.all()->project(~[str: x | $x.string])->sort(ascending(~str))} + ); +} + +function <> meta::pure::tds::toRelation::testDistinct():Boolean[1] +{ + test( + {| TestClass.all()->project(col(x | $x.string, 'str'))->distinct()}, + {| TestClass.all()->project(~[str: x | $x.string])->distinct()} + ); +} + +function <> meta::pure::tds::toRelation::testTakeLimit():Boolean[1] +{ + test( + [ + {| TestClass.all()->project(col(x | $x.string, 'str'))->limit(1)}, + {| TestClass.all()->project(col(x | $x.string, 'str'))->take(1)} + ], + {| TestClass.all()->project(~[str: x | $x.string])->limit(1)} + ); +} + +function <> meta::pure::tds::toRelation::testSlice():Boolean[1] +{ + test( + {| TestClass.all()->project(col(x | $x.string, 'str'))->slice(1, 2)}, + {| TestClass.all()->project(~[str: x | $x.string])->slice(1, 2)} + ); +} + +function <> meta::pure::tds::toRelation::testDrop():Boolean[1] +{ + test( + {| TestClass.all()->project(col(x | $x.string, 'str'))->drop(1)}, + {| TestClass.all()->project(~[str: x | $x.string])->drop(1)} + ); +} + +function <> meta::pure::tds::toRelation::testConcatenate():Boolean[1] +{ + test( + {| TestClass.all()->project(col(x | $x.string, 'str'))->concatenate(TestClass.all()->project(col(x | $x.float, 'str')))}, + {| TestClass.all()->project(~[str: x | $x.string])->concatenate(TestClass.all()->project(~[str: x | $x.float]))} + ); +} + +function <> meta::pure::tds::toRelation::testFilter():Boolean[1] +{ + test( + {| TestClass.all() + ->filter(x | $x.string == 'abc') + ->project([ + col(x | $x.string, 'str'), + col(x | $x.integer, 'int') + ]) + ->filter(x | + ($x.getString('str') == 'abc') || ($x.getString('str') == 'def') + && + ($x.getInteger('int')->toOne()->in([1, $x.getInteger('int')->toOne()])) + ) + }, + {| TestClass.all() + ->filter(x | $x.string == 'abc') + ->project(~[ + str: x | $x.string, + int: x | $x.integer + ]) + ->filter(x | + ($x.str == 'abc') || ($x.str == 'def') + && + ($x.int->toOne()->in([1, $x.int->toOne()])) + ) + } + ); +} + + +function <> meta::pure::tds::toRelation::testRenameSingle():Boolean[1] +{ + test( + [ + {| TestClass.all()->project(col(x | $x.string, 'str'))->renameColumns(pair('str', 'STR'))}, + {| TestClass.all()->project(col(x | $x.string, 'str'))->renameColumns(^meta::pure::functions::collection::Pair(first = 'str', second ='STR'))}, + {| TestClass.all()->project(col(x | $x.string, 'str'))->renameColumn('str', 'STR')} + ], + {| TestClass.all()->project(~[str: x | $x.string])->rename(~str, ~STR)} + ); +} + +function <> meta::pure::tds::toRelation::testRenameMulti():Boolean[1] +{ + test( + [ + {| TestClass.all()->project([ + col(x | $x.string, 'str'), + col(x | $x.integer, 'int') + ])->renameColumns([ + pair('str', 'STR'), + pair('int', 'INT') + ])} + ], + {| TestClass.all() + ->project(~[str: x | $x.string, int: x | $x.integer]) + ->rename(~str, ~STR) + ->rename(~int, ~INT) + } + ); +} + +function <> meta::pure::tds::toRelation::testRestrictDistinct():Boolean[1] +{ + test( + {| TestClass.all()->project([ + col(x | $x.string, 'str'), + col(x | $x.integer, 'int') + ])->restrictDistinct('int') + }, + {| TestClass.all() + ->project(~[str: x | $x.string, int: x | $x.integer]) + ->select(~[int]) + ->distinct() + } + ); +} + +function <> meta::pure::tds::toRelation::testRestrictSingle():Boolean[1] +{ + test( + {| TestClass.all()->project([ + col(x | $x.string, 'str'), + col(x | $x.integer, 'int') + ])->restrict('int') + }, + {| TestClass.all() + ->project(~[str: x | $x.string, int: x | $x.integer]) + ->select(~[int]) + } + ); +} + +function <> meta::pure::tds::toRelation::testRestrictMulti():Boolean[1] +{ + test( + {| TestClass.all()->project([ + col(x | $x.string, 'str'), + col(x | $x.integer, 'int') + ])->restrict(['int', 'str']) + }, + {| TestClass.all() + ->project(~[str: x | $x.string, int: x | $x.integer]) + ->select(~[int, str]) + } + ); +} + +function <> meta::pure::tds::toRelation::testOlap():Boolean[1] +{ + test( + {| TestClass.all()->project([ + col(x | $x.string, 'str'), + col(x | $x.number, 'int') + ])->olapGroupBy(['str'], asc('int'), y | $y->meta::pure::functions::math::olap::rowNumber(), 'ROW') + ->olapGroupBy(['str'], desc('int'), func(y | $y->meta::pure::functions::math::olap::denseRank()), 'DENSE RANK') + ->olapGroupBy(['str'], asc('int'), func(y | $y->meta::pure::functions::math::olap::rank()), 'RANK') + ->olapGroupBy(['str'], [], func('int', y | $y->max()), 'MAX') + ->olapGroupBy(['str'], func('int', y | $y->max()), 'MAX2') + ->olapGroupBy(func('int', y|$y->min()),'MIN') + ->olapGroupBy(asc('int'),func('int', y|$y->max()),'MAX3') + ->olapGroupBy(['int'], y|$y->meta::pure::functions::math::olap::rank(),'RANK2') + ->olapGroupBy(asc('int'), y|$y->meta::pure::functions::math::olap::rank(),'RANK3') + }, + + {| TestClass.all() + ->project(~[str: x | $x.string, int: x | $x.number]) + ->extend(over(~[str], [~int->ascending()]), ~[ROW:{p,w,r| $p->rowNumber($r)}]) + ->extend(over(~[str], [~int->descending()]), ~['DENSE RANK':{p,w,r| $p->denseRank($w, $r)}]) + ->extend(over(~[str], [~int->ascending()]), ~[RANK:{p,w,r| $p->rank($w, $r)}]) + ->extend(over(~[str], []), ~[MAX:{p,w,r|$r.int}:y|$y->max()]) + ->extend(over(~[str]), ~[MAX2:{p,w,r|$r.int}:y|$y->max()]) + ->extend(over([], [], []), ~[MIN:{p,w,r|$r.int}:y|$y->min()]) + ->extend(over(ascending(~int)), ~[MAX3:{p,w,r|$r.int}:y|$y->max()]) + ->extend(over(~[int]), ~[RANK2:{p,w,r| $p->rank($w, $r)}]) + ->extend(over([~int->ascending()]), ~[RANK3:{p,w,r| $p->rank($w, $r)}]) + } + ) +} + +function meta::pure::tds::toRelation::test(inputs:LambdaFunction[*], expected:LambdaFunction[1]):Boolean[1] +{ + test($inputs, $expected, []) +} + +function meta::pure::tds::toRelation::test(inputs:LambdaFunction[*], expected:LambdaFunction[1], extensions:Extension[*]):Boolean[1] +{ + $inputs->forAll(input | + let transformed = $input->meta::pure::tds::toRelation::transform($extensions); + assertLambdaJSONEquals($expected, $transformed); + ) +} + +function meta::pure::tds::toRelation::assertLambdaJSONEquals(expected:FunctionDefinition[1], actual:Lambda[1]): Boolean[1] +{ + assertEquals($expected->functionJSON(), $actual->meta::json::toJSON(100)); +} + +function meta::pure::tds::toRelation::functionJSON(func:FunctionDefinition[1]): String[1] +{ + $func->meta::protocols::pure::vX_X_X::transformation::fromPureGraph::transformLambda([])->meta::json::toJSON(100); +} diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/extensions/extension.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/extensions/extension.pure index f739960c152..b58d06291a2 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/extensions/extension.pure +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/extensions/extension.pure @@ -155,6 +155,7 @@ function meta::relational::extension::relationalExtension() : meta::pure::extens let varMultiplicity = if($parameterVal->instanceOf(List), |ZeroMany, |PureOne); convertPlaceHolderToSQLString(^meta::relational::functions::pureToSqlQuery::metamodel::VarPlaceHolder(name = $name, type=$type, multiplicity=$varMultiplicity), $dbConfig.dbExtension.literalProcessor, $d.timeZone); }, + tdsToRelation = meta::pure::tds::toRelation::tdsToRelationExtension(), tdsSchema_resolveSchemaImpl = {fe:FunctionExpression[1], openVars:Map>[1], extensions:Extension[*]| [ join_TabularDataSet_1__TabularDataSet_1__JoinType_1__String_$1_MANY$__String_$1_MANY$__TabularDataSet_1_, diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/tds/relation/tdsToRelation.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/tds/relation/tdsToRelation.pure new file mode 100644 index 00000000000..0498a21cd14 --- /dev/null +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/tds/relation/tdsToRelation.pure @@ -0,0 +1,58 @@ +// Copyright 2025 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import meta::protocols::pure::vX_X_X::transformation::helpers::*; +import meta::protocols::pure::vX_X_X::metamodel::valueSpecification::raw::*; +import meta::pure::extension::*; +import meta::pure::tds::toRelation::*; + +function meta::pure::tds::toRelation::tdsToRelationExtension():TdsToRelationExtension_V_X_X[1] + { + ^TdsToRelationExtension_V_X_X( + transfers = {a:meta::protocols::pure::vX_X_X::metamodel::m3::valuespecification::AppliedFunction[1], extensions:Extension[*] | + [ + pair(join_TabularDataSet_1__TabularDataSet_1__JoinType_1__String_$1_MANY$__TabularDataSet_1_->cast(@Function), + | + let left = $a.parameters->at(0)->transform($extensions); + let right = $a.parameters->at(1)->transform($extensions); + let joinKind = $a.parameters->at(2)->transformJoinKind(); + let joinCols = $a.parameters->at(3)->fromCollection()->cast(@CString).value; + let func = transformJoinColsToFunc($joinCols, $joinCols); + + appliedFunction(join_Relation_1__Relation_1__JoinKind_1__Function_1__Relation_1_, [$left, $right, $joinKind, $func]); + ), + pair(join_TabularDataSet_1__TabularDataSet_1__JoinType_1__String_$1_MANY$__String_$1_MANY$__TabularDataSet_1_->cast(@Function), + | + let left = $a.parameters->at(0)->transform($extensions); + let right = $a.parameters->at(1)->transform($extensions); + let joinKind = $a.parameters->at(2)->transformJoinKind(); + let leftJoinCols = $a.parameters->at(3)->fromCollection()->cast(@CString).value; + let rightJoinCols = $a.parameters->at(4)->fromCollection()->cast(@CString).value; + let func = transformJoinColsToFunc($leftJoinCols, $rightJoinCols); + + appliedFunction(join_Relation_1__Relation_1__JoinKind_1__Function_1__Relation_1_, [$left, $right, $joinKind, $func]); + ), + pair(join_TabularDataSet_1__TabularDataSet_1__JoinType_1__Function_1__TabularDataSet_1_->cast(@Function), + | + let left = $a.parameters->at(0)->transform($extensions); + let right = $a.parameters->at(1)->transform($extensions); + let joinKind = $a.parameters->at(2)->transformJoinKind(); + let func = $a.parameters->at(3)->transform($extensions); + + appliedFunction(join_Relation_1__Relation_1__JoinKind_1__Function_1__Relation_1_, [$left, $right, $joinKind, $func]); + ) + ] + } + ) + } diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/tds/relation/testTdsToRelation.pure b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/tds/relation/testTdsToRelation.pure new file mode 100644 index 00000000000..4a7aedeb643 --- /dev/null +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-pure/legend-engine-xt-relationalStore-core-pure/src/main/resources/core_relational/relational/tds/relation/testTdsToRelation.pure @@ -0,0 +1,64 @@ +// Copyright 2025 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import meta::relational::metamodel::join::*; +import meta::pure::tds::toRelation::*; + +function <> meta::pure::tds::toRelation::testJoinUsing():Boolean[1] +{ + test( + {| TestClass.all()->project([ + col(x | $x.string, 'str1'), + col(x | $x.integer, 'int1') + ])->join(TestClass.all()->project([ + col(x | $x.string, 'str2'), + col(x | $x.integer, 'int2') + ]), JoinType.INNER, ['int1', 'str1'], ['int2', 'str2'] + ) + }, + + {| TestClass.all() + ->project(~[str1: x | $x.string, int1: x | $x.integer]) + ->join(TestClass.all() + ->project(~[str2: x | $x.string, int2: x | $x.integer]), + JoinKind.INNER, {x, y | ($x.int1 == $y.int2) && $x.str1 == $y.str2} + ) + }, + meta::relational::extension::relationalExtension() + ) +} + +function <> meta::pure::tds::toRelation::testJoinFunc():Boolean[1] +{ + test( + {| TestClass.all()->project([ + col(x | $x.string, 'str1'), + col(x | $x.integer, 'int1') + ])->join(TestClass.all()->project([ + col(x | $x.string, 'str2'), + col(x | $x.integer, 'int2') + ]), JoinType.INNER, {x, y | $x.getInteger('int1') == $y.getInteger('int2')} + ) + }, + + {| TestClass.all() + ->project(~[str1: x | $x.string, int1: x | $x.integer]) + ->join(TestClass.all() + ->project(~[str2: x | $x.string, int2: x | $x.integer]), + JoinKind.INNER, {x, y | $x.int1 == $y.int2} + ) + }, + meta::relational::extension::relationalExtension() + ) +}