@sap/cds-compiler 6.3.6 → 6.4.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 (62) hide show
  1. package/CHANGELOG.md +101 -3
  2. package/LICENSE +32 -0
  3. package/README.md +14 -2
  4. package/bin/cdsse.js +0 -3
  5. package/doc/CHANGELOG_BETA.md +1 -1
  6. package/doc/CHANGELOG_DEPRECATED.md +1 -1
  7. package/lib/base/message-registry.js +9 -2
  8. package/lib/base/messages.js +1 -1
  9. package/lib/base/model.js +2 -0
  10. package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
  11. package/lib/checks/existsMustEndInAssoc.js +1 -1
  12. package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
  13. package/lib/checks/validator.js +4 -2
  14. package/lib/compiler/assert-consistency.js +3 -2
  15. package/lib/compiler/builtins.js +5 -6
  16. package/lib/compiler/checks.js +37 -26
  17. package/lib/compiler/define.js +1 -1
  18. package/lib/compiler/extend.js +39 -50
  19. package/lib/compiler/finalize-parse-cdl.js +1 -1
  20. package/lib/compiler/lsp-api.js +1 -1
  21. package/lib/compiler/populate.js +2 -2
  22. package/lib/compiler/propagator.js +29 -6
  23. package/lib/compiler/resolve.js +13 -3
  24. package/lib/compiler/shared.js +157 -133
  25. package/lib/compiler/tweak-assocs.js +87 -29
  26. package/lib/compiler/xpr-rewrite.js +164 -160
  27. package/lib/edm/annotations/edmJson.js +206 -37
  28. package/lib/edm/csn2edm.js +13 -0
  29. package/lib/edm/edmUtils.js +2 -2
  30. package/lib/gen/BaseParser.js +106 -72
  31. package/lib/gen/CdlGrammar.checksum +1 -1
  32. package/lib/gen/CdlParser.js +1501 -1509
  33. package/lib/json/to-csn.js +8 -5
  34. package/lib/language/genericAntlrParser.js +0 -0
  35. package/lib/main.js +19 -16
  36. package/lib/model/csnRefs.js +589 -521
  37. package/lib/model/csnUtils.js +8 -5
  38. package/lib/model/enrichCsn.js +1 -0
  39. package/lib/parsers/AstBuildingParser.js +73 -28
  40. package/lib/render/toCdl.js +2 -1
  41. package/lib/render/toHdbcds.js +6 -3
  42. package/lib/render/toSql.js +5 -0
  43. package/lib/transform/db/applyTransformations.js +1 -1
  44. package/lib/transform/db/assertUnique.js +4 -1
  45. package/lib/transform/db/assocsToQueries/transformExists.js +3 -10
  46. package/lib/transform/db/assocsToQueries/utils.js +0 -5
  47. package/lib/transform/db/cdsPersistence.js +17 -18
  48. package/lib/transform/db/expansion.js +179 -3
  49. package/lib/transform/db/flattening.js +16 -5
  50. package/lib/transform/db/rewriteCalculatedElements.js +79 -283
  51. package/lib/transform/effective/main.js +8 -1
  52. package/lib/transform/forOdata.js +1 -1
  53. package/lib/transform/forRelationalDB.js +21 -80
  54. package/lib/transform/localized.js +75 -127
  55. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +89 -63
  56. package/lib/transform/transformUtils.js +23 -21
  57. package/lib/transform/translateAssocsToJoins.js +7 -5
  58. package/lib/transform/tupleExpansion.js +16 -3
  59. package/package.json +3 -3
  60. package/doc/DeprecatedOptions_v2.md +0 -150
  61. package/doc/NameResolution.md +0 -837
  62. package/lib/transform/parseExpr.js +0 -415
@@ -1,15 +1,20 @@
1
1
  'use strict';
2
2
 
3
- const { applyTransformations, transformAnnotationExpression } = require('../../model/csnUtils');
4
- const { isBuiltinType } = require('../../base/builtins');
3
+ const { applyTransformations, transformAnnotationExpression, implicitAs } = require('../../model/csnUtils');
5
4
 
6
5
 
7
- function replaceForeignKeyRefsInExpressionAnnotations(csn, options, messageFunctions, csnUtils, iterateOptions = {}) {
6
+ /**
7
+ * If a path in an annotation expression can be interpreted as accessing a local foreign key, then
8
+ * the foreign key reference is replaced with the foreign key itself.
9
+ * Exception is when the association path step has a filter.
10
+ *
11
+ * @param {CSN} csn
12
+ * @param {Object} csnUtils
13
+ * @param {Object} iterateOptions
14
+ */
15
+ function replaceForeignKeyRefsInExpressionAnnotations(csn, csnUtils, iterateOptions = {}) {
8
16
  const transformers = {
9
- elements: processRef,
10
- params: processRef,
11
- actions: processRef,
12
- // '@': processRef
17
+ '@': processRef,
13
18
  };
14
19
  applyTransformations(csn, transformers, [ processRef ], iterateOptions);
15
20
 
@@ -17,29 +22,27 @@ function replaceForeignKeyRefsInExpressionAnnotations(csn, options, messageFunct
17
22
  transformAnnotationExpression(parent, prop, {
18
23
  ref: (parent, _prop, ref, path, _p, _ppn, ctx) => {
19
24
  const { art, links }
20
- = (parent._art && parent._links)
25
+ = parent._art && parent._links
21
26
  ? { art: parent._art, links: parent._links }
22
27
  : csnUtils.inspectRef(path);
28
+
23
29
  // if a reference points to a structure(managed assoc or structured element), then we do not process
24
30
  // as we can't guess which specific foreign key is targeted
25
- if (!art || csnUtils.isManagedAssociation(art) || csnUtils.isStructured(art))
31
+ if (
32
+ !art ||
33
+ csnUtils.isManagedAssociation(art) ||
34
+ csnUtils.isStructured(art)
35
+ )
26
36
  return;
27
37
 
28
- const allMngAssocsInRef = links.filter(link => csnUtils.isManagedAssociation(link.art));
29
- if (!allMngAssocsInRef.length)
30
- return;
31
- let firstAssocToProcess = allMngAssocsInRef[0];
38
+ const modifiedRef = replaceRefsWithFKs(ref, links, art);
32
39
 
33
- const mngAssocsWithFilter = allMngAssocsInRef.filter(assoc => typeof ref[assoc.idx] !== 'string');
34
- if (mngAssocsWithFilter.length) {
35
- const refTail = links.slice(mngAssocsWithFilter.at(-1).idx + 1);
36
- firstAssocToProcess = refTail.find(link => csnUtils.isManagedAssociation(link.art));
37
- }
38
-
39
- const match = findMatchingForeignKeyForAssoc(firstAssocToProcess, art, ref, links);
40
- if (match) {
41
- const refHead = ref.slice(0, match.idx);
42
- parent.ref = [ ...refHead, match.fkName ];
40
+ // update the ref and string token to true if there was FK replacement
41
+ if (
42
+ modifiedRef.length !== ref.length ||
43
+ !modifiedRef.every((val, index) => val === ref[index])
44
+ ) {
45
+ parent.ref = modifiedRef;
43
46
  if (ctx?.annoExpr?.['='])
44
47
  ctx.annoExpr['='] = true;
45
48
  }
@@ -48,50 +51,73 @@ function replaceForeignKeyRefsInExpressionAnnotations(csn, options, messageFunct
48
51
  path);
49
52
  }
50
53
 
51
- function findMatchingForeignKeyForAssoc(assoc, refArt, ref, links) {
52
- if (!assoc)
53
- return undefined;
54
-
55
- const expectedFkName = findExpectedFkName(assoc, ref, links);
56
- const gfks = assoc.art?.$generatedForeignKeys;
57
- if (!gfks)
58
- return undefined;
59
- const matchedFk = gfks.find(fk => fk.source === refArt && fk.name === expectedFkName);
60
- if (matchedFk)
61
- return { fkName: matchedFk.name, idx: assoc.idx };
62
-
63
- // try to find FK substitution in the next assoc in the ref (if there is such assoc)
64
- const refTail = links.slice(assoc.idx + 1);
65
- const nextAssoc = refTail.find(link => csnUtils.isManagedAssociation(link.art));
66
- return findMatchingForeignKeyForAssoc(nextAssoc, refArt, ref, links);
67
-
68
-
69
- function findExpectedFkName(assoc, ref, links) {
70
- let expectedFkName = ref[assoc.idx];
71
- const refAliasMapping = assoc.art.keys.reduce( (acc, key) => {
72
- acc[key.ref.join('_')] = key.as;
73
- return acc;
74
- }, {});
75
- let bufferRef = [];
76
- for (let i = assoc.idx + 1; i < links.length; i++) {
77
- const link = links[i];
78
- bufferRef.push(ref[i]);
79
- if (csnUtils.isManagedAssociation(link.art)) {
80
- const subFkName = findExpectedFkName(link, ref, links);
81
- if (!subFkName)
82
- return undefined;
83
- expectedFkName += bufferRef.length > 1
84
- ? `_${ bufferRef.slice(0, -1).join('_') }_${ subFkName }`
85
- : `_${ subFkName }`;
86
- break;
54
+ // Replace references to foreign keys
55
+ function replaceRefsWithFKs(originalRef, links, expectedFkArt) {
56
+ let result = [ ...originalRef ];
57
+ // stringify the tail of the ref for finding the potential foreign key
58
+ const refTail = [ originalRef[originalRef.length - 1] ];
59
+
60
+ for (let i = originalRef.length - 2; i >= 0; i--) {
61
+ const currentRef = originalRef[i];
62
+ const currentLink = links[i].art;
63
+
64
+ // skip processing if the current reference is a filter
65
+ if (typeof currentRef !== 'string')
66
+ return result;
67
+
68
+ // check if the current link is a managed association
69
+ if (csnUtils.isManagedAssociation(currentLink)) {
70
+ const matchedForeignKey = findMatchingForeignKeyForAssoc(currentLink, currentRef, refTail, expectedFkArt);
71
+
72
+ if (matchedForeignKey) {
73
+ // update the result and refTailAsStr with the matched foreign key
74
+ result = [ ...result.slice(0, i), matchedForeignKey.name ];
75
+ refTail.unshift(currentRef);
87
76
  }
88
- else if (isBuiltinType(link.art.type)) {
89
- expectedFkName += `_${ refAliasMapping[bufferRef.join('_')] || ref[i] }`;
90
- bufferRef = [];
77
+ else {
78
+ return result; // return if no matching foreign key is found
91
79
  }
92
80
  }
93
- return expectedFkName;
81
+ else {
82
+ // update refTail for non-association links
83
+ refTail.unshift(currentRef);
84
+ }
85
+ }
86
+ return result;
87
+ }
88
+
89
+ // Lookup the foreign key in the association's generated foreign keys
90
+ function findMatchingForeignKeyForAssoc(assoc, assocName, refTail, expectedFkArt) {
91
+ const expectedFkName = getExpectedForeignKeyName(assoc, assocName, refTail);
92
+ const matchedFk = assoc.$generatedForeignKeys?.find(fk => fk.source === expectedFkArt && fk.name === expectedFkName);
93
+ return matchedFk;
94
+ }
95
+
96
+ // Generate the expected foreign key name, considering aliases, tuple expansion name changes, etc.
97
+ function getExpectedForeignKeyName(assoc, assocName, refTail) {
98
+ const refAliasMapping = assoc.keys.reduce( (acc, key) => {
99
+ acc[key.ref.join('_')] = key.as || implicitAs(key.ref);
100
+ return acc;
101
+ }, {});
102
+ // generate the string representation of the reference tail
103
+ const refTailAsStr = replaceRefsIfAliased(refTail, refAliasMapping) || refTail.join('_');
104
+ return `${ assocName }_${ refTailAsStr }`;
105
+ }
106
+
107
+ // Check if any prefix of refTail matches an alias in refAliasMapping and replace it
108
+ function replaceRefsIfAliased(refTail, refAliasMapping) {
109
+ // loop through refTail and try to find a match in the refAliasMapping
110
+ // no need to look for the longest match as it is not allowed to declare
111
+ // duplicate key references in one FKs scope
112
+ let candidate = '';
113
+ for (let idx = 0; idx < refTail.length; idx++) {
114
+ candidate = candidate ? `${ candidate }_${ refTail[idx] }` : refTail[idx];
115
+ if (refAliasMapping[candidate]) {
116
+ refTail.splice(0, idx + 1, refAliasMapping[candidate]);
117
+ return refTail.join('_');
118
+ }
94
119
  }
120
+ return undefined;
95
121
  }
96
122
  }
97
123
 
@@ -54,7 +54,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
54
54
  extractValidFromToKeyElement,
55
55
  checkMultipleAssignments,
56
56
  checkAssignment,
57
- recurseElements,
57
+ recurseElements, // standalone-function
58
58
  renameAnnotation,
59
59
  setAnnotation,
60
60
  resetAnnotation,
@@ -808,26 +808,6 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
808
808
  }
809
809
  }
810
810
 
811
- /**
812
- * Calls `callback` for each element in `elements` property of `artifact` recursively.
813
- *
814
- * @param {CSN.Artifact} artifact the artifact
815
- * @param {CSN.Path} path path to get to `artifact` (mainly used for error messages)
816
- * @param {(art: CSN.Artifact, path: CSN.Path) => any} callback Function called for each element recursively.
817
- */
818
- function recurseElements(artifact, path, callback) {
819
- callback(artifact, path);
820
- const { elements } = artifact;
821
- if (elements) {
822
- path.push('elements', null);
823
- forEach(elements, (name, obj) => {
824
- path[path.length - 1] = name;
825
- recurseElements(obj, path, callback);
826
- });
827
- // reset path for subsequent usages
828
- path.length -= 2; // equivalent to 2x pop()
829
- }
830
- }
831
811
 
832
812
  // Rename annotation 'fromName' in 'node' to 'toName' (both names including '@')
833
813
  function renameAnnotation(node, fromName, toName) {
@@ -920,8 +900,30 @@ function rewriteBuiltinTypeRef(csn) {
920
900
  });
921
901
  }
922
902
 
903
+ /**
904
+ * Calls `callback` for each element in `elements` property of `artifact` recursively.
905
+ *
906
+ * @param {CSN.Artifact} artifact the artifact
907
+ * @param {CSN.Path} path path to get to `artifact` (mainly used for error messages)
908
+ * @param {(art: CSN.Artifact, path: CSN.Path) => any} callback Function called for each element recursively.
909
+ */
910
+ function recurseElements(artifact, path, callback) {
911
+ callback(artifact, path);
912
+ const { elements } = artifact;
913
+ if (elements) {
914
+ path.push('elements', null);
915
+ forEach(elements, (name, obj) => {
916
+ path[path.length - 1] = name;
917
+ recurseElements(obj, path, callback);
918
+ });
919
+ // reset path for subsequent usages
920
+ path.length -= 2; // equivalent to 2x pop()
921
+ }
922
+ }
923
+
923
924
  module.exports = {
924
925
  // This function retrieves the actual exports
925
926
  getTransformers,
926
927
  rewriteBuiltinTypeRef,
928
+ recurseElements,
927
929
  };
@@ -487,10 +487,12 @@ function translateAssocsToJoins(model, inputOptions = {}) {
487
487
  // create a new toplevel AND op otherwise
488
488
  const onCond = (Array.isArray(node.on) ? node.on[0] : node.on);
489
489
 
490
- if (onCond.op.val === 'and')
491
- onCond.args.push(parenthesise(filter));
492
- else
493
- node.on = parenthesise({ op: { val: 'and' }, args: [ parenthesise(onCond), parenthesise(filter) ] });
490
+ if (filter.args?.length !== 0) {
491
+ if (onCond.op.val === 'and')
492
+ onCond.args.push(parenthesise(filter));
493
+ else
494
+ node.on = parenthesise({ op: { val: 'and' }, args: [ parenthesise(onCond), parenthesise(filter) ] });
495
+ }
494
496
  }
495
497
  return node;
496
498
 
@@ -1496,7 +1498,7 @@ function translateAssocsToJoins(model, inputOptions = {}) {
1496
1498
  */
1497
1499
  let qatName = pathStep.id;
1498
1500
 
1499
- if (pathStep.where)
1501
+ if (pathStep.where && pathStep.where?.args?.length !== 0)
1500
1502
  qatName += JSON.stringify(compactExpr(pathStep.where));
1501
1503
 
1502
1504
  if (pathStep.args) {
@@ -14,7 +14,7 @@ const { cloneCsnNonDict } = require('../model/cloneCsn');
14
14
  *
15
15
  * @type {string[]}
16
16
  */
17
- const RelationalOperators = [ '=', '<>', '==', '!=', 'is', 'is not' /* , 'like' */ ];
17
+ const RelationalOperators = [ '=', '<>', '==', '!=', 'is', 'is not' ];
18
18
 
19
19
  /**
20
20
  * Operators that to be used to combine expanded expressions by keeping logical relations.
@@ -64,6 +64,11 @@ function tupleExpansion(csn, csnUtils, msgFunctions) {
64
64
  having: expandExpr,
65
65
  where: expandExpr,
66
66
  xpr: expandExpr,
67
+ list: (parent, name, args, path) => {
68
+ // Don't iterate `group by (foo, bar)`
69
+ if (path.at(-2) !== 'groupBy' && path.at(-2) !== 'orderBy')
70
+ expandExpr(parent, name, args, path);
71
+ },
67
72
  args: (parent, name, args, path) => {
68
73
  if (!parent.id && !parent.func)
69
74
  return; // ensure we're not in JOIN
@@ -262,6 +267,10 @@ function tupleExpansion(csn, csnUtils, msgFunctions) {
262
267
  }
263
268
  }
264
269
 
270
+ /**
271
+ * @param expr
272
+ * @param {CSN.Path} location
273
+ */
265
274
  function rejectAnyDirectStructureReference(expr, location) {
266
275
  if (expr[0] === 'exists') {
267
276
  // we ignore WHERE EXISTS clauses; they are not relevant for OData,
@@ -318,11 +327,15 @@ function tupleExpansion(csn, csnUtils, msgFunctions) {
318
327
  * `{ _art: <leaf_artifact>, ref: [...] }`
319
328
  * with `_art` identifying `ref[ref.length-1]`
320
329
  *
321
- * A produced path has the form `{ _art: <ref>, ref: [ <id> (, <id>)* ] }`
330
+ * A produced path has the form `{ _art: <ref>, ref: [ <id> (, <id>)* ], comparisonRef: [ <id> (, <id>)* ] }`
322
331
  *
323
332
  * Flattening stops on all non-structured elements, if followMgdAssoc=false.
324
333
  *
325
- * If fullRef is true, a path step is produced as `{ id: <id>, _art: <link> }`
334
+ * If fullRef is true, a path step is produced as `{ id: <id>, _art: <link> }`.
335
+ *
336
+ * The returned paths will have a property 'comparisonRef', that may differ from 'ref'
337
+ * for managed associations (as it uses the foreign key name).
338
+ * The caller may need to delete that property.
326
339
  */
327
340
  function flattenPath(path, fullRef = false, followMgdAssoc = false) {
328
341
  let art = path._art;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "6.3.6",
3
+ "version": "6.4.6",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -33,8 +33,8 @@
33
33
  "deployHdbcdsGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdbcds test3/test.deploy.git-diffs.js",
34
34
  "deployHdiGitDiffs": "CDS_COMPILER_DEPLOY_HANA=1 mocha --reporter-option maxDiffSize=0 --extensions .hdi test3/test.deploy.git-diffs.js",
35
35
  "gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
36
- "coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
37
- "coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
36
+ "coverage": "cross-env npx nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && npx nyc report --reporter=lcov",
37
+ "coverage:piper": "cross-env npx nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && npx nyc report --reporter=cobertura && npx nyc report --reporter=lcov",
38
38
  "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ && node scripts/linter/lintConstants.js && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
39
39
  "lint:edmx": "node scripts/odata/lint-edmx-v4.js",
40
40
  "tslint": "tsc --pretty -p .",
@@ -1,150 +0,0 @@
1
- # Deprecated Options and How to Avoid Them
2
-
3
- __Important__: With compiler v3, these deprecated options were removed!
4
-
5
- To ease the migration to CDS Compiler Version 2,
6
- the compiler can be called with an option `deprecated`
7
- which makes the compiler behave more like Compiler Version 1 for certain features.
8
-
9
- As the name suggest, this option should be used only for a limited time.
10
- The support for certain v1 features might also be dropped after a while
11
- (without an increase of the compiler major version).
12
-
13
- __When the `deprecated` option is set, the `beta` option is ignored,
14
- and several new features are not available.__
15
-
16
- The value of the option `deprecated` is a dictionary
17
- mapping v1 feature names to (usually boolean) values.
18
- This document lists all those features,
19
- and describes what you can do instead of setting these features.
20
-
21
-
22
- ## Deprecated features influencing the name of generated entities
23
-
24
- The `compile()` function generates entities in the following cases:
25
-
26
- 1. When an element in an entity is specified to be `localized`,
27
- it creates a __texts entity__ for that entity.
28
- 2. For managed composition of aspects,
29
- it creates a __target entity__ based on the provided target aspect.
30
- 3. A projection in a service is automatically generated
31
- for correspondingly tagged entities in the model (which is then “__auto-exposed__”)
32
- if an association/composition to the model entity is to be _implicitly redirected_
33
- to an exposed entity in the service and no such entity exists yet.
34
-
35
- As a short example for 1 and 3 (2 is similar to 1):
36
-
37
- ```
38
- entity my.Model.Base {
39
- key id: UUID;
40
- text: localized String;
41
- }
42
- service our.Service {
43
- entity Proj as projection on my.Model.Base;
44
- }
45
- ```
46
-
47
- The compiled model contains the following generated entities:
48
-
49
- * the text entity `my.Model.Base.texts`,
50
- which is a composition target of the generated element `my.Model.Base:texts`
51
- * the auto-exposed projection `our.Service.Proj.texts`
52
- which is a composition target of `our.Service.Proj:texts`
53
-
54
- For the following subsections (and in general), it is important to understand that
55
- you can define all auto-exposed entities yourself (well, they are not
56
- _auto_-exposed anymore)
57
- _without_ any difference in the compiled model
58
- except for the sequence of entities in `‹csn›.definitions`.
59
- That means, you can append the following line to the above example:
60
-
61
- ```
62
- @cds.autoexposed entity our.Service.Proj.texts as projection on my.Model.Base.texts;
63
- ```
64
-
65
- The annotation `@cds.autoexposed` ensures that this self-exposed entity
66
- really behaves exactly like an auto-exposed entity:
67
-
68
- * it is only used as a direct redirection target, not as an indirect one
69
- (a detailed explanation of this topic is out-of-scope for this document),
70
- * runtimes also attach a runtime semantics to the annotation `@cds.autoexposed`.
71
-
72
- Thus, if you do not like the name of the generated auto-exposed entity,
73
- you can simply __expose the model entity__ yourself and choose the name you like.
74
- __Never ever__ define a projection on the auto-exposed entity,
75
- which has worked in v1 versions and in the v2.1.x versions in certain situations.
76
-
77
-
78
- ### Deprecated `generatedEntityNameWithUnderscore`
79
-
80
- With compiler v1,
81
- the generated entities had no suffix starting with a `.` like `.texts` for texts entities,
82
- but a suffix starting with a `_`.
83
-
84
- If you have a reference to a generated entity in your model,
85
- you now have to change the model accordingly.
86
- For example, if you had for v1
87
-
88
- ```
89
- using { my.Model.Base, my.Model.Base_texts } from './myModel';
90
- entity Root {
91
- key ID: UUID;
92
- base: Association to Base;
93
- texts: Association to Base_texts;
94
- }
95
- ```
96
-
97
- you now have to write for v2 (you see that it is usually actually simpler now)
98
-
99
- ```
100
- using { my.Model.Base } from './myModel';
101
- entity Root {
102
- key ID: UUID;
103
- base: Association to Base;
104
- texts: Association to Base.texts;
105
- }
106
- ```
107
-
108
- If you are a CSN consumer and analyse the compiled model, you might need
109
- to adopt your code for the name change from `my.Model.Base_texts` to `my.Model.Base.texts`.
110
-
111
- In the following areas, nothing will change:
112
-
113
- * In the OData backend,
114
- the “new” `.`s are replaced by `_`s to make the names conform to the OData naming rules.
115
- In other words, the EDMX (for the generated entities) looks the same as with v1.
116
- * In the SQL/Hana backends,
117
- the “new” `.`s are also replaced by `_`s to adopt to HANA CDS naming restrictions
118
- (with the standard naming mode `plain`, all `.`s are replaced by `_`s anyway).
119
- In other words, no texts table migration will take place.
120
-
121
- But anyway, you might temporarily want to keep the v1 behavior
122
- by setting the option `deprecated.generatedEntityNameWithUnderscore`.
123
- If you do so, scoped definitions are not possible (like they aren't in v1).
124
-
125
- ### Deprecated `shortAutoexposed`
126
-
127
- In compiler v1 without an option
128
- (especially the v1 option `dependentAutoexposed` which basically leads the v2 default behavior),
129
- the name for auto-exposed entities were constructed by
130
- adding the name part after the last `.` to the service name.
131
-
132
- That is, for the above example,
133
- the auto-exposed projection on `my.Model.Base_texts` (in v1) is named
134
- `our.Service.Base_texts` in v1.
135
-
136
- You can temporarily enable that behavior in v2 by setting the options
137
- `deprecated.generatedEntityNameWithUnderscore` and `deprecated.shortAutoexposed`.
138
-
139
- If you just set `deprecated.shortAutoexposed`, you get `our.Service.Base.texts`.
140
-
141
- If you really need that name (instead of the v2 name `our.Service.Proj.texts`),
142
- you can expose the texts entity manually instead of setting the deprecated option:
143
-
144
- ```
145
- @cds.autoexposed entity our.Service.Base.texts as projection on my.Model.Base.texts;
146
- ```
147
-
148
- Again, never define a projection on the auto-exposed entity –
149
- you get an error for that starting with compiler v2.2.0
150
- (and earlier for certain definition sequences anyway, actually in v1 also).