@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.
Files changed (51) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/bin/cdsc.js +2 -2
  3. package/bin/cdshi.js +24 -17
  4. package/bin/cdsse.js +17 -18
  5. package/lib/api/main.js +19 -2
  6. package/lib/api/options.js +4 -1
  7. package/lib/base/builtins.js +1 -0
  8. package/lib/base/message-registry.js +16 -3
  9. package/lib/base/model.js +0 -10
  10. package/lib/checks/actionsFunctions.js +0 -12
  11. package/lib/checks/structuredAnnoExpressions.js +10 -14
  12. package/lib/compiler/assert-consistency.js +19 -11
  13. package/lib/compiler/builtins.js +1 -1
  14. package/lib/compiler/define.js +6 -4
  15. package/lib/compiler/extend.js +5 -5
  16. package/lib/compiler/populate.js +9 -9
  17. package/lib/compiler/propagator.js +1 -0
  18. package/lib/compiler/resolve.js +29 -34
  19. package/lib/compiler/shared.js +7 -8
  20. package/lib/compiler/tweak-assocs.js +155 -64
  21. package/lib/compiler/utils.js +1 -1
  22. package/lib/compiler/xpr-rewrite.js +4 -3
  23. package/lib/edm/annotations/genericTranslation.js +13 -9
  24. package/lib/edm/csn2edm.js +26 -2
  25. package/lib/edm/edm.js +23 -8
  26. package/lib/edm/edmInboundChecks.js +5 -7
  27. package/lib/edm/edmPreprocessor.js +43 -30
  28. package/lib/gen/BaseParser.js +720 -0
  29. package/lib/gen/CdlParser.js +4421 -0
  30. package/lib/gen/language.checksum +1 -1
  31. package/lib/gen/language.interp +1 -1
  32. package/lib/gen/languageParser.js +4006 -4001
  33. package/lib/language/antlrParser.js +62 -0
  34. package/lib/language/genericAntlrParser.js +28 -0
  35. package/lib/model/csnUtils.js +2 -0
  36. package/lib/model/revealInternalProperties.js +2 -0
  37. package/lib/modelCompare/utils/filter.js +70 -42
  38. package/lib/optionProcessor.js +9 -3
  39. package/lib/parsers/AstBuildingParser.js +1172 -0
  40. package/lib/parsers/CdlGrammar.g4 +1940 -0
  41. package/lib/parsers/Lexer.js +239 -0
  42. package/lib/render/toCdl.js +23 -27
  43. package/lib/render/toSql.js +5 -5
  44. package/lib/transform/db/applyTransformations.js +54 -16
  45. package/lib/transform/draft/odata.js +10 -11
  46. package/lib/transform/effective/flattening.js +10 -14
  47. package/lib/transform/odata/flattening.js +42 -31
  48. package/lib/transform/odata/toFinalBaseType.js +7 -6
  49. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  50. package/package.json +2 -2
  51. package/share/messages/redirected-to-ambiguous.md +5 -4
@@ -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 EXTEND on the generated $(ART)' );
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 EXTEND on the generated $(ART)' );
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) {
@@ -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._pathHead || query._parent.kind !== 'select')) {
793
- // _pathHead => inline/expand; _parent -> only allowed in sub-selects
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._pathHead; // TODO: rename _pathHead to _columnParent
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 pathHead can't be found
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._pathHead;
942
+ function wildcardColumnEnv( wildcard, query ) { // etc. wildcard._columnParent;
943
943
  // if (envParent) console.log( 'CE:', envParent._origin, query );
944
- const colParent = wildcard._pathHead;
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, pathHead, origin, name, location ) {
989
- setLink( queryElem, '_pathHead', pathHead );
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 = pathHead?.value?.path?.[0]?._navigation;
995
+ // const alias = columnParent?.value?.path?.[0]?._navigation;
996
996
  // if (alias?.kind === '$tableAlias')
997
997
  // pushLink( alias.elements[name], '_projections', queryElem );
998
998
  }
@@ -66,6 +66,7 @@ function propagate( model ) {
66
66
  __proto__: null,
67
67
  never,
68
68
  onlyViaArtifact,
69
+ onlyViaParent,
69
70
  notWithPersistenceTable,
70
71
  };
71
72
  for (const rule in propagationRules)
@@ -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._pathHead) {
178
- if (elem._pathHead?.kind !== '$inline')
179
- // we're traversing top-level elements of the query; other _pathHead kinds can't happen
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 (path[index].where || path[index].args)
209
- return;
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 || step.where || step.args)
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 projection
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._pathHead || !elem.value?.path || isPathBreakout( elem.value ))
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 pathHead = elem._pathHead;
243
- while (pathHead) {
244
- if (pathHead.kind !== '$inline' || !pathHead.value?.path ||
245
- isPathBreakout( pathHead.value )) {
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(...pathHead.value.path);
250
- pathHead = pathHead._pathHead;
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, _pathHead } ) {
293
+ function inheritedSourceKeyProp( { value, _columnParent } ) {
289
294
  if (!value || !value.path)
290
295
  return null;
291
- const nav = !_pathHead && pathNavigation( value );
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 || _pathHead?.kind !== '$inline')
300
+ if (value.path.length !== 1 || _columnParent?.kind !== '$inline')
296
301
  return null;
297
- const hpath = _pathHead.value?.path;
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._pathHead ? columnParentPath( elem ) : elem.value.path;
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._pathHead && obj.type && !obj.type.$inferred && art._main && art._main.query) {
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 === '$tablealias')
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 {}; // should not happen
1590
+ return {}; // should not happen
1591
+ // table alias
1597
1592
  return { navigation: root.elements?.[item.id], item, tableAlias: root };
1598
1593
  }
1599
1594
 
@@ -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._pathHead && !semantics.isMainRef) { // in expand/inline
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: _pathHead?
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 _pathHead consultation for key prop and renaming support)
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._pathHead;
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._pathHead._origin;
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._pathHead) ? 'struct' : 'init';
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._pathHead?._origin;
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;