@sap/cds-compiler 3.6.2 → 3.7.2

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 (68) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +3 -0
  3. package/bin/cdsc.js +9 -5
  4. package/doc/CHANGELOG_BETA.md +20 -2
  5. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  6. package/lib/api/main.js +2 -1
  7. package/lib/base/dictionaries.js +10 -0
  8. package/lib/base/message-registry.js +56 -12
  9. package/lib/base/messages.js +39 -20
  10. package/lib/base/model.js +1 -0
  11. package/lib/base/shuffle.js +2 -1
  12. package/lib/checks/elements.js +29 -1
  13. package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +9 -5
  14. package/lib/checks/nonexpandableStructured.js +1 -1
  15. package/lib/checks/onConditions.js +8 -5
  16. package/lib/checks/types.js +6 -1
  17. package/lib/checks/validator.js +7 -3
  18. package/lib/compiler/assert-consistency.js +20 -23
  19. package/lib/compiler/base.js +1 -2
  20. package/lib/compiler/builtins.js +2 -2
  21. package/lib/compiler/checks.js +237 -242
  22. package/lib/compiler/define.js +63 -75
  23. package/lib/compiler/extend.js +325 -22
  24. package/lib/compiler/finalize-parse-cdl.js +1 -55
  25. package/lib/compiler/kick-start.js +6 -7
  26. package/lib/compiler/populate.js +284 -288
  27. package/lib/compiler/propagator.js +15 -13
  28. package/lib/compiler/resolve.js +136 -306
  29. package/lib/compiler/shared.js +42 -44
  30. package/lib/compiler/tweak-assocs.js +29 -27
  31. package/lib/compiler/utils.js +29 -3
  32. package/lib/edm/annotations/genericTranslation.js +7 -13
  33. package/lib/edm/annotations/preprocessAnnotations.js +3 -0
  34. package/lib/edm/csn2edm.js +0 -4
  35. package/lib/edm/edm.js +6 -4
  36. package/lib/edm/edmAnnoPreprocessor.js +1 -0
  37. package/lib/edm/edmPreprocessor.js +1 -5
  38. package/lib/gen/Dictionary.json +34 -2
  39. package/lib/gen/language.checksum +1 -1
  40. package/lib/gen/language.interp +1 -1
  41. package/lib/gen/languageParser.js +2429 -2401
  42. package/lib/inspect/inspectPropagation.js +2 -0
  43. package/lib/json/from-csn.js +87 -41
  44. package/lib/json/to-csn.js +47 -16
  45. package/lib/language/errorStrategy.js +1 -0
  46. package/lib/language/genericAntlrParser.js +109 -28
  47. package/lib/language/language.g4 +20 -4
  48. package/lib/model/csnRefs.js +1 -1
  49. package/lib/model/csnUtils.js +1 -0
  50. package/lib/model/revealInternalProperties.js +1 -2
  51. package/lib/modelCompare/compare.js +2 -1
  52. package/lib/render/manageConstraints.js +5 -2
  53. package/lib/render/toCdl.js +20 -7
  54. package/lib/render/toHdbcds.js +2 -8
  55. package/lib/render/toSql.js +4 -3
  56. package/lib/render/utils/common.js +9 -5
  57. package/lib/transform/db/assertUnique.js +2 -1
  58. package/lib/transform/db/expansion.js +2 -0
  59. package/lib/transform/db/flattening.js +37 -36
  60. package/lib/transform/db/rewriteCalculatedElements.js +559 -0
  61. package/lib/transform/db/transformExists.js +4 -0
  62. package/lib/transform/db/views.js +40 -37
  63. package/lib/transform/forRelationalDB.js +38 -28
  64. package/lib/transform/odata/typesExposure.js +50 -15
  65. package/lib/transform/parseExpr.js +14 -8
  66. package/lib/transform/transformUtilsNew.js +6 -5
  67. package/lib/transform/translateAssocsToJoins.js +49 -33
  68. package/package.json +1 -1
@@ -156,11 +156,10 @@ function define( model ) {
156
156
  const { options } = model;
157
157
  // Get simplified "resolve" functionality and the message function:
158
158
  const {
159
- error, warning, info, messages,
159
+ error, warning, info, message, messages,
160
160
  } = model.$messageFunctions;
161
161
  const {
162
162
  resolveUncheckedPath,
163
- checkAnnotate,
164
163
  } = model.$functions;
165
164
  const { shuffleDict, shuffleArray } = shuffleGen( options.testMode );
166
165
 
@@ -548,6 +547,7 @@ function define( model ) {
548
547
  setLink( art, '_from', [] ); // for sequence of resolve steps
549
548
  if (!setLink( art, '_leadingQuery', initQueryExpression( art.query, art ) ) )
550
549
  return; // null or undefined in case of parse error
550
+ // if (art._leadingQuery !== art.$queries [0]) throw Error('FOO');
551
551
  setLink( art._leadingQuery, '_$next', art );
552
552
  if (art.elements) { // specified element via compilation of client-style CSN
553
553
  // TODO: consider this part of a revamped on-demand 'extend' functionality
@@ -595,7 +595,7 @@ function define( model ) {
595
595
  * - for members in compile(): init annotations via extendMembers/annotateMembers
596
596
  * - for members in parse.cdl(): init annotation via initMembers
597
597
  *
598
- * In the future (after name cleanup):
598
+ * In the future (after name cleanup): -- TODO --
599
599
  *
600
600
  * - also initialize members and member extensions/annotations here
601
601
  * - we might also do other things, like calculating whether an `extend` is
@@ -708,7 +708,6 @@ function define( model ) {
708
708
  setLink( self, '_origin', query );
709
709
  setLink( self, '_parent', query );
710
710
  setLink( self, '_main', query._main );
711
- setLink( self, '_effectiveType', query ); // TODO: remove
712
711
  query.$tableAliases.$self = self;
713
712
  query.$tableAliases.$projection = self;
714
713
  }
@@ -770,6 +769,7 @@ function define( model ) {
770
769
  // _origin is set when we resolve the ref
771
770
  if (query._parent.kind !== 'select')
772
771
  query._main._from.push( table ); // store tabref if outside "real" subquery
772
+ // (tab refs on the right of union are unnecessary)
773
773
  }
774
774
  else if (table.query) {
775
775
  if (!table.name || !table.name.id) {
@@ -778,7 +778,6 @@ function define( model ) {
778
778
  return;
779
779
  }
780
780
  addAsAlias();
781
- setLink( table, '_effectiveType', table.query ); // TODO: remove!
782
781
  // Store _origin to leading query of table.query for name resolution
783
782
  setLink( table, '_origin', initQueryExpression( table.query, table ) );
784
783
  }
@@ -1010,12 +1009,14 @@ function define( model ) {
1010
1009
  *
1011
1010
  * If not for extensions: construct === parent
1012
1011
  *
1012
+ * Param `initExtensions` is for parse.cdl
1013
+ *
1013
1014
  * TODO: separate extension!
1014
1015
  */
1015
1016
  function initMembers( construct, parent, block, initExtensions = false ) {
1016
1017
  // TODO: split extend from init
1017
1018
  const main = parent._main || parent;
1018
- const isQueryExtension = kindProperties[construct.kind].isExtension && main.query;
1019
+ const isQueryExtension = construct.kind === 'extend' && main.query;
1019
1020
  let obj = construct;
1020
1021
  let { items } = obj;
1021
1022
  while (items) {
@@ -1043,46 +1044,8 @@ function define( model ) {
1043
1044
  'A managed aspect composition can\'t have a specified ON-condition' );
1044
1045
  delete obj.on; // continuation semantics: not specified
1045
1046
  }
1046
- if (targetAspect.elements) {
1047
- // TODO: main?
1048
- const inEntity = parent._main && parent._main.kind === 'entity';
1049
- // TODO: also allow indirectly (component in component in entity)?
1050
- setLink( targetAspect, '_outer', obj );
1051
- setLink( targetAspect, '_parent', parent._parent );
1052
- setLink( targetAspect, '_main', null ); // for name resolution
1053
-
1054
- parent = targetAspect;
1055
- construct = parent; // avoid extension behavior
1056
- targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
1057
- setLink( targetAspect, '_block', block );
1058
- initDollarSelf( targetAspect );
1059
- // allow ref of up_ in anonymous aspect inside entity
1060
- // (TODO: complain if used and the managed composition is included into
1061
- // another entity - might induce auto-redirection):
1062
- if (inEntity && !targetAspect.elements.up_) {
1063
- const up = {
1064
- name: {
1065
- id: 'up_',
1066
- alias: 'up_',
1067
- element: obj.name.element,
1068
- absolute: obj.name.absolute,
1069
- },
1070
- kind: '$navElement',
1071
- location: obj.location,
1072
- };
1073
- setLink( up, '_parent', targetAspect );
1074
- setLink( up, '_main', targetAspect ); // used on main artifact
1075
- // recompilation case: both target and targetAspect → allow up_ in that case, too:
1076
- const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
1077
- const entity = name && model.definitions[name];
1078
- if (entity && entity.elements)
1079
- setLink( up, '_origin', entity.elements.up_ );
1080
- // processAspectComposition/expand() sets _origin to element of
1081
- // generated target entity
1082
- targetAspect.$tableAliases.up_ = up;
1083
- }
1084
- obj = targetAspect;
1085
- }
1047
+ if (targetAspect.elements)
1048
+ initAnonymousAspect();
1086
1049
  }
1087
1050
  if (obj !== parent && obj.elements && parent.enum) { // applying the extension
1088
1051
  initElementsAsEnum();
@@ -1102,7 +1065,8 @@ function define( model ) {
1102
1065
  forEachInOrder( construct, 'params', init );
1103
1066
  const { returns } = construct;
1104
1067
  if (returns) {
1105
- returns.kind = (kindProperties[construct.kind].isExtension) ? construct.kind : 'param';
1068
+ const { kind } = construct;
1069
+ returns.kind = (kind === 'extend' || kind === 'annotate') ? kind : 'param';
1106
1070
  init( returns, '' ); // '' is special name for returns parameter
1107
1071
  }
1108
1072
  return;
@@ -1129,6 +1093,47 @@ function define( model ) {
1129
1093
  forEachGeneric( { enum: obj.elements }, 'enum', init );
1130
1094
  }
1131
1095
 
1096
+ function initAnonymousAspect() {
1097
+ // TODO: main?
1098
+ const inEntity = parent._main && parent._main.kind === 'entity';
1099
+ // TODO: also allow indirectly (component in component in entity)?
1100
+ setLink( targetAspect, '_outer', obj );
1101
+ setLink( targetAspect, '_parent', parent._parent );
1102
+ setLink( targetAspect, '_main', null ); // for name resolution
1103
+
1104
+ parent = targetAspect;
1105
+ construct = parent; // avoid extension behavior
1106
+ targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
1107
+ setLink( targetAspect, '_block', block );
1108
+ initDollarSelf( targetAspect );
1109
+ // allow ref of up_ in anonymous aspect inside entity
1110
+ // (TODO: complain if used and the managed composition is included into
1111
+ // another entity - might induce auto-redirection):
1112
+ if (inEntity && !targetAspect.elements.up_) {
1113
+ const up = {
1114
+ name: {
1115
+ id: 'up_',
1116
+ alias: 'up_',
1117
+ element: obj.name.element,
1118
+ absolute: obj.name.absolute,
1119
+ },
1120
+ kind: '$navElement',
1121
+ location: obj.location,
1122
+ };
1123
+ setLink( up, '_parent', targetAspect );
1124
+ setLink( up, '_main', targetAspect ); // used on main artifact
1125
+ // recompilation case: both target and targetAspect → allow up_ in that case, too:
1126
+ const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
1127
+ const entity = name && model.definitions[name];
1128
+ if (entity && entity.elements)
1129
+ setLink( up, '_origin', entity.elements.up_ );
1130
+ // processAspectComposition/expand() sets _origin to element of
1131
+ // generated target entity
1132
+ targetAspect.$tableAliases.up_ = up;
1133
+ }
1134
+ obj = targetAspect;
1135
+ }
1136
+
1132
1137
  function init( elem, name, prop ) {
1133
1138
  if (!elem.kind) // wrong CSN input
1134
1139
  elem.kind = dictKinds[prop];
@@ -1138,12 +1143,12 @@ function define( model ) {
1138
1143
  elem.name = Object.assign( { $inferred: 'as' },
1139
1144
  ref.path[ref.path.length - 1] );
1140
1145
  }
1141
- else { // if JSON parser misses to set name
1146
+ else { // RETURNS, parser robustness
1142
1147
  elem.name = { id: name, location: elem.location };
1143
1148
  }
1144
1149
  }
1145
1150
  // if (!kindProperties[ elem.kind ]) console.log(elem.kind,elem.name)
1146
- if (kindProperties[elem.kind].isExtension && !initExtensions) {
1151
+ if ((elem.kind === 'extend' || elem.kind === 'annotate') && !initExtensions) {
1147
1152
  storeExtension( elem, name, prop, parent, block );
1148
1153
  return;
1149
1154
  }
@@ -1161,8 +1166,6 @@ function define( model ) {
1161
1166
  setMemberParent( elem, name, parent, add && prop );
1162
1167
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1163
1168
  checkRedefinition( elem );
1164
- if (elem.kind === 'annotate' || elem.kind === 'extend')
1165
- checkAnnotate( elem, elem );
1166
1169
  initAnnotations( elem, bl );
1167
1170
  initMembers( elem, elem, bl, initExtensions );
1168
1171
  if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
@@ -1174,31 +1177,16 @@ function define( model ) {
1174
1177
  elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
1175
1178
  return;
1176
1179
  // -> it's a calculated element
1177
- checkCalculatedElement(elem);
1178
- elem.$syntax = 'calc';
1179
- }
1180
- }
1181
-
1182
- function checkCalculatedElement( elem ) {
1183
- const loc = [ elem.value.location, elem ];
1184
- if (elem._main.kind !== 'entity' && elem._main.kind !== 'aspect' &&
1185
- elem._main.kind !== 'extend') {
1186
- error( 'syntax-invalid-calc-elem', loc, { '#': elem._main.kind } );
1187
- }
1188
- else if (!isBetaEnabled( options, 'calculatedElements' )) {
1189
- error( 'def-unsupported-calc-elem', loc,
1190
- 'Calculated elements are not supported' );
1191
- }
1192
- else {
1193
- const noTruthyAllowed = [ 'localized', 'key', 'virtual' ];
1194
- for (const prop of noTruthyAllowed) {
1195
- if (elem[prop]?.val) {
1196
- // probably better than a parse error (which is good for DEFAULT vs calc),
1197
- // also appears with parse-cdl:
1198
- error('syntax-invalid-calc-elem', loc, { '#': prop });
1199
- return; // one error is enough
1200
- }
1180
+ if (!elem.type && elem.value?.type) { // top-level CAST( expr AS type )
1181
+ if (!elem.target)
1182
+ elem.type = { ...elem.value.type, $inferred: 'cast' };
1201
1183
  }
1184
+ if (!isBetaEnabled( options, 'calculatedElements' )) {
1185
+ const loc = [ elem.value.location, elem ];
1186
+ // TODO: this could be considered a syntax check
1187
+ message( 'def-unsupported-calc-elem', loc, { '#': 'std' } );
1188
+ }
1189
+ elem.$syntax = 'calc';
1202
1190
  }
1203
1191
  }
1204
1192