@sap/cds-compiler 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/bin/cdsc.js +2 -2
- package/bin/cdshi.js +24 -17
- package/bin/cdsse.js +17 -18
- package/lib/api/main.js +19 -2
- package/lib/api/options.js +4 -1
- package/lib/base/builtins.js +1 -0
- package/lib/base/message-registry.js +16 -3
- package/lib/base/model.js +0 -10
- package/lib/checks/actionsFunctions.js +0 -12
- package/lib/checks/structuredAnnoExpressions.js +10 -14
- package/lib/compiler/assert-consistency.js +19 -11
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/define.js +6 -4
- package/lib/compiler/extend.js +5 -5
- package/lib/compiler/populate.js +9 -9
- package/lib/compiler/propagator.js +1 -0
- package/lib/compiler/resolve.js +29 -34
- package/lib/compiler/shared.js +7 -8
- package/lib/compiler/tweak-assocs.js +155 -64
- package/lib/compiler/utils.js +1 -1
- package/lib/compiler/xpr-rewrite.js +4 -3
- package/lib/edm/annotations/genericTranslation.js +13 -9
- package/lib/edm/csn2edm.js +26 -2
- package/lib/edm/edm.js +23 -8
- package/lib/edm/edmInboundChecks.js +5 -7
- package/lib/edm/edmPreprocessor.js +43 -30
- package/lib/gen/BaseParser.js +720 -0
- package/lib/gen/CdlParser.js +4421 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +4006 -4001
- package/lib/language/antlrParser.js +62 -0
- package/lib/language/genericAntlrParser.js +28 -0
- package/lib/model/csnUtils.js +2 -0
- package/lib/model/revealInternalProperties.js +2 -0
- package/lib/modelCompare/utils/filter.js +70 -42
- package/lib/optionProcessor.js +9 -3
- package/lib/parsers/AstBuildingParser.js +1172 -0
- package/lib/parsers/CdlGrammar.g4 +1940 -0
- package/lib/parsers/Lexer.js +239 -0
- package/lib/render/toCdl.js +23 -27
- package/lib/render/toSql.js +5 -5
- package/lib/transform/db/applyTransformations.js +54 -16
- package/lib/transform/draft/odata.js +10 -11
- package/lib/transform/effective/flattening.js +10 -14
- package/lib/transform/odata/flattening.js +42 -31
- package/lib/transform/odata/toFinalBaseType.js +7 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +2 -2
- package/share/messages/redirected-to-ambiguous.md +5 -4
package/lib/compiler/extend.js
CHANGED
|
@@ -441,8 +441,8 @@ function extend( model ) {
|
|
|
441
441
|
function applySingleExtension( art, ext, prop ) {
|
|
442
442
|
if (prop === 'includes') {
|
|
443
443
|
if (ext.kind === 'extend' && art.$inferred) {
|
|
444
|
-
error( 'extend-for-generated', [ ext.name.location, ext ], { art },
|
|
445
|
-
'You can\'t use
|
|
444
|
+
error( 'extend-for-generated', [ ext.name.location, ext ], { art, keyword: 'extend' },
|
|
445
|
+
'You can\'t use $(KEYWORD) on the generated $(ART)' );
|
|
446
446
|
}
|
|
447
447
|
else if (art.kind !== 'annotate' && !art._outer) { // not with elem extension in targetAspect
|
|
448
448
|
const { id } = art.name;
|
|
@@ -797,7 +797,7 @@ function extend( model ) {
|
|
|
797
797
|
const dict = parent[prop];
|
|
798
798
|
if (!dict) {
|
|
799
799
|
// TODO: check - for each name? - better locations
|
|
800
|
-
const location = ext._parent[prop]?.[$location] || ext.name.location;
|
|
800
|
+
const location = ext._parent?.[prop]?.[$location] || ext.name.location;
|
|
801
801
|
// Remark: no `elements` dict location with `annotate Main:elem`
|
|
802
802
|
switch (prop) {
|
|
803
803
|
// TODO: change texts, somehow similar to checkDefinitions() ?
|
|
@@ -1052,8 +1052,8 @@ function extend( model ) {
|
|
|
1052
1052
|
if (ext.name._artifact === undefined) { // not already applied
|
|
1053
1053
|
setArtifactLink( ext.name, art );
|
|
1054
1054
|
if (noExtend && ext.kind === 'extend') {
|
|
1055
|
-
error( 'extend-for-generated', [ ext.name.location, ext ], { art },
|
|
1056
|
-
'You can\'t use
|
|
1055
|
+
error( 'extend-for-generated', [ ext.name.location, ext ], { art, keyword: 'extend' },
|
|
1056
|
+
'You can\'t use $(KEYWORD) on the generated $(ART)' );
|
|
1057
1057
|
continue;
|
|
1058
1058
|
}
|
|
1059
1059
|
if (ext.includes) {
|
package/lib/compiler/populate.js
CHANGED
|
@@ -789,8 +789,8 @@ function populate( model ) {
|
|
|
789
789
|
return col.name.id;
|
|
790
790
|
}
|
|
791
791
|
}
|
|
792
|
-
else if (col.expand || col.value && (col.
|
|
793
|
-
//
|
|
792
|
+
else if (col.expand || col.value && (col._columnParent || query._parent.kind !== 'select')) {
|
|
793
|
+
// _columnParent => inline/expand; _parent -> only allowed in sub-selects
|
|
794
794
|
error( 'query-req-name', [ col.value?.location || col.location, query ], {},
|
|
795
795
|
'Alias name is required for this select item' );
|
|
796
796
|
}
|
|
@@ -867,7 +867,7 @@ function populate( model ) {
|
|
|
867
867
|
const inferred = query._main.$inferred;
|
|
868
868
|
const excludingDict = (colParent || query).excludingDict || Object.create( null );
|
|
869
869
|
|
|
870
|
-
const envParent = wildcard.
|
|
870
|
+
const envParent = wildcard._columnParent;
|
|
871
871
|
const env = wildcardColumnEnv( wildcard, query );
|
|
872
872
|
if (!env)
|
|
873
873
|
return;
|
|
@@ -931,7 +931,7 @@ function populate( model ) {
|
|
|
931
931
|
// already done in populateQuery (TODO: change that and check whether
|
|
932
932
|
// `*` is allowed at all in definer)
|
|
933
933
|
if (!colParent || colParent.value._artifact) {
|
|
934
|
-
// avoid "not found" messages if
|
|
934
|
+
// avoid "not found" messages if columnParent can't be found
|
|
935
935
|
const user = colParent || query;
|
|
936
936
|
for (const name in user.excludingDict)
|
|
937
937
|
resolveExcluding( name, env, excludingDict, query );
|
|
@@ -939,9 +939,9 @@ function populate( model ) {
|
|
|
939
939
|
}
|
|
940
940
|
}
|
|
941
941
|
|
|
942
|
-
function wildcardColumnEnv( wildcard, query ) { // etc. wildcard.
|
|
942
|
+
function wildcardColumnEnv( wildcard, query ) { // etc. wildcard._columnParent;
|
|
943
943
|
// if (envParent) console.log( 'CE:', envParent._origin, query );
|
|
944
|
-
const colParent = wildcard.
|
|
944
|
+
const colParent = wildcard._columnParent;
|
|
945
945
|
if (!colParent)
|
|
946
946
|
return userQuery( query )._combined; // see combinedSourcesOrParentElements
|
|
947
947
|
|
|
@@ -985,14 +985,14 @@ function populate( model ) {
|
|
|
985
985
|
}
|
|
986
986
|
}
|
|
987
987
|
|
|
988
|
-
function setWildcardExpandInline( queryElem,
|
|
989
|
-
setLink( queryElem, '
|
|
988
|
+
function setWildcardExpandInline( queryElem, columnParent, origin, name, location ) {
|
|
989
|
+
setLink( queryElem, '_columnParent', columnParent );
|
|
990
990
|
const path = [ { id: name, location } ];
|
|
991
991
|
queryElem.value = { path, location }; // TODO: can we omit that? We have _origin
|
|
992
992
|
setArtifactLink( path[0], origin );
|
|
993
993
|
setLink( queryElem, '_origin', origin );
|
|
994
994
|
// set _projections when inline with table alias:
|
|
995
|
-
// const alias =
|
|
995
|
+
// const alias = columnParent?.value?.path?.[0]?._navigation;
|
|
996
996
|
// if (alias?.kind === '$tableAlias')
|
|
997
997
|
// pushLink( alias.elements[name], '_projections', queryElem );
|
|
998
998
|
}
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -174,9 +174,10 @@ function resolve( model ) {
|
|
|
174
174
|
// TODO: or should we push elems with `expand` sibling to extra list for
|
|
175
175
|
// better messages? (Whatever that means exactly.)
|
|
176
176
|
|
|
177
|
-
if (elem.
|
|
178
|
-
if (elem.
|
|
179
|
-
// we're traversing top-level elements of the query;
|
|
177
|
+
if (elem._columnParent) {
|
|
178
|
+
if (elem._columnParent?.kind !== '$inline')
|
|
179
|
+
// we're traversing top-level elements of the query;
|
|
180
|
+
// other _columnParent kinds can't happen
|
|
180
181
|
throw new CompilerAssertion('found unexpected "expand", but expected "inline"');
|
|
181
182
|
|
|
182
183
|
if (!isPathBreakout( elem.value )) {
|
|
@@ -205,13 +206,17 @@ function resolve( model ) {
|
|
|
205
206
|
if (!nav.item._navigation) // first non-table-alias
|
|
206
207
|
setLink( nav.item, '_navigation', navItem );
|
|
207
208
|
|
|
208
|
-
if
|
|
209
|
-
|
|
209
|
+
// We consider an element only projected if the path doesn't have
|
|
210
|
+
// either arguments or filters; but we build up the navigation env
|
|
211
|
+
// nonetheless, as it makes rewriting paths later on easier.
|
|
212
|
+
let isComplexPath = !!(path[index].where || path[index].args);
|
|
213
|
+
|
|
210
214
|
++index;
|
|
211
215
|
while (navItem && index < path.length) {
|
|
212
216
|
const step = path[index];
|
|
213
|
-
if (!step?.id
|
|
217
|
+
if (!step?.id)
|
|
214
218
|
break;
|
|
219
|
+
isComplexPath ||= !!(step.where || step.args);
|
|
215
220
|
if (!navItem.elements?.[step.id]) {
|
|
216
221
|
const elements = navItem._origin?.elements ||
|
|
217
222
|
navItem._origin?.target?._artifact?.elements;
|
|
@@ -228,26 +233,26 @@ function resolve( model ) {
|
|
|
228
233
|
setLink( step, '_navigation', navItem );
|
|
229
234
|
++index;
|
|
230
235
|
}
|
|
231
|
-
// Last path step, if found, is a simple
|
|
236
|
+
// Last path step, if found, is a projected, either complex or simple.
|
|
232
237
|
if (index === path.length && navItem)
|
|
233
|
-
pushLink( navItem, '_projections', elem );
|
|
238
|
+
pushLink( navItem, isComplexPath ? '_complexProjections' : '_projections', elem );
|
|
234
239
|
}
|
|
235
240
|
}
|
|
236
241
|
|
|
237
242
|
function columnParentPath( elem ) {
|
|
238
|
-
if (!elem.
|
|
243
|
+
if (!elem._columnParent || !elem.value?.path || isPathBreakout( elem.value ))
|
|
239
244
|
return elem.value?.path;
|
|
240
245
|
|
|
241
246
|
const fullPath = [ ...elem.value.path ];
|
|
242
|
-
let
|
|
243
|
-
while (
|
|
244
|
-
if (
|
|
245
|
-
isPathBreakout(
|
|
247
|
+
let columnParent = elem._columnParent;
|
|
248
|
+
while (columnParent) {
|
|
249
|
+
if (columnParent.kind !== '$inline' || !columnParent.value?.path ||
|
|
250
|
+
isPathBreakout( columnParent.value )) {
|
|
246
251
|
// path breakout for e.g. `$self.{ foo }`, `1 as a .{ foo }`
|
|
247
252
|
return null;
|
|
248
253
|
}
|
|
249
|
-
fullPath.unshift(...
|
|
250
|
-
|
|
254
|
+
fullPath.unshift(...columnParent.value.path);
|
|
255
|
+
columnParent = columnParent._columnParent;
|
|
251
256
|
}
|
|
252
257
|
return fullPath;
|
|
253
258
|
}
|
|
@@ -285,16 +290,16 @@ function resolve( model ) {
|
|
|
285
290
|
return false;
|
|
286
291
|
}
|
|
287
292
|
|
|
288
|
-
function inheritedSourceKeyProp( { value,
|
|
293
|
+
function inheritedSourceKeyProp( { value, _columnParent } ) {
|
|
289
294
|
if (!value || !value.path)
|
|
290
295
|
return null;
|
|
291
|
-
const nav = !
|
|
296
|
+
const nav = !_columnParent && pathNavigation( value );
|
|
292
297
|
const item = value.path[value.path.length - 1];
|
|
293
298
|
if (nav?.navigation && nav.item === item)
|
|
294
299
|
return item._artifact?.key;
|
|
295
|
-
if (value.path.length !== 1 ||
|
|
300
|
+
if (value.path.length !== 1 || _columnParent?.kind !== '$inline')
|
|
296
301
|
return null;
|
|
297
|
-
const hpath =
|
|
302
|
+
const hpath = _columnParent.value?.path;
|
|
298
303
|
const head = hpath?.length === 1 && hpath[0]._navigation;
|
|
299
304
|
return head?.kind === '$tableAlias' && item._artifact?.key;
|
|
300
305
|
}
|
|
@@ -344,7 +349,7 @@ function resolve( model ) {
|
|
|
344
349
|
const elem = query.elements[name];
|
|
345
350
|
|
|
346
351
|
if (!elem.$inferred && elem.value?.path) {
|
|
347
|
-
const path = elem.
|
|
352
|
+
const path = elem._columnParent ? columnParentPath( elem ) : elem.value.path;
|
|
348
353
|
if (testExpr({ path }, selectTest, () => false, elem))
|
|
349
354
|
propagateKeys = false;
|
|
350
355
|
}
|
|
@@ -1008,7 +1013,7 @@ function resolve( model ) {
|
|
|
1008
1013
|
}
|
|
1009
1014
|
const target = resolvePath( obj.target, 'target', art );
|
|
1010
1015
|
|
|
1011
|
-
if (obj.
|
|
1016
|
+
if (obj._columnParent && obj.type && !obj.type.$inferred && art._main && art._main.query) {
|
|
1012
1017
|
// New association inside expand/inline: The on-condition can't be properly checked,
|
|
1013
1018
|
// so abort early. See #8797
|
|
1014
1019
|
error( 'query-unexpected-assoc', [ obj.name.location, art ], {},
|
|
@@ -1253,18 +1258,7 @@ function resolve( model ) {
|
|
|
1253
1258
|
error( 'type-invalid-cast', [ elem.type.location, elem ], { '#': 'assoc' } );
|
|
1254
1259
|
return;
|
|
1255
1260
|
}
|
|
1256
|
-
// console.log(message( null, elem.location, elem, {target,art:assoc}, 'Info','RE')
|
|
1257
|
-
// .toString(), elem.value)
|
|
1258
|
-
const nav = elem._main && elem._main.query && elem.value && pathNavigation( elem.value );
|
|
1259
|
-
if (nav && nav.item !== elem.value.path[elem.value.path.length - 1]) {
|
|
1260
|
-
if (!elem.on && origType.on) {
|
|
1261
|
-
error( 'rewrite-not-supported', [ elem.target.location, elem ] );
|
|
1262
|
-
return;
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
1261
|
const origTarget = origType.target._artifact;
|
|
1266
|
-
// console.log(require('../model/revealInternalProperties').ref(elem),
|
|
1267
|
-
// !!origTarget,!!origType._effectiveType,!!origType.target)
|
|
1268
1262
|
if (!origTarget || !target)
|
|
1269
1263
|
return;
|
|
1270
1264
|
|
|
@@ -1377,7 +1371,7 @@ function resolve( model ) {
|
|
|
1377
1371
|
if (a.path && a.kind !== '$self' && a.kind !== 'mixin')
|
|
1378
1372
|
sources.push( a );
|
|
1379
1373
|
}
|
|
1380
|
-
if (alias.kind === '$
|
|
1374
|
+
if (alias.kind === '$tableAlias')
|
|
1381
1375
|
news.push( { chain: [ alias, ...chain ], sources } );
|
|
1382
1376
|
else
|
|
1383
1377
|
news.push( { chain, sources } );
|
|
@@ -1593,7 +1587,8 @@ function pathNavigation( ref ) {
|
|
|
1593
1587
|
if (root.kind === '$self')
|
|
1594
1588
|
return { item, tableAlias: root };
|
|
1595
1589
|
if (root.kind !== '$tableAlias' || ref.path.length < 2)
|
|
1596
|
-
return {};
|
|
1590
|
+
return {}; // should not happen
|
|
1591
|
+
// table alias
|
|
1597
1592
|
return { navigation: root.elements?.[item.id], item, tableAlias: root };
|
|
1598
1593
|
}
|
|
1599
1594
|
|
package/lib/compiler/shared.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Compiler functions and utilities shared across all phases
|
|
2
|
-
// TODO: rename to paths.js and move non resolve-paths functions to somewhere else
|
|
3
2
|
|
|
4
3
|
'use strict';
|
|
5
4
|
|
|
@@ -546,7 +545,7 @@ function fns( model ) {
|
|
|
546
545
|
ruser = ruser._outer;
|
|
547
546
|
|
|
548
547
|
// Handle expand/inline, `type of`, :param, global (internally for CDL):
|
|
549
|
-
if (user.
|
|
548
|
+
if (user._columnParent && !semantics.isMainRef) { // in expand/inline
|
|
550
549
|
const { name } = semantics;
|
|
551
550
|
semantics = semantics.nestedColumn();
|
|
552
551
|
semantics.name = name;
|
|
@@ -563,7 +562,7 @@ function fns( model ) {
|
|
|
563
562
|
|
|
564
563
|
// Search in lexical environments, including $self/$projection:
|
|
565
564
|
const { isMainRef } = semantics;
|
|
566
|
-
const lexical = semantics.lexical?.( ruser ); // TODO:
|
|
565
|
+
const lexical = semantics.lexical?.( ruser ); // TODO: _columnParent?
|
|
567
566
|
if (lexical) {
|
|
568
567
|
const [ nextProp, dictProp ] = (isMainRef)
|
|
569
568
|
? [ '_block', 'artifacts' ]
|
|
@@ -608,7 +607,7 @@ function fns( model ) {
|
|
|
608
607
|
// element item in the path)
|
|
609
608
|
// TODO - think about setting _navigation for all $navElement – the
|
|
610
609
|
// "ref: ['tabAlias']: inline: […]" handling might be easier
|
|
611
|
-
// (no
|
|
610
|
+
// (no _columnParent consultation for key prop and renaming support)
|
|
612
611
|
function getPathItem( ref, semantics, user ) {
|
|
613
612
|
// let art = (headArt && headArt.kind === '$tableAlias') ? headArt._origin : headArt;
|
|
614
613
|
const { path } = ref;
|
|
@@ -918,7 +917,7 @@ function fns( model ) {
|
|
|
918
917
|
}
|
|
919
918
|
|
|
920
919
|
function nestedElements( user ) {
|
|
921
|
-
const colParent = user.
|
|
920
|
+
const colParent = user._columnParent;
|
|
922
921
|
Functions.effectiveType( colParent ); // set _origin
|
|
923
922
|
const path = colParent?.value?.path;
|
|
924
923
|
if (!path?.length)
|
|
@@ -1129,7 +1128,7 @@ function fns( model ) {
|
|
|
1129
1128
|
}
|
|
1130
1129
|
|
|
1131
1130
|
function undefinedNestedElement( user, head, valid, _dict, _art, path, semantics ) {
|
|
1132
|
-
const art = user.
|
|
1131
|
+
const art = user._columnParent._origin;
|
|
1133
1132
|
if (!art)
|
|
1134
1133
|
return null; // no consequential error
|
|
1135
1134
|
return undefinedItemElement( user, head, valid, null, art, path, semantics );
|
|
@@ -1551,7 +1550,7 @@ function fns( model ) {
|
|
|
1551
1550
|
const location = (user.expand || user.inline)[$location];
|
|
1552
1551
|
// mention `table alias` in text only with initial single path item ref,
|
|
1553
1552
|
// but do not mention that $self { … } is allowed, shouldn't be advertised:
|
|
1554
|
-
const txt = (path.length > 1 || user.
|
|
1553
|
+
const txt = (path.length > 1 || user._columnParent) ? 'struct' : 'init';
|
|
1555
1554
|
const code = (user.expand) ? '{ ‹expand› }' : '.{ ‹inline› }';
|
|
1556
1555
|
message( 'def-unexpected-nested-proj', [ location, user ], { '#': txt, code } );
|
|
1557
1556
|
}
|
|
@@ -1655,7 +1654,7 @@ function fns( model ) {
|
|
|
1655
1654
|
|
|
1656
1655
|
function checkOnlyForeignKeyNavigation( user, path, startIndex = 0, msgPrefix = '' ) {
|
|
1657
1656
|
// has to be run after foreign-key rewrite
|
|
1658
|
-
const outer = user.
|
|
1657
|
+
const outer = user._columnParent?._origin;
|
|
1659
1658
|
let assoc = outer?.foreignKeys &&
|
|
1660
1659
|
pathStartsWithSelf( { path } ) == null && // not $self or CDS var like $now
|
|
1661
1660
|
outer;
|