@sap/cds-compiler 2.15.8 → 3.1.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 (127) hide show
  1. package/CHANGELOG.md +102 -1590
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +61 -46
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  6. package/doc/CHANGELOG_BETA.md +26 -5
  7. package/doc/CHANGELOG_DEPRECATED.md +55 -1
  8. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  9. package/doc/Versioning.md +20 -1
  10. package/lib/api/.eslintrc.json +2 -2
  11. package/lib/api/main.js +282 -156
  12. package/lib/api/options.js +17 -88
  13. package/lib/api/validate.js +6 -10
  14. package/lib/base/keywords.js +280 -110
  15. package/lib/base/message-registry.js +85 -25
  16. package/lib/base/messages.js +119 -89
  17. package/lib/base/model.js +46 -2
  18. package/lib/base/optionProcessorHelper.js +53 -21
  19. package/lib/checks/actionsFunctions.js +15 -12
  20. package/lib/checks/annotationsOData.js +1 -1
  21. package/lib/checks/cdsPersistence.js +1 -0
  22. package/lib/checks/elements.js +6 -6
  23. package/lib/checks/invalidTarget.js +1 -1
  24. package/lib/checks/nonexpandableStructured.js +1 -1
  25. package/lib/checks/queryNoDbArtifacts.js +2 -1
  26. package/lib/checks/selectItems.js +101 -15
  27. package/lib/checks/types.js +7 -8
  28. package/lib/checks/utils.js +2 -2
  29. package/lib/checks/validator.js +3 -3
  30. package/lib/compiler/assert-consistency.js +78 -21
  31. package/lib/compiler/base.js +6 -4
  32. package/lib/compiler/builtins.js +177 -10
  33. package/lib/compiler/checks.js +1 -1
  34. package/lib/compiler/define.js +28 -23
  35. package/lib/compiler/extend.js +75 -18
  36. package/lib/compiler/finalize-parse-cdl.js +25 -18
  37. package/lib/compiler/index.js +27 -11
  38. package/lib/compiler/moduleLayers.js +7 -0
  39. package/lib/compiler/populate.js +26 -39
  40. package/lib/compiler/propagator.js +12 -7
  41. package/lib/compiler/resolve.js +207 -236
  42. package/lib/compiler/shared.js +100 -93
  43. package/lib/compiler/tweak-assocs.js +13 -20
  44. package/lib/compiler/utils.js +20 -6
  45. package/lib/edm/annotations/preprocessAnnotations.js +12 -13
  46. package/lib/edm/csn2edm.js +35 -37
  47. package/lib/edm/edm.js +22 -13
  48. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  49. package/lib/edm/edmInboundChecks.js +85 -0
  50. package/lib/edm/edmPreprocessor.js +338 -689
  51. package/lib/edm/edmUtils.js +97 -67
  52. package/lib/gen/Dictionary.json +29 -9
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +8 -31
  55. package/lib/gen/language.tokens +105 -114
  56. package/lib/gen/languageLexer.interp +1 -34
  57. package/lib/gen/languageLexer.js +892 -1007
  58. package/lib/gen/languageLexer.tokens +95 -106
  59. package/lib/gen/languageParser.js +20629 -22474
  60. package/lib/inspect/.eslintrc.json +4 -0
  61. package/lib/inspect/index.js +14 -0
  62. package/lib/inspect/inspectModelStatistics.js +81 -0
  63. package/lib/inspect/inspectPropagation.js +189 -0
  64. package/lib/inspect/inspectUtils.js +44 -0
  65. package/lib/json/from-csn.js +74 -69
  66. package/lib/json/to-csn.js +17 -14
  67. package/lib/language/antlrParser.js +2 -2
  68. package/lib/language/docCommentParser.js +61 -38
  69. package/lib/language/errorStrategy.js +52 -40
  70. package/lib/language/genericAntlrParser.js +424 -292
  71. package/lib/language/language.g4 +604 -687
  72. package/lib/language/multiLineStringParser.js +14 -42
  73. package/lib/language/textUtils.js +44 -0
  74. package/lib/main.d.ts +28 -42
  75. package/lib/main.js +104 -81
  76. package/lib/model/api.js +1 -1
  77. package/lib/model/csnRefs.js +57 -30
  78. package/lib/model/csnUtils.js +189 -287
  79. package/lib/model/revealInternalProperties.js +32 -10
  80. package/lib/model/sortViews.js +32 -31
  81. package/lib/modelCompare/compare.js +3 -0
  82. package/lib/optionProcessor.js +91 -57
  83. package/lib/render/.eslintrc.json +1 -1
  84. package/lib/render/DuplicateChecker.js +4 -7
  85. package/lib/render/manageConstraints.js +70 -2
  86. package/lib/render/toCdl.js +387 -367
  87. package/lib/render/toHdbcds.js +20 -16
  88. package/lib/render/toRename.js +44 -22
  89. package/lib/render/toSql.js +81 -59
  90. package/lib/render/utils/common.js +16 -3
  91. package/lib/render/utils/sql.js +20 -19
  92. package/lib/sql-identifier.js +6 -0
  93. package/lib/transform/db/.eslintrc.json +3 -2
  94. package/lib/transform/db/associations.js +43 -35
  95. package/lib/transform/db/cdsPersistence.js +5 -16
  96. package/lib/transform/db/constraints.js +1 -1
  97. package/lib/transform/db/expansion.js +7 -6
  98. package/lib/transform/db/flattening.js +16 -18
  99. package/lib/transform/db/transformExists.js +7 -5
  100. package/lib/transform/db/views.js +3 -3
  101. package/lib/transform/draft/.eslintrc.json +2 -2
  102. package/lib/transform/draft/db.js +6 -6
  103. package/lib/transform/draft/odata.js +6 -7
  104. package/lib/transform/forHanaNew.js +30 -24
  105. package/lib/transform/forOdataNew.js +14 -16
  106. package/lib/transform/localized.js +35 -25
  107. package/lib/transform/odata/toFinalBaseType.js +10 -10
  108. package/lib/transform/odata/typesExposure.js +17 -8
  109. package/lib/transform/odata/utils.js +1 -38
  110. package/lib/transform/transformUtilsNew.js +63 -77
  111. package/lib/transform/translateAssocsToJoins.js +2 -2
  112. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  113. package/lib/transform/universalCsn/coreComputed.js +11 -6
  114. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  115. package/lib/utils/file.js +31 -21
  116. package/lib/utils/moduleResolve.js +0 -1
  117. package/lib/utils/timetrace.js +20 -21
  118. package/package.json +34 -4
  119. package/share/messages/syntax-expected-integer.md +9 -8
  120. package/doc/ApiMigration.md +0 -237
  121. package/doc/CommandLineMigration.md +0 -58
  122. package/doc/ErrorMessages.md +0 -175
  123. package/doc/FioriAnnotations.md +0 -94
  124. package/doc/ODataTransformation.md +0 -273
  125. package/lib/backends.js +0 -529
  126. package/lib/checks/unknownMagic.js +0 -41
  127. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -41,9 +41,7 @@ function forEachDefinition(csn, cb) {
41
41
  * in HANA CDS style, used by 'toHana', toSql' and 'toRename'.
42
42
  * The behavior is controlled by the following options:
43
43
  * options = {
44
- * forHana.names // See the behavior of 'names' in toHana, toSql and toRename
45
- * forHana.alwaysResolveDerivedTypes // Always resolve derived type chains (by default, this is only
46
- * // done for 'quoted' names). FIXME: Should always be done in general.
44
+ * sqlMapping // See the behavior of 'sqlMapping' in toHana, toSql and toRename
47
45
  * }
48
46
  * The result model will always have 'options.forHana' set, to indicate that these transformations have happened.
49
47
  * The following transformations are made:
@@ -69,9 +67,9 @@ function forEachDefinition(csn, cb) {
69
67
  * - (110) Actions and functions (bound or unbound) are ignored.
70
68
  * - (120) (a) Services become contexts.
71
69
  * - (130) (not for to.hdbcds with hdbcds names): Elements having structured types are flattened into
72
- * multiple elements (using '_' or '.' as name separator, depending on 'forHana.names').
70
+ * multiple elements (using '_' or '.' as name separator, depending on 'sqlMapping').
73
71
  * - (140) (not for to.hdbcds with hdbcds names): Managed associations get explicit ON-conditions, with
74
- * generated foreign key elements (also using '_' or '.' as name separator, depending on 'forHana.names').
72
+ * generated foreign key elements (also using '_' or '.' as name separator, depending on 'sqlMapping').
75
73
  * - (150) (a) Elements from inherited (included) entities are copied into the receiving entity
76
74
  * (b) The 'include' property is removed from entities.
77
75
  * - (160) Projections become views, with MIXINs for association elements (adding $projection where
@@ -94,7 +92,7 @@ function forEachDefinition(csn, cb) {
94
92
  * (d) Managed association entries in ORDER BY
95
93
  * - (240) All artifacts (a), elements, foreign keys, parameters (b) that have a DB representation are annotated
96
94
  * with their database name (as '@cds.persistence.name') according to the naming convention chosen
97
- * in 'options.forHana.names'.
95
+ * in 'options.sqlMapping'.
98
96
  * - (250) Remove name space definitions again (only in forHanaNew). Maybe we can omit inserting namespace definitions
99
97
  * completely (TODO)
100
98
  *
@@ -108,17 +106,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
108
106
  /** @type {CSN.Model} */
109
107
  let csn = cloneCsnNonDict(inputModel, options);
110
108
 
111
-
112
-
113
109
  checkCSNVersion(csn, options);
114
110
 
115
- const pathDelimiter = (options.forHana.names === 'hdbcds') ? '.' : '_';
111
+ const pathDelimiter = (options.sqlMapping === 'hdbcds') ? '.' : '_';
116
112
 
117
113
  let message, error, warning, info; // message functions
118
114
  /** @type {() => void} */
119
115
  let throwWithAnyError;
120
116
  let artifactRef, inspectRef, effectiveType, get$combined,
121
- getFinalBaseType, // csnUtils (csnRefs)
122
117
  addDefaultTypeFacets, expandStructsInExpression; // transformUtils
123
118
 
124
119
  bindCsnReference();
@@ -130,14 +125,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
130
125
  bindCsnReference();
131
126
  }
132
127
 
133
- const dialect = options.forHana && options.forHana.dialect || options.toSql && options.toSql.dialect;
128
+ const dialect = options.sqlDialect;
134
129
  const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
135
130
  if (!doA2J)
136
131
  forEachDefinition(csn, handleMixinOnConditions);
137
132
 
138
133
  // Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
139
134
  const cleanup = validate.forHana(csn, {
140
- message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, getFinalBaseType, isAspect
135
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, isAspect
141
136
  });
142
137
 
143
138
  // Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
@@ -222,6 +217,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
222
217
  flattenStructuredElement,
223
218
  flattenStructStepsInRef,
224
219
  isAssociationOperand, isDollarSelfOrProjectionOperand,
220
+ csnUtils,
225
221
  } = transformUtils.getTransformers(csn, options, pathDelimiter);
226
222
 
227
223
  const {
@@ -229,7 +225,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
229
225
  isAssocOrComposition,
230
226
  addStringAnnotationTo,
231
227
  cloneWithTransformations,
232
- } = getUtils(csn);
228
+ } = csnUtils;
233
229
 
234
230
  // (000) Rename primitive types, make UUID a String
235
231
  transformCsn(csn, {
@@ -269,10 +265,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
269
265
  // To be done after handleAssociations, since then the foreign keys of the managed assocs
270
266
  // are part of the elements
271
267
  if (doA2J)
272
- forEachDefinition(csn, associations.getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter));
268
+ forEachDefinition(csn, associations.getFKAccessFinalizer(csn, pathDelimiter));
273
269
 
274
270
  // Create convenience views for localized entities/views.
275
- // To be done after getManagedAssocStepsInOnConditionFinalizer because associations are
271
+ // To be done after getFKAccessFinalizer because associations are
276
272
  // handled and before handleDBChecks which removes the localized attribute.
277
273
  // Association elements of localized convenience views do not have hidden properties
278
274
  // like $managed set, so we cannot do this earlier on.
@@ -396,6 +392,15 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
396
392
  setProp(SET, 'elements', query.SELECT.elements);
397
393
  }
398
394
  }
395
+ },
396
+
397
+ }
398
+
399
+ if(options.sqlDialect === 'postgres') {
400
+ killers.length = (parent) => {
401
+ if (parent.type === 'cds.Binary' || parent.type === 'cds.hana.BINARY') {
402
+ delete parent.length;
403
+ }
399
404
  }
400
405
  }
401
406
 
@@ -409,8 +414,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
409
414
 
410
415
  function bindCsnReference(){
411
416
  ({ error, warning, info, message, throwWithAnyError } = makeMessageFunction(csn, options, moduleName));
412
- ({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
413
417
  ({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
418
+ // TODO: Can we use csnUtils of the call above (transformUtils.getTransformers)?
419
+ ({ artifactRef, inspectRef, effectiveType, get$combined } = getUtils(csn));
414
420
  }
415
421
 
416
422
  function bindCsnReferenceOnly(){
@@ -509,7 +515,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
509
515
  function recursivelyApplyCommon(artifact, artifactName) {
510
516
  if (!artifact._ignore) {
511
517
  if (artifact.kind !== 'service' && artifact.kind !== 'context')
512
- addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.forHana.names, csn), artifact);
518
+ addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
513
519
 
514
520
  forEachMemberRecursively(artifact, (member, memberName, property, path) => {
515
521
  transformCommon(member, memberName, path);
@@ -517,7 +523,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
517
523
  // Virtual elements in entities and types are not annotated, as they have no DB representation.
518
524
  // In views they are, as we generate a null expression for them (null as <colname>)
519
525
  if ((!member.virtual || artifact.query))
520
- addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.forHana.names), member);
526
+ addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
521
527
  }, [ 'definitions', artifactName ]);
522
528
  }
523
529
  }
@@ -543,7 +549,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
543
549
  function transformSelfInBacklinks(artifact, artifactName, dummy, path) {
544
550
  // Fixme: For toHana mixins must be transformed, for toSql -d hana
545
551
  // mixin elements must be transformed, why can't toSql also use mixins?
546
- if(artifact.kind === 'entity' || artifact.query || (options.toHana && options.toHana.names === 'hdbcds' && artifact.kind == 'type'))
552
+ if(artifact.kind === 'entity' || artifact.query || (options.forHana && options.sqlMapping === 'hdbcds' && artifact.kind === 'type'))
547
553
  doit(artifact.elements, path.concat([ 'elements' ]));
548
554
  if (artifact.query && artifact.query.SELECT && artifact.query.SELECT.mixin)
549
555
  doit(artifact.query.SELECT.mixin, path.concat([ 'query', 'SELECT', 'mixin' ]));
@@ -574,7 +580,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
574
580
  // For HANA: Report an error on
575
581
  // - view with parameters that has an element of type association/composition
576
582
  // - association that points to entity with parameters
577
- if (options.forHana.dialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
583
+ if (options.sqlDialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
578
584
  if (artifact.params) {
579
585
  // HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
580
586
  // SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
@@ -617,7 +623,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
617
623
  sql: 'Table-like entities with parameters are not supported for conversion to SQL',
618
624
  });
619
625
  }
620
- else if (options.forHana.dialect === 'sqlite') { // view with params
626
+ else if (options.sqlDialect === 'sqlite') { // view with params
621
627
  // Allow with plain
622
628
  error(null, [ 'definitions', artifactName ], `SQLite does not support entities with parameters`);
623
629
  }
@@ -626,8 +632,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
626
632
  if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
627
633
  warning(null, [ 'definitions', artifactName, 'params', pname ], `Expecting regular SQL-Identifier`);
628
634
  }
629
- else if (options.forHana.names !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
630
- warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.forHana.names },
635
+ else if (options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
636
+ warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.sqlMapping },
631
637
  'Expecting parameter to be uppercase in naming mode $(NAME)');
632
638
  }
633
639
  }
@@ -1048,7 +1054,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1048
1054
  * @returns {boolean}
1049
1055
  */
1050
1056
  function isMaxParameterLengthRestricted(type) {
1051
- return !(options.toSql && type === 'cds.String' && (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain'));
1057
+ return !(options.toSql && type === 'cds.String' && (options.sqlDialect === 'sqlite' || options.sqlDialect === 'plain'));
1052
1058
  }
1053
1059
 
1054
1060
  /**
@@ -3,8 +3,7 @@
3
3
  const { makeMessageFunction } = require('../base/messages');
4
4
  const { isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
5
5
  const transformUtils = require('./transformUtilsNew');
6
- const { getUtils,
7
- cloneCsnNonDict,
6
+ const { cloneCsnNonDict,
8
7
  forEachDefinition,
9
8
  forEachMemberRecursively,
10
9
  applyTransformationsOnNonDictionary,
@@ -86,10 +85,10 @@ function transform4odataWithCsn(inputModel, options) {
86
85
  extractValidFromToKeyElement,
87
86
  checkAssignment, checkMultipleAssignments,
88
87
  recurseElements, setAnnotation, renameAnnotation,
89
- expandStructsInExpression
88
+ expandStructsInExpression,
89
+ csnUtils,
90
90
  } = transformers;
91
91
 
92
- const csnUtils = getUtils(csn);
93
92
  const {
94
93
  getCsnDef,
95
94
  getServiceName,
@@ -98,11 +97,10 @@ function transform4odataWithCsn(inputModel, options) {
98
97
  inspectRef,
99
98
  artifactRef,
100
99
  effectiveType,
101
- getFinalBaseType,
102
100
  } = csnUtils;
103
101
 
104
102
  // are we working with structured OData or not
105
- const structuredOData = options.toOdata.odataFormat === 'structured' && options.toOdata.version === 'v4';
103
+ const structuredOData = options.odataFormat === 'structured' && options.odataVersion === 'v4';
106
104
 
107
105
  // collect all declared non-abstract services from the model
108
106
  // use the array when there is a need to identify if an artifact is in a service or not
@@ -115,17 +113,17 @@ function transform4odataWithCsn(inputModel, options) {
115
113
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
116
114
  enrichUniversalCsn(csn, options);
117
115
 
118
- const keepLocalizedViews = isDeprecatedEnabled(options, 'createLocalizedViews');
116
+ const keepLocalizedViews = isDeprecatedEnabled(options, '_createLocalizedViews');
119
117
 
120
118
  function acceptLocalizedView(_name, parent) {
121
119
  csn.definitions[parent].$localized = true;
122
120
  return keepLocalizedViews && !isExternalServiceMember(undefined, parent);
123
121
  }
124
122
 
125
- addLocalizationViews(csn, options, acceptLocalizedView);
123
+ addLocalizationViews(csn, options, { acceptLocalizedView, ignoreUnknownExtensions: true });
126
124
 
127
125
  const cleanup = validate.forOdata(csn, {
128
- message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
126
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, isAspect, isExternalServiceMember
129
127
  });
130
128
 
131
129
 
@@ -180,7 +178,7 @@ function transform4odataWithCsn(inputModel, options) {
180
178
  // To be done after handleManagedAssociationsAndCreateForeignKeys,
181
179
  // since then the foreign keys of the managed assocs are part of the elements
182
180
  if(!structuredOData)
183
- forEachDefinition(csn, associations.getManagedAssocStepsInOnConditionFinalizer(csn, '_'));
181
+ forEachDefinition(csn, associations.getFKAccessFinalizer(csn, '_'));
184
182
 
185
183
  // structure flattener reports errors, further processing is not safe -> throw exception in case of errors
186
184
  throwWithAnyError();
@@ -214,16 +212,16 @@ function transform4odataWithCsn(inputModel, options) {
214
212
 
215
213
  // Annotate artifacts with their DB names if requested.
216
214
  // Skip artifacts that have no DB equivalent anyway
217
- if (options.toOdata.names && !(def.kind in skipPersNameKinds))
218
- def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.toOdata.names, csn);
215
+ if (options.sqlMapping && !(def.kind in skipPersNameKinds))
216
+ def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
219
217
 
220
218
  forEachMemberRecursively(def, (member, memberName, propertyName) => {
221
219
  // Annotate elements, foreign keys, parameters, etc. with their DB names if requested
222
220
  // Only these are actually required and don't annotate virtual elements in entities or types
223
221
  // as they have no DB representation (although in views)
224
- if (options.toOdata.names && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
222
+ if (options.sqlMapping && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
225
223
  // If we have a 'preserved dotted name' (i.e. we are a result of flattening), use that for the @cds.persistence.name annotation
226
- member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.toOdata.names);
224
+ member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
227
225
  }
228
226
 
229
227
  // Mark fields with @odata.on.insert/update as @Core.Computed
@@ -326,7 +324,7 @@ function transform4odataWithCsn(inputModel, options) {
326
324
  // but '@Core.Immutable' for everything else.
327
325
  if (!(node['@readonly'] && node['@insertonly'])) {
328
326
  if (name === '@readonly' && node[name] !== null) {
329
- if (node.kind === 'entity') {
327
+ if (node.kind === 'entity' || node.kind === 'aspect') {
330
328
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
331
329
  setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
332
330
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -336,7 +334,7 @@ function transform4odataWithCsn(inputModel, options) {
336
334
  }
337
335
  // @insertonly is effective on entities/queries only
338
336
  else if (name === '@insertonly' && node[name] !== null) {
339
- if (node.kind === 'entity') {
337
+ if (node.kind === 'entity' || node.kind === 'aspect') {
340
338
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
341
339
  setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
342
340
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const { makeMessageFunction } = require('../base/messages');
4
- const { setProp } = require('../base/model');
4
+ const { setProp, isDeprecatedEnabled} = require('../base/model');
5
5
  const { hasErrors } = require('../base/messages');
6
- const { cloneCsnDictionary, applyDefinitionAnnotationsFromExtensions} = require('../model/csnUtils');
6
+ const { forEachKey } = require('../utils/objectUtils');
7
7
  const { cleanSymbols } = require('../base/cleanSymbols.js');
8
8
  const {
9
+ cloneCsnDictionary,
9
10
  cloneCsnNonDict,
11
+ applyAnnotationsFromExtensions,
10
12
  forEachDefinition,
11
13
  forEachGeneric,
12
14
  forAllQueries,
@@ -67,9 +69,9 @@ const _targetFor = Symbol('_targetFor');
67
69
  * @param {CSN.Options} options
68
70
  * @param {boolean} useJoins If true, rewrite the "localized" association to a
69
71
  * join in direct convenience views.
70
- * @param {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
72
+ * @param {object} config
71
73
  */
72
- function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = null) {
74
+ function _addLocalizationViews(csn, options, useJoins, config) {
73
75
  // Don't try to create convenience views with errors.
74
76
  if (hasErrors(options.messages))
75
77
  return csn;
@@ -78,6 +80,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
78
80
  if (hasExistingLocalizationViews(csn, options, messageFunctions))
79
81
  return csn;
80
82
 
83
+ const { acceptLocalizedView, ignoreUnknownExtensions } = config;
81
84
  const noCoalesce = (options.localizedLanguageFallback === 'none' ||
82
85
  options.localizedWithoutCoalesce);
83
86
 
@@ -87,9 +90,13 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
87
90
  forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
88
91
 
89
92
  // In case that the user tried to annotate `localized.*` artifacts, apply them.
90
- applyDefinitionAnnotationsFromExtensions(csn, {
91
- overwrite: true,
92
- filter: (name) => name.startsWith('localized.')
93
+ applyAnnotationsFromExtensions(csn, {
94
+ override: true,
95
+ filter: (name) => name.startsWith('localized.'),
96
+ notFound(name, index) {
97
+ if (!ignoreUnknownExtensions)
98
+ messageFunctions.message('anno-undefined-art', [ 'extensions', index ], { name })
99
+ },
93
100
  });
94
101
 
95
102
  sortCsnDefinitionsForTests(csn, options);
@@ -147,7 +154,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
147
154
  else
148
155
  view = createLocalizedViewForEntity(art, artName, textElements);
149
156
 
150
- copyPersistenceAnnotations(view, art);
157
+ copyPersistenceAnnotations(view, art, options);
151
158
  csn.definitions[viewName] = view;
152
159
  }
153
160
 
@@ -605,10 +612,10 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
605
612
  *
606
613
  * @param {CSN.Model} csn
607
614
  * @param {CSN.Options} options
608
- * @param [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
615
+ * @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
609
616
  */
610
- function addLocalizationViews(csn, options, acceptLocalizedView = null) {
611
- return _addLocalizationViews(csn, options, false, acceptLocalizedView);
617
+ function addLocalizationViews(csn, options, config = {}) {
618
+ return _addLocalizationViews(csn, options, false, config);
612
619
  }
613
620
 
614
621
  /**
@@ -618,10 +625,10 @@ function addLocalizationViews(csn, options, acceptLocalizedView = null) {
618
625
  *
619
626
  * @param {CSN.Model} csn
620
627
  * @param {CSN.Options} options
621
- * @param [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
628
+ * @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
622
629
  */
623
- function addLocalizationViewsWithJoins(csn, options, acceptLocalizedView = null) {
624
- return _addLocalizationViews(csn, options, true, acceptLocalizedView);
630
+ function addLocalizationViewsWithJoins(csn, options, config = {}) {
631
+ return _addLocalizationViews(csn, options, true, config);
625
632
  }
626
633
 
627
634
  /**
@@ -667,21 +674,24 @@ function copyLocation(target, source) {
667
674
  }
668
675
 
669
676
  /**
670
- * Copy some @cds.persistence.* annotations from the source to
671
- * the target. Ignores existing annotations on the target.
677
+ * Copy @cds.persistence.exists/skip annotations from the source to
678
+ * the target. Ignores existing annotations on the _target_.
672
679
  *
673
680
  * @param {CSN.Artifact} target
674
681
  * @param {CSN.Artifact} source
682
+ * @param {CSN.Options} options
675
683
  */
676
- function copyPersistenceAnnotations(target, source) {
677
- Object.keys(source)
678
- .forEach(anno => {
679
- // Do NOT copy ".exists" at the moment. ".exists" is not propagated
680
- // and this would lead to some localization views referencing not-existing
681
- // "localized.XYZ" views.
682
- if (anno === '@cds.persistence.skip')
683
- target[anno] = source[anno];
684
- });
684
+ function copyPersistenceAnnotations(target, source, options) {
685
+ const doNotCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
686
+ forEachKey(source, anno => {
687
+ // Note:
688
+ // Because `.exists` is copied to the convenience view, it could
689
+ // lead to some localization views referencing non-existing ones.
690
+ // But that is the contract: User says that it already exists!
691
+ // In v2, `.exists` was never copied.
692
+ if (anno === '@cds.persistence.skip' || (!doNotCopyExists && anno === '@cds.persistence.exists'))
693
+ target[anno] = source[anno];
694
+ });
685
695
  }
686
696
 
687
697
  /**
@@ -7,7 +7,7 @@ const {
7
7
  const { isArtifactInSomeService, isArtifactInService } = require('./utils');
8
8
 
9
9
  function expandToFinalBaseType(csn, transformers, csnUtils, services, options, isExternalServiceMember) {
10
- const isV4 = options.toOdata.version === 'v4';
10
+ const isV4 = options.odataVersion === 'v4';
11
11
  forEachDefinition(csn, (def, defName) => {
12
12
  // Unravel derived type chains to final one for elements, actions, action parameters (propagating annotations)
13
13
  forEachMemberRecursively(def, (member) => {
@@ -87,9 +87,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
87
87
  if (node.kind === 'event') return;
88
88
 
89
89
  // elements have precedence over type
90
- if (node.type && (!isBuiltinType(node.type) &&isExpandable(node, defName) || node.kind === 'type')) {
90
+ if (node.type && (!isBuiltinType(node.type) && isExpandable(node, defName) || node.kind === 'type')) {
91
91
  // 1. Get the final type of the node (resolve derived type chain)
92
- const finalType = csnUtils.getFinalBaseType(node.type);
92
+ const finalType = csnUtils.getFinalBaseTypeWithProps(node.type);
93
93
  if (finalType) {
94
94
  // The type replacement depends on whether 'node' is a definition or a member[element].
95
95
  if (node.kind) {
@@ -101,7 +101,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
101
101
  // type A: B; -> {...}
102
102
  // type B: C; -> { ... }
103
103
  // type C { .... };
104
- if (isBuiltinType(finalType)) {
104
+ if (isBuiltinType(finalType.type)) {
105
105
  // use transformUrilsNew::toFinalBaseType for the moment,
106
106
  // as it is collects along the chain of types
107
107
  // attributes that need to be propagated
@@ -129,8 +129,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
129
129
  // type A: B; -> {...}
130
130
  // type B: C; -> { ... }
131
131
  // type C { .... };
132
- if (isBuiltinType(finalType)) {
133
- // use transformUrilsNew::toFinalBaseType for the moment,
132
+ if (isBuiltinType(finalType.type)) {
133
+ // use transformUtilsNew::toFinalBaseType for the moment,
134
134
  // as it is collects along the chain of types
135
135
  // attributes that need to be propagated
136
136
  // enum, length, scale, etc.
@@ -148,8 +148,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
148
148
  // example in actions: 'action act() return Primitive; type Primitive: array of String;'
149
149
  const currService = csnUtils.getServiceName(defName);
150
150
  const finalType = csnUtils.getFinalTypeDef(node.type);
151
- if (finalType.items &&
152
- (isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseType(finalType.items.type))))
151
+ if (finalType.items &&
152
+ (isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseTypeWithProps(finalType.items.type)?.type)))
153
153
  {
154
154
  if (!isArtifactInService(node.type, currService) || !isV4) {
155
155
  node.items = finalType.items;
@@ -186,8 +186,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
186
186
  function isUserDefinedBuiltinFromTheCurrService(node, defName) {
187
187
  // in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
188
188
  // a builtin from the service - do not expand to the final base type
189
- let finalBaseType = csnUtils.getFinalBaseType(node.type);
190
- // if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseType(finalBaseType.items);
189
+ let finalBaseType = csnUtils.getFinalBaseTypeWithProps(node.type).type;
190
+ // if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseTypeWithProps(finalBaseType.items);
191
191
  const currService = csnUtils.getServiceName(defName);
192
192
  return node.type && !node.type.ref
193
193
  && isBuiltinType(finalBaseType) && !csnUtils.isAssocOrComposition(finalBaseType)
@@ -10,11 +10,12 @@ const { setProp } = require('../../base/model');
10
10
  const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
11
11
  const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember } = require('../../model/csnUtils');
12
12
  const { copyAnnotations } = require('../../model/csnUtils');
13
+ const { isBetaEnabled } = require('../../base/model.js');
13
14
 
14
15
  function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
15
16
  const { error } = message;
16
17
  // are we working with OData proxies or cross-service refs
17
- const isMultiSchema = options.toOdata.version === 'v4' && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs);
18
+ const isMultiSchema = options.odataVersion === 'v4' && (options.odataProxies || options.odataXServiceRefs);
18
19
  // collect in this variable all the newly exposed types
19
20
  const schemas = Object.create(null);
20
21
  const exposedTypes = Object.create(null);
@@ -92,11 +93,16 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
92
93
  : getAnonymousTypeNameInMultiSchema(newTypeName, parentName || defName))
93
94
  : `${serviceName}.${newTypeName}`;
94
95
 
95
- // as soon as we leave of the anonymous world,
96
- // we're no longer in a key def => don't set notNull:true on named types
97
- if (!isAnonymous && isKey)
98
- isKey = false;
99
-
96
+ if (!isAnonymous) {
97
+ // as soon as we leave of the anonymous world,
98
+ // we're no longer in a key def => don't set notNull:true on named types
99
+ if(isKey)
100
+ isKey = false;
101
+ // in case this was a named type and if the openess does not match the type definition
102
+ // expose the type as a new one not changing the original definition.
103
+ if((!!node['@open'] !== !!typeDef['@open']) && isBetaEnabled(options, 'odataOpenType'))
104
+ fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
105
+ }
100
106
  // check if that type is already defined
101
107
  let newType = csn.definitions[fullQualifiedNewTypeName];
102
108
  if (newType) {
@@ -111,6 +117,9 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
111
117
  * Treat items.elements as ordinary elements for now.
112
118
  */
113
119
  newType = createNewStructType(elements);
120
+ // if using node enforces open/closed, set it on type
121
+ if(node['@open'] !== undefined)
122
+ newType['@open'] = node['@open']
114
123
  if (node.$location)
115
124
  setProp(newType, '$location', node.$location);
116
125
 
@@ -122,7 +131,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
122
131
  if (node.elements && node.elements[elemName].$location)
123
132
  setProp(newElem, '$location', node.elements[elemName].$location);
124
133
  defName = typeDef.kind === 'type' ? typeName : defName;
125
- exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
134
+ exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
126
135
  getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
127
136
  });
128
137
  copyAnnotations(typeDef, newType);
@@ -151,7 +160,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
151
160
  * 1) If it's an anonymous structured type (items.elements || elements)
152
161
  * 2) If it's a named type resolve to the final type definition and
153
162
  * check if this is a structured type
154
- * Returns an object that indicates
163
+ * Returns an object that indicates
155
164
  * - wether or not the type needs exposure
156
165
  * - the elements dictionary that needs to be cloned
157
166
  * - the typeDef, either the resolved type def or the node itself
@@ -1,38 +1,4 @@
1
- const {
2
- forEachDefinition,
3
- forEachMemberRecursively,
4
- } = require('../../model/csnUtils');
5
-
6
-
7
- // Return true if 'artifact' has an association type
8
- function isAssociation(artifact) {
9
- return (artifact.type === 'cds.Association' || artifact.type === 'Association');
10
- }
11
-
12
- // Return true if 'artifact' has a composition type
13
- function isComposition(artifact) {
14
- return (artifact.type === 'cds.Composition' || artifact.type === 'Composition')
15
- }
16
-
17
- function isAssociationOrComposition(artifact) {
18
- return isAssociation(artifact) || isComposition(artifact);
19
- }
20
-
21
- function isManagedAssociation(artifact) {
22
- return artifact.target !== undefined && artifact.on === undefined;
23
- }
24
-
25
- function forEachManagedAssociation(csn, callback, isExternalServiceMember) {
26
-
27
- forEachDefinition(csn, (def) => {
28
- forEachMemberRecursively(def, (element) => {
29
- if (isAssociationOrComposition(element) && !element.on) {
30
- callback(element)
31
- }
32
- })
33
- }, { skipArtifact: isExternalServiceMember });
34
-
35
- }
1
+ 'use strict';
36
2
 
37
3
  /**
38
4
  * Return the definition name, without the prefixed service name
@@ -87,12 +53,9 @@ function isLocalizedArtifactInService(artName, services) {
87
53
  }
88
54
 
89
55
  module.exports = {
90
- forEachManagedAssociation,
91
56
  defNameWithoutServiceOrContextName,
92
57
  getServiceOfArtifact,
93
58
  isArtifactInService,
94
59
  isArtifactInSomeService,
95
- isAssociationOrComposition,
96
60
  isLocalizedArtifactInService,
97
- isManagedAssociation,
98
61
  }