core_relational.relational.relationalMappingExecution.pure Maven / Gradle / Ivy
// Copyright 2021 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::executionPlan::connection::*;
import meta::relational::functions::typeInference::*;
import meta::pure::deepfetch::execution::*;
import meta::pure::router::extension::*;
import meta::relational::functions::reportDelta::*;
import meta::relational::mapping::*;
import meta::pure::milestoning::*;
import meta::pure::executionPlan::*;
import meta::relational::functions::*;
import meta::pure::router::systemMapping::*;
import meta::pure::router::execution::*;
import meta::pure::mapping::*;
import meta::relational::functions::pureToSqlQuery::*;
import meta::relational::functions::sqlQueryToString::*;
import meta::relational::mapping::*;
import meta::relational::metamodel::*;
import meta::relational::metamodel::datatype::*;
import meta::relational::metamodel::execute::*;
import meta::relational::metamodel::join::*;
import meta::relational::metamodel::relation::*;
import meta::pure::metamodel::path::*;
import meta::pure::runtime::*;
import meta::relational::runtime::*;
import meta::relational::milestoning::*;
import meta::pure::router::routing::*;
// Execution Plan
function meta::relational::mapping::planExecution(sq:meta::pure::mapping::StoreQuery[1], ext:RoutedValueSpecification[0..1], m:Mapping[0..1], runtime:Runtime[0..1], exeCtx:ExecutionContext[1], extensions:RouterExtension[*], debug:DebugContext[1]):ExecutionNode[1]
{
let store = $sq.store->cast(@Database);
let fe = $sq.fe->evaluateAndDeactivate();
if($fe.func->in([meta::pure::graphFetch::execution::graphFetch_T_MANY__RootGraphFetchTree_1__T_MANY_, meta::pure::graphFetch::execution::graphFetch_T_MANY__RootGraphFetchTree_1__Integer_1__T_MANY_]),
|
// Graph Fetch Flow
$sq->meta::pure::graphFetch::executionPlan::planGraphFetchExecution($ext, $m->toOne(), $runtime->toOne(), $exeCtx, $extensions, $debug),
|
// Normal Flow
let connections = $runtime.connections->filter(c | $c->instanceOf(DatabaseConnection));
let storeRuntime = if($connections->size() <= 1,
| $runtime->toOne(),
| let oldRuntime = $runtime->toOne();
let dbConn = $oldRuntime->connectionByElement($store)->cast(@DatabaseConnection);
^$oldRuntime(connections = $dbConn);
);
let queryExeCtx = if($exeCtx->instanceOf(RelationalExecutionContext),|$exeCtx,|[])->cast(@RelationalExecutionContext);
let originalQuery = $sq.fe->toSQLQuery($m->toOne(), $sq.inScopeVars, $queryExeCtx, $debug, $extensions);
$originalQuery->postProcessSQLQuery($store, $ext, $m->toOne(), $storeRuntime, $exeCtx, $extensions)
->generateExecutionNodeForPostProcessedResult($sq, $store, $ext, $m->toOne(), $storeRuntime, $exeCtx, $debug, $extensions);
);
}
function meta::relational::mapping::generateExecutionNodeForPostProcessedResult(postProcessorResult:PostProcessorResult[1], sq:meta::pure::mapping::StoreQuery[1], store:Database[1], ext:RoutedValueSpecification[0..1], m:Mapping[1], runtime:Runtime[1], exeCtx:ExecutionContext[1], debug:DebugContext[1], extensions:RouterExtension[*]):ExecutionNode[1]
{
generateExecutionNodeForPostProcessedResult($postProcessorResult, $sq, $store, $ext, $m, $runtime, $exeCtx, $debug, true, $extensions)
}
function meta::relational::mapping::generateExecutionNodeForPostProcessedResult(postProcessorResult:PostProcessorResult[1], sq:meta::pure::mapping::StoreQuery[1], store:Database[1], ext:RoutedValueSpecification[0..1], m:Mapping[1], runtime:Runtime[1], exeCtx:ExecutionContext[1], debug:DebugContext[1], wrapInInstantiationNode: Boolean[1], extensions:RouterExtension[*]):ExecutionNode[1]
{
let sqlNode = generateSQLExecutionNode($postProcessorResult.query, $runtime->toOne()->connectionByElement($store)->cast(@DatabaseConnection), $extensions);
let relationalNode = if($wrapInInstantiationNode, | generateInstantiationExecutionNode($sq, $ext, $postProcessorResult.query, $sqlNode, $m), | $sqlNode);
let nodes = $postProcessorResult.executionNodes->concatenate($relationalNode)->concatenate($postProcessorResult.postExecutionNodes);
if($nodes->size()>1,
| if($nodes->map(node | $node->allNodes($extensions))->exists(node | $node->instanceOf(CreateAndPopulateTempTableExecutionNode)),
|^RelationalBlockExecutionNode(resultType = $relationalNode.resultType, executionNodes = $nodes, supportFunctions = $postProcessorResult.templateFunctions),
|^SequenceExecutionNode(resultType = $relationalNode.resultType, executionNodes = $nodes, supportFunctions = $postProcessorResult.templateFunctions)),
| $relationalNode
);
}
function meta::relational::mapping::generateInstantiationExecutionNode(sq:meta::pure::mapping::StoreQuery[1], ext:RoutedValueSpecification[0..1], query:SQLQuery[1], node:ExecutionNode[1], m:Mapping[1]):ExecutionNode[1]
{
let possibleClass = $sq.fe.genericType.rawType->toOne();
let class = if ($possibleClass == Any, | let getAllClass = findMainClassInGetAllExpression($sq.fe); if($getAllClass->isEmpty(), | $possibleClass, | $getAllClass);, | $possibleClass );
let resultType = if($class->_subTypeOf(TabularDataSet),
|let paths = $query->cast(@TdsSelectSqlQuery).paths;
^TDSResultType
(
type = $class,
tdsColumns = $paths->map(p|
^TDSColumn
(
name = $p.first,
type = $p.second.type->cast(@meta::pure::metamodel::type::DataType),
documentation = $p.second.documentation,
enumMappingId = $p.second.propertyMapping->match([r:RelationalPropertyMapping[0..1] | $r.transformer->cast(@EnumerationMapping).name, p:PropertyMapping[0..1] | []]),
offset = $paths->indexOf($p),
sourceDataType = if($p.second.relationalType->isNotEmpty(),
| $p.second.relationalType,
| $p.second.propertyMapping->match([
rpm: RelationalPropertyMapping[0..1] | $rpm->getRelationalTypeFromRelationalPropertyMapping($p.first),
cpm: meta::pure::router::clustering::CrossSetImplementationPrimtivePropertyMapping[0..1] | $cpm.sourcePropertyMapping->match([rpm: RelationalPropertyMapping[0..1] | $rpm->getRelationalTypeFromRelationalPropertyMapping($p.first), pm: PropertyMapping[0..1] | []]),
pm: PropertyMapping[0..1] | []
]))
);
)
);,
| if($class->_subTypeOf(RelationData),
| let relationDataQuery = $query->cast(@RelationDataSelectSqlQuery);
^RelationResultType
(
relationName = $relationDataQuery.relation.name,
relationType = $relationDataQuery.relation->instanceOf(View)->if(|RelationType.VIEW,|RelationType.TABLE),
schemaName = $relationDataQuery.relation->match([t:Table[1]|$t.schema,v:View[1]|$v.schema])->cast(@Schema).name,
database = $relationDataQuery.relation->match([t:Table[1]|$t.schema,v:View[1]|$v.schema])->cast(@Schema).database->elementToPath(),
columns = $relationDataQuery.columns->cast(@Alias)->map(x | ^Column(name = $x.name, type=$x.relationalElement->cast(@TableAliasColumn).column.type)),
type = $class
);,
| $class->match([
e : Enumeration[1] | ^DataTypeResultType(type=$class),
p : PrimitiveType[1] | ^DataTypeResultType(type=$class),
c : Class[1] | ^ClassResultType
(
type=$class,
setImplementations = if($ext->isEmpty(),
| $m->toOne().rootClassMappingByClass($c)->toOne(),
| $ext->cast(@ExtendedRoutedValueSpecification).sets->at(0)
)->resolveOperation($m->toOne())
)
]);
)
);
$resultType->match([
t : TDSResultType[1] | ^RelationalTdsInstantiationExecutionNode(executionNodes = $node, resultType = $t),
c : ClassResultType[1] | ^RelationalClassInstantiationExecutionNode(executionNodes = $node, resultType = $c, resultSizeRange = $sq.fe.multiplicity),
r : RelationResultType[1] | ^RelationalRelationDataInstantiationExecutionNode(executionNodes = $node, resultType = $r, resultSizeRange = ZeroMany),
c : DataTypeResultType[1] | ^RelationalDataTypeInstantiationExecutionNode(executionNodes = $node, resultType = $c, resultSizeRange = $sq.fe.multiplicity)
]);
}
function <> meta::relational::mapping::getRelationalTypeFromRelationalPropertyMapping(r:RelationalPropertyMapping[0..1], propertyName:String[1]):meta::relational::metamodel::datatype::DataType[0..1]
{
if($r->isEmpty(),
| [] //TO FIX: support all existing tests before introducing a fail fail( 'Unsupported plan execution - check that you are setting Path Types - no type found for column '+$propertyName );,
,| $r->toOne().relationalOperationElement->inferRelationalType(false);
)->cast(@meta::relational::metamodel::datatype::DataType);
}
function meta::relational::mapping::generateSQLExecutionNode(query:SQLQuery[1], connection:DatabaseConnection[1], extensions:RouterExtension[*]):ExecutionNode[1]
{
let sql = sqlQueryToString($query, $connection.type, $connection.timeZone, $extensions);
^SQLExecutionNode
(
sqlQuery = $sql,
resultColumns = $query->match([sel:SelectSQLQuery[1] | $sel.columns->map(c| ^SQLResultColumn(label = $c->columnLabel($connection, $extensions), dataType = $c->getRelationalTypeFromRelationalOperationElement())), a:Any[1] | []]),
resultType = ^ResultType(type = Any),
connection = $connection->meta::relational::mapping::updateConnection($extensions)->cast(@DatabaseConnection),
supportFunctions = relationalPlanSupportFunctions($connection)
);
}
function meta::relational::mapping::updateConnection(conn:Connection[1], extensions:RouterExtension[*]):Connection[1]
{
$conn->match($extensions->map(e|$e.moduleExtension('relational')->cast(@RelationalExtension).relational_plan_updateConnection)->concatenate([
c:DatabaseConnection[1] | $c
])->toOneMany())
}
function <> meta::relational::mapping::columnLabel(v:RelationalOperationElement[1], connection: DatabaseConnection[1], extensions:RouterExtension[*]):String[1]
{
$v->match(
[
a:Alias[1] | if($a.name->startsWith('"'), |'"%s"'->format($a.name->replace('"', '')), |$a.name),
r:RelationalOperationElement[1] | $r->meta::relational::functions::sqlQueryToString::processOperation($connection.type, $extensions)
]
)
}
function <> meta::relational::mapping::getRelationalTypeFromRelationalOperationElement(r:RelationalOperationElement[1]):meta::relational::metamodel::datatype::DataType[0..1]
{
$r->match([
t:TableAliasColumn[1] | $t.column.type,
a:Alias[1] | $a.relationalElement->getRelationalTypeFromRelationalOperationElement(),
c:Column[1] | $c.type,
a:Any[1] | []// TOFIX - support all possible Types fail($a->type().name ->toOne() + ' is not supported in planExecution yet')
])->cast(@meta::relational::metamodel::datatype::DataType);
}
function meta::relational::mapping::relationalPlanSupportFunctions(c:DatabaseConnection[1]):String[*]
{
if($c.timeZone->isNotEmpty() && !meta::pure::functions::date::systemDefaultTimeZones()->contains($c.timeZone->toOne()),|utcToTimeZoneSupportFunction(),|[])->concatenate(collectionRenderFunction())->concatenate(collectionSizeFunction());
}
function meta::relational::mapping::collectionSizeFunction():String[1]
{
'<#function collectionSize collection> <#return collection?size?c> #function>';
}
function meta::relational::mapping::collectionRenderFunction():String[1]
{
'<#function renderCollection collection separator><#return collection?join(separator)>#function>';
}
function meta::relational::mapping::utcToTimeZoneSupportFunction():String[1]
{
'<#function GMTtoTZ tz paramDate><#return (tz+" "+paramDate)?date.@alloyDate>#function>'
+
'<#function renderCollectionWithTz collection timeZone separator><#assign result = [] /><#list collection as c><#assign result +=[(timeZone+" "+c)?date.@alloyDate]>#list><#return result?join(separator)>#function>';
}
// Execution flow
function meta::relational::mapping::execution(sq:meta::pure::mapping::StoreQuery[1], ext:RoutedValueSpecification[0..1], m:Mapping[1], runtime:Runtime[1], exeCtx:ExecutionContext[1], extensions:RouterExtension[*], debug:DebugContext[1]):Result[1]
{
execution($sq.store->cast(@Database), $sq.fe, $ext, $m, $runtime, $sq.inScopeVars, $exeCtx, $extensions, $debug);
}
function meta::relational::mapping::execution(store: Database[1], f:FunctionExpression[1], ext:RoutedValueSpecification[0..1], m:Mapping[1], runtime:Runtime[1], inScopeVars:Map>[1], exeCtx:ExecutionContext[1], extensions:meta::pure::router::extension::RouterExtension[*], debug:DebugContext[1]):Result[1]
{
let toSqlQueryStart = now();
let relationalExecutionContext = $exeCtx->match([r:RelationalExecutionContext[1]|$r, a:Any[0..1]|[]]);
let originalQuery = $f->toSQLQuery($m, $inScopeVars, $relationalExecutionContext, $debug, $extensions);
let toSqlQueryDurationMilliseconds = ($toSqlQueryStart->dateDiff(now(),DurationUnit.MILLISECONDS));
let connections = $runtime.connections->filter(c | $c->instanceOf(DatabaseConnection));
let storeRuntime = if($connections->size() <= 1,
| $runtime->toOne(),
| let oldRuntime = $runtime->toOne();
let dbConn = $oldRuntime->connectionByElement($store)->cast(@DatabaseConnection);
^$oldRuntime(connections = $dbConn);
);
let postProcessorResult = postProcessQuery($exeCtx, $originalQuery, $storeRuntime, $store, $extensions);
let connection = $runtime->connectionByElement($store)->cast(@DatabaseConnection);
let sqlQueryToStringStart = now();
let sql = if($relationalExecutionContext.insertDriverTablePkInTempTable->isEmpty(),
|sqlQueryToStringPretty($postProcessorResult.query, $connection.type, $connection.timeZone, $extensions),
|meta::relational::functions::toDDL::buildInsertSQLQueryResultIntoTable
(
$relationalExecutionContext.insertDriverTablePkInTempTable->toOne(),
$postProcessorResult.query->cast(@SelectSQLQuery),
$connection,
$extensions,
$debug
)
);
print(if($debug->instanceOf(RelationalDebugContext) && $debug->cast(@RelationalDebugContext).logOnlyLastSQL == true,|$sql+'\n',|''));
let sqlQueryToStringDurationMilliseconds = ($sqlQueryToStringStart->dateDiff(now(),DurationUnit.MILLISECONDS));
let sqlGenerationTimeInNanoSecond = ($toSqlQueryDurationMilliseconds + $sqlQueryToStringDurationMilliseconds) * 1000000;
let possibleClass = $f.genericType.rawType;
//Required if the class is a dynamically created variable expression
let class = if ($possibleClass == Any, | let getAllClass = findMainClassInGetAllExpression($f); if($getAllClass->isEmpty(), | $possibleClass, | $getAllClass);, | $possibleClass );
let result = executeQuery($sql, $postProcessorResult.query, if($class->toOne()->_subTypeOf(TabularDataSet), | $postProcessorResult.query->cast(@TdsSelectSqlQuery).paths, | []), $connection, $runtime, $class, $ext, $m, $sqlGenerationTimeInNanoSecond, $exeCtx.queryTimeOutInSeconds, $exeCtx, $extensions, $debug);
$postProcessorResult.resultPostProcessor->fold({pp,res| $pp->eval($res)},$result);
}
function meta::relational::mapping::executeQuery(sql:String[1], query:SQLQuery[0..1], paths: Pair[*], connection : DatabaseConnection[1], runtime: Runtime[1], class:Type[0..1], ext:RoutedValueSpecification[0..1], m: Mapping[1], sqlGenerationTimeInNanoSecond:Integer[0..1], g_queryTimeOutInSeconds: Integer[0..1], exeCtx:ExecutionContext[1], extensions: RouterExtension[*], debug:DebugContext[1]):Result[1]
{
print(if(!$debug.debug, |'', | $debug.space+'>Execute SQL: '+$sql+'\n'));
let queryTimeOutInSeconds = if($g_queryTimeOutInSeconds->isNotEmpty(),
| $g_queryTimeOutInSeconds->toOne(),
| 3600 //1 hour
);
let res = executeInDb($sql, $connection->cast(@DatabaseConnection), $queryTimeOutInSeconds, 1000);
//let res = ^ResultSet(connectionAcquisitionTimeInNanoSecond=1,executionTimeInNanoSecond=2);
print(if(!$debug.debug, |'', |let ds = $res.dataSource->toOne();
$debug.space+'>Query executed in '+$res.executionTimeInNanoSecond->toString()+'ns, host: \''+$ds.host+':'+$ds.port->toOne()->toString()+'\', name: \''+$ds.name+'\', type: \''+$ds.type->toOne()->toString()+'\'\n';));
if ($class->toOne()->_subTypeOf(TabularDataSet),
| buildExecutionResultInTDS($sql, $res, $paths, $sqlGenerationTimeInNanoSecond),
| if($class->toOne()->_subTypeOf(RelationData),
| buildExecutionResultInRelationData($sql, $query, $res, $paths, $sqlGenerationTimeInNanoSecond),
| let objs = $class->match([
e : Enumeration[1] | fail('Direct query on Enumerations is not supported yet!'); [];,
p : PrimitiveType[1] | if($res.rows->isEmpty(),
| [],
| let offset = $res.rows->at(0).values->size()-1;
$res.rows->map(
r| let val = $r.values->at($offset); if(is($val->type(), SQLNull), |[], |$val);
);
),
c : Class[1] | let setImplementation = if($ext->isEmpty(),
| $m.rootClassMappingByClass($c)->toOne();,
| $ext->cast(@ExtendedRoutedValueSpecification).sets->at(0)
);
$setImplementation->match(
[
r:RelationalInstanceSetImplementation[1]|buildClasses($r, $res, $m, $connection, $runtime, [], $exeCtx, $extensions, $debug),
o:OperationSetImplementation[1]|buildClasses($o->resolveOperation($m)->cast(@RelationalInstanceSetImplementation), $res, $m, $connection, $runtime, [], $exeCtx, $extensions, $debug)
]
);
]);
let result = ^Result(values=$objs, activities=^RelationalActivity(sql=$sql, executionTimeInNanoSecond=$res.executionTimeInNanoSecond, executionPlanInformation = $res.executionPlanInformation, dataSource = $res.dataSource, sqlGenerationTimeInNanoSecond = $sqlGenerationTimeInNanoSecond, connectionAcquisitionTimeInNanoSecond = $res.connectionAcquisitionTimeInNanoSecond));
$result;
)
);
}
function meta::relational::mapping::buildExecutionResultInTDS(sql:String[1], res:ResultSet[1], paths: Pair[*], sqlGenerationTimeInNanoSecond:Integer[0..1]):Result[1]
{
let propertyMappings = $res.columnNames->map(c| let matchedPath = $paths->filter(p|$p.first->toLower() == $c->toLower());
if ($matchedPath->isEmpty(),
| {a:Any[1]|$a},
| if ($matchedPath->toOne().second.type == Boolean, | {a:Any[1]|$a == 'true' || $a == true},
| let pm = $matchedPath->toOne().second;
if ($pm.propertyMapping == [] && $pm.type != Decimal, |{a:Any[1]|$a},
| if ($pm.propertyMapping == [] && $pm.type == Decimal, |{a:Any[1]|$a->cast(@Number)->toDecimal()},
| if ($pm.type == Decimal, |{a:Any[1]|$pm.propertyMapping->cast(@RelationalPropertyMapping)->toOne().transform($a)->cast(@Number)->toOne()->toDecimal()},
| {a:Any[1]|$pm.propertyMapping->cast(@RelationalPropertyMapping)->toOne().transform($a)}););););
);
);
let indices = range(0, $propertyMappings->size(), 1);
let tdsNull = ^TDSNull();
let tds = ^TabularDataSet(
columns = $res.columnNames->map(c|let i = $res.columnNames->indexOf($c);
let matchedPath = $paths->filter(p|$p.first->toLower() == $c->toLower());
let type = if ($matchedPath->isEmpty(),|[],|$matchedPath->toOne().second.type)->cast(@meta::pure::metamodel::type::DataType);
let doc = if ($matchedPath->isEmpty(),|[],|$matchedPath->toOne().second.documentation);
^TDSColumn(type=$type,
name=$c,
offset=$i,
documentation = $doc
);
)
);
let tdsRows = $res.rows->map(
r | let values = $r.values;
^TDSRow(
parent = $tds,
values = $indices->map(i|let val = $values->at($i); if(is($val->type(), SQLNull), | $tdsNull, | $propertyMappings->at($i)->eval($val));)
);
);
$tds->mutateAdd('rows', $tdsRows);
^Result
(
values = $tds,
activities=^RelationalActivity(sql=$sql, executionTimeInNanoSecond=$res.executionTimeInNanoSecond, executionPlanInformation = $res.executionPlanInformation, dataSource = $res.dataSource, sqlGenerationTimeInNanoSecond = $sqlGenerationTimeInNanoSecond, connectionAcquisitionTimeInNanoSecond = $res.connectionAcquisitionTimeInNanoSecond)
);
}
function meta::relational::mapping::buildExecutionResultInRelationData(sql:String[1], query:SQLQuery[0..1], res:ResultSet[1], paths: Pair[*], sqlGenerationTimeInNanoSecond:Integer[0..1]):Result[1]
{
let tdsNull = ^TDSNull();
let relationDataQuery = $query->toOne()->cast(@RelationDataSelectSqlQuery);
let relationData = ^RelationData
(
relation = $relationDataQuery.relation,
columnSubset = $relationDataQuery.columnSubset
);
let dataRows = $res.rows->map(r | let values = $r.values;
^DataRow
(
values = $values->map(x | if(is($x->type(), SQLNull), | $tdsNull, | $x))
);
);
$relationData->mutateAdd('rows', $dataRows);
^Result
(
values = $relationData,
activities=^RelationalActivity(sql=$sql, executionTimeInNanoSecond=$res.executionTimeInNanoSecond, executionPlanInformation = $res.executionPlanInformation, dataSource = $res.dataSource, sqlGenerationTimeInNanoSecond = $sqlGenerationTimeInNanoSecond, connectionAcquisitionTimeInNanoSecond = $res.connectionAcquisitionTimeInNanoSecond)
);
}
function meta::relational::mapping::buildClasses(setImplementations:RelationalInstanceSetImplementation[*], res:ResultSet[1], mapping:Mapping[1], connection:Connection[1], runtime: Runtime[1], propLookUpIndices:Integer[*], exeCtx:ExecutionContext[1], extensions: RouterExtension[*], debug:DebugContext[1]):Any[*]
{
let builders = buildBuilders($setImplementations, $res, $mapping, $connection, $runtime, [], $exeCtx, $extensions, $debug);
$res.rows->map(r|let buildInder = if ($setImplementations->size() == 1,|$builders->at(0),
|$builders->at($r.values->at(0)->cast(@String)->parseInteger()));
let class = buildClass($buildInder, $r, $mapping, $connection, $runtime, $exeCtx);
$class;
);
}
function meta::relational::mapping::buildBuilders(setImplementations:RelationalInstanceSetImplementation[*], res:ResultSet[1], mapping:Mapping[1], connection:Connection[1], runtime: Runtime[1], overridePropLookUpIndices:Integer[*], exeCtx:ExecutionContext[1], extensions: RouterExtension[*], debug:DebugContext[1]):BuilderInfo[*]
{
let builders = $setImplementations->removeDuplicates()
->fold({setImplementation,a|
let classForNew = $setImplementation->match([
r:RootRelationalInstanceSetImplementation[1] | $setImplementation.class;,
e:EmbeddedRelationalInstanceSetImplementation[1] | $e.property->genericType().typeArguments->at(1).rawType->toOne()->cast(@Class)
]);
let propertyMappings = $setImplementation.allPropertyMappings()
->filter(pm|$pm.localMappingProperty==false &&
$pm.property->instanceOf(Property) &&
$pm.property->genericType().typeArguments->at(1).rawType->toOne()->instanceOf(meta::pure::metamodel::type::DataType)
);
let colNamesFromRS = $res.columnNames->map(c|$c->toUpper());
let propLookUpIndices = if(!$overridePropLookUpIndices->isEmpty(),|$overridePropLookUpIndices
,|$propertyMappings->map(pm| let colName = $pm.property.name->toOne();
$colNamesFromRS->indexOf($colName->toUpper());
);
);
let keys = $propertyMappings->map(pm | $pm.property.name);
let transforms = $propertyMappings->map(p|if($p.property->functionReturnType().rawType == Boolean,|{a:Any[1]|$a == 'true' || $a == true},
| if($p.property->functionReturnType().rawType == Decimal,|{a:Any[1]|$p->cast(@RelationalPropertyMapping).transform($a)->cast(@Number)->toDecimal()},|{a:Any[1]|$p->cast(@RelationalPropertyMapping).transform($a)});));
let indices = range(0, $propertyMappings->size(), 1);
let primaryKeyColumns = $setImplementation->match([
r:RootRelationalInstanceSetImplementation[1] | $r.resolvePrimaryKey();,
e:EmbeddedRelationalInstanceSetImplementation[1] | $e.setMappingOwner.resolvePrimaryKey()
]);
assert(!$primaryKeyColumns->isEmpty(), 'Must have a valid primary key to allow gettor to work');
let pks = range($a.first, $a.first+$primaryKeyColumns->size(),1);
let list = $a.second;
let temporalStrategy = getTemporalMilestoningStrategy($classForNew);
pair($a.first+$primaryKeyColumns->size(),
^$list(values += ^BuilderInfo( class = $classForNew,
keys = $keys,
transforms = $transforms,
indices = $indices,
pks = $pks,
propLookUpIndices = $propLookUpIndices,
milestoningStrategy = if($temporalStrategy->isEmpty(),
| {r:Row[1]|[]},
| {r:Row[1]|$temporalStrategy->toOne()->getKeyValueBuildClasses($r)}
),
static = ^StaticMappingInstanceData
(
runtime = $runtime,
mapping = $mapping,
systemMapping = relationalSystemMapping(),
setImplementation = $setImplementation,
exeCtx = $exeCtx,
debug = $debug,
extensions = $extensions
),
sqlNull = ^SQLNull()
))
);
}, pair(if($setImplementations->size()==1,|0,|1), ^List())).second.values;
}
function meta::relational::mapping::buildClass(buildInder:BuilderInfo[1], r:Row[1], mapping:Mapping[1], connection:Connection[1], runtime: Runtime[1], exeCtx:ExecutionContext[1]):Any[1]
{
let keyExpressions = $buildInder.indices->map( i | let value = $r.values->at($buildInder.propLookUpIndices->at($i));
^KeyValue(
key = $buildInder.keys->at($i),
value = if($value == $buildInder.sqlNull,| [], | $buildInder.transforms->at($i)->eval($value))
);
)->concatenate($buildInder.milestoningStrategy->eval($r));
let keyInformation = ^KeyInformation (
static = $buildInder.static,
pk = $buildInder.pks->map(pk|$r.values->at($pk)),
sourceConnection = $connection,
buildMethod = BuildMethod.TypeQuery
);
$buildInder.class->dynamicNew(
$keyExpressions,
meta::pure::mapping::xStore::crossGetterOverrideToOne_Any_1__Property_1__Any_$0_1$_,
meta::pure::mapping::xStore::crossGetterOverrideToMany_Any_1__Property_1__Any_MANY_,
$keyInformation,
$exeCtx->getConstraintsManager()
);
}
// Lazy Execution of property
function meta::relational::mapping::getterOverrideMapped(o:Any[1], propertyMapping:PropertyMapping[1]):Any[*]
{
$o->processProperty($propertyMapping.property);
}
Class meta::relational::mapping::BuilderInfo
{
class : Class[1];
keys : String[*];
transforms : Function<{Any[1]->Any[1]}>[*];
indices : Integer[*];
pks : Integer[*];
propLookUpIndices : Integer[*];
milestoningStrategy : Function<{Row[1]->KeyValue[*]}>[1];
sqlNull : SQLNull[1];
static : StaticMappingInstanceData[1];
}
Enum meta::relational::mapping::BuildMethod
{
TypeQuery
}
Class meta::relational::mapping::KeyInformation extends MappingInstanceData
{
pk : Any[*];
sourceConnection : Connection[1];
deepFetchCache : DeepFetchCache[0..1];
buildMethod : BuildMethod[1];
}
function meta::relational::mapping::processProperty(o:Any[1], property:Property[1]):Any[*]
{
if ($property.name == 'classifierGenericType' || $property->functionReturnType().rawType->toOne()->instanceOf(meta::pure::metamodel::type::DataType)
,| $property->rawEvalProperty($o);
,|
let keyInformation = $o->getHiddenPayload()->cast(@KeyInformation);
let setImplementation = $keyInformation.static.setImplementation->toOne()->cast(@RelationalInstanceSetImplementation);
let extensions = $keyInformation.static.extensions;
let mapping = $keyInformation.static.mapping;
let state = ^State(inScopeVars = ^Map>(), mapping=$mapping, supportedFunctions=getSupportedFunctions(), inProject=false, inFilter=false, filterChainDepth= 0, inProjectFunctions=false, processingProjectionThread = false, shouldIsolate=false);
let sourceDb = $keyInformation.sourceConnection->cast(@DatabaseConnection).element;
let db = if ($sourceDb->instanceOf(Database),|$sourceDb,|[])->cast(@Database);
let sqlQueryToStringStart = now();
let targetImplSqlQueryPair = generatePropertySql($setImplementation, $property, JoinType.INNER, $keyInformation.pk, $mapping, $keyInformation.static.runtime, $db, $keyInformation.static.exeCtx, $extensions);
let sql = sqlQueryToStringPretty($targetImplSqlQueryPair.second, $keyInformation.sourceConnection->cast(@DatabaseConnection).type, $keyInformation.sourceConnection->cast(@DatabaseConnection).timeZone, $extensions);
let sqlQueryToStringDurationMilliseconds = ($sqlQueryToStringStart->dateDiff(now(),DurationUnit.MILLISECONDS));
let executed = executeInDb($sql, $keyInformation.sourceConnection->cast(@DatabaseConnection));
meta::pure::functions::io::logActivities(^RelationalActivity(sql=$sql,
sqlGenerationTimeInNanoSecond = $sqlQueryToStringDurationMilliseconds*1000000,
dataSource = $executed.dataSource,
executionTimeInNanoSecond=$executed.executionTimeInNanoSecond,
connectionAcquisitionTimeInNanoSecond = $executed.connectionAcquisitionTimeInNanoSecond)->toOneMany());
buildClasses($targetImplSqlQueryPair.first, $executed, $mapping, $keyInformation.sourceConnection, $keyInformation.static.runtime, [], $keyInformation.static.exeCtx, $keyInformation.static.extensions, $keyInformation.static.debug);
);
}
function meta::relational::mapping::generatePropertySql(setImplementation: RelationalInstanceSetImplementation[1], property:Property[1], joinType:JoinType[1], propertyPkFilterValues: Any[*], mapping:Mapping[1], runtime: Runtime[1], db:Database[0..1], exeCtx:ExecutionContext[1], extensions:RouterExtension[*]):Pair[1]
{
let state = ^State(inScopeVars = ^Map>(), mapping=$mapping, supportedFunctions=getSupportedFunctions(), inProject=false, inFilter=false, filterChainDepth= 0, inProjectFunctions=false, processingProjectionThread = false, shouldIsolate=false);
let srcSetImplementation = $setImplementation->match([
s:RootRelationalInstanceSetImplementation[1]|$s,
s:EmbeddedRelationalInstanceSetImplementation[1]|$s.setMappingOwner;
]);
let context = ^DebugContext(debug=false, space='');
let mainTable = $srcSetImplementation.mainRelation->processRelation([], '', false, 0, false, [], $state, $context, $extensions);
let innerJoinFilterExists = $srcSetImplementation->getFilter().joinTreeNode.joinType == JoinType.INNER;
let updatedMainTable = if ($innerJoinFilterExists,
| getRelationalElementWithInnerJoin($srcSetImplementation, $mainTable, [], '', $state, $context, $extensions),
| $mainTable
);
let currentNode = ^RootJoinTreeNode(alias=^TableAlias(name = 'root', relationalElement = $updatedMainTable));
let base = ^SelectWithCursor(
select = ^SelectSQLQuery(
data = $currentNode
),
currentTreeNode = $currentNode
);
let nodeId = '_a';
let primaryKeys = $setImplementation->match([
r:RootRelationalInstanceSetImplementation[1] | $r.resolvePrimaryKey();,
e:EmbeddedRelationalInstanceSetImplementation[1] | $e.setMappingOwner.resolvePrimaryKey()
]);
assert(!$primaryKeys->isEmpty(), 'Must have a valid primary key to allow gettor to work');
let pks = if(!$propertyPkFilterValues->isEmpty(),| let pkOffsets = range(0, $primaryKeys->size(), 1);
$pkOffsets->map(offset| let pm = $primaryKeys->at($offset);
let newQuery = $pm->processColumnsInRelationalOperationElements($state, $base, $nodeId, ^List(), false, $context, $extensions);
^SelectSQLQuery( data = $newQuery.select.data, filteringOperation = ^DynaFunction(name = 'equal', parameters=[$newQuery.select.columns->toOne(), ^Literal(value=$propertyPkFilterValues->at($offset))])););
,| []);
let groupByQuery = $base->applyGroupBy($srcSetImplementation, $nodeId);
let filterQuery = if($innerJoinFilterExists, | $groupByQuery, | $groupByQuery->applyTypeFilter($srcSetImplementation, $nodeId, $state, $context, $extensions));
let pksMergeSelect = $filterQuery.select->concatenate($pks)->mergeSQLQueryData($nodeId, $state, $context, $extensions);
let pkFilter = $pksMergeSelect.filteringOperation->concatenate($pksMergeSelect.savedFilteringOperation.second)->andFilters($extensions);//^DynaFunction(name='and', parameters=$pksMergeSelect.filteringOperation->concatenate($pksMergeSelect.savedFilteringOperation.second));
let wrappedMerged = ^SelectWithCursor(
select = ^SelectSQLQuery(
data = $pksMergeSelect.data,
filteringOperation = $pkFilter
),
currentTreeNode = $pksMergeSelect.data
);
let propertySetImplementation = $setImplementation->match([e:EmbeddedSetImplementation[1]|$e->meta::pure::mapping::propertyMappingsByPropertyName($property.name->toOne()),
s:PropertyMappingsImplementation[1]|$s.propertyMappingsByPropertyName($property.name->toOne())]);
let res = processPropertyMapping($propertySetImplementation, $setImplementation.class, $wrappedMerged, ^$state(inGetterFlow=true), $joinType, $nodeId, ^List(), $context, $extensions);
let newClass = $property->functionReturnType()->genericTypeClass();
let targetSetImplementation = getPropertyTargetSetImplementation($setImplementation, $property, $mapping);
let properties = $targetSetImplementation->dataTypePropertyMappings()
->map(pm|let newQuery = $pm->cast(@RelationalPropertyMapping).relationalOperationElement->processColumnsInRelationalOperationElements($state, $res->cast(@SelectWithCursor), $nodeId, ^List(), false, $context, $extensions);
rebuildSelectWithCursor($pm.property.name->toOne(), [], $newQuery, true););
let targetPrimaryKeys = $targetSetImplementation->match([
r:RootRelationalInstanceSetImplementation[1] | $r.resolvePrimaryKey();,
e:EmbeddedRelationalInstanceSetImplementation[1] | $e.setMappingOwner.resolvePrimaryKey()
]);
assert(!$targetPrimaryKeys->isEmpty(), 'Must have a valid primary key to allow gettor to work');
let targetPks = $targetPrimaryKeys->map(pm|let offset = $targetPrimaryKeys->indexOf($pm);
let newQuery = $pm->processColumnsInRelationalOperationElements($state, $res->cast(@SelectWithCursor), $nodeId, ^List(), false, $context, $extensions);
rebuildSelectWithCursor('pk_'+$offset->toString(), [], $newQuery, true););
let newQuery_pre = $targetPks->concatenate($properties)->add($res->cast(@SelectWithCursor))->map(c|$c.select)->cast(@SelectSQLQuery)->mergeSQLQueryData($nodeId, $state, $context, $extensions);
let newQuery = ^$newQuery_pre(filteringOperation=$newQuery_pre.filteringOperation->concatenate($newQuery_pre.savedFilteringOperation.second)->andFilters($extensions));
let queryProcessorHolder = postProcessQuery($exeCtx, $newQuery, $runtime, $db, $extensions);
pair($targetSetImplementation, $queryProcessorHolder.query);
}
function meta::relational::mapping::getPropertyTargetSetImplementation(sourceSetImplementation: RelationalInstanceSetImplementation[1], property:AbstractProperty[1], mapping:Mapping[1]):RelationalInstanceSetImplementation[1]
{
let propertySetImplementation = $sourceSetImplementation.propertyMappingsByPropertyName($property.name->toOne());
let propertyReturnTypeClass = $property->functionReturnType()->genericTypeClass();
$propertySetImplementation->match([
o:OtherwiseEmbeddedRelationalInstanceSetImplementation[1]|$mapping.classMappingById($o.otherwisePropertyMapping.targetSetImplementationId)->cast(@RelationalInstanceSetImplementation)->toOne();,
e:EmbeddedRelationalInstanceSetImplementation[1] | $e ,
r:RelationalPropertyMapping[1] | let classMappings = $mapping.rootClassMappingByClass($propertyReturnTypeClass)->potentiallyResolveOperation($sourceSetImplementation.parent);
assertSize($classMappings, 1, 'Expected 1 class mapping for %s, found %d', [$propertyReturnTypeClass.name->toOne(), $classMappings->size()]);
$classMappings->toOne()->cast(@RelationalInstanceSetImplementation);
]);
}
// Functions supported by relational execution
function meta::relational::mapping::supports(f:FunctionExpression[1]):Boolean[1]
{
!meta::relational::functions::pureToSqlQuery::findSupportedFunction(
$f,
meta::relational::functions::pureToSqlQuery::getSupportedFunctions()
)->isEmpty()
//These are here because the router does not go deep enough, should remove
|| [
meta::pure::functions::math::sum_Float_MANY__Float_1_,
meta::pure::functions::math::sum_Integer_MANY__Integer_1_,
meta::pure::functions::math::sum_Number_MANY__Number_1_
]->contains($f.func);
}
function meta::relational::mapping::supportsStream(f:FunctionExpression[1]):Boolean[1]
{
[
meta::pure::functions::collection::in_Any_1__Any_MANY__Boolean_1_,
meta::pure::functions::collection::in_Any_$0_1$__Any_MANY__Boolean_1_
]->contains($f.func);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy