@sap/cds-compiler 2.12.0 → 2.13.6

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 (118) hide show
  1. package/CHANGELOG.md +110 -15
  2. package/bin/cdsc.js +13 -13
  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 +28 -63
  8. package/lib/api/options.js +3 -3
  9. package/lib/api/validate.js +0 -5
  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 +25 -4
  16. package/lib/base/messages.js +16 -26
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +158 -123
  19. package/lib/checks/annotationsOData.js +1 -1
  20. package/lib/checks/cdsPersistence.js +2 -1
  21. package/lib/checks/enricher.js +17 -1
  22. package/lib/checks/invalidTarget.js +3 -1
  23. package/lib/checks/managedWithoutKeys.js +3 -1
  24. package/lib/checks/selectItems.js +4 -4
  25. package/lib/checks/sql-snippets.js +27 -26
  26. package/lib/checks/types.js +1 -1
  27. package/lib/checks/validator.js +4 -7
  28. package/lib/compiler/assert-consistency.js +5 -3
  29. package/lib/compiler/builtins.js +8 -6
  30. package/lib/compiler/checks.js +14 -3
  31. package/lib/compiler/cycle-detector.js +1 -1
  32. package/lib/compiler/define.js +1103 -0
  33. package/lib/compiler/extend.js +983 -0
  34. package/lib/compiler/finalize-parse-cdl.js +231 -0
  35. package/lib/compiler/index.js +32 -13
  36. package/lib/compiler/kick-start.js +190 -0
  37. package/lib/compiler/moduleLayers.js +4 -4
  38. package/lib/compiler/populate.js +1226 -0
  39. package/lib/compiler/propagator.js +111 -46
  40. package/lib/compiler/resolve.js +1433 -0
  41. package/lib/compiler/shared.js +64 -37
  42. package/lib/compiler/tweak-assocs.js +529 -0
  43. package/lib/compiler/utils.js +197 -33
  44. package/lib/edm/.eslintrc.json +5 -0
  45. package/lib/edm/annotations/genericTranslation.js +5 -9
  46. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  47. package/lib/edm/csn2edm.js +9 -8
  48. package/lib/edm/edm.js +11 -12
  49. package/lib/edm/edmPreprocessor.js +137 -73
  50. package/lib/edm/edmUtils.js +116 -22
  51. package/lib/gen/Dictionary.json +10 -3
  52. package/lib/gen/language.checksum +1 -1
  53. package/lib/gen/language.interp +9 -1
  54. package/lib/gen/language.tokens +86 -83
  55. package/lib/gen/languageLexer.interp +10 -1
  56. package/lib/gen/languageLexer.js +860 -833
  57. package/lib/gen/languageLexer.tokens +78 -75
  58. package/lib/gen/languageParser.js +5282 -4265
  59. package/lib/json/from-csn.js +12 -1
  60. package/lib/json/to-csn.js +126 -66
  61. package/lib/language/docCommentParser.js +2 -2
  62. package/lib/language/genericAntlrParser.js +76 -3
  63. package/lib/language/language.g4 +297 -130
  64. package/lib/language/multiLineStringParser.js +5 -5
  65. package/lib/main.d.ts +468 -59
  66. package/lib/main.js +35 -9
  67. package/lib/model/api.js +3 -1
  68. package/lib/model/csnRefs.js +225 -156
  69. package/lib/model/csnUtils.js +192 -223
  70. package/lib/model/enrichCsn.js +70 -29
  71. package/lib/model/revealInternalProperties.js +27 -6
  72. package/lib/model/sortViews.js +2 -1
  73. package/lib/modelCompare/compare.js +17 -12
  74. package/lib/optionProcessor.js +5 -4
  75. package/lib/render/manageConstraints.js +35 -32
  76. package/lib/render/toCdl.js +73 -288
  77. package/lib/render/toHdbcds.js +25 -23
  78. package/lib/render/toSql.js +98 -41
  79. package/lib/render/utils/common.js +5 -10
  80. package/lib/render/utils/sql.js +4 -3
  81. package/lib/render/utils/stringEscapes.js +111 -0
  82. package/lib/sql-identifier.js +1 -1
  83. package/lib/transform/.eslintrc.json +5 -0
  84. package/lib/transform/db/.eslintrc.json +2 -0
  85. package/lib/transform/db/applyTransformations.js +35 -12
  86. package/lib/transform/db/assertUnique.js +1 -1
  87. package/lib/transform/db/associations.js +103 -305
  88. package/lib/transform/db/cdsPersistence.js +2 -2
  89. package/lib/transform/db/constraints.js +55 -52
  90. package/lib/transform/db/expansion.js +46 -24
  91. package/lib/transform/db/flattening.js +553 -102
  92. package/lib/transform/db/groupByOrderBy.js +3 -1
  93. package/lib/transform/db/transformExists.js +59 -6
  94. package/lib/transform/db/views.js +5 -4
  95. package/lib/transform/draft/.eslintrc.json +38 -0
  96. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  97. package/lib/transform/draft/odata.js +227 -0
  98. package/lib/transform/forHanaNew.js +67 -183
  99. package/lib/transform/forOdataNew.js +17 -171
  100. package/lib/transform/localized.js +34 -19
  101. package/lib/transform/odata/generateForeignKeyElements.js +1 -1
  102. package/lib/transform/odata/referenceFlattener.js +95 -89
  103. package/lib/transform/odata/structureFlattener.js +1 -1
  104. package/lib/transform/odata/toFinalBaseType.js +86 -12
  105. package/lib/transform/odata/typesExposure.js +5 -5
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +36 -22
  108. package/lib/transform/translateAssocsToJoins.js +2 -19
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +170 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/objectUtils.js +30 -0
  114. package/package.json +1 -1
  115. package/share/messages/README.md +26 -0
  116. package/lib/compiler/definer.js +0 -2361
  117. package/lib/compiler/resolver.js +0 -3079
  118. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { setProp, isBetaEnabled } = require('../base/model');
4
4
  const { getUtils, cloneCsn,
5
- forEachMemberRecursively, forEachRef, forAllQueries,
5
+ forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
6
6
  getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
7
7
  isAspect, walkCsnPath,
8
8
  } = require('../model/csnUtils');
@@ -12,7 +12,7 @@ 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');
@@ -23,12 +23,13 @@ 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) {
@@ -113,12 +114,12 @@ 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
119
  let throwWithError;
119
- let artifactRef, inspectRef, effectiveType, // csnRefs
120
- addDefaultTypeFacets, expandStructsInExpression, toFinalBaseType, getFinalBaseType, // transformUtils
121
- get$combined; // csnUtils
120
+ let artifactRef, inspectRef, effectiveType, get$combined,
121
+ getFinalBaseType, // csnUtils (csnRefs)
122
+ addDefaultTypeFacets, expandStructsInExpression; // transformUtils
122
123
 
123
124
  bindCsnReference();
124
125
 
@@ -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
@@ -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
  ]);
@@ -433,7 +384,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
433
384
  '$key': killProp
434
385
  }
435
386
 
436
- applyTransformations(csn, killers, [], false);
387
+ applyTransformations(csn, killers, [], { skipIgnore: false});
437
388
 
438
389
  redoProjections.forEach(fn => fn());
439
390
 
@@ -442,10 +393,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
442
393
  /* ----------------------------------- Functions start here -----------------------------------------------*/
443
394
 
444
395
  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));
396
+ ({ error, warning, info, message, throwWithError } = makeMessageFunction(csn, options, moduleName));
397
+ ({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
398
+ ({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
449
399
  }
450
400
 
451
401
  function bindCsnReferenceOnly(){
@@ -472,8 +422,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
472
422
  }
473
423
  })
474
424
  }
475
- }
476
- , [ 'definitions', artifactName, 'query' ]);
425
+ }, [ 'definitions', artifactName, 'query' ]);
426
+
477
427
  function getResolvedMixinOnCondition(csn, mixinAssociation, query, assocName, path){
478
428
  const { inspectRef } = csnRefs(csn);
479
429
  const referencedThroughStar = query.SELECT.columns.some((column) => column === '*');
@@ -524,7 +474,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
524
474
  function transformViews(artifact, artifactName) {
525
475
  if (!artifact._ignore) {
526
476
  // Do things specific for entities and views (pass 2)
527
- if ((artifact.kind === 'entity' || artifact.kind === 'view') && artifact.query) {
477
+ if ((artifact.kind === 'entity') && artifact.query) {
528
478
  forAllQueries(artifact.query, (q, p) => {
529
479
  transformEntityOrViewPass2(q, artifact, artifactName, p)
530
480
  replaceAssociationsInGroupByOrderBy(q, options, inspectRef, error, p);
@@ -539,7 +489,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
539
489
  */
540
490
  function recursivelyApplyCommon(artifact, artifactName) {
541
491
  if (!artifact._ignore) {
542
- if (![ 'service', 'context', 'namespace', 'annotation', 'action', 'function' ].includes(artifact.kind))
492
+ if (artifact.kind !== 'service' && artifact.kind !== 'context')
543
493
  addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.forHana.names, csn), artifact);
544
494
 
545
495
  forEachMemberRecursively(artifact, (member, memberName, property, path) => {
@@ -558,9 +508,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
558
508
  * @param {string} artifactName
559
509
  */
560
510
  function removeKeyPropInType(artifact, artifactName) {
561
- if (!artifact._ignore) {
511
+ if (!artifact._ignore && artifact.kind === 'type') {
562
512
  forEachMemberRecursively(artifact, (member) => {
563
- if (artifact.kind === 'type' && member.key)
513
+ if (member.key)
564
514
  delete member.key;
565
515
  }, [ 'definitions', artifactName ]);
566
516
  }
@@ -609,25 +559,27 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
609
559
  if (artifact.params) {
610
560
  // HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
611
561
  // 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');
562
+ message('def-unexpected-paramview-assoc', path, { '#': 'view' });
613
563
  }
614
564
  else if(artifact['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
615
565
  // 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 }`);
566
+ const anno = artifact['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
567
+ message('def-unexpected-calcview-assoc', path, { '#': 'entity-persistence', anno });
617
568
  }
618
569
  if (csn.definitions[member.target].params) {
619
570
  // HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
620
571
  // SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
621
- error(null, path, 'Unexpected parameterized association target');
572
+ message('def-unexpected-paramview-assoc', path, { '#': 'target' });
622
573
  }
623
- else if(csn.definitions[member.target]['@cds.persistence.udf'] || artifact['@cds.persistence.calcview']) {
574
+ else if(csn.definitions[member.target]['@cds.persistence.udf'] || csn.definitions[member.target]['@cds.persistence.calcview']) {
624
575
  // HANA won't check the assoc target but when querying an association with target UDF, this is the error:
625
576
  // SAP DBTech JDBC: [259]: invalid table name: target object SYSTEM.UDF does not exist: line 3 col 6 (at pos 43)
626
577
  // CREATE TABLE F (id INTEGER NOT NULL);
627
578
  // CREATE FUNCTION UDF RETURNS TABLE (ID INTEGER) LANGUAGE SQLSCRIPT SQL SECURITY DEFINER AS BEGIN RETURN SELECT ID FROM F; END;
628
579
  // 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
580
  // 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 }`);
581
+ const anno = csn.definitions[member.target]['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
582
+ message('def-unexpected-calcview-assoc', path, { '#': 'target-persistence', anno });
631
583
  }
632
584
  }
633
585
  }, [ 'definitions', artifactName ]);
@@ -638,7 +590,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
638
590
  * @param {string} artifactName
639
591
  */
640
592
  function handleChecksForWithParameters(artifact, artifactName) {
641
- if (!artifact._ignore && artifact.params && (artifact.kind === 'entity' || artifact.kind === 'view')) {
593
+ if (!artifact._ignore && artifact.params && (artifact.kind === 'entity')) {
642
594
  if (!artifact.query) { // table entity with params
643
595
  // Allow with plain
644
596
  error(null, [ 'definitions', artifactName ], { '#': options.toSql ? 'sql' : 'std' }, {
@@ -908,7 +860,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
908
860
  else if (assoc.on)
909
861
  return transformDollarSelfComparisonWithUnmanagedAssoc(assocOp, assoc, assocName, elemName);
910
862
 
911
- throw new Error(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
863
+ throw new ModelError(`Expected either managed or unmanaged association in $self-comparison: ${ JSON.stringify(elem.on) }`);
912
864
  }
913
865
 
914
866
  // For a condition `<elemName>.<assoc> = $self` in the ON-condition of element <elemName>,
@@ -963,22 +915,23 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
963
915
  const newOnCond = cloneWithTransformations(assoc.on, {
964
916
  ref: (value) => cloneWithTransformations(value, {}),
965
917
  });
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);
918
+ applyTransformationsOnNonDictionary({on: newOnCond}, 'on', {
919
+ ref: (parent, prop, ref) => {
920
+ if (ref[0] === assocName) // we are in the "path" from the forwarding assoc => need to remove the first part of the path
921
+ {
922
+ ref.shift();
923
+ } else if(ref && ref.length > 1 && ref[0] === '$self' && ref[1] === assocName) {
924
+ // We could also have a $self infront of the assoc name - so we would need to shift twice
925
+ ref.shift();
926
+ ref.shift();
927
+ }
928
+ else { // we are in the backlink assoc "path" => need to push at the beginning the association's id
929
+ ref.unshift(elemName);
930
+ // if there was a $self identifier in the forwarding association onCond
931
+ // we do not need it any more, as we prepended in the previous step the back association's id
932
+ if (ref[1] === '$self')
933
+ ref.splice(1, 1);
934
+ }
982
935
  }
983
936
  });
984
937
  return newOnCond;
@@ -1090,7 +1043,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1090
1043
  function flattenIndexes(art, artName) {
1091
1044
  // Flatten structs in indexes (unless explicitly asked to keep structs)
1092
1045
  const tc = art.technicalConfig;
1093
- if ((art.kind === 'entity' || art.kind === 'view') && doA2J) {
1046
+ if (art.kind === 'entity') {
1094
1047
  if (tc && tc[dialect]) {
1095
1048
  // Secondary and fulltext indexes
1096
1049
  for (const name in tc[dialect].indexes) {
@@ -1144,75 +1097,6 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
1144
1097
  }
1145
1098
  }
1146
1099
  }
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
1100
  }
1217
1101
 
1218
1102