@sap/cds-compiler 2.4.4 → 2.10.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 (106) hide show
  1. package/CHANGELOG.md +241 -1
  2. package/bin/.eslintrc.json +17 -0
  3. package/bin/cds_update_identifiers.js +8 -7
  4. package/bin/cdsc.js +180 -132
  5. package/bin/cdshi.js +18 -11
  6. package/bin/cdsse.js +38 -32
  7. package/bin/cdsv2m.js +8 -7
  8. package/doc/CHANGELOG_BETA.md +36 -1
  9. package/lib/api/main.js +81 -100
  10. package/lib/api/options.js +17 -11
  11. package/lib/api/validate.js +12 -8
  12. package/lib/backends.js +0 -81
  13. package/lib/base/keywords.js +32 -2
  14. package/lib/base/location.js +2 -2
  15. package/lib/base/message-registry.js +66 -4
  16. package/lib/base/messages.js +84 -27
  17. package/lib/base/model.js +2 -61
  18. package/lib/checks/arrayOfs.js +0 -1
  19. package/lib/checks/defaultValues.js +27 -2
  20. package/lib/checks/elements.js +1 -6
  21. package/lib/checks/enricher.js +8 -2
  22. package/lib/checks/foreignKeys.js +0 -6
  23. package/lib/checks/managedWithoutKeys.js +17 -0
  24. package/lib/checks/nonexpandableStructured.js +38 -0
  25. package/lib/checks/onConditions.js +9 -45
  26. package/lib/checks/queryNoDbArtifacts.js +27 -9
  27. package/lib/checks/selectItems.js +25 -2
  28. package/lib/checks/types.js +26 -2
  29. package/lib/checks/unknownMagic.js +38 -0
  30. package/lib/checks/utils.js +61 -0
  31. package/lib/checks/validator.js +66 -13
  32. package/lib/compiler/assert-consistency.js +24 -12
  33. package/lib/compiler/builtins.js +2 -0
  34. package/lib/compiler/checks.js +6 -4
  35. package/lib/compiler/definer.js +101 -39
  36. package/lib/compiler/index.js +88 -59
  37. package/lib/compiler/resolver.js +455 -209
  38. package/lib/compiler/shared.js +57 -33
  39. package/lib/edm/annotations/genericTranslation.js +183 -187
  40. package/lib/edm/csn2edm.js +128 -99
  41. package/lib/edm/edm.js +18 -21
  42. package/lib/edm/edmPreprocessor.js +361 -127
  43. package/lib/edm/edmUtils.js +103 -33
  44. package/lib/gen/Dictionary.json +74 -28
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +18 -4
  47. package/lib/gen/language.tokens +124 -118
  48. package/lib/gen/languageLexer.interp +13 -1
  49. package/lib/gen/languageLexer.js +870 -839
  50. package/lib/gen/languageLexer.tokens +116 -111
  51. package/lib/gen/languageParser.js +5894 -5614
  52. package/lib/json/from-csn.js +152 -67
  53. package/lib/json/to-csn.js +334 -135
  54. package/lib/language/antlrParser.js +4 -3
  55. package/lib/language/errorStrategy.js +1 -0
  56. package/lib/language/genericAntlrParser.js +24 -14
  57. package/lib/language/language.g4 +188 -128
  58. package/lib/main.d.ts +435 -0
  59. package/lib/main.js +31 -7
  60. package/lib/model/api.js +78 -0
  61. package/lib/model/csnRefs.js +463 -187
  62. package/lib/model/csnUtils.js +280 -136
  63. package/lib/model/enrichCsn.js +75 -4
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/modelCompare/compare.js +70 -25
  66. package/lib/optionProcessor.js +13 -10
  67. package/lib/render/.eslintrc.json +4 -1
  68. package/lib/render/DuplicateChecker.js +8 -5
  69. package/lib/render/toCdl.js +123 -40
  70. package/lib/render/toHdbcds.js +156 -65
  71. package/lib/render/toSql.js +87 -11
  72. package/lib/render/utils/common.js +55 -9
  73. package/lib/render/utils/sql.js +3 -3
  74. package/lib/sql-identifier.js +6 -1
  75. package/lib/transform/{sql → db}/.eslintrc.json +0 -0
  76. package/lib/transform/{sql → db}/assertUnique.js +7 -8
  77. package/lib/transform/{sql → db}/constraints.js +35 -20
  78. package/lib/transform/db/draft.js +353 -0
  79. package/lib/transform/db/expansion.js +582 -0
  80. package/lib/transform/db/flattening.js +325 -0
  81. package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
  82. package/lib/transform/{sql → db}/helpers.js +0 -0
  83. package/lib/transform/{sql → db}/transformExists.js +256 -60
  84. package/lib/transform/forHanaNew.js +216 -765
  85. package/lib/transform/forOdataNew.js +60 -56
  86. package/lib/transform/localized.js +48 -26
  87. package/lib/transform/odata/attachPath.js +19 -4
  88. package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
  89. package/lib/transform/odata/generateForeignKeyElements.js +13 -12
  90. package/lib/transform/odata/referenceFlattener.js +60 -36
  91. package/lib/transform/odata/sortByAssociationDependency.js +4 -4
  92. package/lib/transform/odata/structuralPath.js +76 -0
  93. package/lib/transform/odata/structureFlattener.js +21 -22
  94. package/lib/transform/odata/toFinalBaseType.js +5 -5
  95. package/lib/transform/odata/typesExposure.js +27 -17
  96. package/lib/transform/odata/utils.js +2 -2
  97. package/lib/transform/transformUtilsNew.js +141 -77
  98. package/lib/transform/translateAssocsToJoins.js +17 -14
  99. package/lib/transform/universalCsnEnricher.js +67 -0
  100. package/lib/utils/file.js +0 -11
  101. package/lib/utils/moduleResolve.js +6 -8
  102. package/lib/utils/timetrace.js +6 -1
  103. package/package.json +2 -1
  104. package/lib/base/deepCopy.js +0 -66
  105. package/lib/json/walker.js +0 -26
  106. package/lib/utils/string.js +0 -17
package/lib/base/model.js CHANGED
@@ -19,7 +19,6 @@ const queryOps = {
19
19
  */
20
20
  const availableBetaFlags = {
21
21
  // enabled by --beta-mode
22
- keylessManagedAssoc: true,
23
22
  foreignKeyConstraints: true,
24
23
  toRename: true,
25
24
  addTextsLanguageAssoc: true,
@@ -28,9 +27,9 @@ const availableBetaFlags = {
28
27
  mapAssocToJoinCardinality: true,
29
28
  ignoreAssocPublishingInUnion: true,
30
29
  nestedProjections: true,
30
+ enableUniversalCsn: true,
31
+ windowFunctions: true,
31
32
  // disabled by --beta-mode
32
- pretransformedCSN: false,
33
- renderSQL: false,
34
33
  nestedServices: false,
35
34
  };
36
35
 
@@ -181,63 +180,6 @@ function setProp (obj, prop, value) {
181
180
  return value;
182
181
  }
183
182
 
184
- // FIXME: this function is only used by old tests -- REMOVE!
185
- //
186
- // Clone 'node', transforming nodes therein recursively. Object 'transformers' is expected
187
- // to contain a mapping of property 'key' names to transformer functions. The node's properties
188
- // are walked recursively, calling each transformer function on its corresponding property
189
- // 'key' of 'node', replacing 'value' in 'resultNode' with the function's return value
190
- // (returning 'undefined' will delete the property).
191
- // If no transformation function is found for 'key', the first letter of 'key' is tried
192
- // instead (this seems to be intended for handling annotations that start with '@' ?)
193
- // FIXME: Do we really need this ?
194
- // If `withArtifactLink` is set, the `_artifact` links of `node` are copied too (not cloned,
195
- // of course).
196
- // Regardless of their names, transformers are never applied to dictionary elements.
197
- //
198
- // The transformer functions are called with the following signature:
199
- // transformer(value, node, resultNode, key)
200
- function cloneWithTransformations(node, transformers, withArtifactLink) {
201
-
202
- return transformNode(node);
203
-
204
- // This general transformation function will be applied to each node recursively
205
- function transformNode(node) {
206
- // Return primitive values and null unchanged, but let objects and dictionaries through
207
- // (Note that 'node instanceof Object' would be false for dictionaries).
208
- if (node === null || typeof node !== 'object') {
209
- return node
210
- }
211
- // Simply return if node is to be ignored
212
- if (node._ignore)
213
- return undefined;
214
- // Transform arrays element-wise
215
- if (Array.isArray(node)) {
216
- return node.map(transformNode);
217
- }
218
- // Things not having 'proto' are dictionaries
219
- const proto = Object.getPrototypeOf(node);
220
- // Iterate own properties of 'node' and transform them into 'resultNode'
221
- const resultNode = Object.create(proto);
222
- for (let key of Object.keys(node)) {
223
- // Dictionary always use transformNode(), other objects their transformer according to key
224
- const transformer = !proto ? transformNode : transformers[key] || transformers[key.charAt(0)];
225
- // Apply transformer, or use transformNode() if there is none
226
- const resultValue = (transformer || transformNode)(node[key], node, resultNode, key);
227
- if (resultValue !== undefined) {
228
- resultNode[key] = resultValue;
229
- }
230
- }
231
- // For non-dictionaries, take over `_artifact` if requested
232
- if (withArtifactLink && proto) {
233
- let pd = Object.getOwnPropertyDescriptor(node, '_artifact')
234
- if (pd)
235
- Object.defineProperty(resultNode, '_artifact', pd);
236
- }
237
- return resultNode;
238
- }
239
- }
240
-
241
183
 
242
184
  module.exports = {
243
185
  isBetaEnabled,
@@ -252,5 +194,4 @@ module.exports = {
252
194
  forEachGeneric,
253
195
  forEachInOrder,
254
196
  setProp,
255
- cloneWithTransformations,
256
197
  };
@@ -48,7 +48,6 @@ function validateAssociationsInItems(member) {
48
48
  *
49
49
  * @param {CSN.Artifact} art Artifact
50
50
  * @param {string} artName Name of the artifact
51
- *
52
51
  */
53
52
  function checkChainedArray(art, artName) {
54
53
  if (!this.csnUtils.getServiceName(artName))
@@ -35,8 +35,33 @@ function validateDefaultValues(member, memberName, prop, path) {
35
35
  * @param {CSN.Path} path Path to the member
36
36
  */
37
37
  function rejectParamDefaultsInHanaCds(member, memberName, prop, path) {
38
- if (member.default && prop === 'params' && this.options.toHana)
38
+ if (member.default && prop === 'params' && this.options.transformation === 'hdbcds')
39
39
  this.error(null, path, 'Parameter default values are not supported in SAP HANA CDS');
40
40
  }
41
41
 
42
- module.exports = { validateDefaultValues, rejectParamDefaultsInHanaCds };
42
+ /**
43
+ * For HANA CDS, we render a default for a mixin if the projected entity contains
44
+ * a derived association with a default defined on it. This leads to a deployment error
45
+ * and should be warned about.
46
+ *
47
+ * @param {CSN.Element} member Member to validate
48
+ * @param {string} memberName Name of the member
49
+ * @param {string} prop Property being looped over
50
+ * @param {CSN.Path} path Path to the member
51
+ */
52
+ function warnAboutDefaultOnAssociationForHanaCds(member, memberName, prop, path) {
53
+ const art = this.csn.definitions[path[1]];
54
+ if (!art.query && this.options.transformation === 'hdbcds' && member.target && member.default) {
55
+ this.warning(null, path, { '#': member._type.type === 'cds.Association' ? 'std' : 'comp' },
56
+ {
57
+ std: 'Unexpected default defined on association',
58
+ comp: 'Unexpected default defined on composition',
59
+ });
60
+ }
61
+ }
62
+
63
+ module.exports = {
64
+ validateDefaultValues,
65
+ rejectParamDefaultsInHanaCds,
66
+ warnAboutDefaultOnAssociationForHanaCds,
67
+ };
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { forEachMember, forEachMemberRecursively } = require('../model/csnUtils');
4
4
  const { isGeoTypeName } = require('../compiler/builtins');
5
- const { isBetaEnabled } = require('../base/model');
6
5
 
7
6
  // Only to be used with validator.js - a correct `this` value needs to be provided!
8
7
 
@@ -103,15 +102,11 @@ function checkVirtualElement(member) {
103
102
  * @param {CSN.Artifact} art The artifact
104
103
  */
105
104
  function checkManagedAssoc(art) {
106
- forEachMemberRecursively(art, (member, memberName) => {
105
+ forEachMemberRecursively(art, (member) => {
107
106
  if (this.csnUtils.isAssocOrComposition(member.type) &&
108
107
  !isManagedComposition.bind(this)(member)) {
109
108
  if (member.on)
110
109
  return;
111
- if (!isBetaEnabled(this.options, 'keylessManagedAssoc') && (!member.keys || member.keys.length === 0)) {
112
- this.error(null, member.$path, { name: memberName },
113
- `The managed association $(NAME) has no foreign keys`);
114
- }
115
110
  const max = member.cardinality && member.cardinality.max;
116
111
  if (max === '*' || Number(max) > 1) {
117
112
  const isNoDb = art['@cds.persistence.skip'] || art['@cds.persistence.exists'];
@@ -31,7 +31,7 @@ function enrichCsn( csn ) {
31
31
  target: simpleRef,
32
32
  includes: simpleRef,
33
33
  // Annotations are ignored.
34
- '@': () => {},
34
+ '@': () => { /* ignore annotations */ },
35
35
  };
36
36
  let cleanupCallbacks = [];
37
37
 
@@ -103,7 +103,9 @@ function enrichCsn( csn ) {
103
103
 
104
104
  // eslint-disable-next-line jsdoc/require-jsdoc
105
105
  function pathRef( node, prop, path ) {
106
- const { links, art, scope } = inspectRef( csnPath );
106
+ const {
107
+ links, art, scope, $env,
108
+ } = inspectRef( csnPath );
107
109
  if (links) {
108
110
  setProp(node, '_links', links);
109
111
  cleanupCallbacks.push(() => delete node._links);
@@ -112,6 +114,10 @@ function enrichCsn( csn ) {
112
114
  setProp(node, '_art', art );
113
115
  cleanupCallbacks.push(() => delete node._art);
114
116
  }
117
+ if ($env) {
118
+ setProp(node, '$env', $env );
119
+ cleanupCallbacks.push(() => delete node.$env);
120
+ }
115
121
  setProp(node, '$scope', scope);
116
122
  cleanupCallbacks.push(() => delete node.$scope);
117
123
  setProp(node, '$path', [ ...csnPath ]);
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { isBetaEnabled } = require('../base/model');
4
-
5
3
  // Only to be used with validator.js - a correct this value needs to be provided!
6
4
 
7
5
  /**
@@ -20,17 +18,13 @@ function validateForeignKeys(member) {
20
18
 
21
19
  // Declared as arrow-function to keep scope the same (this value)
22
20
  const handleAssociation = (mem) => {
23
- let elementCount = 0;
24
21
  for (let i = 0; i < mem.keys.length; i++) {
25
22
  if (mem.keys[i].ref) {
26
23
  if (!mem.keys[i]._art)
27
24
  continue;
28
25
  // eslint-disable-next-line no-use-before-define
29
26
  checkForItems(mem.keys[i]._art);
30
- elementCount++;
31
27
  }
32
- if (!isBetaEnabled(this.options, 'keylessManagedAssoc') && elementCount === 0)
33
- this.error(null, member.$path, 'Empty structured types/elements must not be used as foreign keys');
34
28
  }
35
29
  };
36
30
 
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+
4
+ /**
5
+ * Trigger a recompilation in case of an association without .keys and without .on
6
+ *
7
+ * @param {CSN.Element} member the element to be checked
8
+ * @param {string} memberName the elements name
9
+ * @param {string} prop which kind of member are we looking at -> only prop "elements"
10
+ */
11
+ function managedWithoutKeys(member, memberName, prop) {
12
+ if (prop === 'elements' && member.target && !member.keys && !member.on) { // trigger recompilation
13
+ throw new Error('Expected association to have either an on-condition or foreign keys.');
14
+ }
15
+ }
16
+
17
+ module.exports = managedWithoutKeys;
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ const { otherSideIsExpandableStructure, resolveArtifactType } = require('./utils');
4
+
5
+ /**
6
+ * Check the given expression for non-expandable structure usage
7
+ *
8
+ * @param {object} parent Object with the expression as a property
9
+ * @param {string} name Name of the expression property on parent
10
+ * @param {Array} expression Expression to check - .on .xpr .having and .where
11
+ */
12
+ function nonexpandableStructuredInExpression(parent, name, expression) {
13
+ for (let i = 0; i < expression.length; i++) {
14
+ if (expression[i].ref) {
15
+ const { ref } = expression[i];
16
+ // eslint-disable-next-line prefer-const
17
+ let { _art, $scope } = expression[i];
18
+ if (!_art)
19
+ continue;
20
+ const validStructuredElement = otherSideIsExpandableStructure.call(this, expression, i);
21
+ if (_art) {
22
+ _art = resolveArtifactType.call(this, _art);
23
+ // Paths of an expression may end on a structured element only if both operands in the expression end on a structured element
24
+ if (_art.elements && !validStructuredElement && $scope !== '$self') { // TODO: Use $self to navigate to struct
25
+ this.error(null, expression[i].$path, { elemref: { ref } },
26
+ 'Unexpected usage of structured type $(ELEMREF)');
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+
33
+ module.exports = {
34
+ on: nonexpandableStructuredInExpression,
35
+ having: nonexpandableStructuredInExpression,
36
+ where: nonexpandableStructuredInExpression,
37
+ xpr: nonexpandableStructuredInExpression,
38
+ };
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { forEachGeneric, isBuiltinType } = require('../model/csnUtils');
3
+ const { forEachGeneric } = require('../model/csnUtils');
4
+ const { otherSideIsExpandableStructure, resolveArtifactType } = require('./utils');
4
5
 
5
6
  // Only to be used with validator.js - a correct this value needs to be provided!
6
7
 
@@ -42,48 +43,6 @@ function otherSideIsValidDollarSelf(on, startIndex) {
42
43
  return false;
43
44
  }
44
45
 
45
- /**
46
- * Check that the opposite operand to a relational term is something
47
- * structured that can be used for tuple expansion. This can either be a
48
- * real 'elements' thing or a managed association/composition with foreign keys.
49
- *
50
- * @param {Array} on the on condition which to check
51
- * @param {number} startIndex the index of the relational term in the on condition array
52
- * @returns {boolean} indicates whether the other side of a relational term is expandable
53
- */
54
- function otherSideIsExpandableStructure(on, startIndex) {
55
- if (on[startIndex - 1] && [ '=', '<', '>', '>=', '<=', '!=', '<>' ].includes(on[startIndex - 1]))
56
- return isOk(resolveArtifactType.call(this, on[startIndex - 2]._art));
57
-
58
- else if (on[startIndex + 1] && [ '=', '<', '>', '>=', '<=', '!=', '<>' ].includes(on[startIndex + 1]))
59
- return isOk(resolveArtifactType.call(this, on[startIndex + 2]._art));
60
-
61
- return false;
62
-
63
- /**
64
- * Artifact is structured or a managed association/compoisition
65
- *
66
- * @param {CSN.Artifact} art Artifact
67
- * @returns {boolean} True if expandable
68
- */
69
- function isOk(art) {
70
- return !!(art && (art.elements || (art.target && art.keys)));
71
- }
72
- }
73
-
74
- /**
75
- * Get the real type of an artifact
76
- *
77
- * @param {object} art Whatever _art by csnRefs can be - element or artifact
78
- * @returns {object} final artifact type
79
- */
80
- function resolveArtifactType(art) {
81
- if (art && art.type && !isBuiltinType(art.type))
82
- return this.getFinalBaseType(art);
83
-
84
- return art;
85
- }
86
-
87
46
  /**
88
47
  * Validate an on-condition
89
48
  *
@@ -100,6 +59,11 @@ function resolveArtifactType(art) {
100
59
  */
101
60
  function validateOnCondition(member, memberName, property, path) {
102
61
  if (member && member.on) {
62
+ // complain about nullability constraint on managed composition
63
+ if (member.targetAspect && {}.hasOwnProperty.call(member, 'notNull')) {
64
+ this.warning(null, path.concat([ 'on' ]),
65
+ 'Unexpected nullability constraint defined on managed composition');
66
+ }
103
67
  for (let i = 0; i < member.on.length; i++) {
104
68
  if (member.on[i].ref) {
105
69
  const { ref } = member.on[i];
@@ -148,8 +112,8 @@ function validateOnCondition(member, memberName, property, path) {
148
112
  // 2) Path ends on an association (managed or unmanaged) and the other operand is a '$self'
149
113
 
150
114
  // If this path ends structured or on an association, perform the check:
151
- if ((_art.elements || _art.target) &&
152
- !( /* 1) */ (_art.elements || _art.target && _art.keys) && validStructuredElement ||
115
+ if ((_art.target) &&
116
+ !( /* 1) */ (_art.target && _art.keys) && validStructuredElement ||
153
117
  /* 2) */ (_art.target && validDollarSelf)) &&
154
118
  !_art.virtual) {
155
119
  this.error(null, onPath, { elemref: { ref } },
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { hasBoolAnnotation, isPersistedOnDatabase, isBuiltinType } = require('../model/csnUtils');
3
+ const { hasAnnotationValue, isPersistedOnDatabase, isBuiltinType } = require('../model/csnUtils');
4
4
  /**
5
5
  * Make sure that all source artifacts and association targets reach the database
6
6
  * (otherwise the view can't be activated), but only if the source artifact is NOT activated against the database
@@ -69,13 +69,31 @@ function checkQueryForNoDBArtifacts(query) {
69
69
  const pathStep = obj.ref[i].id ? obj.ref[i].id : obj.ref[i];
70
70
  const name = art.target ? art.target : pathStep;
71
71
  if (!isPersistedOnDatabase(endArtifact)) {
72
- const cdsPersistenceSkipped = hasBoolAnnotation(endArtifact, '@cds.persistence.skip');
73
- this.error( null, obj.$path, {
74
- id: pathStep, elemref: obj, name, '#': cdsPersistenceSkipped ? 'std' : 'abstract',
75
- }, {
76
- std: 'Unexpected “@cds.persistence.skip” annotation on association target $(NAME) of $(ID) in path $(ELEMREF)',
77
- abstract: 'Unexpected “abstract” association target $(NAME) of $(ID) in path $(ELEMREF)',
78
- } );
72
+ const nextElement = obj.ref[i + 1];
73
+ /**
74
+ * if we only navigate to foreign keys of the managed association in a view, we do not need to join,
75
+ * thus we can produce the view even if the target of the association is not persisted
76
+ *
77
+ * @param {CSN.Element} assoc association in ref
78
+ * @param {string} nextStep the ref step following the association
79
+ * @returns {boolean} true if no join will be generated
80
+ */
81
+ const isJoinRelevant = (assoc, nextStep) => {
82
+ if (!assoc.keys)
83
+ return true;
84
+ const isExposedColumnAssocOrComposition = this.csnUtils.isAssocOrComposition(obj._art.type);
85
+ return !assoc.keys
86
+ .some(fk => fk.ref[0] === nextStep && !isExposedColumnAssocOrComposition);
87
+ };
88
+ if (isJoinRelevant(art, nextElement)) {
89
+ const cdsPersistenceSkipped = hasAnnotationValue(endArtifact, '@cds.persistence.skip');
90
+ this.error( null, obj.$path, {
91
+ id: pathStep, elemref: obj, name, '#': cdsPersistenceSkipped ? 'std' : 'abstract',
92
+ }, {
93
+ std: 'Unexpected “@cds.persistence.skip” annotation on association target $(NAME) of $(ID) in path $(ELEMREF)',
94
+ abstract: 'Unexpected “abstract” association target $(NAME) of $(ID) in path $(ELEMREF)',
95
+ } );
96
+ }
79
97
  }
80
98
  // check managed association to have foreign keys array filled
81
99
  if (art.keys && leafCount(art) === 0) {
@@ -101,7 +119,7 @@ function checkQueryForNoDBArtifacts(query) {
101
119
  }
102
120
  };
103
121
 
104
- if (isPersistedOnDatabase(this.artifact) && !hasBoolAnnotation(this.artifact, '@cds.persistence.table')) {
122
+ if (isPersistedOnDatabase(this.artifact) && !hasAnnotationValue(this.artifact, '@cds.persistence.table')) {
105
123
  const generalQueryProperties = [ 'from', 'columns', 'where', 'groupBy', 'orderBy', 'having', 'limit' ];
106
124
  for (const prop of generalQueryProperties) {
107
125
  const queryPart = (query.SELECT || query.SET)[prop];
@@ -11,7 +11,7 @@ const { forEachGeneric } = require('../model/csnUtils');
11
11
  */
12
12
  function validateSelectItems(query) {
13
13
  const { SELECT } = query;
14
- if (!SELECT || !SELECT.columns)
14
+ if (!SELECT)
15
15
  return;
16
16
 
17
17
  forEachGeneric(SELECT, 'columns', (selectItem) => {
@@ -24,5 +24,28 @@ function validateSelectItems(query) {
24
24
  }
25
25
  }
26
26
  });
27
+ // .call() with 'this' to ensure we have access to the options
28
+ rejectManagedAssociationsAndStructuresForHdbcsNames.call(this, SELECT, SELECT.$path);
27
29
  }
28
- module.exports = validateSelectItems;
30
+
31
+
32
+ /**
33
+ * For the to.hdbcds transformation with naming mode 'hdbcds', structures and managed associations are not flattened/resolved.
34
+ * It is therefore not possible to publish such elements in a view.
35
+ * This function iterates over all published elements of a query artifact and asserts that no such elements are published.
36
+ *
37
+ * @param {CSN.Artifact} queryArtifact the query artifact which should be checked
38
+ * @param {CSN.Path} artifactPath the path to that artifact
39
+ */
40
+ function rejectManagedAssociationsAndStructuresForHdbcsNames(queryArtifact, artifactPath) {
41
+ if (this.options.transformation === 'hdbcds' && this.options.sqlMapping === 'hdbcds') {
42
+ forEachGeneric(queryArtifact, 'elements', (selectItem, elemName, prop, elementPath) => {
43
+ if (this.csnUtils.isManagedAssociationElement(selectItem))
44
+ this.error('query-unexpected-assoc-hdbcds', elementPath);
45
+ if (this.csnUtils.isStructured(selectItem))
46
+ this.error('query-unexpected-structure-hdbcds', elementPath);
47
+ }, artifactPath);
48
+ }
49
+ }
50
+
51
+ module.exports = { validateSelectItems, rejectManagedAssociationsAndStructuresForHdbcsNames };
@@ -1,9 +1,28 @@
1
1
  'use strict';
2
2
 
3
- const { getUtils, isBuiltinType } = require('../model/csnUtils');
3
+ const { getUtils, isBuiltinType, hasAnnotationValue } = require('../model/csnUtils');
4
4
 
5
5
  // Only to be used with validator.js - a correct this value needs to be provided!
6
6
 
7
+ /**
8
+ * Scale must not be 'variable' or 'floating'
9
+ *
10
+ * scale property is always propagated
11
+ *
12
+ * @param {CSN.Element} member the element to be checked
13
+ * @param {string} memberName the elements name
14
+ * @param {string} prop which kind of member are we looking at -> only prop "elements"
15
+ * @param {CSN.Path} path the path to the member
16
+ */
17
+ function checkDecimalScale(member, memberName, prop, path) {
18
+ if (hasAnnotationValue(this.artifact, '@cds.persistence.exists') ||
19
+ // skip is already filtered in validator, here for completeness
20
+ hasAnnotationValue(this.artifact, '@cds.persistence.skip'))
21
+ return;
22
+ if (member.scale && [ 'variable', 'floating' ].includes(member.scale))
23
+ this.error(null, path, { name: member.scale }, 'Unexpected scale $(NAME)');
24
+ }
25
+
7
26
  /**
8
27
  * View parameter for hana must be of scalar type
9
28
  *
@@ -165,4 +184,9 @@ function hasArtifactTypeInformation(artifact) {
165
184
  artifact.type; // => `type A : [type of] Integer`
166
185
  }
167
186
 
168
- module.exports = { checkTypeDefinitionHasType, checkElementTypeDefinitionHasType, checkTypeIsScalar };
187
+ module.exports = {
188
+ checkTypeDefinitionHasType,
189
+ checkElementTypeDefinitionHasType,
190
+ checkTypeIsScalar,
191
+ checkDecimalScale,
192
+ };
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ // We only care about the "wild" ones - $at is validated by the compiler
4
+ const magicVariables = {
5
+ $user: [
6
+ 'id', // $user.id
7
+ 'locale', // $user.locale
8
+ ],
9
+ $session: [
10
+ // no valid ways for this
11
+ ],
12
+ };
13
+
14
+ /**
15
+ * Check that the given ref does not use magic variables for which we don't have
16
+ * a valid way of rendering.
17
+ *
18
+ * Valid ways:
19
+ * - We know what to do -> $user.id on HANA
20
+ * - The user tells us what to do -> options.magicVars
21
+ *
22
+ * @param {object} parent Object with the ref as a property
23
+ * @param {string} name Name of the ref property on parent
24
+ * @param {Array} ref to check
25
+ */
26
+ function unknownMagicVariable(parent, name, ref) {
27
+ if (parent.$scope && parent.$scope === '$magic') {
28
+ const [ head, ...rest ] = ref;
29
+ const tail = rest.join('.');
30
+ const magicVariable = magicVariables[head];
31
+ if (magicVariable && magicVariable.indexOf(tail) === -1)
32
+ this.error(null, parent.$location, { id: tail, elemref: parent }, 'Magic variable is not supported - path $(ELEMREF), step $(ID)');
33
+ }
34
+ }
35
+
36
+ module.exports = {
37
+ ref: unknownMagicVariable,
38
+ };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const { isBuiltinType } = require('../model/csnUtils');
4
+
5
+ /**
6
+ * Prepare the ref steps so that they are loggable
7
+ *
8
+ * @param {any} refStep part of a ref
9
+ * @returns {string} Loggable string
10
+ */
11
+ function logReady(refStep) {
12
+ return refStep.id || refStep;
13
+ }
14
+
15
+ /**
16
+ * Check that the opposite operand to a relational term is something
17
+ * structured that can be used for tuple expansion. This can either be a
18
+ * real 'elements' thing or a managed association/composition with foreign keys.
19
+ *
20
+ * @param {Array} on the on condition which to check
21
+ * @param {number} startIndex the index of the relational term in the on condition array
22
+ * @returns {boolean} indicates whether the other side of a relational term is expandable
23
+ */
24
+ function otherSideIsExpandableStructure(on, startIndex) {
25
+ if (on[startIndex - 1] && [ '=', '<', '>', '>=', '<=', '!=', '<>' ].includes(on[startIndex - 1]))
26
+ return isOk(resolveArtifactType.call(this, on[startIndex - 2]._art));
27
+
28
+ else if (on[startIndex + 1] && [ '=', '<', '>', '>=', '<=', '!=', '<>' ].includes(on[startIndex + 1]))
29
+ return isOk(resolveArtifactType.call(this, on[startIndex + 2]._art));
30
+
31
+ return false;
32
+
33
+ /**
34
+ * Artifact is structured or a managed association/compoisition
35
+ *
36
+ * @param {CSN.Artifact} art Artifact
37
+ * @returns {boolean} True if expandable
38
+ */
39
+ function isOk(art) {
40
+ return !!(art && (art.elements || (art.target && art.keys)));
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Get the real type of an artifact
46
+ *
47
+ * @param {object} art Whatever _art by csnRefs can be - element or artifact
48
+ * @returns {object} final artifact type
49
+ */
50
+ function resolveArtifactType(art) {
51
+ if (art && art.type && !isBuiltinType(art.type))
52
+ return this.getFinalBaseType(art);
53
+
54
+ return art;
55
+ }
56
+
57
+ module.exports = {
58
+ logReady,
59
+ otherSideIsExpandableStructure,
60
+ resolveArtifactType,
61
+ };