@sap/cds-compiler 5.7.4 → 5.8.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 (71) hide show
  1. package/CHANGELOG.md +60 -2
  2. package/bin/cdsse.js +13 -1
  3. package/doc/CHANGELOG_BETA.md +7 -0
  4. package/lib/api/options.js +2 -1
  5. package/lib/api/validate.js +9 -0
  6. package/lib/base/message-registry.js +55 -20
  7. package/lib/base/messages.js +5 -2
  8. package/lib/base/model.js +4 -1
  9. package/lib/checks/assocOutsideService.js +40 -0
  10. package/lib/checks/featureFlags.js +4 -1
  11. package/lib/checks/types.js +7 -4
  12. package/lib/checks/validator.js +3 -0
  13. package/lib/compiler/assert-consistency.js +11 -5
  14. package/lib/compiler/checks.js +79 -17
  15. package/lib/compiler/define.js +57 -3
  16. package/lib/compiler/extend.js +1 -2
  17. package/lib/compiler/generate.js +1 -1
  18. package/lib/compiler/populate.js +17 -6
  19. package/lib/compiler/propagator.js +1 -1
  20. package/lib/compiler/resolve.js +181 -150
  21. package/lib/compiler/shared.js +276 -22
  22. package/lib/compiler/tweak-assocs.js +15 -4
  23. package/lib/compiler/xpr-rewrite.js +76 -50
  24. package/lib/edm/annotations/edmJson.js +1 -1
  25. package/lib/edm/annotations/genericTranslation.js +2 -2
  26. package/lib/edm/csn2edm.js +2 -2
  27. package/lib/edm/edmPreprocessor.js +15 -9
  28. package/lib/edm/edmUtils.js +12 -5
  29. package/lib/gen/CdlGrammar.checksum +1 -0
  30. package/lib/gen/CdlParser.js +2234 -2233
  31. package/lib/gen/Dictionary.json +55 -8
  32. package/lib/json/from-csn.js +37 -17
  33. package/lib/json/to-csn.js +4 -0
  34. package/lib/language/genericAntlrParser.js +7 -0
  35. package/lib/main.d.ts +5 -0
  36. package/lib/model/cloneCsn.js +1 -0
  37. package/lib/model/csnRefs.js +1 -0
  38. package/lib/model/csnUtils.js +0 -5
  39. package/lib/modelCompare/utils/filter.js +2 -2
  40. package/lib/optionProcessor.js +2 -0
  41. package/lib/parsers/AstBuildingParser.js +47 -17
  42. package/lib/parsers/CdlGrammar.g4 +10 -12
  43. package/lib/parsers/XprTree.js +206 -0
  44. package/lib/render/toCdl.js +61 -89
  45. package/lib/render/toSql.js +59 -29
  46. package/lib/render/utils/standardDatabaseFunctions.js +252 -15
  47. package/lib/transform/addTenantFields.js +9 -3
  48. package/lib/transform/db/assocsToQueries/transformExists.js +3 -0
  49. package/lib/transform/db/assocsToQueries/utils.js +10 -3
  50. package/lib/transform/db/expansion.js +3 -1
  51. package/lib/transform/db/flattening.js +7 -3
  52. package/lib/transform/db/killAnnotations.js +1 -0
  53. package/lib/transform/db/processSqlServices.js +70 -17
  54. package/lib/transform/draft/db.js +8 -3
  55. package/lib/transform/draft/odata.js +27 -4
  56. package/lib/transform/effective/main.js +37 -10
  57. package/lib/transform/effective/misc.js +4 -9
  58. package/lib/transform/effective/service.js +34 -0
  59. package/lib/transform/effective/types.js +28 -17
  60. package/lib/transform/forOdata.js +36 -10
  61. package/lib/transform/forRelationalDB.js +30 -18
  62. package/lib/transform/odata/adaptAnnotationRefs.js +37 -21
  63. package/lib/transform/odata/createForeignKeys.js +121 -117
  64. package/lib/transform/odata/flattening.js +12 -9
  65. package/lib/transform/transformUtils.js +58 -25
  66. package/lib/transform/translateAssocsToJoins.js +10 -6
  67. package/lib/transform/universalCsn/coreComputed.js +5 -1
  68. package/package.json +1 -1
  69. package/share/messages/message-explanations.json +1 -0
  70. package/share/messages/rewrite-not-supported.md +5 -0
  71. package/share/messages/rewrite-undefined-key.md +94 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,63 @@
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 5.8.2 - 2025-03-07
11
+
12
+ ### Fixed
13
+
14
+ - for.odata: Generate foreign key elements for events again.
15
+
16
+ ## Version 5.8.0 - 2025-02-27
17
+
18
+ ### Added
19
+
20
+ - Type definitions can now be projections on other types, i.e. `type Proj : projection on OtherType { elem }`.
21
+ Use it to create types based on other types, e.g. by selecting only certain elements.
22
+ Only available with the new parser (`newParser: true`)
23
+ - Analyze enum symbols like `#ENUM_SYMB` in all (sub) expressions and conditions.
24
+ It can be validated if the compiler can deduce its `enum` type from its use context:
25
+ + when the enum symbol is used as `default` value, `select` column expression,
26
+ argument when navigating along an association to an entity with a parameter,
27
+ or argument of a `cast` function call, or
28
+ + when the enum symbol is compared to a reference or `cast` function call; we
29
+ consider the operators `=`, `<>`, `!=`, `in`, `not in` and also analyze enum
30
+ symbols as `when` operands if the `case` operand is a reference/`cast`.
31
+ + We not only consider simple enum symbols, but also lists of enum symbols (on
32
+ the right side of `in`/`not in`), and a `case … end` (sub) expression with
33
+ enum symbols after the `then`s and/or the `else`.
34
+ + An enum symbol can be validated if the deduced type is a direct or indirect
35
+ `enum` type, or an managed association with one foreign key having an `enum` type.
36
+ + For the effects in the compiler, IDE and backends, see the changelog entry for v5.7.0.
37
+ Hint: the deprecated hdbcds backend does not support enum symbols.
38
+ + Remark: the support for enum symbols used as annotation values is still limited.
39
+ - to.sql.migration: Allow extending `precision` of `cds.Decimal` and allow extending
40
+ `scale` if `precision` is increased by at least the same amount.
41
+ - to.edm(x): `@assert.range` now supports "exclusive" values by writing values
42
+ in parentheses such as `[ (1), (2) ]`, as well as "infinite" by using `[ _, _ ]`.
43
+ - for.odata/to.edm(x)/for.seal: Propagate annotation expressions from managed associations
44
+ to the foreign keys
45
+
46
+
47
+ ### Changed
48
+
49
+ - Top-level CSN property `csnInteropEffective` is ignored and no longer warned about.
50
+ - Update OData vocabularies: 'Analytics', 'Common', 'Hierarchy', 'UI'
51
+
52
+ ### Fixed
53
+
54
+ - New CDL parser: parse all entity definitions using `projection on` without a
55
+ terminating `;` if they had been accepted by the old parser, i.e. for compatibility,
56
+ we gave up the idea of removing the existing special handling in this case.
57
+ - Old and new parser: issue a warning for an ignored filter on the result
58
+ of a function or method call.
59
+ - CSN annotation expressions with value `true` for `=` were not checked.
60
+ - Annotation `@Core.Computed` was not set for select items that are paths into structured parameters.
61
+ - Annotation expression path rewriting has been improved.
62
+ + Paths on foreign keys are rewritten.
63
+ - for.seal:
64
+ + References into structured parameters were incorrectly flattened.
65
+ + Set `@cds.persistence.name` only on persistence-relevant things.
66
+
10
67
  ## Version 5.7.4 - 2025-02-05
11
68
 
12
69
  ### Fixed
@@ -15,6 +72,7 @@ The compiler behavior concerning `beta` features can change at any time without
15
72
  + Improve code completion
16
73
  + Fix further edge cases in error recovery
17
74
 
75
+
18
76
  ## Version 5.7.2 - 2025-01-30
19
77
 
20
78
  ### Fixed
@@ -49,9 +107,9 @@ The compiler behavior concerning `beta` features can change at any time without
49
107
  - CDL parser: doc comment parser was susceptible to ReDos
50
108
  - to.sql/hdi: Paths inside calculated elements that are simple functions were not properly rewritten.
51
109
  - for.odata: Re-add foreign keys in property `targetAspect` in the OData CSN.
52
- - to.edm(x): In annotation translation, by default map `SemanticObjectMappingAbstract` to `SemanticObjectMappingType` to avoid regreessions.
110
+ - to.edm(x): In annotation translation, by default map `SemanticObjectMappingAbstract` to `SemanticObjectMappingType` to avoid regressions.
53
111
  - to.cdl: Fix quoting of identifier `many` in `Composition of`/`Association to`
54
- - for.seal: Allow annotation paths to end in `many`-elements, not just scalar, liek we allow in for.odata
112
+ - for.seal: Allow annotation paths to end in `many`-elements, not just scalar, like we allow in for.odata
55
113
 
56
114
  ## Version 5.6.0 - 2024-12-12
57
115
 
package/bin/cdsse.js CHANGED
@@ -93,6 +93,12 @@ function complete( err, buf ) {
93
93
  }
94
94
  }
95
95
 
96
+ const autoNavigateKinds = {
97
+ $navElement: art => art._origin,
98
+ $tableAlias: art => art._origin,
99
+ using: () => true,
100
+ }
101
+
96
102
  // For finding the definition for reference under cursor, do the following
97
103
  // * replace identifier under cursor by an undefined name
98
104
  // * call compiler and retrieve valid names at cursor position
@@ -116,7 +122,13 @@ function find( err, buf ) {
116
122
 
117
123
  function show() {
118
124
  const vn = messageAt( messages, 'validNames', off.col ) || Object.create(null);
119
- const art = vn[buf.substring( off.prefix, off.cursor )];
125
+ let art = vn[buf.substring( off.prefix, off.cursor )];
126
+ while (art?._origin && art.$inferred && art._effectiveType ||
127
+ autoNavigateKinds[art.kind]?.( art ))
128
+ art = art._origin || art.extern._artifact;
129
+ // TODO: set _origin in using proxies
130
+ // TODO: why no _effectiveType for $navElement ?
131
+ // TODO: display both the (first) inferred _and_ the "final" one
120
132
  if (art)
121
133
  console.log( `${locationString( art.name.location || art.location )}: Definition` );
122
134
  return true;
@@ -8,6 +8,13 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
  **Don't use `beta` fixes, changes and features in productive mode.**
10
10
 
11
+ ## Version 5.8.0 - 2025-02-27
12
+
13
+ ### Added `v6preview`
14
+
15
+ Use this beta flag to enable features that will be available for cds-compiler v6.
16
+ It also enables new warnings and errors that are intended for the next version.
17
+
11
18
  ## Version 5.3.0 - 2024-09-25
12
19
 
13
20
  ### Removed `optionalActionFunctionParameters`
@@ -55,6 +55,7 @@ const publicOptionsNewAPI = [
55
55
  'resolveProjections',
56
56
  'remapOdataAnnotations',
57
57
  'keepLocalized',
58
+ 'effectiveServiceName',
58
59
  // for.seal
59
60
  'deriveAnalyticalAnnotations',
60
61
  // to.sql.migration
@@ -217,7 +218,7 @@ module.exports = {
217
218
  const defaultOptions = {
218
219
  sqlMapping: 'plain', resolveSimpleTypes: true, resolveProjections: true, remapOdataAnnotations: false, keepLocalized: false,
219
220
  };
220
- const processed = translateOptions(options, defaultOptions, hardOptions, null, [ 'sql-dialect-and-naming' ], 'for.effective');
221
+ const processed = translateOptions(options, defaultOptions, hardOptions, null, [ 'sql-dialect-and-naming', 'effectiveServiceName-and-type-resolution' ], 'for.effective');
221
222
 
222
223
  return Object.assign({}, processed);
223
224
  },
@@ -101,6 +101,11 @@ const validators = {
101
101
  expected: () => 'type array of string',
102
102
  found: val => `type ${ typeof val }`,
103
103
  },
104
+ effectiveServiceName: {
105
+ validate: val => typeof val === 'string',
106
+ expected: () => 'type string',
107
+ found: val => `type ${ typeof val }`,
108
+ },
104
109
  defaultBinaryLength: {
105
110
  validate: val => !Number.isNaN(Number(val)) && Number.isInteger(Number.parseFloat(val)),
106
111
 
@@ -154,6 +159,10 @@ const allCombinationValidators = {
154
159
  if (options.beta && !options.testMode)
155
160
  message.warning('api-unexpected-combination', null, { '#': 'beta-no-test', option: 'beta' });
156
161
  },
162
+ 'effectiveServiceName-and-type-resolution': (options, message) => {
163
+ if (options.effectiveServiceName && !options.resolveSimpleTypes)
164
+ message.error('api-invalid-combination', null, { '#': 'effectiveServiceName-and-type-resolution', name: 'effectiveServiceName', prop: 'resolveSimpleTypes' });
165
+ },
157
166
  };
158
167
 
159
168
  const alwaysRunValidators = [ 'beta-no-test' ];
@@ -14,7 +14,8 @@
14
14
  // always configurable; useful for issues like deprecated syntax variants which
15
15
  // do not affect the compiler or CSN processors. Temporarily, we also allow
16
16
  // value `deprecated` for errors which are only configurable if the option
17
- // `deprecated.downgradableErrors` is set.
17
+ // `deprecated.downgradableErrors` is set. If a string like `v6`, it works like
18
+ // `true`, but is meant to be removed with the next major version.
18
19
 
19
20
  // Messages other than errors can always be reclassified by the user except if
20
21
  // the module is listed in the message's `errorFor` property.
@@ -43,6 +44,7 @@ const { createDict } = require('../utils/objectUtils');
43
44
  *
44
45
  * configurableFor: truthy = error can be downgraded in certain situations
45
46
  * - true = can always be downgraded, we do not really care
47
+ * - 'v4', 'v6' etc: like `true`, but is intended to be removed with next major
46
48
  * - [‹module›, …] = can be downgraded in compiler function ‹module›
47
49
  * - 'deprecated' = severity can only be changed with deprecated.downgradableErrors
48
50
  *
@@ -91,10 +93,12 @@ const centralMessages = {
91
93
 
92
94
  'ref-deprecated-orderby': { severity: 'Error', configurableFor: true },
93
95
  'ref-deprecated-self-element': { severity: 'Error', configurableFor: true },
96
+ 'ref-deprecated-variable': { severity: 'Warning', errorFor: [ 'v6' ] },
94
97
  'ref-invalid-type': { severity: 'Error' },
95
98
  'ref-unexpected-self': { severity: 'Error' },
96
99
  'ref-invalid-include': { severity: 'Error' },
97
100
  'type-unexpected-typeof': { severity: 'Error' },
101
+ 'type-unexpected-null': { severity: 'Warning', errorFor: [ 'v6' ] },
98
102
  'type-ignoring-argument': { severity: 'Error', configurableFor: true },
99
103
  'type-expected-builtin': { severity: 'Error', configurableFor: true },
100
104
  'type-expecting-service-target': { severity: 'Error', configurableFor: true },
@@ -159,15 +163,15 @@ const centralMessages = {
159
163
  // fallback, but then parse.cdl is not supposed to work correctly (it can
160
164
  // then either issue an error or produce a CSN missing some annotations):
161
165
  'syntax-duplicate-annotate': { severity: 'Error' },
162
- 'syntax-duplicate-clause': { severity: 'Error', configurableFor: true },
166
+ 'syntax-duplicate-clause': { severity: 'Error', configurableFor: 'v6' },
163
167
  // remark: a hard syntax error in new parser for `null` together with `not null`
164
168
  'syntax-duplicate-equal-clause': { severity: 'Warning', errorFor: [ 'v6' ] },
165
169
  'syntax-invalid-name': { severity: 'Error', configurableFor: 'deprecated' },
166
170
  'syntax-missing-as': { severity: 'Error', configurableFor: true },
167
171
  'syntax-missing-proj-semicolon': { severity: 'Warning', errorFor: [ 'v6' ] },
168
172
  'syntax-unexpected-after': { severity: 'Warning', errorFor: [ 'v6' ] },
173
+ 'syntax-unexpected-filter': { severity: 'Warning', errorFor: [ 'v6' ] },
169
174
  'syntax-unexpected-many-one': { severity: 'Error', configurableFor: true }, // TODO: remove `configurableFor` soon, latest v6
170
- 'syntax-unexpected-null': { severity: 'Error', configurableFor: true },
171
175
  'syntax-unexpected-reserved-word': { severity: 'Error', configurableFor: true },
172
176
  'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
173
177
  'syntax-unsupported-masked': { severity: 'Error', configurableFor: 'deprecated' },
@@ -278,6 +282,7 @@ const centralMessageTexts = {
278
282
  'sql-dialect-and-naming': 'sqlDialect $(NAME) can\'t be combined with sqlMapping $(PROP)',
279
283
  'tenant-and-naming': 'Option $(OPTION) can\'t be combined with sqlMapping $(PROP) - expected sqlMapping $(VALUE)',
280
284
  'dry-and-script': 'script:true must be combined with dry:true, found $(VALUE)',
285
+ 'effectiveServiceName-and-type-resolution': 'Option $(NAME) can\'t be used without $(PROP)',
281
286
  },
282
287
  'api-unexpected-combination': {
283
288
  std: 'Unexpected option combination: $(OPTION) and $(PROP)', // unused
@@ -378,6 +383,7 @@ const centralMessageTexts = {
378
383
  many: 'Unexpected $(KEYWORD) after array type',
379
384
  enum: 'Unexpected annotation assignment after enum type',
380
385
  },
386
+ 'syntax-unexpected-filter': 'Unexpected filter on the result of a function call',
381
387
  'syntax-unexpected-many-one': 'Replace $(CODE) with $(DELIMITED) to avoid an ambiguity with managed compositions of anonymous aspects',
382
388
  'syntax-invalid-name': {
383
389
  std: 'Identifier must consist of at least one character', // only via delimited id
@@ -458,7 +464,6 @@ const centralMessageTexts = {
458
464
  std: 'Unexpected $(KEYWORD) inside $(CODE) block',
459
465
  'new-parser': 'Unexpected $(OFFENDING) inside $(CODE) block, expecting $(EXPECTING)',
460
466
  },
461
- 'syntax-unexpected-null': 'Keyword $(KEYWORD) must appear after the enum definition and not before',
462
467
  'syntax-unexpected-token': {
463
468
  std: 'Mismatched $(OFFENDING), expecting $(EXPECTING)',
464
469
  unwanted: 'Extraneous $(OFFENDING), expecting $(EXPECTING)',
@@ -512,6 +517,7 @@ const centralMessageTexts = {
512
517
  'syntax-expecting-string': {
513
518
  std: 'Expecting non-empty string for property $(PROP)',
514
519
  'or-object': 'Expecting non-empty string or object for property $(PROP)',
520
+ 'or-bool': 'Expecting non-empty string or boolean for property $(PROP)',
515
521
  'or-null': 'Expecting non-empty string or null for property $(PROP)',
516
522
  },
517
523
  // 'syntax-expecting-boolean' (Warning), 'syntax-expecting-scalar',
@@ -581,6 +587,21 @@ const centralMessageTexts = {
581
587
  },
582
588
  // 'syntax-unknown-property' (Warning? Better configurable Error)
583
589
 
590
+ 'expr-unexpected-argument': {
591
+ // TODO: change to `Arguments…`
592
+ std: 'Parameters can only be provided when navigating along associations',
593
+ from: 'Parameters can only be provided for the source entity or associations',
594
+ tableAlias: 'Arguments can\'t be provided for table aliases, only when navigating along associations',
595
+ // no-params TODO: extra message id or a variant of expr-undefined-param
596
+ 'no-params': 'Unexpected arguments for entity $(ART) without parameters',
597
+ },
598
+ 'expr-unexpected-filter': {
599
+ std: 'A filter can only be provided when navigating along associations',
600
+ // to help users for `… from E:toF { toF[…].x }`
601
+ tableAlias: 'A filter can only be provided when navigating along associations, but found table alias',
602
+ from: 'A filter can only be provided for the source entity or associations',
603
+ },
604
+
584
605
  // multi-line strings: --------------------------------------------------------
585
606
  'syntax-unknown-escape': 'Unknown escape sequence $(CODE)',
586
607
  'syntax-invalid-escape': {
@@ -717,11 +738,11 @@ const centralMessageTexts = {
717
738
  },
718
739
 
719
740
  'ref-unexpected-navigation': {
720
- std: 'Can\'t follow association $(ID) in path $(ELEMREF) in an ON-condition; only foreign keys can be referred to, but not $(NAME)',
721
- unmanaged: 'Can\'t follow unmanaged association $(ID) in path $(ELEMREF) in an ON-condition',
741
+ std: 'Can\'t follow association $(ID) in path $(ELEMREF) in an ON-condition; only foreign keys can be referred to, but not $(NAME)',
742
+ unmanaged: 'Can\'t follow association $(ID) in path $(ELEMREF) in an ON-condition',
722
743
  unmanagedleaf: 'Unexpected unmanaged association as final path step of $(ELEMREF) in an ON-condition',
723
- 'calc-non-fk': 'Can\'t follow association $(ID) in path $(ELEMREF) in a stored calculated element; only foreign keys can be referred to, but not $(NAME)',
724
- 'calc-unmanaged': 'Can\'t follow unmanaged association $(ID) in path $(ELEMREF) in a stored calculated element',
744
+ 'calc-non-fk': 'Can\'t follow association $(ID) in path $(ELEMREF) in a stored calculated element; only foreign keys can be referred to, but not $(NAME)',
745
+ 'calc-unmanaged': 'Can\'t follow association $(ID) in path $(ELEMREF) in a stored calculated element',
725
746
  },
726
747
  'ref-unexpected-filter': {
727
748
  std: 'Unexpected filter in path $(ELEMREF)', // unused
@@ -759,7 +780,7 @@ const centralMessageTexts = {
759
780
  std: 'Rewriting the ON-condition not supported here', // unused: merge with 'rewrite-not-supported'
760
781
  'sub-element': 'Rewriting the ON-condition of unmanaged association in sub element is not supported',
761
782
  },
762
- 'rewrite-undefined-key': 'Expected element $(ID) to be available in $(TARGET), as it is required to match the foreign key of $(NAME)',
783
+ 'rewrite-undefined-key': 'Can\'t redirect association $(NAME), as redirection target $(TARGET) does not project element $(ID), which is required to match foreign key of $(NAME)',
763
784
 
764
785
  'type-unexpected-typeof': {
765
786
  std: 'Unexpected $(KEYWORD) for the type reference here',
@@ -805,6 +826,16 @@ const centralMessageTexts = {
805
826
  targetAspect: 'Unexpected $(KEYWORD) on composition of aspect',
806
827
  map: 'Unexpected $(KEYWORD) for type $(TYPE)',
807
828
  },
829
+ 'type-unexpected-null': {
830
+ std: 'Unexpected default value $(VALUE) for non-nullable artifact',
831
+
832
+ typeDefaultNull: 'Unexpected default value $(VALUE) for non-nullable type',
833
+ typeNotNull: 'Unexpected $(KEYWORD) for type with default value $(VALUE)',
834
+ elementDefaultNull: 'Unexpected default value $(VALUE) for non-nullable element',
835
+ elementNotNull: 'Unexpected $(KEYWORD) for element with default value $(VALUE)',
836
+ paramDefaultNull: 'Unexpected default value $(VALUE) for non-nullable parameter',
837
+ paramNotNull: 'Unexpected $(KEYWORD) for parameter with default value $(VALUE)',
838
+ },
808
839
  'type-expecting-service-target': {
809
840
  std: 'Expecting service entity $(TARGET)',
810
841
  ref: 'Expecting service entity $(TARGET); its element $(ID) referred to at line $(LINE), column $(COL) is not from an element with the same name in the provided model target',
@@ -868,7 +899,10 @@ const centralMessageTexts = {
868
899
  invalidType: 'Unexpected $(PROP) for element of type $(TYPE)',
869
900
  },
870
901
  'def-unexpected-localized': {
871
- std: '$(ART) can\'t have localized elements',
902
+ std: 'Unexpected $(KEYWORD)',
903
+ struct: 'Unexpected $(KEYWORD) for structured type',
904
+ map: 'Unexpected $(KEYWORD) for map type',
905
+ elements: '$(ART) can\'t have localized elements',
872
906
  // TODO: Better message?
873
907
  include: '$(ART) can\'t have localized elements (through include)',
874
908
  },
@@ -971,8 +1005,9 @@ const centralMessageTexts = {
971
1005
  event: 'A type, an element, an event, or a service entity is expected here',
972
1006
  },
973
1007
  'ref-invalid-source': {
974
- std: 'A query source must be an entity or an association',
975
- event: 'An event\'s projection source must be an entity, structured type, event or an association',
1008
+ std: 'A query source must be an entity or an association element',
1009
+ event: 'An event\'s projection source must be an entity, structured type, aspect, event, or an association element',
1010
+ type: 'A type\'s projection source must be an entity, structured type, aspect, event, or an association element',
976
1011
  },
977
1012
  'extend-columns': {
978
1013
  std: 'Artifact $(ART) can\'t be extended with columns, only simple views/projections without JOINs and UNIONs can',
@@ -1144,10 +1179,10 @@ const centralMessageTexts = {
1144
1179
  'odata-spec-violation-assoc': 'Unexpected association in structured type for OData $(VERSION)',
1145
1180
  'odata-spec-violation-constraints': 'Partial referential constraints produced for OData $(VERSION)',
1146
1181
  'odata-spec-violation-id': {
1147
- std: 'Expected EDM name $(ID) to start with an alphabetic character or underscore, followed by a maximum of 127 alphabetic characters, digits, or underscores',
1148
- v2firstchar: 'Unexpected first character $(PROP) of EDM Name $(ID) for OData $(VERSION)',
1149
- qualifier: 'Expected annotation qualifier $(ID) to start with an alphabetic character or underscore, followed by a maximum of 127 alphabetic characters, digits, or underscores',
1150
- vocrefalias: 'Expected value $(VALUE) of vocabulary reference attribute $(ID) to start with an alphabetic character or underscore, followed by a maximum of 127 alphabetic characters, digits, or underscores',
1182
+ std: 'Expected OData name $(ID) to start with an alphabetic character or underscore, followed by a maximum of 127 alphabetic characters, digits, or underscores',
1183
+ v2firstChar: 'Unexpected first character $(PROP) of OData name $(ID) for OData $(VERSION)',
1184
+ qualifier: 'Expected OData annotation qualifier $(ID) to start with an alphabetic character or underscore, followed by a maximum of 127 alphabetic characters, digits, or underscores',
1185
+ vocRefAlias: 'Expected value $(VALUE) of OData vocabulary reference attribute $(ID) to start with an alphabetic character or underscore, followed by a maximum of 127 alphabetic characters, digits, or underscores',
1151
1186
  },
1152
1187
  // version independent messages
1153
1188
  'odata-spec-violation-key-array': {
@@ -1310,8 +1345,8 @@ const centralMessageTexts = {
1310
1345
  changed: 'Changed element $(ID) is a primary key change and will not work if the table contains data',
1311
1346
  },
1312
1347
  'migration-unsupported-precision-change': {
1313
- std: 'Changed element $(ID) is a precision change and is not supported',
1314
- script: 'Changed element $(ID) is a precision change and might lead to data loss',
1348
+ std: 'Changed element $(ID) is a lossy precision change and is not supported',
1349
+ script: 'Changed element $(ID) is a lossy precision change and might lead to data loss',
1315
1350
  },
1316
1351
  'migration-unsupported-element-drop': {
1317
1352
  std: 'Dropping elements is not supported',
@@ -1322,8 +1357,8 @@ const centralMessageTexts = {
1322
1357
  script: 'Changed element $(ID) is a length reduction and might lead to data loss',
1323
1358
  },
1324
1359
  'migration-unsupported-scale-change': {
1325
- std: 'Changed element $(ID) is a scale change and is not supported',
1326
- script: 'Changed element $(ID) is a scale change and might lead to data loss',
1360
+ std: 'Changed element $(ID) is a lossy scale change and is not supported',
1361
+ script: 'Changed element $(ID) is a lossy scale change and might lead to data loss',
1327
1362
  },
1328
1363
  'migration-unsupported-change': {
1329
1364
  std: 'Changed element $(ID) is a lossy type change from $(NAME) to $(TYPE) and is not supported',
@@ -1465,8 +1465,11 @@ function artName( art, omit ) {
1465
1465
 
1466
1466
  if (art.kind === '$self')
1467
1467
  r.push( `alias:${ quoted( name.alias ) }` ); // should be late due to $self in anonymous aspect
1468
- if (suffix)
1469
- r.push( art.targetAspect ? 'target' : art.items?.items ? `items:${ suffix }` : 'items');
1468
+
1469
+ if (suffix && art.targetAspect)
1470
+ r.push( 'target' );
1471
+ else if (suffix)
1472
+ r.push( art.items?.items ? `items:${ suffix }` : 'items' );
1470
1473
 
1471
1474
  return r.join('/');
1472
1475
  }
package/lib/base/model.js CHANGED
@@ -25,9 +25,12 @@ const availableBetaFlags = {
25
25
  tenantVariable: true,
26
26
  calcAssoc: true,
27
27
  temporalRawProjection: true,
28
+ v6preview: true,
29
+ draftMessages: true,
30
+ rewriteAnnotationExpressionsViaType: true,
31
+ sqlServiceDummies: true,
28
32
  // disabled by --beta-mode
29
33
  nestedServices: false,
30
- rewriteAnnotationExpressionsViaType: false,
31
34
  };
32
35
 
33
36
  // Used by isDeprecatedEnabled() to check if any flag ist set.
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Asserts that there is no association usage outside of the specified service.
5
+ * We do not check in type-ofs - we resolve them, so they are not a problem.
6
+ *
7
+ * @param {object} parent - The parent object in the CSN (Core Schema Notation).
8
+ * @param {string} prop - The property name of the parent object.
9
+ * @param {object} ref - The reference object.
10
+ * @param {Array} path - The path array indicating the location in the CSN.
11
+ * @param {object} grandparent - The grandparent object in the CSN.
12
+ * @param {string} parentProp - The property name of the grandparent object.
13
+ */
14
+ function assertNoAssocUsageOutsideOfService( parent, prop, ref, path, grandparent, parentProp ) {
15
+ const artifactName = path[1];
16
+ if (parentProp === 'type')
17
+ return;
18
+
19
+ if (this.csn.definitions[this.options.effectiveServiceName]?.kind !== 'service' ||
20
+ !artifactName.startsWith(`${ this.options.effectiveServiceName }.`))
21
+ return;
22
+
23
+ const { _links } = parent;
24
+ if (_links?.length <= 1)
25
+ return;
26
+
27
+ for (let i = 0; i < _links.length - 1; i++) {
28
+ const { art } = _links[i];
29
+ if (art.target && !art.target.startsWith(`${ this.options.effectiveServiceName }.`)) {
30
+ this.error('assoc-invalid-outside-service', path.concat('ref', i),
31
+ { name: this.options.effectiveServiceName, id: ref[i].id || ref[i] },
32
+ 'Association $(ID) pointing outside of service $(NAME) must not be used');
33
+ return;
34
+ }
35
+ }
36
+ }
37
+
38
+ module.exports = {
39
+ ref: assertNoAssocUsageOutsideOfService,
40
+ };
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { setProp } = require('../base/model');
4
4
  const { featureFlags } = require('../transform/featureFlags');
5
- const { isSqlService } = require('../transform/db/processSqlServices');
5
+ const { isSqlService, isDummyService } = require('../transform/db/processSqlServices');
6
6
 
7
7
  /**
8
8
  *
@@ -29,5 +29,8 @@ module.exports = {
29
29
  kind: function setFeatureFlagForSqlService( artifact ) {
30
30
  if (isSqlService(artifact))
31
31
  setFeatureFlag( '$sqlService' ).call(this);
32
+
33
+ if (isDummyService(artifact, this.options))
34
+ setFeatureFlag( '$dummyService' ).call(this);
32
35
  },
33
36
  };
@@ -52,10 +52,13 @@ function checkElementTypeDefinitionHasType( member, memberName, prop, path ) {
52
52
  // are skipped here. References to such columns are checked further below.
53
53
  const parent = this.csn.definitions[path[1]];
54
54
 
55
- // should only happen with csn input, not in cdl
56
- // calculated elements on-read may not have a .type (requires beta flag)
55
+ // Type elements are not required to have a type.
56
+ // This can also happen via type projections.
57
+ // Elements of views are allowed to be typeless.
58
+ // Calculated elements on-read may not have a .type (requires beta flag)
57
59
  if ((!member.value || member.value.stored) &&
58
- !parent.projection && !parent.query && !hasArtifactTypeInformation(member)) {
60
+ (parent.kind === 'type' || !parent.projection && !parent.query) &&
61
+ !hasArtifactTypeInformation(member)) {
59
62
  errorAboutMissingType(this.error, path, member, memberName, true);
60
63
  return;
61
64
  }
@@ -69,7 +72,7 @@ function checkElementTypeDefinitionHasType( member, memberName, prop, path ) {
69
72
  );
70
73
  }
71
74
  else if (member._type) {
72
- if ( this.isAspect(member._type) || member._type.kind === 'type' && member._type.$syntax === 'aspect')
75
+ if ( member._type?.kind === 'aspect' || member._type.kind === 'type' && member._type.$syntax === 'aspect')
73
76
  this.error('ref-sloppy-type', path, {}, 'A type or an element is expected here');
74
77
  }
75
78
  return;
@@ -50,6 +50,7 @@ const {
50
50
  checkSqlAnnotationOnArtifact,
51
51
  checkSqlAnnotationOnElement,
52
52
  } = require('./sql-snippets');
53
+ const assertNoAssocUsageOutsideOfService = require('./assocOutsideService');
53
54
  const featureFlags = require('./featureFlags');
54
55
 
55
56
  const forRelationalDBMemberValidators
@@ -196,6 +197,8 @@ function getDBCsnValidators( options ) {
196
197
  validations.push(checkForParams.csnValidator);
197
198
  if (options.sqlDialect === 'h2' || options.sqlDialect === 'postgres')
198
199
  validations.push(checkForHanaTypes);
200
+ if (options.transformation === 'effective' && options.effectiveServiceName)
201
+ validations.push(assertNoAssocUsageOutsideOfService);
199
202
 
200
203
  return validations;
201
204
  }
@@ -427,17 +427,17 @@ function assertConsistency( model, stage ) {
427
427
  },
428
428
  $tokenTexts: {
429
429
  parser: true,
430
- test: isString,
430
+ test: isStringOrBool,
431
431
  },
432
432
  value: {
433
433
  optional: [
434
434
  'location', '$inferred', 'sort', 'nulls',
435
435
  'param', 'scope', // for dynamic parameter '?'
436
- // A2J wrongly propagates the following into a CAST of the CSN passed to compileX:
436
+ // before recompilation for A2J, named types are replaced by the base
437
+ // definition (TODO v6 remove, we can probably remove elements+items now)
437
438
  'elements', 'items', 'enum',
438
- // CSN parser may let these properties slip through to XSN, even if input is invalid.
439
439
  'args', 'op', 'func', 'suffix',
440
- // calculated elements on-write
440
+ // calculated elements on-write - TODO: outside `value`
441
441
  'stored',
442
442
  ],
443
443
 
@@ -472,6 +472,8 @@ function assertConsistency( model, stage ) {
472
472
  '$parens',
473
473
  '_artifact', // _artifact with "localized data"s 'coalesce'
474
474
  'sort', 'nulls', // if used in GROUP BY
475
+ 'enum', // for CAST in A2J recompilation (TODO v6: remove)
476
+ // `elements` of type is not put into cast, many will be cds.LargeString
475
477
  ...typeProperties, // for CAST
476
478
  ],
477
479
  },
@@ -643,7 +645,6 @@ function assertConsistency( model, stage ) {
643
645
  'where', 'columns', 'mixin', 'quantifier', 'offset',
644
646
  'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
645
647
  '$limit', 'limit', '_status', '_origin',
646
- '_effectiveType', '$effectiveSeqNo',
647
648
  ],
648
649
  },
649
650
  _leadingQuery: { kind: true, test: TODO },
@@ -1025,6 +1026,11 @@ function assertConsistency( model, stage ) {
1025
1026
  isString( node, parent, prop, spec );
1026
1027
  }
1027
1028
 
1029
+ function isStringOrBool( node, parent, prop, spec ) {
1030
+ if (typeof node !== 'boolean')
1031
+ isString( node, parent, prop, spec );
1032
+ }
1033
+
1028
1034
  function isString( node, parent, prop, spec ) {
1029
1035
  if (typeof node !== 'string')
1030
1036
  throw new InternalConsistencyError( `Expected string but found ${ typeof node }${ at( [ node, parent ], prop ) }` );