@sap/cds-compiler 2.13.8 → 3.0.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 +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -296
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { setProp, isBetaEnabled } = require('../base/model');
4
- const { getUtils, cloneCsn,
4
+ const { getUtils, cloneCsnNonDict,
5
5
  forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
6
6
  getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
7
7
  isAspect, walkCsnPath,
@@ -16,7 +16,7 @@ const { rejectManagedAssociationsAndStructuresForHdbcdsNames } = require('../che
16
16
  const { addLocalizationViewsWithJoins, addLocalizationViews } = require('../transform/localized');
17
17
  const { timetrace } = require('../utils/timetrace');
18
18
  const { createReferentialConstraints, assertConstraintIdentifierUniqueness } = require('./db/constraints');
19
- const { createDict } = require('../utils/objectUtils');
19
+ const { createDict, forEach } = require('../utils/objectUtils');
20
20
  const handleExists = require('./db/transformExists');
21
21
  const replaceAssociationsInGroupByOrderBy = require('./db/groupByOrderBy');
22
22
  const _forEachDefinition = require('../model/csnUtils').forEachDefinition;
@@ -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
  *
@@ -106,38 +104,35 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
106
104
  // copy the model as we don't want to change the input model
107
105
  timetrace.start('HANA transformation');
108
106
  /** @type {CSN.Model} */
109
- let csn = cloneCsn(inputModel, options);
110
-
111
-
107
+ let csn = cloneCsnNonDict(inputModel, options);
112
108
 
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
- let throwWithError;
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();
125
120
 
126
- throwWithError(); // reclassify and throw in case of non-configurable errors
121
+ throwWithAnyError(); // reclassify and throw in case of non-configurable errors
127
122
 
128
123
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
129
124
  enrichUniversalCsn(csn, options);
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
@@ -146,13 +141,13 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
146
141
  // Check if structured elements and managed associations are compared in an expression
147
142
  // and expand these structured elements. This tuple expansion allows all other
148
143
  // subsequent procession steps (especially a2j) to see plain paths in expressions.
149
- // If errors are detected, throwWithError() will return from further processing
144
+ // If errors are detected, throwWithAnyError() will return from further processing
150
145
 
151
146
  // If this function is ever undefined, we have a bug in our logic.
152
147
  // @ts-ignore
153
148
  expandStructsInExpression(csn, { drillRef: true });
154
149
 
155
- throwWithError();
150
+ throwWithAnyError();
156
151
 
157
152
  // FIXME: This does something very similar to cloneWithTransformations -> refactor?
158
153
  const transformCsn = transformUtils.transformModel;
@@ -167,7 +162,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
167
162
 
168
163
  if(doA2J) {
169
164
  // Expand a structured thing in: keys, columns, order by, group by
170
- expansion.expandStructureReferences(csn, options, pathDelimiter, {error, info, throwWithError});
165
+ expansion.expandStructureReferences(csn, options, pathDelimiter, {error, info, throwWithAnyError});
171
166
  bindCsnReference();
172
167
  }
173
168
 
@@ -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, {
@@ -353,7 +349,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
353
349
  if(doA2J)
354
350
  flattening.removeLeadingSelf(csn);
355
351
 
356
- throwWithError();
352
+ throwWithAnyError();
357
353
 
358
354
  timetrace.stop();
359
355
 
@@ -381,7 +377,22 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
381
377
  // Set when we turn UUID into String, checked during generateDraftForHana
382
378
  '$renamed': killProp,
383
379
  // Set when we remove .key from temporal things, used in localized.js
384
- '$key': killProp
380
+ '$key': killProp,
381
+ // We need .elements easily for rendering - otherwise we have to compute it then
382
+ // Does not fit in the "killers" theme - TODO: Find a better place
383
+ SET: (parent, prop, SET) => {
384
+ if(!SET.elements) {
385
+ const stack = [parent];
386
+ while(stack.length > 0) {
387
+ const query = stack.pop();
388
+
389
+ if(query.SET)
390
+ stack.push(query.SET.args[0]);
391
+ else if(query.SELECT)
392
+ setProp(SET, 'elements', query.SELECT.elements);
393
+ }
394
+ }
395
+ }
385
396
  }
386
397
 
387
398
  applyTransformations(csn, killers, [], { skipIgnore: false});
@@ -393,9 +404,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
393
404
  /* ----------------------------------- Functions start here -----------------------------------------------*/
394
405
 
395
406
  function bindCsnReference(){
396
- ({ error, warning, info, message, throwWithError } = makeMessageFunction(csn, options, moduleName));
397
- ({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
407
+ ({ error, warning, info, message, throwWithAnyError } = makeMessageFunction(csn, options, moduleName));
398
408
  ({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
409
+ // TODO: Can we use csnUtils of the call above (transformUtils.getTransformers)?
410
+ ({ artifactRef, inspectRef, effectiveType, get$combined } = getUtils(csn));
399
411
  }
400
412
 
401
413
  function bindCsnReferenceOnly(){
@@ -448,7 +460,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
448
460
  // get$combined also includes elements which are part of "excluding {}"
449
461
  // this shouldn't be an issue here, as such references get rejected
450
462
  const elementsOfQuerySources = get$combined(query);
451
- Object.entries(elementsOfQuerySources).forEach(([id, element]) => {
463
+ forEach(elementsOfQuerySources, (id, element) => {
452
464
  // if the ref points to an element which is not explicitly exposed in the column list,
453
465
  // but through the '*' operator -> replace the $projection / $self with the correct source entity
454
466
  if(id === columnToReplace)
@@ -458,7 +470,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
458
470
 
459
471
  // No implicit CAST in on-condition
460
472
  if(replaceWith && replaceWith.cast) {
461
- const clone = cloneCsn(replaceWith, options);
473
+ const clone = cloneCsnNonDict(replaceWith, options);
462
474
  delete clone.cast;
463
475
  return clone;
464
476
  }
@@ -475,10 +487,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
475
487
  if (!artifact._ignore) {
476
488
  // Do things specific for entities and views (pass 2)
477
489
  if ((artifact.kind === 'entity') && artifact.query) {
478
- forAllQueries(artifact.query, (q, p) => {
479
- transformEntityOrViewPass2(q, artifact, artifactName, p)
480
- replaceAssociationsInGroupByOrderBy(q, options, inspectRef, error, p);
481
- }, [ 'definitions', artifactName, 'query' ]);
490
+ const process = (parent, prop, query, path) => {
491
+ transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop))
492
+ replaceAssociationsInGroupByOrderBy(parent, options, inspectRef, error, path.concat(prop));
493
+ return query;
494
+ }
495
+ applyTransformationsOnNonDictionary(csn.definitions, artifactName, {
496
+ SELECT: process
497
+ }, {}, [ 'definitions']);
482
498
  }
483
499
  }
484
500
  }
@@ -490,7 +506,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
490
506
  function recursivelyApplyCommon(artifact, artifactName) {
491
507
  if (!artifact._ignore) {
492
508
  if (artifact.kind !== 'service' && artifact.kind !== 'context')
493
- addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.forHana.names, csn), artifact);
509
+ addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
494
510
 
495
511
  forEachMemberRecursively(artifact, (member, memberName, property, path) => {
496
512
  transformCommon(member, memberName, path);
@@ -498,7 +514,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
498
514
  // Virtual elements in entities and types are not annotated, as they have no DB representation.
499
515
  // In views they are, as we generate a null expression for them (null as <colname>)
500
516
  if ((!member.virtual || artifact.query))
501
- addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.forHana.names), member);
517
+ addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
502
518
  }, [ 'definitions', artifactName ]);
503
519
  }
504
520
  }
@@ -524,7 +540,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
524
540
  function transformSelfInBacklinks(artifact, artifactName, dummy, path) {
525
541
  // Fixme: For toHana mixins must be transformed, for toSql -d hana
526
542
  // mixin elements must be transformed, why can't toSql also use mixins?
527
- if(artifact.kind === 'entity' || artifact.query || (options.toHana && options.toHana.names === 'hdbcds' && artifact.kind == 'type'))
543
+ if(artifact.kind === 'entity' || artifact.query || (options.forHana && options.sqlMapping === 'hdbcds' && artifact.kind === 'type'))
528
544
  doit(artifact.elements, path.concat([ 'elements' ]));
529
545
  if (artifact.query && artifact.query.SELECT && artifact.query.SELECT.mixin)
530
546
  doit(artifact.query.SELECT.mixin, path.concat([ 'query', 'SELECT', 'mixin' ]));
@@ -555,7 +571,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
555
571
  // For HANA: Report an error on
556
572
  // - view with parameters that has an element of type association/composition
557
573
  // - association that points to entity with parameters
558
- if (options.forHana.dialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
574
+ if (options.sqlDialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
559
575
  if (artifact.params) {
560
576
  // HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
561
577
  // SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
@@ -598,7 +614,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
598
614
  sql: 'Table-like entities with parameters are not supported for conversion to SQL',
599
615
  });
600
616
  }
601
- else if (options.forHana.dialect === 'sqlite') { // view with params
617
+ else if (options.sqlDialect === 'sqlite') { // view with params
602
618
  // Allow with plain
603
619
  error(null, [ 'definitions', artifactName ], `SQLite does not support entities with parameters`);
604
620
  }
@@ -607,8 +623,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
607
623
  if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
608
624
  warning(null, [ 'definitions', artifactName, 'params', pname ], `Expecting regular SQL-Identifier`);
609
625
  }
610
- else if (options.forHana.names !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
611
- warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.forHana.names },
626
+ else if (options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
627
+ warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.sqlMapping },
612
628
  'Expecting parameter to be uppercase in naming mode $(NAME)');
613
629
  }
614
630
  }
@@ -618,7 +634,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
618
634
 
619
635
  function handleAssocToJoins() {
620
636
  // With flattening errors, it makes little sense to continue.
621
- throwWithError();
637
+ throwWithAnyError();
622
638
  // the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
623
639
  // simply make it invisible and copy it over to the result csn
624
640
  forEachDefinition(csn, art => art.technicalConfig && setProp(art, 'technicalConfig', art.technicalConfig));
@@ -998,24 +1014,22 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
998
1014
  // (which can currently only be 'positiveInteger') and (optional) the value is in a given range
999
1015
  function checkTypeParamValue(node, paramName, range = null, path = null) {
1000
1016
  const paramValue = node[paramName];
1001
- if (paramValue == undefined) {
1017
+ if (paramValue === undefined || paramValue === null) {
1002
1018
  if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type)) {
1003
1019
  return true;
1004
1020
  } else {
1005
- return error('missing-type-parameter', path, { name: paramName, id: node.type, $reviewed: false });
1021
+ return error('type-missing-argument', path, { name: paramName, id: node.type, $reviewed: false });
1006
1022
  }
1007
1023
  }
1008
1024
  if (range) {
1009
1025
  if (isMaxParameterLengthRestricted(node.type) && range.max && paramValue > range.max) {
1010
- error(null, path,
1011
- { prop: paramName, type: node.type, number: range.max, $reviewed: false },
1012
- 'Expecting parameter $(PROP) for type $(TYPE) to not exceed $(NUMBER)');
1026
+ error('type-unexpected-argument', path,
1027
+ { '#': 'max', prop: paramName, type: node.type, number: range.max, $reviewed: false });
1013
1028
  return false;
1014
1029
  }
1015
1030
  if (range.min && paramValue < range.min) {
1016
- error(null, path,
1017
- { prop: paramName, type: node.type, number: range.min, $reviewed: false },
1018
- 'Expecting parameter $(PROP) for type $(TYPE) to be greater than or equal to $(NUMBER)');
1031
+ error('type-unexpected-argument', path,
1032
+ { '#': 'min', prop: paramName, type: node.type, number: range.min, $reviewed: false });
1019
1033
  return false;
1020
1034
  }
1021
1035
  }
@@ -1031,7 +1045,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1031
1045
  * @returns {boolean}
1032
1046
  */
1033
1047
  function isMaxParameterLengthRestricted(type) {
1034
- return !(options.toSql && type === 'cds.String' && (options.toSql.dialect === 'sqlite' || options.toSql.dialect === 'plain'));
1048
+ return !(options.toSql && type === 'cds.String' && (options.sqlDialect === 'sqlite' || options.sqlDialect === 'plain'));
1035
1049
  }
1036
1050
 
1037
1051
  /**
@@ -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
- cloneCsn,
6
+ const { cloneCsnNonDict,
8
7
  forEachDefinition,
9
8
  forEachMemberRecursively,
10
9
  applyTransformationsOnNonDictionary,
@@ -17,14 +16,12 @@ const { getUtils,
17
16
  const { checkCSNVersion } = require('../json/csnVersion');
18
17
  const validate = require('../checks/validator');
19
18
  const { isArtifactInSomeService, isLocalizedArtifactInService } = require('./odata/utils');
20
- const ReferenceFlattener = require('./odata/referenceFlattener');
21
- const { flattenCSN } = require('./odata/structureFlattener');
22
- const generateForeignKeys = require('./odata/generateForeignKeyElements');
23
- const expandStructKeysInAssociations = require('./odata/expandStructKeysInAssociations');
24
19
  const expandToFinalBaseType = require('./odata/toFinalBaseType');
25
20
  const { timetrace } = require('../utils/timetrace');
26
- const { attachPath } = require('./odata/attachPath');
27
21
  const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
22
+ const flattening = require('./db/flattening');
23
+ const associations = require('./db/associations')
24
+ const expansion = require('./db/expansion');
28
25
  const generateDrafts = require('./draft/odata');
29
26
 
30
27
  const { addLocalizationViews } = require('./localized');
@@ -74,10 +71,10 @@ module.exports = { transform4odataWithCsn };
74
71
  function transform4odataWithCsn(inputModel, options) {
75
72
  timetrace.start('OData transformation');
76
73
  // copy the model as we don't want to change the input model
77
- let csn = cloneCsn(inputModel, options);
74
+ let csn = cloneCsnNonDict(inputModel, options);
78
75
 
79
- const { message, error, warning, info, throwWithError } = makeMessageFunction(csn, options, 'for.odata');
80
- throwWithError();
76
+ const { message, error, warning, info, throwWithAnyError } = makeMessageFunction(csn, options, 'for.odata');
77
+ throwWithAnyError();
81
78
 
82
79
  // the new transformer works only with new CSN
83
80
  checkCSNVersion(csn, options);
@@ -88,24 +85,22 @@ function transform4odataWithCsn(inputModel, options) {
88
85
  extractValidFromToKeyElement,
89
86
  checkAssignment, checkMultipleAssignments,
90
87
  recurseElements, setAnnotation, renameAnnotation,
91
- expandStructsInExpression
88
+ expandStructsInExpression,
89
+ csnUtils,
92
90
  } = transformers;
93
91
 
94
- const csnUtils = getUtils(csn);
95
92
  const {
96
93
  getCsnDef,
97
94
  getServiceName,
98
95
  isAssocOrComposition,
99
96
  isAssociation,
100
- isStructured,
101
97
  inspectRef,
102
98
  artifactRef,
103
99
  effectiveType,
104
- getFinalBaseType,
105
100
  } = csnUtils;
106
101
 
107
102
  // are we working with structured OData or not
108
- const structuredOData = options.toOdata.odataFormat === 'structured' && options.toOdata.version === 'v4';
103
+ const structuredOData = options.odataFormat === 'structured' && options.odataVersion === 'v4';
109
104
 
110
105
  // collect all declared non-abstract services from the model
111
106
  // use the array when there is a need to identify if an artifact is in a service or not
@@ -118,7 +113,7 @@ function transform4odataWithCsn(inputModel, options) {
118
113
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
119
114
  enrichUniversalCsn(csn, options);
120
115
 
121
- const keepLocalizedViews = isDeprecatedEnabled(options, 'createLocalizedViews');
116
+ const keepLocalizedViews = isDeprecatedEnabled(options, '_createLocalizedViews');
122
117
 
123
118
  function acceptLocalizedView(_name, parent) {
124
119
  csn.definitions[parent].$localized = true;
@@ -127,13 +122,13 @@ function transform4odataWithCsn(inputModel, options) {
127
122
 
128
123
  addLocalizationViews(csn, options, acceptLocalizedView);
129
124
 
130
- validate.forOdata(csn, {
131
- message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
125
+ const cleanup = validate.forOdata(csn, {
126
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, isAspect, isExternalServiceMember
132
127
  });
133
128
 
134
129
 
135
130
  // Throw exception in case of errors
136
- throwWithError();
131
+ throwWithAnyError();
137
132
 
138
133
  // Semantic checks before flattening regarding temporal data
139
134
  // TODO: Move in the validator
@@ -155,35 +150,38 @@ function transform4odataWithCsn(inputModel, options) {
155
150
  // Check if structured elements and managed associations are compared in an expression
156
151
  // and expand these structured elements. This tuple expansion allows all other
157
152
  // subsequent procession steps (especially a2j) to see plain paths in expressions.
158
- // If errors are detected, throwWithError() will return from further processing
153
+ // If errors are detected, throwWithAnyError() will return from further processing
159
154
  expandStructsInExpression(csn, { skipArtifact: isExternalServiceMember, drillRef: true });
160
155
 
161
- // handles reference flattening
162
- let referenceFlattener = new ReferenceFlattener();
163
- referenceFlattener.resolveAllReferences(csn, inspectRef, isStructured);
164
- attachPath(csn);
165
-
166
- referenceFlattener.applyAliasesInOnCond(csn, inspectRef);
167
-
168
156
  if (!structuredOData) {
169
- // flatten structures
170
- // @ts-ignore
171
- flattenCSN(csn, csnUtils, options, referenceFlattener, error, isExternalServiceMember);
172
- // flatten references
173
- referenceFlattener.flattenAllReferences(csn);
157
+ expansion.expandStructureReferences(csn, options, '_', { error, info, throwWithAnyError }, { skipArtifact: isExternalServiceMember });
158
+ const resolved = new WeakMap();
159
+ // No refs with struct-steps exist anymore
160
+ flattening.flattenAllStructStepsInRefs(csn, options, resolved, '_', { skipArtifact: isExternalServiceMember });
161
+ // No type references exist anymore
162
+ // Needs to happen exactly between flattenAllStructStepsInRefs and flattenElements to keep model resolvable.
163
+ // OData doesn't resolve type chains after the first 'items'
164
+ flattening.resolveTypeReferences(csn, options, resolved, '_',
165
+ { skip: [ 'action', 'aspect', 'event', 'function', 'type'], skipArtifact: isExternalServiceMember, skipStandard: { items: true } });
166
+ // No structured elements exists anymore
167
+ flattening.flattenElements(csn, options, '_', error,
168
+ { skip: ['action', 'aspect', 'event', 'function', 'type'], skipArtifact: isExternalServiceMember,
169
+ skipStandard: { items: true }, // don't drill further into .items
170
+ skipDict: { actions: true } }); // don't drill further into .actions -> bound actions, action-artifacts are handled by skip
174
171
  }
175
172
 
176
- // structure flattener reports errors, further processing is not safe -> throw exception in case of errors
177
- throwWithError();
173
+ // TODO: add the generated foreign keys to the columns when we are in a view
174
+ // see db/views.js::addForeignKeysToColumns
175
+ flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, error, '_', !structuredOData, { skipArtifact: isExternalServiceMember });
178
176
 
179
- // Process associations
180
- // 1. In case we generate flat mode, expand the structured foreign keys.
181
- // This logic rewrites the 'ref' for such keys with the corresponding flattened
182
- // elements.
183
- if (!structuredOData)
184
- expandStructKeysInAssociations(csn, referenceFlattener, csnUtils, isExternalServiceMember);
185
- // 2. generate foreign keys for managed associations
186
- generateForeignKeys(csn, options, referenceFlattener, csnUtils, error, isExternalServiceMember);
177
+ // Allow using managed associations as steps in on-conditions to access their fks
178
+ // To be done after handleManagedAssociationsAndCreateForeignKeys,
179
+ // since then the foreign keys of the managed assocs are part of the elements
180
+ if(!structuredOData)
181
+ forEachDefinition(csn, associations.getManagedAssocStepsInOnConditionFinalizer(csn, '_'));
182
+
183
+ // structure flattener reports errors, further processing is not safe -> throw exception in case of errors
184
+ throwWithAnyError();
187
185
 
188
186
  // Apply default type facets as set by options
189
187
  // Flatten on-conditions in unmanaged associations
@@ -207,22 +205,23 @@ function transform4odataWithCsn(inputModel, options) {
207
205
  generateDrafts(csn, options, services)
208
206
 
209
207
  // Deal with all kind of annotations manipulations here
208
+ const skipPersNameKinds = {'service':1, 'context':1, 'namespace':1, 'annotation':1, 'action':1, 'function':1};
210
209
  forEachDefinition(csn, (def, defName) => {
211
210
  // Resolve annotation shorthands for entities, types, annotations, ...
212
211
  renameShorthandAnnotations(def);
213
212
 
214
213
  // Annotate artifacts with their DB names if requested.
215
214
  // Skip artifacts that have no DB equivalent anyway
216
- if (options.toOdata.names && !['service', 'context', 'namespace', 'annotation', 'action', 'function'].includes(def.kind))
217
- 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"
218
217
 
219
- forEachMemberRecursively(def, (member, memberName, propertyName, path) => {
218
+ forEachMemberRecursively(def, (member, memberName, propertyName) => {
220
219
  // Annotate elements, foreign keys, parameters, etc. with their DB names if requested
221
220
  // Only these are actually required and don't annotate virtual elements in entities or types
222
221
  // as they have no DB representation (although in views)
223
- if (options.toOdata.names && typeof member === 'object' && !['action', 'function'].includes(member.kind) && 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)) {
224
223
  // If we have a 'preserved dotted name' (i.e. we are a result of flattening), use that for the @cds.persistence.name annotation
225
- member['@cds.persistence.name'] = getElementDatabaseNameOf(referenceFlattener.getElementNameWithDots(path) || memberName, options.toOdata.names);
224
+ member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
226
225
  }
227
226
 
228
227
  // Mark fields with @odata.on.insert/update as @Core.Computed
@@ -252,9 +251,9 @@ function transform4odataWithCsn(inputModel, options) {
252
251
  }, { skipArtifact: isExternalServiceMember })
253
252
 
254
253
  // Throw exception in case of errors
255
- throwWithError();
256
-
257
- if (options.testMode) csn = cloneCsn(csn, options); // sort, keep hidden properties
254
+ throwWithAnyError();
255
+ cleanup();
256
+ if (options.testMode) csn = cloneCsnNonDict(csn, options); // sort, keep hidden properties
258
257
  timetrace.stop();
259
258
  return csn;
260
259
 
@@ -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 } = require('../model/csnUtils');
6
+ const { forEachKey } = require('../utils/objectUtils');
7
7
  const { cleanSymbols } = require('../base/cleanSymbols.js');
8
8
  const {
9
- cloneCsn,
9
+ cloneCsnDictionary,
10
+ cloneCsnNonDict,
11
+ applyAnnotationsFromExtensions,
10
12
  forEachDefinition,
11
13
  forEachGeneric,
12
14
  forAllQueries,
@@ -67,7 +69,8 @@ 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 {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view
73
+ * name and its parent name provided as parameter should be created
71
74
  */
72
75
  function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = null) {
73
76
  // Don't try to create convenience views with errors.
@@ -86,6 +89,12 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
86
89
 
87
90
  forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
88
91
 
92
+ // In case that the user tried to annotate `localized.*` artifacts, apply them.
93
+ applyAnnotationsFromExtensions(csn, {
94
+ overwrite: true,
95
+ filter: (name) => name.startsWith('localized.')
96
+ });
97
+
89
98
  sortCsnDefinitionsForTests(csn, options);
90
99
  return csn;
91
100
 
@@ -141,9 +150,8 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
141
150
  else
142
151
  view = createLocalizedViewForEntity(art, artName, textElements);
143
152
 
153
+ copyPersistenceAnnotations(view, art, options);
144
154
  csn.definitions[viewName] = view;
145
-
146
- copyPersistenceAnnotations(csn.definitions[viewName], art);
147
155
  }
148
156
 
149
157
  /**
@@ -245,9 +253,9 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
245
253
  };
246
254
 
247
255
  if (view.query)
248
- convenienceView.query = cloneCsn(view.query, options);
256
+ convenienceView.query = cloneCsnNonDict(view.query, options);
249
257
  else if (view.projection)
250
- convenienceView.projection = cloneCsn(view.projection, options);
258
+ convenienceView.projection = cloneCsnNonDict(view.projection, options);
251
259
 
252
260
  convenienceView.elements = cloneCsnDictionary(view.elements, options);
253
261
  convenienceView[_isViewForView] = true;
@@ -549,7 +557,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
549
557
  if (!obj || typeof obj !== 'object' || Array.isArray(obj))
550
558
  return;
551
559
 
552
- for (const prop of [ 'ref', 'target', 'on' ]) {
560
+ for (const prop of [ 'ref', 'target' ]) {
553
561
  const val = obj[prop];
554
562
  if (prop === 'ref') {
555
563
  rewriteRefToLocalized(obj);
@@ -662,21 +670,24 @@ function copyLocation(target, source) {
662
670
  }
663
671
 
664
672
  /**
665
- * Copy some @cds.persistence.* annotations from the source to
666
- * the target. Ignores existing annotations on the target.
673
+ * Copy @cds.persistence.exists/skip annotations from the source to
674
+ * the target. Ignores existing annotations on the _target_.
667
675
  *
668
676
  * @param {CSN.Artifact} target
669
677
  * @param {CSN.Artifact} source
678
+ * @param {CSN.Options} options
670
679
  */
671
- function copyPersistenceAnnotations(target, source) {
672
- Object.keys(source)
673
- .forEach(anno => {
674
- // Do NOT copy ".exists" at the moment. ".exists" is not propagated
675
- // and this would lead to some localization views referencing not-existing
676
- // "localized.XYZ" views.
677
- if (anno === '@cds.persistence.skip')
678
- target[anno] = source[anno];
679
- });
680
+ function copyPersistenceAnnotations(target, source, options) {
681
+ const doNotCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
682
+ forEachKey(source, anno => {
683
+ // Note:
684
+ // Because `.exists` is copied to the convenience view, it could
685
+ // lead to some localization views referencing non-existing ones.
686
+ // But that is the contract: User says that it already exists!
687
+ // In v2, `.exists` was never copied.
688
+ if (anno === '@cds.persistence.skip' || (!doNotCopyExists && anno === '@cds.persistence.exists'))
689
+ target[anno] = source[anno];
690
+ });
680
691
  }
681
692
 
682
693
  /**