@sap/cds-compiler 2.12.0 → 2.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  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 -290
  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
  128. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const { setProp, isBetaEnabled } = require('../base/model');
4
- const { getUtils, cloneCsn,
5
- forEachMemberRecursively, forEachRef, forAllQueries,
4
+ const { getUtils, cloneCsnNonDict,
5
+ forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
6
6
  getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
7
7
  isAspect, walkCsnPath,
8
8
  } = require('../model/csnUtils');
@@ -12,23 +12,24 @@ const { translateAssocsToJoinsCSN } = require('./translateAssocsToJoins');
12
12
  const { csnRefs, pathId } = require('../model/csnRefs');
13
13
  const { checkCSNVersion } = require('../json/csnVersion');
14
14
  const validate = require('../checks/validator');
15
- const { rejectManagedAssociationsAndStructuresForHdbcsNames } = require('../checks/selectItems');
15
+ const { rejectManagedAssociationsAndStructuresForHdbcdsNames } = require('../checks/selectItems');
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;
23
23
  const flattening = require('./db/flattening');
24
24
  const expansion = require('./db/expansion');
25
25
  const assertUnique = require('./db/assertUnique');
26
- const generateDrafts = require('./db/draft');
27
- const enrichUniversalCsn = require('./universalCsnEnricher');
26
+ const generateDrafts = require('./draft/db');
27
+ const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
28
28
  const { getViewTransformer } = require('./db/views');
29
29
  const cdsPersistence = require('./db/cdsPersistence');
30
30
  const temporal = require('./db/temporal');
31
31
  const associations = require('./db/associations')
32
+ const { ModelError } = require("../base/error");
32
33
 
33
34
  // By default: Do not process non-entities/views
34
35
  function forEachDefinition(csn, cb) {
@@ -105,7 +106,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
105
106
  // copy the model as we don't want to change the input model
106
107
  timetrace.start('HANA transformation');
107
108
  /** @type {CSN.Model} */
108
- let csn = cloneCsn(inputModel, options);
109
+ let csn = cloneCsnNonDict(inputModel, options);
109
110
 
110
111
 
111
112
 
@@ -113,16 +114,16 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
113
114
 
114
115
  const pathDelimiter = (options.forHana.names === 'hdbcds') ? '.' : '_';
115
116
 
116
- let error, warning, info; // message functions
117
+ let message, error, warning, info; // message functions
117
118
  /** @type {() => void} */
118
- let throwWithError;
119
- let artifactRef, inspectRef, effectiveType, // csnRefs
120
- addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType, getFinalBaseType, // transformUtils
121
- get$combined; // csnUtils
119
+ let throwWithAnyError;
120
+ let artifactRef, inspectRef, effectiveType, get$combined,
121
+ getFinalBaseType, // csnUtils (csnRefs)
122
+ addDefaultTypeFacets, expandStructsInExpression; // transformUtils
122
123
 
123
124
  bindCsnReference();
124
125
 
125
- throwWithError(); // reclassify and throw in case of non-configurable errors
126
+ throwWithAnyError(); // reclassify and throw in case of non-configurable errors
126
127
 
127
128
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
128
129
  enrichUniversalCsn(csn, options);
@@ -136,7 +137,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
136
137
 
137
138
  // Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
138
139
  const cleanup = validate.forHana(csn, {
139
- error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, getFinalBaseType, isAspect
140
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, getFinalBaseType, isAspect
140
141
  });
141
142
 
142
143
  // Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
@@ -145,13 +146,13 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
145
146
  // Check if structured elements and managed associations are compared in an expression
146
147
  // and expand these structured elements. This tuple expansion allows all other
147
148
  // subsequent procession steps (especially a2j) to see plain paths in expressions.
148
- // If errors are detected, throwWithError() will return from further processing
149
+ // If errors are detected, throwWithAnyError() will return from further processing
149
150
 
150
151
  // If this function is ever undefined, we have a bug in our logic.
151
152
  // @ts-ignore
152
153
  expandStructsInExpression(csn, { drillRef: true });
153
154
 
154
- throwWithError();
155
+ throwWithAnyError();
155
156
 
156
157
  // FIXME: This does something very similar to cloneWithTransformations -> refactor?
157
158
  const transformCsn = transformUtils.transformModel;
@@ -166,7 +167,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
166
167
 
167
168
  if(doA2J) {
168
169
  // Expand a structured thing in: keys, columns, order by, group by
169
- expansion.expandStructureReferences(csn, options, pathDelimiter, {error, info, throwWithError});
170
+ expansion.expandStructureReferences(csn, options, pathDelimiter, {error, info, throwWithAnyError});
170
171
  bindCsnReference();
171
172
  }
172
173
 
@@ -232,34 +233,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
232
233
 
233
234
  // (000) Rename primitive types, make UUID a String
234
235
  transformCsn(csn, {
235
- type: (val, node, key, path) => {
236
- // Resolve type-of chains
237
- function fn() {
238
- // val can be undefined: books as myBooks : redirected to Model.MyBooks
239
- if (val && val.ref) {
240
- const { art } = inspectRef(path);
241
- if (art && art.type) {
242
- val = art.type;
243
- // This is somehow needed to update the ref so that inspectRef sees it
244
- node[key] = val;
245
- if (val.ref)
246
- fn();
247
- }
248
- else {
249
- // Doesn't seem to ever ocurr
250
- }
251
- }
252
- }
253
- fn();
236
+ type: (val, node, key) => {
254
237
  renamePrimitiveTypesAndUuid(val, node, key);
255
238
  addDefaultTypeFacets(node);
256
239
  },
257
- cast: (val) => {
258
- if (options.forHana.names === 'plain' || options.toSql )
259
- toFinalBaseType(val);
260
- renamePrimitiveTypesAndUuid(val.type, val, 'type');
261
- addDefaultTypeFacets(val);
262
- },
263
240
  // HANA/SQLite do not support array-of - turn into CLOB/Text
264
241
  items: (val, node) => {
265
242
  node.type = 'cds.LargeString';
@@ -270,24 +247,19 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
270
247
  // (040) Ignore entities and views that are abstract or implemented
271
248
  // or carry the annotation cds.persistence.skip/exists
272
249
  // These entities are not removed from the csn, but flagged as "to be ignored"
273
- forEachDefinition(csn, cdsPersistence.getAnnoProcessor(csn, options, {warning}));
250
+ forEachDefinition(csn, cdsPersistence.getAnnoProcessor());
274
251
 
275
252
 
276
253
  // (050) Check @cds.valid.from/to only on entity
277
254
  // Views are checked in (001), unbalanced valid.from/to's or mismatching origins
278
- // Temporal only in beta-mode
279
255
  forEachDefinition(csn, temporal.getAnnotationHandler(csn, options, pathDelimiter, {error}));
280
256
 
281
- handleManagedAssociationsAndCreateForeignKeys();
257
+ // eliminate the doA2J in the functions 'handleManagedAssociationFKs' and 'createForeignKeyElements'
258
+ doA2J && flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, error, pathDelimiter, true, { allowArtifact: artifact => (artifact.kind === 'entity') });
282
259
 
283
- function handleManagedAssociationsAndCreateForeignKeys() {
284
- forEachDefinition(csn, associations.getForeignKeyFlattener(csn, options, pathDelimiter));
285
- forEachDefinition(csn, associations.getForeignKeyElementCreator(csn, options, pathDelimiter, { error }));
286
- }
287
-
288
- forEachDefinition(csn, flattenIndexes);
289
- // Basic handling of associations in views and entities
290
- forEachDefinition(csn, associations.getManagedAssociationTransformer(csn, options, pathDelimiter));
260
+ doA2J && forEachDefinition(csn, flattenIndexes);
261
+ // Managed associations get an on-condition - in views and entities
262
+ doA2J && associations.attachOnConditions(csn, pathDelimiter);
291
263
 
292
264
  // (045) Strip all query-ish properties from views and projections annotated with '@cds.persistence.table',
293
265
  // and make them entities
@@ -296,10 +268,11 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
296
268
  // Allow using managed associations as steps in on-conditions to access their fks
297
269
  // To be done after handleAssociations, since then the foreign keys of the managed assocs
298
270
  // are part of the elements
299
- forEachDefinition(csn, handleManagedAssocStepsInOnCondition);
271
+ if (doA2J)
272
+ forEachDefinition(csn, associations.getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter));
300
273
 
301
274
  // Create convenience views for localized entities/views.
302
- // To be done after handleManagedAssocStepsInOnCondition because associations are
275
+ // To be done after getManagedAssocStepsInOnConditionFinalizer because associations are
303
276
  // handled and before handleDBChecks which removes the localized attribute.
304
277
  // Association elements of localized convenience views do not have hidden properties
305
278
  // like $managed set, so we cannot do this earlier on.
@@ -308,11 +281,11 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
308
281
  else
309
282
  addLocalizationViews(csn, options);
310
283
 
311
- forEachDefinition(csn, (definition, artName, prop, path) => {
284
+ !doA2J && forEachDefinition(csn, (definition, artName, prop, path) => {
312
285
  if (definition.query) {
313
286
  // reject managed association and structure publishing for to-hdbcds.hdbcds
314
287
  const that = { csnUtils: getUtils(csn), options, error };
315
- rejectManagedAssociationsAndStructuresForHdbcsNames.call(that, definition, path)
288
+ rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path)
316
289
  }
317
290
  });
318
291
 
@@ -336,18 +309,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
336
309
  // because otherwise we would produce wrong ON-conditions for the keys involved. Sigh ...
337
310
  forEachDefinition(csn, transformSelfInBacklinks);
338
311
 
339
- if(options.forHana){
340
- /**
341
- * Referential Constraints are only supported for sql-dialect "hana" and "sqlite".
342
- * For to.hdbcds with naming mode "hdbcds", no foreign keys are calculated,
343
- * hence we do not generate the referential constraints for them.
344
- */
345
- const validOptionsForConstraint = () => {
346
- return (options.forHana.dialect === 'sqlite' || options.forHana.dialect === 'hana') && doA2J;
347
- }
348
- if(validOptionsForConstraint())
349
- createReferentialConstraints(csn, options);
350
- }
312
+ /**
313
+ * Referential Constraints are only supported for sql-dialect "hana" and "sqlite".
314
+ * For to.hdbcds with naming mode "hdbcds", no foreign keys are calculated,
315
+ * hence we do not generate the referential constraints for them.
316
+ */
317
+ if((options.sqlDialect === 'hana' || options.sqlDialect === 'sqlite') && doA2J)
318
+ createReferentialConstraints(csn, options);
319
+
351
320
  // no constraints for drafts
352
321
  generateDrafts(csn, options, pathDelimiter, { info, warning, error });
353
322
 
@@ -370,30 +339,12 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
370
339
  const checkConstraintIdentifiers = (artifact, artifactName, prop, path) => {
371
340
  assertConstraintIdentifierUniqueness(artifact, artifactName, path, error);
372
341
  };
373
- const removeNamespaces = (artifact, artifactName) => {
374
- if (artifact.kind === 'namespace')
375
- delete csn.definitions[artifactName];
376
- };
377
- const ignoreNonPersistedArtifactsWithAnonymousAspectComposition = (artifact) => {
378
- if(artifact.kind === 'type' || artifact.kind === 'aspect' || artifact.kind === 'entity' && artifact.abstract){
379
- if(artifact.elements && Object.keys(artifact.elements).some((elementName) => {
380
- const element = artifact.elements[elementName];
381
- return !element.target && element.targetAspect && typeof element.targetAspect !== 'string';
382
- })) {
383
- artifact._ignore = true;
384
- }
385
- }
386
- };
387
342
 
388
343
  forEachDefinition(csn, [
389
344
  /* assert that there will be no conflicting unique- and foreign key constraint identifiers */
390
345
  checkConstraintIdentifiers,
391
- /* (250) Remove all namespaces from definitions */
392
- removeNamespaces,
393
346
  /* Check Type Parameters (precision, scale, length ...) */
394
347
  checkTypeParameters,
395
- /* Filter out aspects/types/abstract entities containing managed compositions of anonymous aspects */
396
- ignoreNonPersistedArtifactsWithAnonymousAspectComposition,
397
348
  // (200) Strip 'key' property from type elements
398
349
  removeKeyPropInType,
399
350
  ]);
@@ -402,7 +353,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
402
353
  if(doA2J)
403
354
  flattening.removeLeadingSelf(csn);
404
355
 
405
- throwWithError();
356
+ throwWithAnyError();
406
357
 
407
358
  timetrace.stop();
408
359
 
@@ -430,10 +381,25 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
430
381
  // Set when we turn UUID into String, checked during generateDraftForHana
431
382
  '$renamed': killProp,
432
383
  // Set when we remove .key from temporal things, used in localized.js
433
- '$key': killProp
384
+ '$key': killProp,
385
+ // We need .elements easily for rendering - otherwise we have to compute it then
386
+ // Does not fit in the "killers" theme - TODO: Find a better place
387
+ SET: (parent, prop, SET) => {
388
+ if(!SET.elements) {
389
+ const stack = [parent];
390
+ while(stack.length > 0) {
391
+ const query = stack.pop();
392
+
393
+ if(query.SET)
394
+ stack.push(query.SET.args[0]);
395
+ else if(query.SELECT)
396
+ setProp(SET, 'elements', query.SELECT.elements);
397
+ }
398
+ }
399
+ }
434
400
  }
435
401
 
436
- applyTransformations(csn, killers, [], false);
402
+ applyTransformations(csn, killers, [], { skipIgnore: false});
437
403
 
438
404
  redoProjections.forEach(fn => fn());
439
405
 
@@ -442,10 +408,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
442
408
  /* ----------------------------------- Functions start here -----------------------------------------------*/
443
409
 
444
410
  function bindCsnReference(){
445
- ({ error, warning, info, throwWithError } = makeMessageFunction(csn, options, moduleName));
446
- ({ artifactRef, inspectRef, effectiveType } = csnRefs(csn));
447
- ({ getFinalBaseType, get$combined } = getUtils(csn));
448
- ({ addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter));
411
+ ({ error, warning, info, message, throwWithAnyError } = makeMessageFunction(csn, options, moduleName));
412
+ ({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
413
+ ({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
449
414
  }
450
415
 
451
416
  function bindCsnReferenceOnly(){
@@ -472,8 +437,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
472
437
  }
473
438
  })
474
439
  }
475
- }
476
- , [ 'definitions', artifactName, 'query' ]);
440
+ }, [ 'definitions', artifactName, 'query' ]);
441
+
477
442
  function getResolvedMixinOnCondition(csn, mixinAssociation, query, assocName, path){
478
443
  const { inspectRef } = csnRefs(csn);
479
444
  const referencedThroughStar = query.SELECT.columns.some((column) => column === '*');
@@ -498,7 +463,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
498
463
  // get$combined also includes elements which are part of "excluding {}"
499
464
  // this shouldn't be an issue here, as such references get rejected
500
465
  const elementsOfQuerySources = get$combined(query);
501
- Object.entries(elementsOfQuerySources).forEach(([id, element]) => {
466
+ forEach(elementsOfQuerySources, (id, element) => {
502
467
  // if the ref points to an element which is not explicitly exposed in the column list,
503
468
  // but through the '*' operator -> replace the $projection / $self with the correct source entity
504
469
  if(id === columnToReplace)
@@ -508,7 +473,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
508
473
 
509
474
  // No implicit CAST in on-condition
510
475
  if(replaceWith && replaceWith.cast) {
511
- const clone = cloneCsn(replaceWith, options);
476
+ const clone = cloneCsnNonDict(replaceWith, options);
512
477
  delete clone.cast;
513
478
  return clone;
514
479
  }
@@ -524,11 +489,15 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
524
489
  function transformViews(artifact, artifactName) {
525
490
  if (!artifact._ignore) {
526
491
  // Do things specific for entities and views (pass 2)
527
- if ((artifact.kind === 'entity' || artifact.kind === 'view') && artifact.query) {
528
- forAllQueries(artifact.query, (q, p) => {
529
- transformEntityOrViewPass2(q, artifact, artifactName, p)
530
- replaceAssociationsInGroupByOrderBy(q, options, inspectRef, error, p);
531
- }, [ 'definitions', artifactName, 'query' ]);
492
+ if ((artifact.kind === 'entity') && artifact.query) {
493
+ const process = (parent, prop, query, path) => {
494
+ transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop))
495
+ replaceAssociationsInGroupByOrderBy(parent, options, inspectRef, error, path.concat(prop));
496
+ return query;
497
+ }
498
+ applyTransformationsOnNonDictionary(csn.definitions, artifactName, {
499
+ SELECT: process
500
+ }, {}, [ 'definitions']);
532
501
  }
533
502
  }
534
503
  }
@@ -539,7 +508,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
539
508
  */
540
509
  function recursivelyApplyCommon(artifact, artifactName) {
541
510
  if (!artifact._ignore) {
542
- if (![ 'service', 'context', 'namespace', 'annotation', 'action', 'function' ].includes(artifact.kind))
511
+ if (artifact.kind !== 'service' && artifact.kind !== 'context')
543
512
  addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.forHana.names, csn), artifact);
544
513
 
545
514
  forEachMemberRecursively(artifact, (member, memberName, property, path) => {
@@ -558,9 +527,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
558
527
  * @param {string} artifactName
559
528
  */
560
529
  function removeKeyPropInType(artifact, artifactName) {
561
- if (!artifact._ignore) {
530
+ if (!artifact._ignore && artifact.kind === 'type') {
562
531
  forEachMemberRecursively(artifact, (member) => {
563
- if (artifact.kind === 'type' && member.key)
532
+ if (member.key)
564
533
  delete member.key;
565
534
  }, [ 'definitions', artifactName ]);
566
535
  }
@@ -609,25 +578,27 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
609
578
  if (artifact.params) {
610
579
  // HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
611
580
  // SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
612
- error(null, path, 'Unexpected association in parameterized view');
581
+ message('def-unexpected-paramview-assoc', path, { '#': 'view' });
613
582
  }
614
583
  else if(artifact['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
615
584
  // UDF/CVs w/o params don't support 'WITH ASSOCIATIONS'
616
- error(null, path, `Associations are not allowed in entities annotated with @cds.persistence { udf, calcview }`);
585
+ const anno = artifact['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
586
+ message('def-unexpected-calcview-assoc', path, { '#': 'entity-persistence', anno });
617
587
  }
618
588
  if (csn.definitions[member.target].params) {
619
589
  // HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
620
590
  // SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
621
- error(null, path, 'Unexpected parameterized association target');
591
+ message('def-unexpected-paramview-assoc', path, { '#': 'target' });
622
592
  }
623
- else if(csn.definitions[member.target]['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
593
+ else if(csn.definitions[member.target]['@cds.persistence.udf'] || csn.definitions[member.target]['@cds.persistence.calcview']) {
624
594
  // HANA won't check the assoc target but when querying an association with target UDF, this is the error:
625
595
  // SAP DBTech JDBC: [259]: invalid table name: target object SYSTEM.UDF does not exist: line 3 col 6 (at pos 43)
626
596
  // CREATE TABLE F (id INTEGER NOT NULL);
627
597
  // CREATE FUNCTION UDF RETURNS TABLE (ID INTEGER) LANGUAGE SQLSCRIPT SQL SECURITY DEFINER AS BEGIN RETURN SELECT ID FROM F; END;
628
598
  // CREATE TABLE Y ( id INTEGER NOT NULL, toUDF_id INTEGER) WITH ASSOCIATIONS (MANY TO ONE JOIN UDF AS toUDF ON (toUDF.id = toUDF_id));
629
599
  // CREATE VIEW U AS SELECT id, toUDF.a FROM Y;
630
- error(null, path, `Associations can't point to entities annotated with @cds.persistence { udf, calcview }`);
600
+ const anno = csn.definitions[member.target]['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
601
+ message('def-unexpected-calcview-assoc', path, { '#': 'target-persistence', anno });
631
602
  }
632
603
  }
633
604
  }, [ 'definitions', artifactName ]);
@@ -638,7 +609,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
638
609
  * @param {string} artifactName
639
610
  */
640
611
  function handleChecksForWithParameters(artifact, artifactName) {
641
- if (!artifact._ignore && artifact.params && (artifact.kind === 'entity' || artifact.kind === 'view')) {
612
+ if (!artifact._ignore && artifact.params && (artifact.kind === 'entity')) {
642
613
  if (!artifact.query) { // table entity with params
643
614
  // Allow with plain
644
615
  error(null, [ 'definitions', artifactName ], { '#': options.toSql ? 'sql' : 'std' }, {
@@ -666,7 +637,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
666
637
 
667
638
  function handleAssocToJoins() {
668
639
  // With flattening errors, it makes little sense to continue.
669
- throwWithError();
640
+ throwWithAnyError();
670
641
  // the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
671
642
  // simply make it invisible and copy it over to the result csn
672
643
  forEachDefinition(csn, art => art.technicalConfig && setProp(art, 'technicalConfig', art.technicalConfig));
@@ -908,7 +879,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
908
879
  else if (assoc.on)
909
880
  return transformDollarSelfComparisonWithUnmanagedAssoc(assocOp, assoc, assocName, elemName);
910
881
 
911
- throw new Error(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
882
+ throw new ModelError(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
912
883
  }
913
884
 
914
885
  // For a condition `<elemName>.<assoc> = $self` in the ON-condition of element <elemName>,
@@ -963,22 +934,23 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
963
934
  const newOnCond = cloneWithTransformations(assoc.on, {
964
935
  ref: (value) => cloneWithTransformations(value, {}),
965
936
  });
966
- // goes through the the newOnCond and transform all the 'path' elements
967
- forEachRef(newOnCond, (ref) => {
968
- if (ref[0] === assocName) // we are in the "path" from the forwarding assoc => need to remove the first part of the path
969
- {
970
- ref.shift();
971
- } else if(ref && ref.length > 1 && ref[0] === '$self' && ref[1] === assocName) {
972
- // We could also have a $self infront of the assoc name - so we would need to shift twice
973
- ref.shift();
974
- ref.shift();
975
- }
976
- else { // we are in the backlink assoc "path" => need to push at the beginning the association's id
977
- ref.unshift(elemName);
978
- // if there was a $self identifier in the forwarding association onCond
979
- // we do not need it any more, as we prepended in the previous step the back association's id
980
- if (ref[1] === '$self')
981
- ref.splice(1, 1);
937
+ applyTransformationsOnNonDictionary({on: newOnCond}, 'on', {
938
+ ref: (parent, prop, ref) => {
939
+ if (ref[0] === assocName) // we are in the "path" from the forwarding assoc => need to remove the first part of the path
940
+ {
941
+ ref.shift();
942
+ } else if(ref && ref.length > 1 && ref[0] === '$self' && ref[1] === assocName) {
943
+ // We could also have a $self infront of the assoc name - so we would need to shift twice
944
+ ref.shift();
945
+ ref.shift();
946
+ }
947
+ else { // we are in the backlink assoc "path" => need to push at the beginning the association's id
948
+ ref.unshift(elemName);
949
+ // if there was a $self identifier in the forwarding association onCond
950
+ // we do not need it any more, as we prepended in the previous step the back association's id
951
+ if (ref[1] === '$self')
952
+ ref.splice(1, 1);
953
+ }
982
954
  }
983
955
  });
984
956
  return newOnCond;
@@ -1045,24 +1017,22 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1045
1017
  // (which can currently only be 'positiveInteger') and (optional) the value is in a given range
1046
1018
  function checkTypeParamValue(node, paramName, range = null, path = null) {
1047
1019
  const paramValue = node[paramName];
1048
- if (paramValue == undefined) {
1020
+ if (paramValue === undefined || paramValue === null) {
1049
1021
  if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type)) {
1050
1022
  return true;
1051
1023
  } else {
1052
- return error('missing-type-parameter', path, { name: paramName, id: node.type, $reviewed: false });
1024
+ return error('type-missing-argument', path, { name: paramName, id: node.type, $reviewed: false });
1053
1025
  }
1054
1026
  }
1055
1027
  if (range) {
1056
1028
  if (isMaxParameterLengthRestricted(node.type) && range.max && paramValue > range.max) {
1057
- error(null, path,
1058
- { prop: paramName, type: node.type, number: range.max, $reviewed: false },
1059
- 'Expecting parameter $(PROP) for type $(TYPE) to not exceed $(NUMBER)');
1029
+ error('type-unexpected-argument', path,
1030
+ { '#': 'max', prop: paramName, type: node.type, number: range.max, $reviewed: false });
1060
1031
  return false;
1061
1032
  }
1062
1033
  if (range.min && paramValue < range.min) {
1063
- error(null, path,
1064
- { prop: paramName, type: node.type, number: range.min, $reviewed: false },
1065
- 'Expecting parameter $(PROP) for type $(TYPE) to be greater than or equal to $(NUMBER)');
1034
+ error('type-unexpected-argument', path,
1035
+ { '#': 'min', prop: paramName, type: node.type, number: range.min, $reviewed: false });
1066
1036
  return false;
1067
1037
  }
1068
1038
  }
@@ -1090,7 +1060,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1090
1060
  function flattenIndexes(art, artName) {
1091
1061
  // Flatten structs in indexes (unless explicitly asked to keep structs)
1092
1062
  const tc = art.technicalConfig;
1093
- if ((art.kind === 'entity' || art.kind === 'view') && doA2J) {
1063
+ if (art.kind === 'entity') {
1094
1064
  if (tc && tc[dialect]) {
1095
1065
  // Secondary and fulltext indexes
1096
1066
  for (const name in tc[dialect].indexes) {
@@ -1144,75 +1114,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1144
1114
  }
1145
1115
  }
1146
1116
  }
1147
-
1148
- /**
1149
- * Loop over all elements and for all unmanaged associations translate
1150
- * <assoc base>.<managed assoc>.<fk> to <assoc base>.<managed assoc>_<fk>
1151
- *
1152
- * Or in other words: Allow using the foreign keys of managed associations in on-conditions
1153
- *
1154
- * @param {CSN.Artifact} artifact Artifact to check
1155
- * @param {string} artifactName Name of the artifact
1156
- */
1157
- function handleManagedAssocStepsInOnCondition(artifact, artifactName) {
1158
- for (const elemName in artifact.elements) {
1159
- const elem = artifact.elements[elemName];
1160
- if (doA2J) {
1161
- // The association is an unmanaged on
1162
- if (!elem.keys && elem.target && elem.on) {
1163
- forEachRef(elem.on, (ref, refOwner, path) => {
1164
- // [<assoc base>.]<managed assoc>.<field>
1165
- if (ref.length > 1) {
1166
- const { links } = inspectRef(path);
1167
- if (links) {
1168
- // eslint-disable-next-line for-direction
1169
- for (let i = links.length - 1; i >= 0; i--) {
1170
- const link = links[i];
1171
- // We found the latest managed assoc path step
1172
- if (link.art && link.art.target && link.art.keys) {
1173
- // Doesn't work when ref-target (filter condition) or similar is used
1174
- if (!ref.slice(i).some(refElement => typeof refElement !== 'string')) {
1175
- // We join the managed assoc with everything following it
1176
- const sourceElementName = ref.slice(i).join(pathDelimiter);
1177
- const source = findSource(links, i - 1) || artifact;
1178
- // allow specifying managed assoc on the source side
1179
- const fks = link.art.keys.filter(fk => ref[i] + pathDelimiter + fk.ref[0] === sourceElementName);
1180
- if(fks && fks.length >= 1){
1181
- const fk = fks[0];
1182
- const managedAssocStepName = refOwner.ref[i];
1183
- const fkName = `${ managedAssocStepName }${ pathDelimiter }${ fk.as }`;
1184
- if(source && source.elements[fkName])
1185
- refOwner.ref = [ ...ref.slice(0, i), fkName ];
1186
- }
1187
- }
1188
- }
1189
- }
1190
- }
1191
- }
1192
- }, [ 'definitions', artifactName, 'elements', elemName, 'on' ]);
1193
- }
1194
- }
1195
- }
1196
-
1197
- /**
1198
- * Find out where the managed association is
1199
- *
1200
- * @param {Array} links
1201
- * @param {Number} startIndex
1202
- * @returns {Object| undefined} CSN definition of the source of the managed association
1203
- *
1204
- */
1205
- function findSource(links, startIndex) {
1206
- for (let i = startIndex; i >= 0; i--) {
1207
- const link = links[i];
1208
- // We found the latest assoc step - now check where that points to
1209
- if (link.art && link.art.target)
1210
- return csn.definitions[link.art.target];
1211
- }
1212
-
1213
- return undefined;
1214
- }
1215
- }
1216
1117
  }
1217
1118
 
1218
1119