@sap/cds-compiler 2.7.0 → 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 (63) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/lib/api/main.js +8 -10
  3. package/lib/api/options.js +13 -9
  4. package/lib/api/validate.js +11 -8
  5. package/lib/base/keywords.js +32 -2
  6. package/lib/base/message-registry.js +16 -0
  7. package/lib/base/messages.js +2 -0
  8. package/lib/base/model.js +1 -0
  9. package/lib/checks/onConditions.js +5 -0
  10. package/lib/checks/types.js +26 -2
  11. package/lib/checks/unknownMagic.js +38 -0
  12. package/lib/checks/validator.js +7 -2
  13. package/lib/compiler/assert-consistency.js +11 -5
  14. package/lib/compiler/builtins.js +2 -0
  15. package/lib/compiler/checks.js +3 -1
  16. package/lib/compiler/definer.js +87 -29
  17. package/lib/compiler/resolver.js +75 -16
  18. package/lib/compiler/shared.js +29 -9
  19. package/lib/edm/annotations/genericTranslation.js +182 -186
  20. package/lib/edm/csn2edm.js +93 -98
  21. package/lib/edm/edm.js +16 -20
  22. package/lib/edm/edmPreprocessor.js +274 -83
  23. package/lib/edm/edmUtils.js +29 -10
  24. package/lib/gen/language.checksum +1 -1
  25. package/lib/gen/language.interp +12 -1
  26. package/lib/gen/language.tokens +57 -53
  27. package/lib/gen/languageLexer.interp +10 -1
  28. package/lib/gen/languageLexer.js +770 -744
  29. package/lib/gen/languageLexer.tokens +49 -46
  30. package/lib/gen/languageParser.js +4727 -4323
  31. package/lib/json/from-csn.js +52 -23
  32. package/lib/json/to-csn.js +185 -71
  33. package/lib/language/errorStrategy.js +1 -0
  34. package/lib/language/genericAntlrParser.js +9 -0
  35. package/lib/language/language.g4 +90 -31
  36. package/lib/main.js +4 -0
  37. package/lib/model/api.js +78 -0
  38. package/lib/model/csnRefs.js +7 -1
  39. package/lib/model/csnUtils.js +5 -4
  40. package/lib/optionProcessor.js +7 -1
  41. package/lib/render/.eslintrc.json +3 -1
  42. package/lib/render/toCdl.js +45 -9
  43. package/lib/render/toHdbcds.js +100 -34
  44. package/lib/render/toSql.js +12 -4
  45. package/lib/render/utils/common.js +5 -9
  46. package/lib/sql-identifier.js +6 -1
  47. package/lib/transform/db/draft.js +6 -4
  48. package/lib/transform/db/expansion.js +14 -4
  49. package/lib/transform/db/flattening.js +13 -5
  50. package/lib/transform/db/transformExists.js +252 -58
  51. package/lib/transform/forHanaNew.js +7 -1
  52. package/lib/transform/forOdataNew.js +12 -8
  53. package/lib/transform/odata/attachPath.js +19 -4
  54. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  55. package/lib/transform/odata/referenceFlattener.js +44 -38
  56. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  57. package/lib/transform/odata/structuralPath.js +76 -0
  58. package/lib/transform/odata/structureFlattener.js +13 -10
  59. package/lib/transform/odata/typesExposure.js +22 -12
  60. package/lib/transform/transformUtilsNew.js +33 -1
  61. package/lib/transform/translateAssocsToJoins.js +6 -4
  62. package/lib/transform/universalCsnEnricher.js +67 -0
  63. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,109 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 2.10.2 - 2021-10-29
11
+
12
+ ### Fixed
13
+
14
+ - to.sql/hdi/hdbcds: Correctly handle `exists` in conjunction with mixin-associations
15
+
16
+ ## Version 2.10.0 - 2021-10-28
17
+
18
+ ### Added
19
+
20
+ - Support arbitrary paths after `$user` - similar to `$session`.
21
+ - Support scale `floating` and `variable` for `cds.Decimal` in CDL and CSN. Backend specific handling is descibed in their sections.
22
+ - Allow select item wildcard (`*`) in a `select`/`projection` at any position, not just the first.
23
+
24
+ - to.edm(x):
25
+ + In Odata V4 generate transitive navigation property binding paths along containment hierarchies and terminate on the
26
+ first non-containment association. The association target is either an explicit Edm.EntitySet in the same EntityContainer
27
+ or in a referred EntityContainer (cross service references) or an implicit EntitySet identified by the containment path
28
+ originating from an explicit EntitySet. This enhancement has an observable effect only in structured format with containment
29
+ turned on.
30
+ + Support for scales `variable` and `floating`:
31
+ + V4: `variable` and `floating` are rendered as `Scale="variable"`. Since V4 does not support `floating`, it is aproximated as `variable`.
32
+ + V2: `variable` and `floating` are announced via property annotation `sap:variable-scale="true"`
33
+
34
+ - to.sql/hdi/hdbcds:
35
+ + Reject scale `floating` and `variable`.
36
+ + Reject arbitrary `$user` or `$session` paths that cannot be translated to valid SQL.
37
+ + Following a valid `exists`, further `exists` can be used inside of the filter-expression: `exists assoc[exists another[1=1]]`
38
+ + `exists` can now be followed by more than one association step.
39
+ `exists assoc.anotherassoc.moreassoc` is semantically equivalent to `exists assoc[exists anotherassoc[exists moreassoc]]`
40
+
41
+ ### Removed
42
+
43
+ ### Changed
44
+
45
+ - to.odata: Inform when overwriting draft action annotations like @Common.DraftRoot.ActivationAction.
46
+
47
+ ### Fixed
48
+
49
+ ## Version 2.9.0 - 2021-10-15
50
+
51
+ ### Changed
52
+
53
+ - to.edm(x): Raise `odata-spec-violation-type` to a downgradable error.
54
+
55
+ ### Fixed
56
+
57
+ - to.edm(x):
58
+ + Fix a bug in annotation propagation to foreign keys.
59
+ + Don't render annotations for not rendered stream element in V2.
60
+ - to.hdi:
61
+ + for naming mode "hdbcds" and "quoted" parameter definitions are not quoted anymore.
62
+ - to.hdi/sql/hdbcds:
63
+ + Correctly handle explicit and implicit alias during flattening.
64
+ + Raise an error for `@odata.draft.enabled` artifacts with elements without types - instead of crashing with internal assertions.
65
+
66
+ ## Version 2.8.0 - 2021-10-07
67
+
68
+ ### Added
69
+
70
+ - Allow defining unmanaged associations in anonymous aspects of compositions.
71
+ - Enable extensions of anonymous aspects for managed compositions of aspects.
72
+ - When the option `addTextsLanguageAssoc` is set to true and
73
+ the model contains an entity `sap.common.Languages` with an element `code`,
74
+ all generated texts entities additionally contain an element `language`
75
+ which is an association to `sap.common.Languages` using element `local`.
76
+ - for.odata:
77
+ + In `--odata-format=flat`, structured view parameters are flattened like elements.
78
+ - to.hdbcds
79
+ + Use "smart quotes" for naming mode "plain" - automatically quote identifier which are reserved keywords or non-regular.
80
+
81
+ ### Changed
82
+
83
+ - for.odata:
84
+ + In `--data-format=structured`, anonymous sub elements of primary keys and parameters are set to `notNull:true`,
85
+ an existing `notNull` attribute is _not_ overwritten. Referred named types are _not_ modified.
86
+ - to.edm(x):
87
+ + Improve specification violation checks of (nested) keys:
88
+ + All (sub-)elements must be `Nullable: false` (error).
89
+ + Must represent a single value (error).
90
+ + In V4 must be a specification compliant Edm.PrimitiveType (warning).
91
+ - to.hdi/hdbcds/sql: $user.\<xy\> now has \<xy\> added as alias - "$user.\<xy\> as \<xy\>"
92
+
93
+ ### Fixed
94
+
95
+ - Properly generate auto-exposed entities for associations in parameters.
96
+ - Correctly apply extensions to anonymous array item types.
97
+ - Correctly apply/render annotations to anonymous action return types.
98
+ - With CSN flavor `plain` (`gensrc`), correctly render annotations on elements
99
+ of referred structure types as `annotate` statements in the CSN's `extensions` property.
100
+ - to.cdl:
101
+ + Correctly render extensions on array item types
102
+ + Correctly render annotations on action return types
103
+ - to/for: Correctly handle CSN input where the prototype of objects is not the "default"
104
+ - to.hdi:
105
+ + for naming mode "hdbcds" and "quoted" parameter definitions are now quoted.
106
+ + for naming mode "plain", smart quotation is applied to parameter definitions if they are reserved words.
107
+ - to.hdi/hdbcds/sql:
108
+ + Ensure that cdl-style casts to localized types do not lose their localized property
109
+ + Fix a small memory leak during rendering of SQL/HDBCDS.
110
+ - to.edm(x): Remove ambiguous `Partner` attribute from `NavigationProperty`. A forward association referred
111
+ to by multiple backlinks (`$self` comparisons) is no longer partner to an arbitrary backlink.
112
+
10
113
  ## Version 2.7.0 - 2021-09-22
11
114
 
12
115
  ### Added
package/lib/api/main.js CHANGED
@@ -494,19 +494,17 @@ function edmall(csn, options = {}) {
494
494
  if (internalOptions.version === 'v2')
495
495
  error(null, null, 'OData JSON output is not available for OData V2');
496
496
 
497
- let servicesAll;
498
-
499
497
  const result = {};
498
+ let oDataCsn = csn;
500
499
 
501
- if (isPreTransformed(csn, 'odata')) {
500
+ if (isPreTransformed(csn, 'odata'))
502
501
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
503
- servicesAll = backends.preparedCsnToEdmAll(csn, internalOptions);
504
- }
505
- else {
506
- const oDataCsn = odataInternal(csn, internalOptions);
507
- servicesAll = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
508
- }
509
- const services = servicesAll.edmj;
502
+
503
+ else
504
+ oDataCsn = odataInternal(csn, internalOptions);
505
+
506
+ const servicesJson = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
507
+ const services = servicesJson.edmj;
510
508
  for (const serviceName in services) {
511
509
  const lEdm = services[serviceName];
512
510
  // FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
@@ -10,11 +10,13 @@ const publicOptionsNewAPI = [
10
10
  // GENERAL
11
11
  'beta',
12
12
  'deprecated',
13
+ 'addTextsLanguageAssoc',
13
14
  'localizedLanguageFallback', // why can't I define the option type here?
14
15
  'severities',
15
16
  'messages',
16
17
  'withLocations',
17
18
  'defaultStringLength',
19
+ 'csnFlavor',
18
20
  // DB
19
21
  'sqlDialect',
20
22
  'sqlMapping',
@@ -68,10 +70,11 @@ const overallOptions = publicOptionsNewAPI.concat(privateOptions);
68
70
  * @param {FlatOptions} [hardRequire={}] Hard requirements to enforce
69
71
  * @param {object} [customValidators] Custom validations to run instead of defaults
70
72
  * @param {string[]} [combinationValidators] Option combinations to validate
73
+ * @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
71
74
  * @returns {TranslatedOptions} General cds options
72
75
  */
73
76
  function translateOptions(input = {}, defaults = {}, hardRequire = {},
74
- customValidators = {}, combinationValidators = []) {
77
+ customValidators = {}, combinationValidators = [], moduleName = '') {
75
78
  const options = Object.assign({}, defaults);
76
79
  const inputOptionNames = Object.keys(input);
77
80
  for (const name of overallOptions) {
@@ -90,6 +93,7 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
90
93
  // Validate the filtered input options
91
94
  // only "new-style" options are here
92
95
  validate(options,
96
+ moduleName,
93
97
  // TODO: is there a better place to specify the type of option values?
94
98
  Object.assign( {
95
99
  localizedLanguageFallback: generateStringValidator([ 'none', 'coalesce' ]),
@@ -141,11 +145,11 @@ function translateOptions(input = {}, defaults = {}, hardRequire = {},
141
145
 
142
146
  module.exports = {
143
147
  to: {
144
- cdl: options => translateOptions(options),
148
+ cdl: options => translateOptions(options, undefined, undefined, undefined, undefined, 'to.cdl'),
145
149
  sql: (options) => {
146
150
  const hardOptions = { src: 'sql' };
147
151
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'plain' };
148
- const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ]);
152
+ const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'sql-dialect-and-naming' ], 'to.sql');
149
153
 
150
154
  const result = Object.assign({}, processed);
151
155
  result.toSql = Object.assign({}, processed);
@@ -155,7 +159,7 @@ module.exports = {
155
159
  hdi: (options) => {
156
160
  const hardOptions = { src: 'hdi' };
157
161
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
158
- const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) });
162
+ const processed = translateOptions(options, defaultOptions, hardOptions, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdi');
159
163
 
160
164
  const result = Object.assign({}, processed);
161
165
  result.toSql = Object.assign({}, processed);
@@ -164,7 +168,7 @@ module.exports = {
164
168
  },
165
169
  hdbcds: (options) => {
166
170
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
167
- const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) });
171
+ const processed = translateOptions(options, defaultOptions, {}, { sqlDialect: generateStringValidator([ 'hana' ]) }, undefined, 'to.hdbcds');
168
172
 
169
173
  const result = Object.assign({}, processed);
170
174
  result.forHana = Object.assign({}, processed);
@@ -174,7 +178,7 @@ module.exports = {
174
178
  edm: (options) => {
175
179
  const hardOptions = { json: true, combined: true };
176
180
  const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
177
- const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ]);
181
+ const processed = translateOptions(options, defaultOptions, hardOptions, { odataVersion: generateStringValidator([ 'v4' ]) }, [ 'valid-structured' ], 'to.edm');
178
182
 
179
183
  const result = Object.assign({}, processed);
180
184
  result.toOdata = Object.assign({}, processed);
@@ -186,7 +190,7 @@ module.exports = {
186
190
  const defaultOptions = {
187
191
  odataVersion: 'v4', odataFormat: 'flat',
188
192
  };
189
- const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ]);
193
+ const processed = translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ], 'to.edmx');
190
194
 
191
195
  const result = Object.assign({}, processed);
192
196
  result.toOdata = Object.assign({}, processed);
@@ -198,7 +202,7 @@ module.exports = {
198
202
 
199
203
  odata: (options) => {
200
204
  const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
201
- const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ]);
205
+ const processed = translateOptions(options, defaultOptions, undefined, undefined, [ 'valid-structured' ], 'for.odata');
202
206
 
203
207
  const result = Object.assign({}, processed);
204
208
  result.toOdata = Object.assign({}, processed);
@@ -208,7 +212,7 @@ module.exports = {
208
212
  },
209
213
  hana: (options) => {
210
214
  const defaultOptions = { sqlMapping: 'plain', sqlDialect: 'hana' };
211
- const processed = translateOptions(options, defaultOptions);
215
+ const processed = translateOptions(options, defaultOptions, undefined, undefined, undefined, 'for.hana');
212
216
 
213
217
  const result = Object.assign({}, processed);
214
218
  result.forHana = Object.assign({}, processed);
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { makeMessageFunction, handleMessages } = require('../base/messages');
3
+ const { makeMessageFunction } = require('../base/messages');
4
4
 
5
5
  /* eslint-disable arrow-body-style */
6
6
  const booleanValidator = {
@@ -94,6 +94,11 @@ const validators = {
94
94
  expected: () => 'Integer literal',
95
95
  found: val => `type ${ typeof val }`,
96
96
  },
97
+ csnFlavor: {
98
+ validate: val => typeof val === 'string',
99
+ expected: () => 'type string',
100
+ found: val => `type ${ typeof val }`,
101
+ },
97
102
  dictionaryPrototype: {
98
103
  validate: () => true,
99
104
  },
@@ -122,16 +127,17 @@ const allCombinationValidators = {
122
127
  * Use a custom validator or "default" custom validator, fallback to Boolean validator.
123
128
  *
124
129
  * @param {object} options Flat options object to validate
130
+ * @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
125
131
  * @param {object} [customValidators] Map of custom validators to use
126
132
  * @param {string[]} [combinationValidators] Validate option combinations
127
133
  * @returns {void}
128
134
  * @throws {CompilationError} Throws in case of invalid option usage
129
135
  */
130
- function validate(options, customValidators = {}, combinationValidators = []) {
136
+ function validate(options, moduleName, customValidators = {}, combinationValidators = []) {
131
137
  // TODO: issuing messages in this function looks very strange...
132
138
  {
133
139
  const messageCollector = { messages: [] };
134
- const { error, throwWithError } = makeMessageFunction(null, messageCollector);
140
+ const { error, throwWithError } = makeMessageFunction(null, messageCollector, moduleName);
135
141
 
136
142
  for (const optionName of Object.keys(options)) {
137
143
  const optionValue = options[optionName];
@@ -143,7 +149,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
143
149
  throwWithError();
144
150
  }
145
151
 
146
- const message = makeMessageFunction(null, options);
152
+ const message = makeMessageFunction(null, options, moduleName);
147
153
 
148
154
  for (const combinationValidatorName of combinationValidators.concat([ 'beta-no-test' ])) {
149
155
  const combinationValidator = allCombinationValidators[combinationValidatorName];
@@ -151,10 +157,7 @@ function validate(options, customValidators = {}, combinationValidators = []) {
151
157
  message[combinationValidator.severity]('invalid-option-combination', null, {}, combinationValidator.getMessage(options));
152
158
  }
153
159
 
154
- // TODO: Replace with message.throwWithError():
155
- // But be aware that it only throws with non-configurable errors and that this
156
- // will lead to issues in test3. See #6037
157
- handleMessages(undefined, options);
160
+ message.throwWithError();
158
161
  }
159
162
  /* eslint-enable jsdoc/no-undefined-types */
160
163
 
@@ -194,7 +194,7 @@ module.exports = {
194
194
  'OTHERS',
195
195
  'TIES',
196
196
  ],
197
- // HANA keywords, used to warn in 'toSql' renderer with dialect 'hana' or in 'toHana' renderer (both with 'plain' names only)
197
+ // HANA keywords, used for smart quoting in to-hdi.plain
198
198
  // Taken from https://help.sap.com/viewer/7c78579ce9b14a669c1f3295b0d8ca16/Cloud/en-US/28bcd6af3eb6437892719f7c27a8a285.html
199
199
  // Better use keywords in ptime/query/parser/syntax/qp_keyword.cc minus those
200
200
  // in rule unreserved_keyword_column (=…_common - "CONSTRAINT") in
@@ -673,5 +673,35 @@ module.exports = {
673
673
  'WITHIN',
674
674
  'XMLTABLE',
675
675
  'YEAR'
676
- ]
676
+ ],
677
+ // HANA CDS keywords, used for smart quoting in to-hdbcds.plain
678
+ hdbcds: [
679
+ 'ALL', 'ALTER', 'AS',
680
+ 'BEFORE', 'BEGIN', 'BOTH',
681
+ 'CASE', 'CHAR', 'CONDITION',
682
+ 'CONNECT', 'CROSS', 'CUBE',
683
+ 'CURRENT_CONNECTION', 'CURRENT_DATE', 'CURRENT_SCHEMA',
684
+ 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'CURRENT_USER',
685
+ 'CURRENT_UTCDATE', 'CURRENT_UTCTIME', 'CURRENT_UTCTIMESTAMP',
686
+ 'CURRVAL', 'CURSOR', 'DECLARE',
687
+ 'DISTINCT', 'ELSE', 'ELSEIF',
688
+ 'ELSIF', 'END', 'EXCEPT',
689
+ 'EXCEPTION', 'EXEC', 'FOR',
690
+ 'FROM', 'FULL', 'GROUP',
691
+ 'HAVING', 'IF', 'IN',
692
+ 'INNER', 'INOUT', 'INTERSECT',
693
+ 'INTO', 'IS', 'JOIN',
694
+ 'LEADING', 'LEFT', 'LIMIT',
695
+ 'LOOP', 'MINUS', 'NATURAL',
696
+ 'NEXTVAL', 'NULL', 'ON',
697
+ 'ORDER', 'OUT', 'OUTER',
698
+ 'PRIOR', 'RETURN', 'RETURNS',
699
+ 'REVERSE', 'RIGHT', 'ROLLUP',
700
+ 'ROWID', 'SELECT', 'SET',
701
+ 'SQL', 'START', 'SYSDATE',
702
+ 'SYSTIME', 'SYSTIMESTAMP', 'SYSUUID',
703
+ 'TOP', 'TRAILING', 'UNION',
704
+ 'USING', 'VALUES', 'WHEN',
705
+ 'WHERE', 'WHILE', 'WITH'
706
+ ]
677
707
  }
@@ -132,6 +132,10 @@ const centralMessages = {
132
132
  'composition-as-key': { severity: 'Error', configurableFor: 'deprecated' }, // is confusing and not supported
133
133
  'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
134
134
  'odata-spec-violation-constraints': { severity: 'Info' }, // more than 30 chars
135
+ 'odata-spec-violation-type': { severity: 'Error', configurableFor: [ 'to.edmx' ] },
136
+ 'odata-spec-violation-key-array': { severity: 'Error' }, // more than 30 chars
137
+ 'odata-spec-violation-key-null': { severity: 'Error' }, // more than 30 chars
138
+ 'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
135
139
  'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
136
140
  'odata-spec-violation-namespace-name': { severity: 'Warning' }, // more than 30 chars
137
141
  };
@@ -246,6 +250,18 @@ const centralMessageTexts = {
246
250
  'odata-spec-violation-assoc': 'Unexpected association in structured type for $(API)',
247
251
  'odata-spec-violation-constraints': 'Partial referential constraints produced for $(API)',
248
252
  // version independent messages
253
+ 'odata-spec-violation-key-array': {
254
+ std: 'Unexpected array type for element $(NAME)',
255
+ scalar: 'Unexpected array type'
256
+ },
257
+ 'odata-spec-violation-key-null': {
258
+ std: 'Expected key element $(NAME) to be not nullable', // structured
259
+ scalar: 'Expected key element to be not nullable' // flat
260
+ },
261
+ 'odata-spec-violation-key-type': {
262
+ std: 'Unexpected $(TYPE) mapped to $(ID) as type for key element $(NAME)', // structured
263
+ scalar: 'Unexpected $(TYPE) mapped to $(ID) as type for key element' // flat
264
+ },
249
265
  'odata-spec-violation-type': 'Expected element to have a type',
250
266
  'odata-spec-violation-property-name': 'Expected element name to be different from declaring $(KIND)',
251
267
  'odata-spec-violation-namespace': 'Expected service name not to be one of the reserved names $(NAMES)',
@@ -649,6 +649,8 @@ const paramsTransform = {
649
649
  // more complex convenience:
650
650
  names: transformManyWith( quoted ),
651
651
  number: n => n,
652
+ line: l => l,
653
+ col: c => c,
652
654
  literal: l => l,
653
655
  art: transformArg,
654
656
  service: transformArg,
package/lib/base/model.js CHANGED
@@ -28,6 +28,7 @@ const availableBetaFlags = {
28
28
  ignoreAssocPublishingInUnion: true,
29
29
  nestedProjections: true,
30
30
  enableUniversalCsn: true,
31
+ windowFunctions: true,
31
32
  // disabled by --beta-mode
32
33
  nestedServices: false,
33
34
  };
@@ -59,6 +59,11 @@ function otherSideIsValidDollarSelf(on, startIndex) {
59
59
  */
60
60
  function validateOnCondition(member, memberName, property, path) {
61
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
+ }
62
67
  for (let i = 0; i < member.on.length; i++) {
63
68
  if (member.on[i].ref) {
64
69
  const { ref } = member.on[i];
@@ -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
+ };
@@ -23,13 +23,17 @@ const {
23
23
  // both
24
24
  const { validateOnCondition, validateMixinOnCondition } = require('./onConditions');
25
25
  const validateForeignKeys = require('./foreignKeys');
26
- const { checkTypeDefinitionHasType, checkElementTypeDefinitionHasType, checkTypeIsScalar } = require('./types');
26
+ const {
27
+ checkTypeDefinitionHasType, checkElementTypeDefinitionHasType,
28
+ checkTypeIsScalar, checkDecimalScale,
29
+ } = require('./types');
27
30
  const { checkPrimaryKey, checkVirtualElement, checkManagedAssoc } = require('./elements');
28
31
  const checkForInvalidTarget = require('./invalidTarget');
29
32
  const { validateAssociationsInItems } = require('./arrayOfs');
30
33
  const checkQueryForNoDBArtifacts = require('./queryNoDbArtifacts');
31
34
  const checkExplicitlyNullableKeys = require('./nullableKeys');
32
35
  const nonexpandableStructuredInExpression = require('./nonexpandableStructured');
36
+ const unknownMagic = require('./unknownMagic');
33
37
  const managedWithoutKeys = require('./managedWithoutKeys');
34
38
 
35
39
  const forHanaMemberValidators
@@ -37,6 +41,7 @@ const forHanaMemberValidators
37
41
  // For HANA CDS specifically, reject any default parameter values, as these are not supported.
38
42
  rejectParamDefaultsInHanaCds,
39
43
  checkTypeIsScalar,
44
+ checkDecimalScale,
40
45
  checkExplicitlyNullableKeys,
41
46
  managedWithoutKeys,
42
47
  warnAboutDefaultOnAssociationForHanaCds,
@@ -50,7 +55,7 @@ const forHanaArtifactValidators
50
55
  checkForEmptyOrOnlyVirtual,
51
56
  ];
52
57
 
53
- const forHanaCsnValidators = [ nonexpandableStructuredInExpression ];
58
+ const forHanaCsnValidators = [ nonexpandableStructuredInExpression, unknownMagic ];
54
59
  /**
55
60
  * @type {Array<(query: CSN.Query, path: CSN.Path) => void>}
56
61
  */
@@ -246,7 +246,7 @@ function assertConsistency( model, stage ) {
246
246
  optional: [
247
247
  'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
248
248
  'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit',
249
- '_projections', '_block', '_parent', '_main', '_effectiveType',
249
+ '_projections', '_block', '_parent', '_main', '_effectiveType', '$expand',
250
250
  '$tableAliases', 'kind', '_$next', '_combined', '$inlines',
251
251
  ],
252
252
  },
@@ -324,7 +324,7 @@ function assertConsistency( model, stage ) {
324
324
  requires: [ 'location' ],
325
325
  optional: [
326
326
  'path', 'elements', '_outer',
327
- 'scope', '_artifact', '$inferred',
327
+ 'scope', '_artifact', '$inferred', '$expand',
328
328
  '_effectiveType', // by propagation
329
329
  ],
330
330
  },
@@ -351,6 +351,7 @@ function assertConsistency( model, stage ) {
351
351
  $delimited: { parser: true, test: isBoolean },
352
352
  scope: { test: isScope },
353
353
  func: { test: TODO },
354
+ suffix: { test: TODO },
354
355
  kind: {
355
356
  isRequired: !stageParser && (() => true),
356
357
  // required to be set by Core Compiler even with parse errors
@@ -398,6 +399,7 @@ function assertConsistency( model, stage ) {
398
399
  optional: [
399
400
  'args',
400
401
  'func',
402
+ 'suffix',
401
403
  'quantifier',
402
404
  '$inferred',
403
405
  '$parens',
@@ -426,7 +428,7 @@ function assertConsistency( model, stage ) {
426
428
  struct: { inherits: 'val', test: isDictionary( definition ) }, // def because double @
427
429
  args: {
428
430
  inherits: 'value',
429
- optional: [ 'name', '$duplicate', '$expected' ],
431
+ optional: [ 'name', '$duplicate', '$expected', 'args', 'suffix' ],
430
432
  test: args,
431
433
  },
432
434
  on: { kind: true, inherits: 'value', test: expression },
@@ -533,7 +535,7 @@ function assertConsistency( model, stage ) {
533
535
  ],
534
536
  optional: [
535
537
  '_effectiveType', '$parens',
536
- '_deps',
538
+ '_deps', '$expand',
537
539
  // query specific
538
540
  'where', 'columns', 'mixin', 'quantifier', 'offset',
539
541
  'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
@@ -574,7 +576,11 @@ function assertConsistency( model, stage ) {
574
576
  $duplicates: { parser: true, kind: true, test: TODO }, // array of arts or true
575
577
  $extension: { kind: true, test: TODO }, // TODO: introduce $applied instead or $status
576
578
  $inferred: { parser: true, kind: true, test: isString },
577
- $expand: { kind: true, test: isString },
579
+
580
+ // Helper property for the XSN-to-CSN transformation, see function setExpandStatus():
581
+ // client, universal: render expanded elements? gensrc: produce annotate statements?
582
+ $expand: { kind: true, test: isString }, // TODO: rename it to $elementsExpand ?
583
+
578
584
  $autoexpose: { kind: [ 'entity' ], test: isBoolean, also: [ null, 'Composition' ] },
579
585
  $a2j: { kind: true, enumerable: true, test: TODO },
580
586
  $extra: { parser: true, test: TODO }, // for unexpected properties in CSN
@@ -83,6 +83,8 @@ const specialFunctions = {
83
83
  const magicVariables = {
84
84
  $user: {
85
85
  elements: { id: {}, locale: {} },
86
+ // Allow $user.<any>
87
+ $uncheckedElements: true,
86
88
  // Allow shortcut in CDL: `$user` becomes `$user.id` in CSN.
87
89
  $autoElement: 'id',
88
90
  }, // CDS-specific, not part of SQL
@@ -15,7 +15,9 @@
15
15
 
16
16
  // const { hasArtifactTypeInformation } = require('../model/csnUtils')
17
17
  const builtins = require('../compiler/builtins');
18
- const { forEachGeneric, forEachDefinition, forEachMember } = require('../base/model');
18
+ const {
19
+ forEachGeneric, forEachDefinition, forEachMember,
20
+ } = require('../base/model');
19
21
 
20
22
  function check( model ) { // = XSN
21
23
  const {