1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15+ import meta::relational::functions::sqlQueryToString::*;
1516import meta::relational::metamodel::join::*;
1617import meta::relational::metamodel::relation::*;
1718import meta::relational::functions::pureToSqlQuery::*;
@@ -26,52 +27,152 @@ import meta::relational::mapping::*;
2627
2728function 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+
4547function <<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
6556function <<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+
0 commit comments