@sap/cds-compiler 4.0.2 → 4.2.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 (101) hide show
  1. package/CHANGELOG.md +200 -5
  2. package/bin/cdsc.js +18 -15
  3. package/doc/CHANGELOG_BETA.md +16 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +33 -13
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +25 -25
  8. package/lib/base/location.js +6 -7
  9. package/lib/base/message-registry.js +123 -42
  10. package/lib/base/messages.js +18 -10
  11. package/lib/base/model.js +43 -10
  12. package/lib/checks/defaultValues.js +6 -6
  13. package/lib/checks/elements.js +11 -10
  14. package/lib/checks/foreignKeys.js +0 -5
  15. package/lib/checks/manyNavigations.js +33 -0
  16. package/lib/checks/onConditions.js +22 -14
  17. package/lib/checks/queryNoDbArtifacts.js +132 -73
  18. package/lib/checks/selectItems.js +4 -55
  19. package/lib/checks/sql-snippets.js +15 -4
  20. package/lib/checks/types.js +3 -3
  21. package/lib/checks/utils.js +4 -3
  22. package/lib/checks/validator.js +3 -1
  23. package/lib/compiler/.eslintrc.json +2 -1
  24. package/lib/compiler/assert-consistency.js +71 -40
  25. package/lib/compiler/base.js +7 -2
  26. package/lib/compiler/builtins.js +40 -41
  27. package/lib/compiler/checks.js +415 -367
  28. package/lib/compiler/classes.js +62 -0
  29. package/lib/compiler/cycle-detector.js +9 -9
  30. package/lib/compiler/define.js +124 -90
  31. package/lib/compiler/extend.js +115 -88
  32. package/lib/compiler/finalize-parse-cdl.js +26 -25
  33. package/lib/compiler/generate.js +57 -49
  34. package/lib/compiler/index.js +56 -56
  35. package/lib/compiler/kick-start.js +10 -7
  36. package/lib/compiler/moduleLayers.js +1 -1
  37. package/lib/compiler/populate.js +180 -144
  38. package/lib/compiler/propagator.js +10 -9
  39. package/lib/compiler/resolve.js +321 -246
  40. package/lib/compiler/shared.js +812 -433
  41. package/lib/compiler/tweak-assocs.js +114 -50
  42. package/lib/compiler/utils.js +241 -46
  43. package/lib/edm/.eslintrc.json +40 -1
  44. package/lib/edm/annotations/genericTranslation.js +721 -707
  45. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  46. package/lib/edm/csn2edm.js +389 -378
  47. package/lib/edm/edm.js +679 -770
  48. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  49. package/lib/edm/edmInboundChecks.js +29 -27
  50. package/lib/edm/edmPreprocessor.js +689 -648
  51. package/lib/edm/edmUtils.js +279 -300
  52. package/lib/gen/Dictionary.json +34 -10
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +1 -1
  55. package/lib/gen/languageParser.js +2857 -2856
  56. package/lib/json/from-csn.js +77 -51
  57. package/lib/json/to-csn.js +15 -15
  58. package/lib/language/antlrParser.js +2 -1
  59. package/lib/language/genericAntlrParser.js +52 -43
  60. package/lib/language/language.g4 +61 -64
  61. package/lib/language/multiLineStringParser.js +2 -0
  62. package/lib/main.d.ts +65 -0
  63. package/lib/model/csnRefs.js +37 -19
  64. package/lib/model/csnUtils.js +51 -18
  65. package/lib/model/revealInternalProperties.js +30 -22
  66. package/lib/modelCompare/compare.js +149 -41
  67. package/lib/modelCompare/utils/filter.js +55 -25
  68. package/lib/optionProcessor.js +21 -9
  69. package/lib/render/manageConstraints.js +20 -17
  70. package/lib/render/toCdl.js +63 -23
  71. package/lib/render/toHdbcds.js +2 -2
  72. package/lib/render/toRename.js +4 -9
  73. package/lib/render/toSql.js +82 -35
  74. package/lib/render/utils/common.js +11 -9
  75. package/lib/render/utils/unique.js +52 -0
  76. package/lib/transform/db/applyTransformations.js +62 -21
  77. package/lib/transform/db/assertUnique.js +7 -8
  78. package/lib/transform/db/associations.js +2 -2
  79. package/lib/transform/db/cdsPersistence.js +9 -9
  80. package/lib/transform/db/constraints.js +47 -17
  81. package/lib/transform/db/expansion.js +138 -68
  82. package/lib/transform/db/flattening.js +98 -30
  83. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  84. package/lib/transform/db/temporal.js +1 -1
  85. package/lib/transform/db/transformExists.js +8 -7
  86. package/lib/transform/db/views.js +73 -33
  87. package/lib/transform/draft/db.js +11 -9
  88. package/lib/transform/draft/odata.js +1 -1
  89. package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
  90. package/lib/transform/forRelationalDB.js +148 -136
  91. package/lib/transform/localized.js +92 -54
  92. package/lib/transform/odata/toFinalBaseType.js +3 -3
  93. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
  94. package/lib/transform/translateAssocsToJoins.js +14 -28
  95. package/lib/utils/file.js +7 -7
  96. package/lib/utils/moduleResolve.js +210 -121
  97. package/lib/utils/objectUtils.js +1 -1
  98. package/package.json +5 -5
  99. package/share/messages/check-proper-type-of.md +1 -1
  100. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  101. package/share/messages/message-explanations.json +1 -1
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { forEachGeneric } = require('../model/csnUtils');
4
4
  const { otherSideIsExpandableStructure, resolveArtifactType } = require('./utils');
5
+ const { pathId } = require('../model/csnRefs');
5
6
 
6
7
  // Only to be used with validator.js - a correct this value needs to be provided!
7
8
 
@@ -93,7 +94,7 @@ function validateOnCondition( member, memberName, property, path ) {
93
94
  }
94
95
  else {
95
96
  // It's a managed association - access of the foreign keys is allowed
96
- checkForeignKeyAccess(member.on[i], j, csnPath, (errorIndex) => {
97
+ requireForeignKeyAccess(member.on[i], j, (errorIndex) => {
97
98
  this.error('ref-unexpected-navigation', csnPath, {
98
99
  '#': 'std', id, elemref, name: ref[errorIndex].id || ref[errorIndex],
99
100
  });
@@ -158,29 +159,36 @@ function validateOnCondition( member, memberName, property, path ) {
158
159
 
159
160
  /**
160
161
  * Ensure that only foreign keys of the association `parent.ref[refIndex]` are accessed in `parent.ref`.
161
- * If a non-fk field is accessed, `callback` is invoked.
162
+ * If a non-fk field is accessed, `noForeignKeyCallback` is invoked.
162
163
  *
163
- * @param {object} parent Object containing `ref` and `_links` from csnRefs.
164
- * @param {number} refIndex Index of the to-be-checked association in `parent.ref`
165
- * @param {CSN.Path} csnPath
166
- * @param {(errorIndex: number) => void} callback Called if there are non-fk path steps. Argument is index in
167
- * `parent.ref` that is faulty. If a fk-step is missing, `errorIndex` will be `> parent.ref.length`.
164
+ * @param {object} parent
165
+ * Object containing `ref` and `_links` from csnRefs.
166
+ *
167
+ * @param {number} refIndex
168
+ * Index of the to-be-checked association in `parent.ref`
169
+ *
170
+ * @param {(errorIndex: number) => void} noForeignKeyCallback
171
+ * Called if there are non-fk path steps. Argument is index in `parent.ref` that is faulty.
172
+ * If a fk-step is missing, `errorIndex` will be `> parent.ref.length`.
168
173
  */
169
- function checkForeignKeyAccess( parent, refIndex, csnPath, callback ) {
174
+ function requireForeignKeyAccess( parent, refIndex, noForeignKeyCallback ) {
170
175
  const { ref, _links } = parent;
171
176
  const assoc = _links[refIndex].art;
172
177
 
173
- const next = ref[refIndex + 1].id || ref[refIndex + 1];
178
+ const next = pathId(ref[refIndex + 1]);
174
179
  let possibleKeys = next && assoc.keys.filter(r => r.ref[0] === next);
175
180
  if (!possibleKeys || possibleKeys.length === 0) {
176
- callback(refIndex + 1);
181
+ noForeignKeyCallback(refIndex + 1);
177
182
  }
178
183
  else {
179
184
  // For cases where `Association to T { struct.one, struct.two };` is used instead of `{ struct }`.
180
- // Note: We know that `{ struct, struct.one }` is not possible, so no prefix check required.
185
+ // We know that `{ struct, struct.one }` is not possible, so no prefix check required.
186
+ // If `ref.length` does not cover any full foreign key, then we call noForeignKeyCallback()
187
+ // as well. This could happen for `struct.one` as foreign key, and `assoc.struct = …`
188
+ // in ON-condition.
181
189
  let fkIndex = 0;
182
190
  let success = false;
183
- while (!success && possibleKeys.length > 0) {
191
+ while (!success && possibleKeys.length > 0 && refIndex + fkIndex + 1 < ref.length) {
184
192
  const pathStep = ref[refIndex + fkIndex + 1].id || ref[refIndex + fkIndex + 1];
185
193
 
186
194
  // Function is immediately executed, before next iteration of loop. Access is fine.
@@ -195,7 +203,7 @@ function checkForeignKeyAccess( parent, refIndex, csnPath, callback ) {
195
203
  ++fkIndex;
196
204
  }
197
205
  if (!success)
198
- callback(refIndex + fkIndex);
206
+ noForeignKeyCallback(refIndex + fkIndex);
199
207
  }
200
208
  }
201
209
 
@@ -211,4 +219,4 @@ function validateMixinOnCondition( query, path ) {
211
219
  forEachGeneric( query.SELECT, 'mixin', validateOnCondition.bind(this), path );
212
220
  }
213
221
 
214
- module.exports = { validateOnCondition, validateMixinOnCondition, checkForeignKeyAccess };
222
+ module.exports = { validateOnCondition, validateMixinOnCondition, requireForeignKeyAccess };
@@ -1,136 +1,195 @@
1
1
  'use strict';
2
2
 
3
3
  const { hasAnnotationValue, isPersistedOnDatabase, isBuiltinType } = require('../model/csnUtils');
4
+ const { requireForeignKeyAccess } = require('./onConditions');
5
+ const { pathId } = require('../model/csnRefs');
6
+
7
+ const generalQueryProperties = [ 'from', 'columns', 'where', 'groupBy', 'orderBy', 'having', 'limit' ];
8
+
4
9
  /**
5
- * Make sure that all source artifacts and association targets reach the database
6
- * (otherwise the view can't be activated), but only if the source artifact is NOT activated against the database // <- what does this mean?
10
+ * Ensure that all source artifacts and association targets are persisted on the database.
11
+ * Otherwise, we would end up with a JOIN against a non-existent table.
7
12
  *
8
13
  * Check the given query for:
9
- * - Associations-traversal over skipped/abstract things
14
+ * - Association-traversal over skipped/abstract things
10
15
  * - Associations (indirectly) using managed associations without foreign keys
11
16
  *
12
17
  * Currently checked:
13
- * - "columns" for something like toF.id, where F is skipped. But publishing toF is fine, will be ignored later on
18
+ * - "columns" for something like toF.field, where F is skipped. But publishing toF is fine, will be ignored later on
14
19
  * - "from" for something like "select from E.toF" where E, F or E AND F are no-db.
15
20
  *
16
- *
17
21
  * @param {CSN.Query} query Query to check
18
22
  */
19
23
  function checkQueryForNoDBArtifacts( query ) {
20
24
  if (isPersistedOnDatabase(this.artifact) && !hasAnnotationValue(this.artifact, '@cds.persistence.table')) {
21
- const generalQueryProperties = [ 'from', 'columns', 'where', 'groupBy', 'orderBy', 'having', 'limit' ];
22
25
  for (const prop of generalQueryProperties) {
23
26
  const queryPart = (query.SELECT || query.SET)[prop];
24
27
  if (Array.isArray(queryPart)) {
25
28
  for (const part of queryPart)
26
- checkRef.call(this, part, prop === 'columns');
29
+ checkQueryRef.call(this, part, prop === 'columns');
27
30
  }
28
31
  else if (typeof queryPart === 'object') {
29
- checkRef.call(this, queryPart, prop === 'columns');
32
+ checkQueryRef.call(this, queryPart, prop === 'columns');
30
33
  }
31
34
  }
32
35
  }
33
36
  }
34
37
 
35
38
  /**
36
- * Count the leaf-elements resulting from a given element.
39
+ * @param {CSN.Element} assoc Definition to check
40
+ * @returns {boolean} True, if there are any foreign keys.
41
+ */
42
+ function hasForeignKeys( assoc ) {
43
+ if (!assoc || !assoc.keys)
44
+ return false;
45
+ return _hasForeignKeyOrElements.call(this, assoc);
46
+ }
47
+
48
+ /**
49
+ * Returns true if the given definition has at least one foreign key or element leaf node.
37
50
  *
38
- * @param {CSN.Element} def Definition to check
39
- * @returns {number} Number of leaf elements
51
+ * @param {CSN.Artifact} def
52
+ * @returns {boolean} True if there are FKs/element leaves.
40
53
  */
41
- function leafCount( def ) {
42
- let c = 0;
43
- if (!def)
44
- return c;
45
- if (def.elements) {
46
- c += Object.values(def.elements).reduce((acc, e) => {
47
- acc += leafCount.call(this, e);
48
- return acc;
49
- }, 0);
54
+ function _hasForeignKeyOrElements( def ) {
55
+ if (!def) {
56
+ return false;
50
57
  }
51
58
  else if (def.keys) {
52
- c += def.keys.reduce((acc, e) => {
53
- acc += leafCount.call(this, e._art);
54
- return acc;
55
- }, 0);
59
+ return def.keys.some(e => _hasForeignKeyOrElements.call(this, e._art));
60
+ }
61
+ else if (def.elements) {
62
+ return Object.values(def.elements).some( e => _hasForeignKeyOrElements.call(this, e));
56
63
  }
57
64
  else if (def.type) {
58
65
  if (isBuiltinType(def.type) && !(def.target))
59
- return 1;
60
- c += leafCount.call(this, this.csn.definitions[def.type]);
66
+ return true;
67
+ return _hasForeignKeyOrElements.call(this, this.artifactRef(def.type, null));
61
68
  }
62
- return c;
69
+ return false;
63
70
  }
64
71
 
65
72
  /**
66
- * Check the given ref for usage of skipped/abstract assoc targets
73
+ * Check the given `obj.ref` for usage of skipped/abstract assoc targets
67
74
  *
68
- * @param {object} obj CSN "thing" to check
75
+ * @param {CSN.Column} obj CSN "thing" to check
69
76
  * @param {boolean} inColumns True if the ref is part of a from
70
77
  */
71
- function checkRef( obj, inColumns ) {
72
- if (!(obj && obj.ref) || !obj._links || obj.$scope === 'alias')
78
+ function checkQueryRef( obj, inColumns ) {
79
+ if (!obj)
73
80
  return;
74
81
 
75
- const links = obj._links;
82
+ if (obj.expand || obj.inline)
83
+ _checkExpandInline.call(this, obj);
84
+
85
+ else if (obj.ref && obj._links)
86
+ _checkRef.call(this, obj.ref, obj._links, obj.$path, inColumns);
87
+ }
88
+
89
+ /**
90
+ * Run _checkRef on all expand/inline structure leaf nodes.
91
+ * We do so by creating artificial paths that follow expand/inline nodes to their leaves.
92
+ *
93
+ * @param {CSN.Column} obj
94
+ * @param {CSN.Path} previousRefs
95
+ * @param {object[]} previousLinks
96
+ */
97
+ function _checkExpandInline( obj, previousRefs = [], previousLinks = [] ) {
98
+ if (obj.ref && obj._links) { // There could be anonymous nested "expand".
99
+ previousRefs = previousRefs.concat(obj.ref);
100
+ previousLinks = previousLinks.concat(obj._links);
101
+ }
102
+
103
+ if (!obj.expand && !obj.inline) {
104
+ if (obj.ref && obj._links) {
105
+ // `inColumns: true` for expand/inline
106
+ _checkRef.call(this, previousRefs, previousLinks, obj.$path, true);
107
+ }
108
+ return;
109
+ }
110
+
111
+ for (const col of obj.expand || obj.inline)
112
+ _checkExpandInline.call(this, col, previousRefs, previousLinks);
113
+ }
114
+
115
+ /**
116
+ * Implementation of checkQueryRef() that works on ref/links arrays instead of a column.
117
+ *
118
+ * @param {CSN.Path} ref
119
+ * @param {object[]} _links
120
+ * @param {CSN.Path} $path
121
+ * @param {boolean} inColumns
122
+ */
123
+ function _checkRef( ref, _links, $path, inColumns ) {
124
+ if (!ref || !_links )
125
+ return;
126
+
127
+ let nonPersistedTarget = null;
128
+ const isPublishedAssoc = this.csnUtils.isAssocOrComposition(_links[_links.length - 1].art);
76
129
 
77
130
  // Don't check the last element - to allow association publishing in columns
78
- for (let i = 0; i < (inColumns ? links.length - 1 : links.length); i++) {
79
- const link = links[i];
131
+ for (let i = 0; i < (inColumns ? _links.length - 1 : _links.length); i++) {
132
+ const link = _links[i];
80
133
  if (!link)
81
134
  continue;
82
-
83
135
  const { art } = link;
84
136
  if (!art)
85
137
  continue;
86
138
 
87
- const endArtifact = art.target ? this.csn.definitions[art.target] : art;
88
- const pathStep = obj.ref[i].id ? obj.ref[i].id : obj.ref[i];
89
- const name = art.target ? art.target : pathStep;
90
- if (!isPersistedOnDatabase(endArtifact)) {
91
- const nextElement = obj.ref[i + 1];
92
- /**
93
- * if we only navigate to foreign keys of the managed association in a view, we do not need to join,
94
- * thus we can produce the view even if the target of the association is not persisted
95
- *
96
- * @param {CSN.Element} assoc association in ref
97
- * @param {string} nextStep the ref step following the association
98
- * @returns {boolean} true if no join will be generated
99
- */
100
- const isJoinRelevant = (assoc, nextStep) => {
101
- if (!assoc.keys)
102
- return true;
103
- const isExposedColumnAssocOrComposition = this.csnUtils.isAssocOrComposition(obj._art.type);
104
- return !assoc.keys
105
- .some(fk => fk.ref[0] === nextStep && !isExposedColumnAssocOrComposition);
106
- };
107
- if (isJoinRelevant(art, nextElement)) {
108
- const cdsPersistenceSkipped = hasAnnotationValue(endArtifact, '@cds.persistence.skip');
109
- this.error( null, obj.$path, {
110
- id: pathStep, elemref: obj, name, '#': cdsPersistenceSkipped ? 'std' : 'abstract',
139
+ const isLast = i >= _links.length - 1;
140
+ const isUnmanagedOrNoKeys = !art.keys;
141
+ const targetArt = art.target ? this.artifactRef(art.target) : art;
142
+ const pathStep = pathId(ref[i]);
143
+ const name = art.target || pathStep;
144
+
145
+ // If any path-step is not persisted, then all following path steps must only access foreign keys.
146
+ // For example, it could be toF.toG.field, where toG is FK of toF; the FK-only-check would succeed,
147
+ // but we only check "field" in the next iteration, where it is seen as access on a non-skipped
148
+ // entity, hence the need to store if any target is skipped.
149
+ if (!isPersistedOnDatabase(targetArt))
150
+ nonPersistedTarget = { name, pathStep };
151
+
152
+ if (nonPersistedTarget) {
153
+ let isJoinRelevant = isPublishedAssoc || // publishing associations is always join relevant
154
+ isLast || // e.g. FROM targets are always join relevant.
155
+ isUnmanagedOrNoKeys; // unmanaged associations are always join relevant -> no FKs
156
+
157
+ if (!isJoinRelevant) {
158
+ // for managed, published associations with more than one $path-step, only FK
159
+ // access is allowed.
160
+ requireForeignKeyAccess({ ref, _links }, i, () => {
161
+ isJoinRelevant = true;
162
+ });
163
+ }
164
+
165
+ if (isJoinRelevant) {
166
+ const cdsPersistenceSkipped = hasAnnotationValue(targetArt, '@cds.persistence.skip');
167
+ this.error( null, $path, {
168
+ '#': cdsPersistenceSkipped ? 'std' : 'abstract',
169
+ id: nonPersistedTarget.pathStep,
170
+ elemref: { ref },
171
+ name: nonPersistedTarget.name,
111
172
  }, {
112
173
  std: 'Unexpected “@cds.persistence.skip” annotation on association target $(NAME) of $(ID) in path $(ELEMREF)',
113
174
  abstract: 'Unexpected “abstract” association target $(NAME) of $(ID) in path $(ELEMREF)',
114
175
  } );
176
+ break; // only one error per path
115
177
  }
116
178
  }
179
+
117
180
  // check managed association to have foreign keys array filled
118
- if (art.keys && leafCount.call(this, art) === 0) {
119
- this.error(null,
120
- obj.$path,
121
- { id: pathStep, elemref: obj },
122
- 'Path step $(ID) of $(ELEMREF) has no foreign keys');
181
+ if (art.keys && !hasForeignKeys.call(this, art)) {
182
+ this.error('expr-missing-foreign-key', $path, { id: pathStep, elemref: { ref } } );
183
+ break; // only one error per path
123
184
  }
124
-
125
- if (art.on) {
126
- for (let j = 0; j < art.on.length; j++) {
127
- if (j < art.on.length - 2 && art.on[j].ref && art.on[j + 1] === '=' && art.on[j + 2].ref) {
185
+ else if (art.on) {
186
+ for (let j = 0; j < art.on.length - 2; j++) {
187
+ if (art.on[j].ref && art.on[j + 1] === '=' && art.on[j + 2].ref) {
128
188
  const [ fwdAssoc, fwdPath ] = getForwardAssociation(pathStep, art.on[j], art.on[j + 2]);
129
- if (fwdAssoc && fwdAssoc.keys && leafCount.call(this, fwdAssoc) === 0) {
130
- this.error(null, obj.$path,
131
- { name: pathStep, elemref: obj, id: fwdPath },
189
+ if (fwdAssoc?.keys && !hasForeignKeys.call(this, fwdAssoc)) {
190
+ this.error(null, $path, { name: pathStep, elemref: { ref }, id: fwdPath },
132
191
  'Path step $(NAME) of $(ELEMREF) is a $self comparison with $(ID) that has no foreign keys');
133
- j += 2;
192
+ break; // only one error per path
134
193
  }
135
194
  }
136
195
  }
@@ -139,7 +198,7 @@ function checkRef( obj, inColumns ) {
139
198
  }
140
199
 
141
200
  /**
142
- * Get the forward association from a backling $self association.
201
+ * Get the forward association from a backlink $self association.
143
202
  *
144
203
  * @param {string} prefix Name of the association
145
204
  * @param {object} lhs Left hand side of the on-condition part
@@ -5,10 +5,12 @@ const { forEachGeneric, applyTransformationsOnNonDictionary } = require('../mode
5
5
  // Only to be used with validator.js - a correct this value needs to be provided!
6
6
 
7
7
  /**
8
- * Validate select items of a query. If a column reference starts with $self or $projection, it must not contain association steps.
8
+ * Validate select items of a query. If a column reference starts with $self or
9
+ * $projection, it must not contain association steps.
9
10
  * Furthermore, for to.hdbcds, window functions are not allowed.
10
11
  *
11
- * For to.hdbcds-hdbcds, structures and managed associations are not allowed as they are not flattened - @see rejectManagedAssociationsAndStructuresForHdbcdsNames
12
+ * For to.hdbcds-hdbcds, structures and managed associations are not allowed
13
+ * as they are not flattened - @see rejectManagedAssociationsAndStructuresForHdbcdsNames
12
14
  *
13
15
  * @param {CSN.Query} query query object
14
16
  * @todo Why do we care about this with $self?
@@ -17,58 +19,6 @@ function validateSelectItems( query ) {
17
19
  const { SELECT } = query;
18
20
  if (!SELECT)
19
21
  return;
20
- /**
21
- * Check for a $self.<assoc> in columns etc. - since the $self.<assoc> references the "outside" view
22
- * of the association, this is not allowed.
23
- *
24
- * @param {string} queryPart Part of the query that is being checked
25
- * @returns {Function} Function as callback for applyTransformations
26
- */
27
- function checkRefForInvalid$Self( queryPart ) {
28
- const signalError = (error, parent, type) => {
29
- if (queryPart === 'columns') {
30
- error(null, parent.$path,
31
- { name: parent.ref[0], type },
32
- 'Select items starting with $(NAME) must not contain path steps of type $(TYPE)');
33
- }
34
- else if (queryPart === 'orderBy') {
35
- error(null, parent.$path,
36
- { id: queryPart, type },
37
- 'Items of the $(ID)-clause must not contain path steps of type $(TYPE)');
38
- }
39
- else {
40
- error(null, parent.$path,
41
- { id: queryPart, name: parent.ref[0], type },
42
- 'Items of the $(ID)-clause starting with $(NAME) must not contain path steps of type $(TYPE)');
43
- }
44
- };
45
- return function checkForInvalid$SelfInRef(parent) {
46
- if (parent.ref && (parent.$scope === '$self' || parent.$scope === '$query')) {
47
- const { _links } = parent;
48
- for (let j = parent.$scope === '$self' ? 1 : 0; j < _links.length - 1; j++) {
49
- if (_links[j].art.target) {
50
- if (_links[j].art.on) {
51
- // It's an unmanaged association - traversal is always forbidden
52
- signalError(this.error, parent, _links[j].art.type);
53
- }
54
- else {
55
- // It's a managed association - access of the foreign keys is allowed
56
- const nextRef = parent.ref[j + 1].id || parent.ref[j + 1];
57
- if (!_links[j].art.keys.some(r => r.ref[0] === nextRef))
58
- signalError(this.error, parent, _links[j].art.type);
59
- }
60
- }
61
- }
62
-
63
- const last = _links[_links.length - 1];
64
-
65
- if (last.art.target && last.art.on) {
66
- // It's an unmanaged association - traversal is always forbidden
67
- signalError(this.error, parent, last.art.type);
68
- } // managed is okay, can be handled via tuple expansion
69
- }
70
- };
71
- }
72
22
 
73
23
  /**
74
24
  * Check the given assoc filter for usage of $self - in an assoc-filter, you must only
@@ -90,7 +40,6 @@ function validateSelectItems( query ) {
90
40
 
91
41
  const aTCB = (parent, prop) => {
92
42
  applyTransformationsOnNonDictionary(parent, prop, {
93
- ref: checkRefForInvalid$Self(prop).bind(this),
94
43
  where: checkFilterForInvalid$Self.bind(this),
95
44
  }, { skipStandard: { on: true }, drillRef: true });
96
45
  };
@@ -18,14 +18,25 @@ function checkSqlAnnotationOnElement( member, memberName, prop, path ) {
18
18
  this.message('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, 'Annotation $(ANNO) can\'t be used on elements' );
19
19
 
20
20
  if (member['@sql.append']) {
21
- if (this.artifact.query || this.artifact.projection)
21
+ if (this.artifact.query || this.artifact.projection) {
22
22
  this.message('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, 'Annotation $(ANNO) can\'t be used on elements in views' );
23
- else if (this.csnUtils.isStructured(member))
23
+ }
24
+ else if (this.csnUtils.isStructured(member)) {
24
25
  this.message('anno-invalid-sql-struct', path, { anno: 'sql.append' }, 'Annotation $(ANNO) can\'t be used on structured elements' );
25
- else if (member.value && !member.value.stored)
26
+ }
27
+ else if (this.csnUtils.isManagedAssociation(member)) {
28
+ this.message('anno-invalid-sql-assoc', path, { anno: 'sql.append', '#': member.type }, {
29
+ std: 'Annotation $(ANNO) can\'t be used here',
30
+ 'cds.Association': 'Annotation $(ANNO) can\'t be used on association elements',
31
+ 'cds.Composition': 'Annotation $(ANNO) can\'t be used on composition elements',
32
+ } );
33
+ }
34
+ else if (member.value && !member.value.stored) {
26
35
  this.message('anno-invalid-sql-calc', path, { anno: 'sql.append' }, 'Annotation $(ANNO) can\'t be used on calculated elements on read' );
27
- else
36
+ }
37
+ else {
28
38
  checkValidAnnoValue(member, '@sql.append', path, this.error, this.options);
39
+ }
29
40
  }
30
41
  }
31
42
 
@@ -162,9 +162,9 @@ function errorAboutMissingType( error, path, artifact, name,
162
162
  let variant = isElement ? 'elm' : 'std';
163
163
  if (artifact.value?.stored)
164
164
  variant = 'calc';
165
- error('check-proper-type', path, { art: name, '#': variant }, {
166
- std: 'Dubious type $(ART) without type information',
167
- elm: 'Dubious element $(ART) without type information',
165
+ error('def-missing-type', path, { art: name, '#': variant }, {
166
+ std: 'Missing type for $(ART)',
167
+ elm: 'Missing type for element $(ART)',
168
168
  calc: 'A stored calculated element must have a type',
169
169
  });
170
170
  }
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { isBuiltinType } = require('../model/csnUtils');
4
- const { RelationalOperators } = require('../transform/transformUtilsNew');
4
+ const { RelationalOperators } = require('../transform/transformUtils');
5
5
  /**
6
6
  * Prepare the ref steps so that they are loggable
7
7
  *
@@ -58,8 +58,9 @@ function otherSideIsExpandableStructure( on, startIndex ) {
58
58
  * @returns {object} final artifact type
59
59
  */
60
60
  function resolveArtifactType( art ) {
61
- if (art && art.type && !isBuiltinType(art.type))
62
- return this.csnUtils.getFinalTypeInfo(art.type);
61
+ const type = art?._type?.type || art?.type;
62
+ if (type && !isBuiltinType(type))
63
+ return this.csnUtils.getFinalTypeInfo(type);
63
64
 
64
65
  return art;
65
66
  }
@@ -11,6 +11,7 @@ const enrich = require('./enricher');
11
11
  const { validateSelectItems } = require('./selectItems');
12
12
  const { rejectParamDefaultsInHanaCds, warnAboutDefaultOnAssociationForHanaCds } = require('./defaultValues');
13
13
  const validateCdsPersistenceAnnotation = require('./cdsPersistence');
14
+ const navigationIntoMany = require('./manyNavigations');
14
15
  const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
15
16
  const validateHasPersistedElements = require('./hasPersistedElements');
16
17
  const checkForHanaTypes = require('./checkForTypes');
@@ -71,7 +72,7 @@ const forRelationalDBArtifactValidators
71
72
  checkSqlAnnotationOnArtifact,
72
73
  ];
73
74
 
74
- const forRelationalDBCsnValidators = [ nonexpandableStructuredInExpression ];
75
+ const forRelationalDBCsnValidators = [ nonexpandableStructuredInExpression, navigationIntoMany ];
75
76
  /**
76
77
  * @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
77
78
  */
@@ -233,6 +234,7 @@ function forRelationalDB( csn, that ) {
233
234
  {
234
235
  skipArtifact: artifact => artifact.abstract ||
235
236
  hasAnnotationValue(artifact, '@cds.persistence.skip') ||
237
+ hasAnnotationValue(artifact, '@cds.persistence.exists') ||
236
238
  [ 'action', 'function', 'event' ].includes(artifact.kind),
237
239
  });
238
240
  }
@@ -2,6 +2,7 @@
2
2
  "root": true,
3
3
  "extends": "../../.eslintrc-ydkjsi.json",
4
4
  "rules": {
5
- "cds-compiler/message-no-quotes": "warn"
5
+ "cds-compiler/message-no-quotes": "warn",
6
+ "cds-compiler/space-in-func-call": "warn"
6
7
  }
7
8
  }