Skip to content

Commit 1160c13

Browse files
authored
support udtf list params (finos#4743)
1 parent c6fca12 commit 1160c13

4 files changed

Lines changed: 557 additions & 37 deletions

File tree

  • 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
  • legend-engine-xts-snowflake

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/postprocessor/functionActivationPostProcessor/replaceFreeMarkerOps.pure

Lines changed: 134 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import meta::relational::functions::sqlQueryToString::*;
1516
import meta::relational::metamodel::join::*;
1617
import meta::relational::metamodel::relation::*;
1718
import meta::relational::functions::pureToSqlQuery::*;
@@ -26,52 +27,152 @@ import meta::relational::mapping::*;
2627

2728
function meta::relational::postProcessor::ReplaceFreeMarkerWithTableFunctionParamHolder(query:SQLQuery[1]): PostProcessorResult[1]
2829
{
29-
let transformFunc = {r: RelationalOperationElement[1] |
30-
$r->match([
31-
l: Literal[1] | $l->replaceLiteral(),
32-
v: VarPlaceHolder[1] | $v->replaceVarPlaceHolder(),
33-
v: VarSetPlaceHolder[1] | fail('replacement of VarSetPlaceHolder with Parameter not supported yet'); $v;,
34-
v: VarCrossSetPlaceHolder[1] | fail('replacement of VarCrossSetPlaceHolder with Parameter not supported yet'); $v;,
35-
f: FreeMarkerOperationHolder[1] | fail('replacement of FreeMarkerOperationHolder with Parameter not supported yet'); $f;,
36-
r: RelationalOperationElement[1] | $r;
37-
]);
38-
};
39-
40-
let transformedQuery = $query->transform($transformFunc, ^Map<RelationalOperationElement, RelationalOperationElement>()).first;
41-
30+
let transformedQuery = $query->transform(transformRelationalElement_RelationalOperationElement_1__RelationalOperationElement_1_, ^Map<RelationalOperationElement, RelationalOperationElement>()).first;
4231
^PostProcessorResult(query = $transformedQuery->cast(@SQLQuery));
4332
}
4433

34+
function <<access.private>> meta::relational::postProcessor::transformRelationalElement(r: RelationalOperationElement[1]): RelationalOperationElement[1]
35+
{
36+
$r->match([
37+
l: Literal[1] | $l->replaceLiteral(),
38+
v: VarPlaceHolder[1] | $v->replaceVarPlaceHolder(),
39+
d: DynaFunction[1] | $d->rewriteDynaFunctionForArrayParams();,
40+
v: VarSetPlaceHolder[1] | fail('replacement of VarSetPlaceHolder with Parameter not supported yet'); $v;,
41+
v: VarCrossSetPlaceHolder[1] | fail('replacement of VarCrossSetPlaceHolder with Parameter not supported yet'); $v;,
42+
f: FreeMarkerOperationHolder[1] | $f->rewriteFreeMarkerOperationHolder();,
43+
r: RelationalOperationElement[1] | $r;
44+
]);
45+
}
46+
4547
function <<access.private>> meta::relational::postProcessor::replaceVarPlaceHolder(v: VarPlaceHolder[1]): RelationalOperationElement[1]
4648
{
4749
let clause = meta::relational::postProcessor::prefixForWrapperAllocationNodeName();
4850
if($v.name->startsWith($clause),
49-
| let paramName = $v.name->substring($clause->length(),$v.name->length());
50-
let flatten = ^SemiStructuredArrayFlattenRelation(navigation=^ColumnName(name = $paramName));
51-
52-
let tableAlias=^TableAlias(name='root',relationalElement=^DynaFunction(name='equal',parameters=[^Literal(value=1),^Literal(value='1')]));
53-
let columnSelected=^TableAliasColumn(column=^Column(type=^meta::relational::metamodel::datatype::Varchar(size=200),name='value'),alias=$tableAlias);
54-
55-
let res=^TableAlias(relationalElement=$flatten,name='root');
56-
57-
let req = ^RootJoinTreeNode(alias=$res);
58-
59-
let s=^SelectSQLQuery(data=$req, columns=$columnSelected);,
60-
|
61-
^TableFunctionParamPlaceHolder(var=$v);
51+
| buildFlattenQuery(^ColumnName(name=$v.name->substring($clause->length(),$v.name->length()))),
52+
| ^TableFunctionParamPlaceHolder(var=$v);
6253
);
6354
}
6455

6556
function <<access.private>> meta::relational::postProcessor::replaceLiteral(l: Literal[1]): RelationalOperationElement[1]
6657
{
6758
if(!$l.value->instanceOf(RelationalOperationElement),
6859
| $l,
69-
| $l.value->match([
70-
v: VarPlaceHolder[1] | $v->replaceVarPlaceHolder(),
71-
v: VarSetPlaceHolder[1] | fail('replacement of VarSetPlaceHolder with Parameter not supported yet'); $v;,
72-
v: VarCrossSetPlaceHolder[1] | fail('replacement of VarCrossSetPlaceHolder with Parameter not supported yet'); $v;,
73-
f: FreeMarkerOperationHolder[1] | fail('replacement of FreeMarkerOperationHolder with Parameter not supported yet'); $f;,
74-
r: RelationalOperationElement[1] | $r;
75-
]);
60+
| $l.value->cast(@RelationalOperationElement)->transformRelationalElement()
7661
);
7762
}
63+
64+
function <<access.private>> meta::relational::postProcessor::rewriteDynaFunctionForArrayParams(d: DynaFunction[1]): RelationalOperationElement[1]
65+
{
66+
let functionMap = arrayParamsDynaFunctionMap();
67+
let hasCollPlaceHolders = $d->hasCollectionPlaceHolders();
68+
let newFunc = $functionMap->get($d.name).second;
69+
if ($hasCollPlaceHolders && $newFunc->isNotEmpty(),
70+
| let newDynaFunc = $newFunc->toOne()->eval($d);
71+
^$newDynaFunc(parameters = $newDynaFunc.parameters->map(p|$p->transformRelationalElement()));,
72+
| // Handle when plan is generated with 'toVariantList', which generates array_construct()
73+
if ($d.name == 'toVariantList' && $hasCollPlaceHolders,
74+
| $d.parameters->at(0)->transformRelationalElement(),
75+
| ^$d(parameters = $d.parameters->map(p|$p->transformRelationalElement()))
76+
);
77+
);
78+
}
79+
80+
function <<access.private>> meta::relational::postProcessor::arrayParamsDynaFunctionMap(): Map<String, Pair<String, Function<{DynaFunction[1]->DynaFunction[1]}>>>[1]
81+
{
82+
meta::relational::postProcessor::multiplicityPreservingDynaFunctionList()->concatenate(meta::relational::postProcessor::multiplicityReducingDynaFunctionList())
83+
->newMap();
84+
}
85+
86+
function <<access.private>> meta::relational::postProcessor::multiplicityPreservingDynaFunctionList(): Pair<String, Pair<String, Function<{DynaFunction[1]->DynaFunction[1]}>>>[*]
87+
{
88+
// pair(original dyna func name, pair(new dyna func name, transformation function)). New dyna func name kept for isCollectionReturningFunction
89+
[
90+
pair('add', pair('array_append', {d: DynaFunction[1] | ^$d(name='array_append')})),
91+
pair('cast', pair('cast', {d: DynaFunction[1] | $d})),
92+
pair('drop', pair('array_drop', {d: DynaFunction[1] | ^$d(name='array_drop')})),
93+
pair('init', pair('array_init', {d: DynaFunction[1] | ^$d(name='array_init')})),
94+
pair('reverse', pair('array_reverse', {d: DynaFunction[1] | ^$d(name='array_reverse')})),
95+
pair('slice', pair('array_slice', {d: DynaFunction[1] | ^$d(name='array_slice')})),
96+
pair('sort', pair('array_sort', {d: DynaFunction[1] | ^$d(name='array_sort')})),
97+
pair('tail', pair('array_tail', {d: DynaFunction[1] | ^$d(name='array_tail')})),
98+
pair('array_construct', pair('array_construct', {d: DynaFunction[1] | $d})),
99+
pair('take', pair('array_take', {d: DynaFunction[1] | ^$d(name='array_take')}))
100+
]
101+
}
102+
103+
function <<access.private>> meta::relational::postProcessor::multiplicityReducingDynaFunctionList(): Pair<String, Pair<String, Function<{DynaFunction[1]->DynaFunction[1]}>>>[*]
104+
{
105+
// pair(original dyna func name, pair(new dyna func name, transformation function)). New dyna func name kept for consistency with multiplicityPreservingDynaFunctionList
106+
[
107+
pair('contains', pair('in', {d: DynaFunction[1] | $d->processContains()})),
108+
pair('greatest', pair('array_max', {d: DynaFunction[1] | ^$d(name='array_max')})),
109+
pair('least', pair('array_min', {d: DynaFunction[1] | ^$d(name='array_min')}))
110+
]
111+
}
112+
113+
function <<access.private>> meta::relational::postProcessor::processContains(d: DynaFunction[1]): DynaFunction[1]
114+
{
115+
// Note: This rewrites only contains() on collection param, not on string param. This case is distinguished by the call to hasCollectionPlaceHolders in rewriteDynaFunctionForArrayParams
116+
let firstParam = $d.parameters->at(0);
117+
let navigation = $firstParam->match([
118+
l: Literal[1] | $l.value->match([
119+
v: VarPlaceHolder[1] | ^ColumnName(name = $v.name),
120+
a: Any[1] | fail('Expected VarPlaceHolder inside Literal for contains() collection rewrite, got: ' + $a->type()->toString()); ^ColumnName(name='');
121+
]),
122+
t: TableFunctionParamPlaceHolder[1] | ^ColumnName(name = $t.var.name),
123+
nested: DynaFunction[1] | $nested->transformRelationalElement(),
124+
r: RelationalOperationElement[1] | fail('Unexpected first parameter type for contains() collection rewrite: ' + $r->type()->toString()); ^ColumnName(name='');
125+
]);
126+
let flattenQuery = $navigation->buildFlattenQuery();
127+
^DynaFunction(name = 'in', parameters = [$d.parameters->at(1), $flattenQuery]);
128+
}
129+
130+
function <<access.private>> meta::relational::postProcessor::rewriteFreeMarkerOperationHolder(f: FreeMarkerOperationHolder[1]): RelationalOperationElement[1]
131+
{
132+
if($f.name == 'variableCollectionSize',
133+
| let params = $f.parameters->map(p | $p->match([
134+
l: Literal[1] | $l.value->match([
135+
v: VarPlaceHolder[1] | ^TableFunctionParamPlaceHolder(var = $v);,
136+
r: RelationalOperationElement[1] | $r;,
137+
a: Any[1] | $l; // Only plain scalar values like String and Integer will go through here
138+
]),
139+
r: RelationalOperationElement[1] | $r;
140+
]));
141+
^DynaFunction(name = 'array_size', parameters = $params);,
142+
| fail('replacement of FreeMarkerOperationHolder \'' + $f.name + '\' with Parameter not supported yet'); $f;
143+
);
144+
}
145+
146+
function <<access.private>> meta::relational::postProcessor::hasCollectionPlaceHolders(d: DynaFunction[1]): Boolean[1]
147+
{
148+
$d.parameters->exists(p | $p->isOrContainsCollectionPlaceHolder());
149+
}
150+
151+
function <<access.private>> meta::relational::postProcessor::isOrContainsCollectionPlaceHolder(r: RelationalOperationElement[1]): Boolean[1]
152+
{
153+
$r->match([
154+
l: Literal[1] | $l.value->instanceOf(VarPlaceHolder) && $l.value->cast(@VarPlaceHolder)->meta::relational::functions::sqlQueryToString::isCollectionPlaceHolder(),
155+
t: TableFunctionParamPlaceHolder[1] | $t.var->meta::relational::functions::sqlQueryToString::isCollectionPlaceHolder(),
156+
d: DynaFunction[1] | $d->isCollectionReturningFunction(),
157+
r: RelationalOperationElement[1] | false
158+
]);
159+
}
160+
161+
function <<access.private>> meta::relational::postProcessor::isCollectionReturningFunction(d: DynaFunction[1]): Boolean[1]
162+
{
163+
let collectionFunctionRewrites = multiplicityPreservingDynaFunctionList();
164+
let collectionFunctionNames = $collectionFunctionRewrites->map(r | [$r.first, $r.second.first]);
165+
$d.name->in($collectionFunctionNames);
166+
}
167+
168+
169+
function <<access.private>> meta::relational::postProcessor::buildFlattenQuery(navigation: RelationalOperationElement[1]): SelectSQLQuery[1]
170+
{
171+
let flatten = ^SemiStructuredArrayFlattenRelation(navigation=$navigation);
172+
let tableAlias=^TableAlias(name='root',relationalElement=^DynaFunction(name='equal',parameters=[^Literal(value=1),^Literal(value='1')]));
173+
let columnSelected=^TableAliasColumn(column=^Column(type=^meta::relational::metamodel::datatype::Varchar(size=200),name='value'),alias=$tableAlias);
174+
let res=^TableAlias(relationalElement=$flatten,name='root');
175+
let req = ^RootJoinTreeNode(alias=$res);
176+
^SelectSQLQuery(data=$req, columns=$columnSelected);
177+
}
178+

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/sqlQueryToString/dbExtension.pure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ function meta::relational::functions::sqlQueryToString::isOptionalPlaceHolder(v:
749749

750750
function meta::relational::functions::sqlQueryToString::isCollectionPlaceHolder(v:VarPlaceHolder[1]):Boolean[1]
751751
{
752-
$v.multiplicity->isNotEmpty() && $v.multiplicity == ZeroMany;
752+
$v.multiplicity->isNotEmpty() && ($v.multiplicity == ZeroMany || $v.multiplicity == OneMany);
753753
}
754754

755755
function <<access.private>> meta::relational::functions::sqlQueryToString::placeHolderDefaultValue():String[1]

0 commit comments

Comments
 (0)