@sap/cds-compiler 3.5.2 → 3.6.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 (85) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/bin/cdsc.js +14 -6
  3. package/doc/CHANGELOG_ARCHIVE.md +10 -10
  4. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  5. package/lib/api/main.js +32 -55
  6. package/lib/api/options.js +1 -0
  7. package/lib/api/validate.js +5 -0
  8. package/lib/base/message-registry.js +104 -32
  9. package/lib/base/messages.js +277 -212
  10. package/lib/base/model.js +33 -22
  11. package/lib/base/optionProcessorHelper.js +9 -2
  12. package/lib/base/shuffle.js +50 -0
  13. package/lib/checks/actionsFunctions.js +37 -20
  14. package/lib/checks/foreignKeys.js +13 -6
  15. package/lib/checks/nonexpandableStructured.js +1 -2
  16. package/lib/checks/onConditions.js +21 -19
  17. package/lib/checks/parameters.js +1 -1
  18. package/lib/checks/queryNoDbArtifacts.js +2 -0
  19. package/lib/checks/types.js +16 -22
  20. package/lib/compiler/assert-consistency.js +31 -28
  21. package/lib/compiler/builtins.js +20 -4
  22. package/lib/compiler/checks.js +72 -63
  23. package/lib/compiler/define.js +396 -314
  24. package/lib/compiler/extend.js +55 -49
  25. package/lib/compiler/index.js +5 -0
  26. package/lib/compiler/populate.js +28 -11
  27. package/lib/compiler/propagator.js +2 -1
  28. package/lib/compiler/resolve.js +29 -20
  29. package/lib/compiler/shared.js +15 -10
  30. package/lib/compiler/utils.js +7 -7
  31. package/lib/edm/annotations/genericTranslation.js +51 -46
  32. package/lib/edm/annotations/preprocessAnnotations.js +39 -42
  33. package/lib/edm/csn2edm.js +69 -21
  34. package/lib/edm/edm.js +2 -2
  35. package/lib/edm/edmInboundChecks.js +6 -8
  36. package/lib/edm/edmPreprocessor.js +88 -80
  37. package/lib/edm/edmUtils.js +6 -15
  38. package/lib/gen/Dictionary.json +81 -13
  39. package/lib/gen/language.checksum +1 -1
  40. package/lib/gen/language.interp +2 -1
  41. package/lib/gen/languageParser.js +4680 -4484
  42. package/lib/inspect/inspectModelStatistics.js +2 -1
  43. package/lib/inspect/inspectPropagation.js +2 -1
  44. package/lib/json/from-csn.js +131 -78
  45. package/lib/json/to-csn.js +39 -23
  46. package/lib/language/antlrParser.js +0 -3
  47. package/lib/language/docCommentParser.js +7 -3
  48. package/lib/language/errorStrategy.js +3 -2
  49. package/lib/language/genericAntlrParser.js +96 -41
  50. package/lib/language/language.g4 +112 -128
  51. package/lib/language/multiLineStringParser.js +2 -1
  52. package/lib/main.d.ts +115 -2
  53. package/lib/main.js +16 -3
  54. package/lib/model/csnRefs.js +3 -3
  55. package/lib/model/csnUtils.js +109 -179
  56. package/lib/model/enrichCsn.js +13 -8
  57. package/lib/model/revealInternalProperties.js +4 -3
  58. package/lib/optionProcessor.js +23 -3
  59. package/lib/render/manageConstraints.js +11 -15
  60. package/lib/render/toCdl.js +144 -47
  61. package/lib/render/toHdbcds.js +22 -22
  62. package/lib/render/toRename.js +3 -4
  63. package/lib/render/toSql.js +29 -20
  64. package/lib/render/utils/delta.js +3 -1
  65. package/lib/render/utils/sql.js +3 -16
  66. package/lib/transform/db/associations.js +6 -6
  67. package/lib/transform/db/cdsPersistence.js +3 -3
  68. package/lib/transform/db/constraints.js +8 -8
  69. package/lib/transform/db/expansion.js +4 -4
  70. package/lib/transform/db/flattening.js +12 -15
  71. package/lib/transform/db/temporal.js +4 -3
  72. package/lib/transform/db/transformExists.js +2 -1
  73. package/lib/transform/draft/db.js +7 -7
  74. package/lib/transform/forOdataNew.js +15 -4
  75. package/lib/transform/forRelationalDB.js +53 -39
  76. package/lib/transform/odata/toFinalBaseType.js +106 -82
  77. package/lib/transform/odata/typesExposure.js +26 -17
  78. package/lib/transform/odata/utils.js +1 -1
  79. package/lib/transform/parseExpr.js +1 -1
  80. package/lib/transform/transformUtilsNew.js +33 -10
  81. package/lib/transform/translateAssocsToJoins.js +8 -7
  82. package/lib/transform/universalCsn/coreComputed.js +7 -5
  83. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
  84. package/lib/utils/timetrace.js +2 -2
  85. package/package.json +1 -2
@@ -33,7 +33,7 @@ const capabilities = Object.keys(require('../gen/Dictionary.json').
33
33
  function initializeModel(csn, _options, messageFunctions, requestedServiceNames=undefined)
34
34
  {
35
35
  const { info, warning, error, message } = messageFunctions;
36
-
36
+ const special$self = !csn?.definitions?.$self && '$self';
37
37
  const csnUtils = getUtils(csn);
38
38
 
39
39
  // proxies are merged into the final model after all proxy elements are collected
@@ -68,7 +68,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
68
68
 
69
69
  // Structural CSN inbound QA checks
70
70
  inboundQualificationChecks(csn, options, messageFunctions,
71
- serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName);
71
+ serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName, csnUtils);
72
72
  // not needed at the moment
73
73
  // resolveForeignKeyRefs();
74
74
 
@@ -101,7 +101,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
101
101
  /*
102
102
  Enrich the CSN by de-anonymizing and exposing types that are required to make the service self contained.
103
103
  Type exposure will add additional schema contexts and group the exposed types in these contexts.
104
- contexts either represent another service (if the type to be exposed resides in that
104
+ Contexts either represent another service (if the type to be exposed resides in that
105
105
  service), the namespace (including (sub-)contexts) or as last resort (if the type name
106
106
  has no prefix path) a 'root' namespace.
107
107
  */
@@ -430,7 +430,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
430
430
  [ 'definitions', containee.name ], true, { elementsOnly: true });
431
431
  }
432
432
  else if(elt.type && !elt.elements) {
433
- // try to find elements to drill down further
433
+ // try to find elements to drill down further
434
434
  while(elt && !isBuiltinType(elt.type) && !elt.elements) {
435
435
  elt = csn.definitions[elt.type];
436
436
  }
@@ -599,7 +599,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
599
599
  });
600
600
  linkAssociationTarget(parameterCsn);
601
601
  initContainments(parameterCsn);
602
- // add assoc to result set, FIXME: is the cardinality correct?
602
+ // add assoc to result set, FIXME: is the cardinality correct?
603
603
  if(!isProxy) {
604
604
  parameterCsn.elements[parameterToTypeAssocName] = {
605
605
  '@odata.contained': true,
@@ -654,7 +654,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
654
654
  // convert $path to path starting at main artifact
655
655
  function $path2path(p) {
656
656
  const path = [];
657
- /** @type {object} */
657
+ /** @type {any} */
658
658
  let env = csn;
659
659
  for (let i = 0; p && env && i < p.length; i++) {
660
660
  const ps = p[i];
@@ -704,7 +704,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
704
704
  element[attrName] = attr;
705
705
  }
706
706
  }
707
- // and eventually remove some afterwards:)
707
+ // and eventually remove some afterwards
708
708
  if(options.isV2())
709
709
  edmAnnoPreproc.setSAPSpecificV2AnnotationsToAssociation(element);
710
710
 
@@ -789,9 +789,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
789
789
  Do not render (ignore) elements as properties
790
790
  In V4:
791
791
  1) If this is a foreign key of an association to a container which *is* used
792
- to establish the containment via composition and $self comparison, then
793
- do not render this foreign key. The $self comparison can only be evaluated
794
- after the ON conditions have been parsed in prepareConstraints().
792
+ to establish the containment via composition and $self comparison.
793
+ The $self comparison can only be evaluated after the ON conditions have been
794
+ parsed in prepareConstraints().
795
795
  2) For all other foreign keys let isEdmPropertyRendered() decide.
796
796
  3) If an element/association is annotated with @odata.containment.ignore and containment is
797
797
  active, assign @cds.api.ignore or @odata.navigable: false
@@ -817,8 +817,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
817
817
  if(assoc)
818
818
  isContainerAssoc = !!(assoc._isToContainer && assoc._selfReferences.length || assoc['@odata.contained']);
819
819
  /*
820
- If this foreign key is NOT a container fk, let isEdmPropertyRendered() decide
821
- Else, if fk is container fk, omit it if it wasn't requested in structured mode
820
+ If this foreign key is NOT a container fk, let isEdmPropertyRendered() decide
821
+ Else, if fk is container fk, omit it if it wasn't requested in structured mode
822
822
  */
823
823
  if((!isContainerAssoc && !isEdmPropertyRendered(element, options)) ||
824
824
  (isContainerAssoc && !options.renderForeignKeys))
@@ -829,7 +829,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
829
829
  delete struct.$keys[element.name];
830
830
  }
831
831
  }
832
- // deprecated._unmanagedUpInComponent:
833
832
  // Only in containment:
834
833
  // Ignore this (foreign key) elment if renderForeignKeys is false
835
834
  if(options.odataContainment && element['@odata.containment.ignore']) {
@@ -842,8 +841,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
842
841
  }
843
842
  // it's an association
844
843
  else if(element['@odata.containment.ignore'] && options.odataContainment && !options.renderForeignKeys) {
845
- // if this is an explicitly containment ignore tagged association,
846
- // ignore it if option odataContainment is true and no foreign keys should be rendered
844
+ // if this is an explicitly containment ignore tagged association,
845
+ // ignore it if option odataContainment is true and no foreign keys should be rendered
847
846
  edmUtils.assignAnnotation(element, '@odata.navigable', false);
848
847
  }
849
848
  }, [], true, { elementsOnly: true });
@@ -865,20 +864,20 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
865
864
  edmUtils.finalizeReferentialConstraints(csn, element, options, info);
866
865
 
867
866
  if(element._constraints._partnerCsn && element.cardinality && element.cardinality.max) {
868
- // if this is a partnership and this assoc has a set target cardinality, assign it as source cardinality to the partner
867
+ // if this is a partnership and this assoc has a set target cardinality, assign it as source cardinality to the partner
869
868
  if(element._constraints._partnerCsn.cardinality) {
870
- // if the forward association has set a src cardinality and it deviates from the backlink target cardinality raise a warning
871
- // in V2 only, in V4 the source cardinality is rendered implicitly at the Type property
869
+ // if the forward association has set a src cardinality and it deviates from the backlink target cardinality raise a warning
870
+ // in V2 only, in V4 the source cardinality is rendered implicitly at the Type property
872
871
  if(element._constraints._partnerCsn.cardinality.src) {
873
872
  let srcMult = (element._constraints._partnerCsn.cardinality.src == 1) ? '0..1' : '*';
874
873
  let newMult = (element.cardinality.max > 1) ? '*' : '0..1';
875
874
  if(options.isV2() && srcMult !== newMult) {
876
- // Association 'E_toF': Multiplicity of Role='E' defined to '*', conflicting with target multiplicity '0..1' from
875
+ // Association 'E_toF': Multiplicity of Role='E' defined to '*', conflicting with target multiplicity '0..1' from
877
876
  warning(null, null, `Source cardinality "${element._constraints._partnerCsn.cardinality.src}" of "${element._constraints._partnerCsn._parent.name}/${element._constraints._partnerCsn.name}" conflicts with target cardinality "${element.cardinality.max}" of association "${element._parent.name}/${element.name}"`);
878
877
  }
879
878
  }
880
879
  else {
881
- // .. but only if the original assoc hasn't set src yet
880
+ // .. but only if the original assoc hasn't set src yet
882
881
  element._constraints._partnerCsn.cardinality.src = element.cardinality.max;
883
882
  }
884
883
  }
@@ -900,7 +899,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
900
899
  if(fqSchemaName.startsWith(srn + '.')) {
901
900
  const targetSchemaName = fqSchemaName.replace(srn + '.', '');
902
901
  if(serviceRootNames.includes(targetSchemaName)) {
903
- // remove all definitions starting with < fqSchemaName >. and add a schema reference
902
+ // remove all definitions starting with < fqSchemaName >. and add a schema reference
904
903
  Object.keys(csn.definitions).forEach(dn => {
905
904
  if(dn.startsWith(fqSchemaName)) {
906
905
  delete csn.definitions[dn];
@@ -1007,7 +1006,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1007
1006
  // and this association is not a good one, don't expose this association.
1008
1007
  muteNavProp(element, 'oncond');
1009
1008
  return;
1010
- }
1009
+ }
1011
1010
  if(proxy) {
1012
1011
  // if a proxy was either already created or could be created and
1013
1012
  // if it's a 'real' proxy, link the _target to it and remove constraints
@@ -1170,45 +1169,45 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1170
1169
 
1171
1170
  // Always expose types referred to by a proxy, never reuse an eventually existing type
1172
1171
  // as the nested elements must all be not nullable
1173
- // elements have precedence over type
1172
+ // elements have precedence over type
1174
1173
  const typeDef = !node.elements && node.type ? csn.definitions[node.type] : node;
1175
1174
 
1176
1175
  if (typeDef) {
1177
1176
  let typeClone;
1178
- // the type clone must be produced for each service as this type may
1179
- // produce references and/or proxies into multiple services
1180
- // (but only once per service, therefore cache it).
1177
+ // the type clone must be produced for each service as this type may
1178
+ // produce references and/or proxies into multiple services
1179
+ // (but only once per service, therefore cache it).
1181
1180
  if(typeDef.$proxyTypes && typeDef.$proxyTypes[globalSchemaPrefix]) {
1182
1181
  // if type has been exposed in a schema use this type
1183
1182
  typeClone = typeDef.$proxyTypes[globalSchemaPrefix];
1184
1183
  }
1185
1184
  else {
1186
- // Set the correct name
1185
+ // Set the correct name
1187
1186
  let typeId = artificialName; // the artificialName has no namespace, it's the element
1188
1187
  if(node.type) {
1189
- // same as for proxies, use schema or namespace, 'root' is last resort
1188
+ // same as for proxies, use schema or namespace, 'root' is last resort
1190
1189
  typeSchemaName = typeDef.$mySchemaName || edmUtils.getSchemaPrefix(node.type);
1191
1190
  typeId = node.type.replace(typeSchemaName + '.', '').replace(/\./g, '_');
1192
- // strip the service root of that type (if any)
1191
+ // strip the service root of that type (if any)
1193
1192
  const myServiceRootName = whatsMyServiceRootName(typeSchemaName);
1194
1193
  if(myServiceRootName)
1195
1194
  typeSchemaName = typeSchemaName.replace(myServiceRootName + '.', '');
1196
1195
  }
1197
1196
 
1198
1197
  if(edmUtils.isStructuredArtifact(typeDef)) {
1199
- // pull forceNotNull to false for named types and non-key nodes
1200
- // only toplevel nodes (elements) can be key
1198
+ // pull forceNotNull to false for named types and non-key nodes
1199
+ // only toplevel nodes (elements) can be key
1201
1200
  forceToNotNull = !!(forceToNotNull && isKey && node.elements && !node.type);
1202
1201
 
1203
1202
  typeClone = cloneStructTypeForProxy(typeSchemaName, `${typeSchemaName}.${typeId}`, typeDef);
1204
1203
  if(typeClone) {
1205
- // Recurse into elements of 'type' (if any)
1204
+ // Recurse into elements of 'type' (if any)
1206
1205
  typeClone.elements && Object.entries(typeClone.elements).forEach(([elemName, elem]) => {
1207
- // if this is a foreign key elment, we must check whether or not the association
1208
- // has been exposed as proxy. If it has not been exposed, no further structured
1209
- // types must be exposed as 'Proxy_' types.
1206
+ // if this is a foreign key elment, we must check whether or not the association
1207
+ // has been exposed as proxy. If it has not been exposed, no further structured
1208
+ // types must be exposed as 'Proxy_' types.
1210
1209
 
1211
- // TODO: expose types of assoc.keys and don't rely on exposed foreign keys
1210
+ // TODO: expose types of assoc.keys and don't rely on exposed foreign keys
1212
1211
  if(!elem['@odata.foreignKey4'] ||
1213
1212
  (elem['@odata.foreignKey4'] && !typeClone.elements[elem['@odata.foreignKey4']].$exposed))
1214
1213
  exposeStructTypeForProxyOf(proxy, elem, `${typeId}_${elemName}`,
@@ -1220,26 +1219,26 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1220
1219
  }
1221
1220
  }
1222
1221
  else {
1223
- // FUTURE: expose scalar type definition as well
1222
+ // FUTURE: expose scalar type definition as well
1224
1223
  }
1225
1224
  }
1226
1225
  if(typeClone) {
1227
- // register the type clone at the proxy
1228
- // Reminder: Each proxy receives a full set of type clones, even if the types are shared
1229
- // (no scattered type clone caching). registerProxy() checks if a clone needs to be added to
1230
- // csn.definitions.
1226
+ // register the type clone at the proxy
1227
+ // Reminder: Each proxy receives a full set of type clones, even if the types are shared
1228
+ // (no scattered type clone caching). registerProxy() checks if a clone needs to be added to
1229
+ // csn.definitions.
1231
1230
  proxy.$exposedTypes[typeClone.name] = typeClone;
1232
1231
 
1233
- // set the node's new type name
1232
+ // set the node's new type name
1234
1233
  node.type = typeClone.name;
1235
- // the key path generator must use the type clone directly, because it can't resolve
1236
- // the type clone in the CSN (its name is the final name and not the definition name).
1234
+ // the key path generator must use the type clone directly, because it can't resolve
1235
+ // the type clone in the CSN (its name is the final name and not the definition name).
1237
1236
  setProp(node, '_type', typeClone);
1238
- // Hack alert:
1239
- // beta feature 'subElemRedirections' (now the default in v2) adds elements to the node by
1240
- // default, without we must do it to get the primary key tuple calculation correct.
1241
- // Remember: node.type is the service local type name (not prepended by the service name),
1242
- // so it can't be resolved in definitions later on
1237
+ // Hack alert:
1238
+ // beta feature 'subElemRedirections' (now the default in v2) adds elements to the node by
1239
+ // default, without we must do it to get the primary key tuple calculation correct.
1240
+ // Remember: node.type is the service local type name (not prepended by the service name),
1241
+ // so it can't be resolved in definitions later on
1243
1242
  if(typeClone.elements)
1244
1243
  node.elements = typeClone.elements;
1245
1244
  }
@@ -1263,7 +1262,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1263
1262
  Object.keys(elem).forEach(prop => type.elements[elemName][prop] = elem[prop])
1264
1263
  }
1265
1264
  else if(elem.keys && !elem.on) {
1266
- // a primary key can never be an unmanaged association
1265
+ // a primary key can never be an unmanaged association
1267
1266
  type.elements[elemName] = createProxyOrSchemaRefForManagedAssoc(elem);
1268
1267
  }
1269
1268
  if(forceToNotNull)
@@ -1436,7 +1435,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1436
1435
  schemaNames.push(schemaName);
1437
1436
  }
1438
1437
  });
1439
- /** @type {object} */
1440
1438
  const alreadyRegistered = csn.definitions[fqProxyName];
1441
1439
  if(!alreadyRegistered) {
1442
1440
  csn.definitions[fqProxyName] = proxy;
@@ -1476,7 +1474,6 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1476
1474
  // don't error on duplicate schemas, if it's already present then all is good....
1477
1475
  }
1478
1476
  });
1479
- // sort the global schemaNames array
1480
1477
  schemaNames.sort((a,b) => b.length-a.length);
1481
1478
 
1482
1479
  function finalizeProxyContainments(proxy) {
@@ -1526,12 +1523,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1526
1523
  if(!options.renderForeignKeys || (options.renderForeignKeys && !k.target))
1527
1524
  def.$edmKeyPaths.push(...produceKeyRefPaths(k, kn, [ 'definitions', defName, 'elements', kn ]));
1528
1525
  }
1529
- // In v2/v4 flat, associations are never rendered
1526
+ // In v2/v4 flat, associations are never rendered
1530
1527
  else if(!k.target) {
1531
1528
  def.$edmKeyPaths.push([kn]);
1532
1529
  }
1533
1530
  // check toplevel key for spec violations
1534
- checkKeySpecViolations(k, ['definitions', def.name, 'elements', k.name]);
1531
+ checkKeySpecViolations(k, ['definitions', defName, 'elements', k.name]);
1535
1532
  }
1536
1533
  });
1537
1534
  }
@@ -1558,10 +1555,12 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1558
1555
  }
1559
1556
  // OData requires all elements along the path to be nullable: false (that is either key or notNull)
1560
1557
 
1561
- const finalType = csnUtils.getFinalTypeDef(eltCsn.items?.type || eltCsn.type);
1562
- const elements = eltCsn.elements || eltCsn.items?.elements ||
1563
- finalType?.elements || finalType?.items?.elements;
1564
- if(elements) {
1558
+ let elements = eltCsn.elements || eltCsn.items?.elements;
1559
+ if (!elements) {
1560
+ const finalType = csnUtils.getFinalBaseTypeWithProps(eltCsn.items?.type || eltCsn.type);
1561
+ elements = finalType?.elements || finalType?.items?.elements;
1562
+ }
1563
+ if (elements) {
1565
1564
  Object.entries(elements).forEach(([eltName, elt]) => {
1566
1565
  if(!elt.$visited) {
1567
1566
  setProp(elt, '$visited', true);
@@ -1616,9 +1615,15 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1616
1615
  message('odata-spec-violation-key-null', location,
1617
1616
  {name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
1618
1617
  }
1619
- // many
1620
- let type = elt.items || elt.type && !isBuiltinType(elt.type) && csnUtils.getFinalTypeDef(elt.type).items;
1618
+ // many is either directly on elements or on the type
1619
+ // due to added proxy types it might be that the type can't be found in definitions
1620
+ let type = elt.items ||
1621
+ (elt.type &&
1622
+ !isBuiltinType(elt.type) &&
1623
+ csn.definitions[elt.type] &&
1624
+ csnUtils.getFinalBaseTypeWithProps(elt.type).items);
1621
1625
  if(type) {
1626
+ // many primary key can be induced by a many parameter of a view
1622
1627
  message('odata-spec-violation-key-array', location,
1623
1628
  {name: pathSegment, '#': pathSegment ? 'std' : 'scalar'});
1624
1629
  }
@@ -1904,13 +1909,13 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1904
1909
  if(rootContainer.kind !== 'entity' || rootContainer.$proxy)
1905
1910
  return;
1906
1911
 
1907
- const isRecursiveContainment =
1908
- !!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
1912
+ const isRecursiveContainment =
1913
+ !!(rootContainer.$containerNames && rootContainer.$containeeAssociations &&
1909
1914
  rootContainer.$containerNames.length === 1 &&
1910
1915
  rootContainer.$containeeAssociations.some(entry => rootContainer.$containerNames.includes(entry.assoc.target)));
1911
1916
 
1912
1917
  // Root nodes are not contained
1913
- const isRootNode =
1918
+ const isRootNode =
1914
1919
  !!(!rootContainer.$containerNames ||
1915
1920
  rootContainer.$containerNames && rootContainer.$containerNames.length === 0);
1916
1921
 
@@ -1921,7 +1926,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1921
1926
  addContainmentAnnotationsRecursively([], rootContainer);
1922
1927
  if(rootRestrictions.length)
1923
1928
  rootContainer[NavResAnno] = rootRestrictions;
1924
-
1929
+
1925
1930
  function addContainmentAnnotationsRecursively(prefix, container) {
1926
1931
  if(container.$containeeAssociations) {
1927
1932
  // copy or create container restrictions, don't modify original
@@ -1951,9 +1956,9 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
1951
1956
 
1952
1957
  if(isMyServiceRequested(containee.name) || containee.$proxy) {
1953
1958
  const localAssocPath = path.join('.');
1954
- let navPropEntry;
1955
- const hasEntry = !!(navPropEntry = localRestrictions.find(p =>
1956
- p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.')));
1959
+ let navPropEntry= localRestrictions.find(p =>
1960
+ p.NavigationProperty && p.NavigationProperty['='] === prefix.concat(localAssocPath).join('.'));
1961
+ const hasEntry = !!navPropEntry;
1957
1962
 
1958
1963
  if(!hasEntry) {
1959
1964
  navPropEntry = { NavigationProperty: { '=': prefix.concat(localAssocPath).join('.') } };
@@ -2008,13 +2013,11 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
2008
2013
  }
2009
2014
 
2010
2015
  function iterateParams(def, loc) {
2011
- let lastOptPos = 0;
2012
- let i = 0;
2013
- for(const pn in def.params) {
2014
- const p = def.params[pn];
2016
+ let optPns = [];
2017
+ def.params && Object.values(def.params).forEach(p => {
2015
2018
  // user assigned annotation, don't touch it
2016
2019
  if(Object.keys(p).some(a => a.startsWith('@Core.OptionalParameter') && p[a] !== null)) {
2017
- lastOptPos = i;
2020
+ optPns.push(p);
2018
2021
  }
2019
2022
  // default value automatically makes param optional
2020
2023
  else if(p.default) {
@@ -2022,19 +2025,24 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
2022
2025
  edmUtils.assignAnnotation(p, '@Core.OptionalParameter.DefaultValue', p.default.val);
2023
2026
  else
2024
2027
  edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
2025
- lastOptPos = i;
2028
+ optPns.push(p);
2026
2029
  }
2027
2030
  // if no default is available, nullable makes param optional
2028
2031
  else if(!p.notNull) {
2029
2032
  edmUtils.assignAnnotation(p, '@Core.OptionalParameter.$Type', '');
2030
- lastOptPos = i;
2033
+ optPns.push(p);
2034
+
2031
2035
  }
2032
- // check if mandatory param follows optional param
2033
- else if(lastOptPos < i) {
2034
- warning('odata-parameter-order', loc.concat(pn));
2036
+ else {
2037
+ // this is a mandatory parameter, warn about all previously collected optional parameters
2038
+ optPns.forEach(p => {
2039
+ const type = p.items?.type || p.type;
2040
+ if(type !== special$self)
2041
+ warning('odata-parameter-order', loc.concat(p.name));
2042
+ });
2043
+ optPns = [];
2035
2044
  }
2036
- i++;
2037
- }
2045
+ });
2038
2046
  }
2039
2047
  }
2040
2048
 
@@ -2043,7 +2051,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
2043
2051
  // Helper section starts here
2044
2052
  //
2045
2053
 
2046
-
2054
+
2047
2055
  function mapCdsToEdmProp(obj) {
2048
2056
  if (obj.type && isBuiltinType(obj.type) && !obj.target && !obj.targetAspect) {
2049
2057
  let edmType = edmUtils.mapCdsToEdmType(obj, messageFunctions, _options.odataVersion === 'v2', obj['@Core.MediaType']);
@@ -2,6 +2,7 @@
2
2
  const { setProp } = require('../base/model');
3
3
  const { isBuiltinType, isEdmPropertyRendered, applyTransformations, cloneAnnotationValue } = require('../model/csnUtils');
4
4
  const { escapeString, hasControlCharacters, hasUnpairedUnicodeSurrogate } = require('../render/utils/stringEscapes');
5
+ const {CompilerAssertion} = require('../base/error');
5
6
 
6
7
  /* eslint max-statements-per-line:off */
7
8
  function validateOptions(_options)
@@ -24,9 +25,6 @@ function validateOptions(_options)
24
25
  options.isStructFormat = options.odataFormat && options.odataFormat === 'structured';
25
26
  options.isFlatFormat = !options.isStructFormat;
26
27
 
27
- if(options.v.filter(v=>v).length !== 1)
28
- throw Error(`Please debug me: EDM V2:${v2}, V4:${v4}`);
29
-
30
28
  options.isV2 = () => v2;
31
29
  options.isV4 = () => v4;
32
30
 
@@ -107,9 +105,6 @@ function isDerivedType(artifact) {
107
105
  }
108
106
 
109
107
  function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions) {
110
- if(!assocCsn._constraints)
111
- throw Error('Please debug me: need _constraints');
112
-
113
108
  const { info, warning } = messageFunctions;
114
109
 
115
110
  if(assocCsn.on)
@@ -170,7 +165,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
170
165
  key id : Integer;
171
166
  toMe: association to E on toMe.id = $self; };
172
167
  */
173
- throw Error('Backlink association element is not an association or composition: "' + originAssocCsn.name);
168
+ throw new CompilerAssertion('Backlink association element is not an association or composition: "' + originAssocCsn.name);
174
169
  }
175
170
  }
176
171
  else
@@ -255,11 +250,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
255
250
 
256
251
  function finalizeReferentialConstraints(csn, assocCsn, options, info)
257
252
  {
258
- if(!assocCsn._constraints)
259
- throw Error('Please debug me: need _constraints');
260
-
261
- if(assocCsn.on)
262
- {
253
+ if (assocCsn.on) {
263
254
  /* example for originalTarget:
264
255
  entity E (with parameters) {
265
256
  ... keys and all the stuff ...
@@ -439,7 +430,7 @@ function determineMultiplicity(csn)
439
430
  1 => 0..1 // Association
440
431
  1 => 1 // Composition
441
432
  n => '*'
442
- * => *
433
+ * => '*'
443
434
 
444
435
  => TGT Cardinality
445
436
  CDS => EDM
@@ -621,7 +612,7 @@ function addTypeFacets(node, csn)
621
612
  */
622
613
  function isODataSimpleIdentifier(identifier){
623
614
  // this regular expression reflects the specifiation from above
624
- const regex = /^[\p{Letter}\p{Nl}_]{1}[_\p{Letter}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]{0,127}$/gu
615
+ const regex = /^[\p{Letter}\p{Nl}_][_\p{Letter}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]{0,127}$/gu
625
616
  return identifier && identifier.match(regex);
626
617
  }
627
618
 
@@ -776,7 +767,7 @@ function mergeIntoNavPropEntry(annoPrefix, navPropEntry, prefix, props) {
776
767
  // don't overwrite existing restrictions
777
768
  const prop = annoPrefix.split('.')[1];
778
769
  if(!navPropEntry[prop]) {
779
- // if dictionary has entries, add them to navPropEnty
770
+ // if dictionary has entries, add them to navPropEntry
780
771
  if(Object.keys(o).length) {
781
772
  // ReadRestrictions may have sub type ReadByKeyRestrictions { Description, LongDescription }
782
773
  // chop annotations into dictionaries