@sap/cds-compiler 4.2.4 → 4.3.0

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 (66) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/bin/cdsc.js +8 -0
  3. package/bin/cdshi.js +3 -3
  4. package/doc/CHANGELOG_BETA.md +7 -0
  5. package/lib/api/main.js +19 -0
  6. package/lib/base/location.js +16 -0
  7. package/lib/base/message-registry.js +47 -16
  8. package/lib/base/messages.js +49 -38
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
  11. package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
  12. package/lib/checks/existsMustEndInAssoc.js +27 -0
  13. package/lib/checks/onConditions.js +47 -1
  14. package/lib/checks/validator.js +10 -1
  15. package/lib/compiler/assert-consistency.js +23 -15
  16. package/lib/compiler/base.js +31 -14
  17. package/lib/compiler/builtins.js +21 -20
  18. package/lib/compiler/checks.js +36 -49
  19. package/lib/compiler/define.js +71 -91
  20. package/lib/compiler/extend.js +27 -25
  21. package/lib/compiler/finalize-parse-cdl.js +1 -1
  22. package/lib/compiler/generate.js +67 -87
  23. package/lib/compiler/kick-start.js +7 -5
  24. package/lib/compiler/populate.js +32 -30
  25. package/lib/compiler/propagator.js +2 -0
  26. package/lib/compiler/resolve.js +29 -25
  27. package/lib/compiler/shared.js +57 -31
  28. package/lib/compiler/tweak-assocs.js +203 -22
  29. package/lib/compiler/utils.js +0 -18
  30. package/lib/gen/Dictionary.json +10 -4
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/languageParser.js +3 -3
  33. package/lib/inspect/inspectPropagation.js +2 -1
  34. package/lib/json/from-csn.js +63 -28
  35. package/lib/json/to-csn.js +23 -13
  36. package/lib/language/antlrParser.js +1 -1
  37. package/lib/language/errorStrategy.js +5 -1
  38. package/lib/language/genericAntlrParser.js +67 -61
  39. package/lib/main.d.ts +26 -1
  40. package/lib/main.js +2 -1
  41. package/lib/model/csnRefs.js +1 -0
  42. package/lib/model/csnUtils.js +28 -0
  43. package/lib/model/revealInternalProperties.js +3 -9
  44. package/lib/optionProcessor.js +17 -1
  45. package/lib/render/toCdl.js +1 -1
  46. package/lib/transform/db/associations.js +3 -4
  47. package/lib/transform/db/backlinks.js +293 -0
  48. package/lib/transform/db/expansion.js +9 -7
  49. package/lib/transform/db/flattening.js +3 -2
  50. package/lib/transform/db/rewriteCalculatedElements.js +1 -67
  51. package/lib/transform/db/transformExists.js +3 -58
  52. package/lib/transform/db/views.js +8 -14
  53. package/lib/transform/effective/.eslintrc.json +4 -0
  54. package/lib/transform/effective/associations.js +101 -0
  55. package/lib/transform/effective/main.js +88 -0
  56. package/lib/transform/effective/misc.js +61 -0
  57. package/lib/transform/effective/queries.js +42 -0
  58. package/lib/transform/effective/types.js +121 -0
  59. package/lib/transform/forRelationalDB.js +12 -235
  60. package/lib/transform/localized.js +22 -3
  61. package/lib/transform/parseExpr.js +7 -3
  62. package/lib/transform/transformUtils.js +5 -22
  63. package/lib/transform/translateAssocsToJoins.js +42 -38
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
  65. package/package.json +1 -2
  66. package/lib/language/language.g4 +0 -3260
package/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@
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 4.3.0 - 2023-09-29
11
+
12
+ ### Added
13
+
14
+ - compiler: it is possible to publish associations with filters in views.
15
+ Managed associations become unmanaged ones. For example:
16
+ ```cds
17
+ entity Proj as projection on Base {
18
+ assoc[id = 1],
19
+ };
20
+ ```
21
+
22
+ ### Changed
23
+
24
+ - Update OData vocabularies: 'Aggregation', 'Capabilities', 'Common', 'PDF', 'PersonalData', 'UI'.
25
+
26
+ ### Fixed
27
+
28
+ - parser: Chained methods without arguments such as `b` in `a().b.c()` were lost.
29
+ - compiler:
30
+ + Type arguments in `cast()` functions, whose column also has an explicit type set, were not
31
+ properly checked. Now the `cast()`s type and type arguments are checked.
32
+ + SQL function `STDDEV(*)` was not parsable.
33
+ + In views, published association's ON-conditions containing `$projection` are now rewritten
34
+ to `$self` in the CSN `elements` property. This ensures recompilability of the CSN.
35
+
10
36
  ## Version 4.2.4 - 2023-09-14
11
37
 
12
38
  ### Fixed
package/bin/cdsc.js CHANGED
@@ -278,6 +278,7 @@ function executeCommandLine( command, options, args ) {
278
278
  manageConstraints,
279
279
  toSql,
280
280
  inspect,
281
+ toEffectiveCsn,
281
282
  };
282
283
  const commandsWithoutCompilation = {
283
284
  explain,
@@ -317,6 +318,13 @@ function executeCommandLine( command, options, args ) {
317
318
  return model;
318
319
  }
319
320
 
321
+ function toEffectiveCsn( model ) {
322
+ const csn = options.directBackend ? model : compactModel(model, options);
323
+ displayNamedCsn(main.for.effective(csn, options), 'effective');
324
+
325
+ return model;
326
+ }
327
+
320
328
  // Execute the command line option 'toCsn' and display the results.
321
329
  // Return the original model (for chaining)
322
330
  function toCsn( model ) {
package/bin/cdshi.js CHANGED
@@ -41,11 +41,11 @@ function highlight( err, buf ) {
41
41
  continue;
42
42
  if (tok.$isSkipped) {
43
43
  if (tok.stop > tok.start) {
44
- chars[tok.start] = (tok.$isSkipped === true ? '\x0f' : '\x16');
45
- chars[tok.stop] = '\x17';
44
+ chars[tok.start] = (tok.$isSkipped === true ? '\x0f' : '\x16'); // ^O / ^V
45
+ chars[tok.stop] = '\x17'; // ^W
46
46
  }
47
47
  else {
48
- chars[tok.start] = (tok.$isSkipped === true ? '\x0e' : '\x15');
48
+ chars[tok.start] = (tok.$isSkipped === true ? '\x0e' : '\x15'); // ^N / ^U
49
49
  }
50
50
  }
51
51
  else {
@@ -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 4.3.0 - 2023-09-29
12
+
13
+ ### Removed `associationDefault`
14
+
15
+ This flag is now the default. Managed associations with exactly one foreign key can now
16
+ have a default value.
17
+
11
18
  ## Version 4.1.0 - 2023-07-28
12
19
 
13
20
  ### Added `associationDefault`
package/lib/api/main.js CHANGED
@@ -18,6 +18,7 @@ const csnUtils = lazyload('../model/csnUtils');
18
18
  const timetrace = lazyload('../utils/timetrace');
19
19
  const forRelationalDB = lazyload('../transform/forRelationalDB');
20
20
  const sqlUtils = lazyload('../render/utils/sql');
21
+ const effective = lazyload('../transform/effective/main');
21
22
 
22
23
  /**
23
24
  * Return the artifact name for use for the hdbresult object
@@ -225,6 +226,23 @@ function forHdbcds( csn, options = {} ) {
225
226
  return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
226
227
  }
227
228
 
229
+ /**
230
+ * Effective CSN transformation
231
+ *
232
+ * @param {CSN.Model} csn Plain input CSN
233
+ * @param {EffectiveCsnOptions} [options={}] Options
234
+ * @returns {CSN.Model} CSN transformed
235
+ * @private
236
+ */
237
+ function forEffective( csn, options = {} ) {
238
+ const internalOptions = prepareOptions.to.sql(options);
239
+ internalOptions.transformation = 'effective';
240
+
241
+ const eCsn = effective.effectiveCsn(csn, internalOptions);
242
+
243
+ return internalOptions.testMode ? toCsn.sortCsn(eCsn, internalOptions) : eCsn;
244
+ }
245
+
228
246
  /**
229
247
  * Process the given CSN into SQL.
230
248
  *
@@ -851,6 +869,7 @@ module.exports = {
851
869
  for_sql: publishCsnProcessor(forSql, 'for.sql'),
852
870
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
853
871
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
872
+ for_effective: publishCsnProcessor(forEffective, 'for.effective'),
854
873
  /** Deprecated, will be removed in cds-compiler@v4 */
855
874
  preparedCsnToEdmx,
856
875
  preparedCsnToEdm,
@@ -71,6 +71,21 @@ function emptyWeakLocation( filename ) {
71
71
  };
72
72
  }
73
73
 
74
+ /**
75
+ * @param {CsnLocation} loc
76
+ * @returns {CsnLocation}
77
+ */
78
+ function weakLocation( loc ) {
79
+ return {
80
+ __proto__: CsnLocation.prototype,
81
+ file: loc.file,
82
+ line: loc.line,
83
+ col: loc.col,
84
+ endLine: undefined,
85
+ endCol: undefined,
86
+ };
87
+ }
88
+
74
89
  /**
75
90
  * Returns a dummy location for built-in definitions.
76
91
  *
@@ -156,6 +171,7 @@ module.exports = {
156
171
  combinedLocation,
157
172
  emptyLocation,
158
173
  emptyWeakLocation,
174
+ weakLocation,
159
175
  builtinLocation,
160
176
  dictLocation,
161
177
  locationString,
@@ -74,7 +74,7 @@ const centralMessages = {
74
74
  'def-unexpected-calcview-assoc': { severity: 'Error' },
75
75
  'chained-array-of': { severity: 'Error' },
76
76
  'def-missing-type': { severity: 'Error', configurableFor: [ 'compile' ] },
77
- 'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename' ] },
77
+ 'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename', 'for.effective' ] },
78
78
 
79
79
  'def-duplicate-autoexposed': { severity: 'Error' },
80
80
  'def-unexpected-default': { severity: 'Error', configurableFor: 'test' },
@@ -92,6 +92,7 @@ const centralMessages = {
92
92
  'type-expected-builtin': { severity: 'Error', configurableFor: true },
93
93
  'type-expecting-service-target': { severity: 'Error', configurableFor: true },
94
94
  'ref-expecting-const': { severity: 'Error' },
95
+ 'ref-expecting-foreign-key': { severity: 'Error' },
95
96
  'ref-invalid-source': { severity: 'Error' },
96
97
  'ref-invalid-target': { severity: 'Error' },
97
98
  'ref-sloppy-target': { severity: 'Error', configurableFor: 'v4' },
@@ -186,13 +187,13 @@ const centralMessages = {
186
187
  'odata-spec-violation-key-null': { severity: 'Error', configurableFor: true }, // more than 30 chars
187
188
  'odata-spec-violation-key-type': { severity: 'Warning' }, // more than 30 chars
188
189
  'odata-spec-violation-property-name': { severity: 'Warning' }, // more than 30 chars
189
- 'odata-anno-preproc': { severity: 'Warning', configurableFor: true },
190
- 'odata-anno-dict': { severity: 'Warning', configurableFor: true },
191
- 'odata-anno-vocref': { severity: 'Warning', configurableFor: true },
190
+ 'odata-anno-preproc': { severity: 'Warning' },
191
+ 'odata-anno-dict': { severity: 'Warning' },
192
+ 'odata-anno-vocref': { severity: 'Warning' },
192
193
  'odata-anno-dict-enum': { severity: 'Error' },
193
- 'odata-anno-value': { severity: 'Warning', configurableFor: true },
194
- 'odata-anno-type': { severity: 'Warning', configurableFor: true },
195
- 'odata-anno-def': { severity: 'Info', configurableFor: true },
194
+ 'odata-anno-value': { severity: 'Warning' },
195
+ 'odata-anno-type': { severity: 'Warning' },
196
+ 'odata-anno-def': { severity: 'Info' },
196
197
  'query-ignoring-assoc-in-union': { severity: 'Info' }
197
198
  };
198
199
 
@@ -274,6 +275,12 @@ const centralMessageTexts = {
274
275
  'anno-unstable-array': 'Unstable order of array items due to repeated assignments for $(ANNO)',
275
276
  'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
276
277
  'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
278
+
279
+ 'anno-unexpected-localized-skip': {
280
+ std: 'Compiler generated entity $(NAME) must not be annotated with $(ANNO) if $(ART) is not skipped',
281
+ view: 'Compiler generated view $(NAME) must not be annotated with $(ANNO) if $(ART) is not skipped',
282
+ },
283
+
277
284
  'chained-array-of': '"Array of"/"many" must not be chained with another "array of"/"many" inside a service',
278
285
 
279
286
  'check-proper-type-of': {
@@ -463,12 +470,15 @@ const centralMessageTexts = {
463
470
  'syntax-missing-property': { // location at sibling or '}' otherwise
464
471
  std: 'Object in $(PARENTPROP) must have the property $(PROP)',
465
472
  sibling: 'Object with property $(SIBLINGPROP) must also have a property $(PROP)',
473
+ bothTargets: 'Object with properties $(SIBLINGPROP) and $(OTHERPROP) must also have a property $(PROP)',
466
474
  columns: 'Object in $(PARENTPROP) must have an expression property like $(PROP)',
467
475
  extensions: 'Object in $(PARENTPROP) must have the property $(PROP) or $(OTHERPROP)',
468
476
  },
469
477
  'syntax-unexpected-property': {
470
478
  std: 'Unexpected CSN property $(PROP)',
471
479
  sibling: 'CSN property $(PROP) is not expected in an object with property $(SIBLINGPROP)',
480
+ target: 'CSN property $(PROP) with sub property $(SUBPROP) is not expected in an object with property $(SIBLINGPROP)',
481
+ targetAspect: 'CSN property $(PROP) is not expected in an object with property $(SIBLINGPROP) having sub property $(SUBPROP)',
472
482
  prop: 'CSN property $(PROP) is not expected in $(PARENTPROP)',
473
483
  top: 'CSN property $(PROP) is not expected top-level',
474
484
  kind: 'CSN property $(PROP) is not expected by a definition of kind $(KIND)',
@@ -590,6 +600,9 @@ const centralMessageTexts = {
590
600
  expr: 'Associations can\'t be used as values in expressions',
591
601
  'expr-comp': 'Compositions can\'t be used as values in expressions',
592
602
  cast: 'Casting to an association is not supported',
603
+
604
+ 'managed-filter': 'Unexpected managed association $(NAME) in filter expression of $(ID)',
605
+ 'unmanaged-filter': 'Unexpected unmanaged association $(NAME) in filter expression of $(ID)'
593
606
  },
594
607
  'ref-unexpected-calculated': {
595
608
  std: 'Unexpected reference to calculated element',
@@ -619,6 +632,9 @@ const centralMessageTexts = {
619
632
  calc: 'Unexpected arguments in path $(ELEMREF) of stored calculated element; only simple paths can be used here'
620
633
  },
621
634
 
635
+ // TODO: Better text ?
636
+ 'rewrite-not-supported': 'The ON-condition is not rewritten here - provide an explicit ON-condition',
637
+
622
638
  'type-unexpected-typeof': {
623
639
  std: 'Unexpected $(KEYWORD) for the type reference here',
624
640
  type: 'Unexpected $(KEYWORD) for the type of a type definition',
@@ -703,7 +719,7 @@ const centralMessageTexts = {
703
719
  },
704
720
  'def-unexpected-key': {
705
721
  std: '$(ART) can\'t have additional keys',
706
- virtual: 'Unexpected $(PROP) for virtual element $(ART)',
722
+ virtual: 'Unexpected $(PROP) for virtual element',
707
723
  // TODO: Better message?
708
724
  include: '$(ART) can\'t have additional keys (through include)',
709
725
  },
@@ -754,7 +770,7 @@ const centralMessageTexts = {
754
770
  'include-elements': 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
755
771
  'include-actions': 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
756
772
  },
757
- // TODO: Remove in v4, use duplicate-definition
773
+ // TODO: Remove in v5, use duplicate-definition (or another ID?)
758
774
  'ref-duplicate-include-member': {
759
775
  std: 'Duplicate member $(NAME) through multiple includes $(SORTED_ARTS)',
760
776
  elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
@@ -773,7 +789,9 @@ const centralMessageTexts = {
773
789
  missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)'
774
790
  },
775
791
 
792
+ 'ref-expecting-assoc': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
776
793
  'ref-expecting-const': 'A constant expression or variable is expected here',
794
+ 'ref-expecting-foreign-key': 'Expecting foreign key access after managed association $(NAME) in filter expression of $(ID), but found $(ALIAS)',
777
795
  'ref-invalid-target': {
778
796
  std: 'An entity, projection or view is expected here', // TODO: change text
779
797
  composition: 'Expecting an entity or aspect as composition target',
@@ -826,6 +844,11 @@ const centralMessageTexts = {
826
844
  std: 'Ignoring nullability constraint on parameter when generating SAP HANA CDS view',
827
845
  sql: 'Ignoring nullability constraint on parameter when generating SQL view'
828
846
  },
847
+ 'query-ignoring-filter': {
848
+ std: 'Ignoring filter on published association due to explicit redirection', // unused
849
+ onCond: 'Ignoring filter on published association due to explicit redirection with ON-condition',
850
+ fKey: 'Ignoring filter on published association due to explicit redirection with explicit foreign keys',
851
+ },
829
852
  'query-expected-identifier': {
830
853
  std: 'Expected identifier for select item',
831
854
  assoc: 'Expected identifier as the association\'s name',
@@ -842,15 +865,19 @@ const centralMessageTexts = {
842
865
  extra: 'Specified element $(NAME) differs from inferred element: it has an additional property $(PROP)',
843
866
  target: 'Expected target $(TARGET) of specified element $(NAME) to be the same as the inferred element\'s target $(ART)',
844
867
  foreignKeys: 'Expected foreign keys of specified element $(NAME) to be the same as the inferred element\'s foreign keys',
868
+ unmanagedToManaged: 'Unexpected foreign keys in specified element $(NAME); inferred element is a managed association',
845
869
  prop: 'Value for $(PROP) of the specified element $(NAME) does not match the inferred element\'s value',
846
870
  enumExtra: 'Specified element $(NAME) differs from inferred element: it has an additional enum element $(ID)',
847
871
  enumVal: 'Specified element $(NAME) differs from inferred element: it has a different value for enum element $(ID)',
848
872
  },
849
-
850
873
  'query-unexpected-property': {
851
874
  std: 'Unexpected property $(PROP) in the specified element $(NAME)',
852
875
  calculatedElement: 'Unexpected property $(PROP) in the specified element $(NAME); calculated elements are not supported in queries',
853
876
  },
877
+ 'query-ignoring-assoc-in-union': {
878
+ 'managed': 'Ignoring managed association $(NAME) that is published in a UNION',
879
+ 'std': 'Ignoring association $(NAME) that is published in a UNION'
880
+ },
854
881
 
855
882
  'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
856
883
 
@@ -900,8 +927,14 @@ const centralMessageTexts = {
900
927
 
901
928
  'i18n-different-value': 'Different translation for key $(PROP) of language $(OTHERPROP) in unrelated layers',
902
929
 
903
- 'expr-missing-foreign-key': 'Path step $(ID) of $(ELEMREF) has no valid foreign keys',
930
+ 'expr-missing-foreign-key': {
931
+ std: 'Path step $(ID) of $(ELEMREF) has no valid foreign keys',
932
+ publishingFilter: 'Can\'t publish managed association $(ID) with filter, as it must have at least one foreign key',
933
+ },
904
934
 
935
+ // -----------------------------------------------------------------------------------
936
+ // OData Message section starts here
937
+ // -----------------------------------------------------------------------------------
905
938
  // OData version dependent messages
906
939
  'odata-spec-violation-array': 'Unexpected array type for OData $(VERSION)',
907
940
  'odata-spec-violation-param' : 'Expected parameter to be typed with either scalar or structured type for OData $(VERSION)',
@@ -1017,11 +1050,9 @@ const centralMessageTexts = {
1017
1050
  'deprecated': '$(ANNO) is deprecated. $(DEPR)',
1018
1051
  'notapplied': '$(ANNO) is not applied (AppliesTo: $(RAWVALUES))',
1019
1052
  },
1020
- 'query-ignoring-assoc-in-union': {
1021
- 'managed': 'Ignoring managed association $(NAME) that is published in a UNION',
1022
- 'std': 'Ignoring association $(NAME) that is published in a UNION'
1023
- }
1024
-
1053
+ // -----------------------------------------------------------------------------------
1054
+ // OData Message section ends here, no messages below this line
1055
+ // -----------------------------------------------------------------------------------
1025
1056
  }
1026
1057
 
1027
1058
  /**
@@ -670,6 +670,7 @@ const paramsTransform = {
670
670
  prop: quote.single,
671
671
  siblingprop: quote.single,
672
672
  parentprop: quote.single,
673
+ subprop: quote.single,
673
674
  otherprop: quote.single,
674
675
  code: quote.single,
675
676
  newcode: quote.single,
@@ -820,10 +821,11 @@ function transformArg( arg, r, args, texts ) {
820
821
  return quoted(`${ arg.ref[0] }:${ arg.ref.slice(1).join('.') }`);
821
822
  return quoted(arg.ref);
822
823
  }
823
- const { name } = arg;
824
- if (!name)
825
- return quoted( name );
824
+ if (!arg.name)
825
+ return quoted( arg.name );
826
+ const name = getArtifactName( arg );
826
827
  const prop = [ 'element', 'param', 'action', 'alias' ].find( p => name[p] );
828
+ //if (!prop) throw Error()
827
829
  if (!prop || !texts[prop] )
828
830
  return shortArtName( arg );
829
831
  r['#'] = texts[name.$variant] && name.$variant || prop; // text variant (set by searchName)
@@ -831,12 +833,6 @@ function transformArg( arg, r, args, texts ) {
831
833
  return artName( arg, prop );
832
834
  }
833
835
 
834
- const nameProp = {
835
- enum: 'element',
836
- key: 'element',
837
- function: 'action',
838
- };
839
-
840
836
  // TODO: very likely delete this function
841
837
  function searchName( art, id, variant ) {
842
838
  if (!variant) {
@@ -844,17 +840,30 @@ function searchName( art, id, variant ) {
844
840
  // originally provided one (TODO: mention that in the message text)
845
841
  const type = art._effectiveType && art._effectiveType.kind !== 'undefined' ? art._effectiveType : art;
846
842
  if (type.elements) { // only mentioned elements
847
- art = type.target && type.target._artifact || type;
843
+ art = type.target?._artifact || type;
848
844
  variant = 'element';
849
845
  }
850
846
  else {
851
847
  variant = 'absolute';
852
848
  }
853
849
  }
854
- const prop = nameProp[variant] || variant;
855
- const name = Object.assign( { $variant: variant }, (art._artifact || art).name );
856
- name[prop] = name[prop] ? `${ name[prop] }.${ id }` : id || '?';
857
- return { name, kind: art.kind };
850
+ if (variant === 'absolute') {
851
+ const absolute = `${ art.name.id }.${ id }`;
852
+ return {
853
+ kind: art.kind,
854
+ name: { id: absolute, $variant: variant },
855
+ };
856
+ }
857
+ const undef = {
858
+ kind: variant || art.kind,
859
+ name: { id, $variant: variant },
860
+ };
861
+ Object.defineProperty( undef, '_parent',
862
+ { value: art, configurable: true, writable: true } );
863
+ Object.defineProperty( undef, '_main',
864
+ { value: art._main || art, configurable: true, writable: true } );
865
+ // console.log('SN:',undef)
866
+ return undef;
858
867
  }
859
868
 
860
869
  function messageText( texts, params, transform ) {
@@ -892,17 +901,6 @@ function replaceInString( text, params ) {
892
901
  return parts.join('');
893
902
  }
894
903
 
895
- /**
896
- * @param {CsnLocation} loc
897
- * @returns {CsnLocation}
898
- */
899
- function weakLocation( loc ) {
900
- if (!loc)
901
- return new CsnLocation();
902
- // no endLine/endCol
903
- return new CsnLocation( loc.file, loc.line, loc.col, undefined, undefined );
904
- }
905
-
906
904
  /**
907
905
  * Return message string with location if present in compact form (i.e. one line).
908
906
  *
@@ -1249,7 +1247,7 @@ function compareMessageSeverityAware( a, b ) {
1249
1247
  /**
1250
1248
  * Return sort-relevant part of semantic location (after the ':').
1251
1249
  * Messages without semantic locations are considered smaller (for syntax errors)
1252
- * and (currently - should not happen in v2) larger for other messages.
1250
+ * and (currently - should not happen in v5) larger for other messages.
1253
1251
  *
1254
1252
  * @param {CompileMessage} msg
1255
1253
  */
@@ -1299,14 +1297,9 @@ function deduplicateMessages( messages ) {
1299
1297
  }
1300
1298
 
1301
1299
  function shortArtName( art ) {
1300
+ if (!art.name)
1301
+ return artName( art );
1302
1302
  const name = getArtifactName( art );
1303
- if (!name) {
1304
- const loc = art.location ? ` at ${ locationString( art.location ) }` : '';
1305
- throw new CompilerAssertion(
1306
- art.path
1307
- ? `No artifact for ${ art.path.map( i => i.id ).join( '.' )}${ loc }`
1308
- : `No name found in ${ Object.keys( art ).join( '+' )}${ loc }` );
1309
- }
1310
1303
  if ([ 'select', 'action', 'alias', 'param' ].every( n => name[n] == null || name[n] === 1 ) &&
1311
1304
  !name.absolute.includes(':'))
1312
1305
  return quote.double( name.element ? `${ name.absolute }:${ name.element }` : name.absolute );
@@ -1314,7 +1307,21 @@ function shortArtName( art ) {
1314
1307
  }
1315
1308
 
1316
1309
  function artName( art, omit ) {
1310
+ let suffix = 0;
1311
+ while (!art.name && art._outer) {
1312
+ ++suffix;
1313
+ art = art._outer;
1314
+ }
1315
+
1317
1316
  const name = getArtifactName( art );
1317
+ if (!name) {
1318
+ const loc = art.location ? ` at ${ locationString( art.location ) }` : '';
1319
+ throw new CompilerAssertion(
1320
+ art.path
1321
+ ? `No artifact for ${ art.path.map( i => i.id ).join( '.' )}${ loc }`
1322
+ : `No name found in ${ Object.keys( art ).join( '+' )}${ loc }` );
1323
+ }
1324
+
1318
1325
  const r = (name.absolute) ? [ quoted( name.absolute ) ] : [];
1319
1326
  if (name.select && name.select > 1 || name.select != null && art.kind !== 'element') // Yes, omit select:1 for element - TODO: re-check
1320
1327
  r.push( (art.kind === 'extend' ? 'block:' : 'query:') + name.select ); // TODO: rename to 'select:1' and consider whether there are more selects
@@ -1328,6 +1335,8 @@ function artName( art, omit ) {
1328
1335
  if (name.element != null && omit !== 'element') {
1329
1336
  if (name.select != null && !art.$inferred)
1330
1337
  r.push( 'column:' + quoted( name.element ) );
1338
+ else if (art.kind === 'builtin')
1339
+ return `$var:${ quoted( name.element ) }`;
1331
1340
  else
1332
1341
  // r.push( `${ art.kind }: ${ quoted( name.element )}` ); or even better element:"assoc"/key:"i" same with enum
1333
1342
  r.push( (art.kind === 'enum' ? 'enum:' : 'element:') + quoted( name.element ) );
@@ -1335,6 +1344,9 @@ function artName( art, omit ) {
1335
1344
 
1336
1345
  if (art.kind === '$self')
1337
1346
  r.push( `alias:${ quoted( name.alias ) }` ); // should be late due to $self in anonymous aspect
1347
+ if (suffix)
1348
+ r.push( art.targetAspect ? 'target' : art.items?.items ? `items:${ suffix }` : 'items');
1349
+
1338
1350
  return r.join('/');
1339
1351
  }
1340
1352
 
@@ -1361,21 +1373,21 @@ function homeName( art, absoluteOnly ) {
1361
1373
  return `using:${ quoted( art.name.id ) }`;
1362
1374
  else if (art.kind === 'extend' || art.kind === 'annotate')
1363
1375
  return !absoluteOnly && homeNameForExtend( art );
1376
+ else if (!art.kind) // annotation assignments are not really supported
1377
+ return (absoluteOnly) ? art.name.id : quoted( `@${ art.name.id }` );
1364
1378
  else if (art.name._artifact) // block, extend, annotate
1365
1379
  return homeName( art.name._artifact, absoluteOnly ); // use corresponding definition
1366
- else if (absoluteOnly)
1367
- return art.name.absolute;
1368
1380
  let main = art._main || art;
1369
1381
  while (main._outer) // anonymous aspect
1370
1382
  main = main._outer._main;
1371
- return `${ main.kind }:${ artName( art ) }`;
1383
+ return (absoluteOnly) ? main.name.id : `${ main.kind }:${ artName( art ) }`;
1372
1384
  }
1373
1385
 
1374
1386
  // The "home" for extensions is handled differently because `_artifact` is not
1375
1387
  // set for unknown extensions, and we could have nested extensions.
1376
1388
  // TODO: delete this function, just set correct name/_parent for extensions
1377
1389
  function homeNameForExtend( art ) {
1378
- if (!art.name.absolute && art._main) // new-style member name
1390
+ if (art._main) // new-style member name
1379
1391
  return `${ art._main.kind }:${ artName( art ) }`;
1380
1392
  const kind = art.kind || 'extend';
1381
1393
  // TODO: fix the following - do like in collectArtifactExtensions() or
@@ -1776,7 +1788,6 @@ function messageIdsWithExplanation() {
1776
1788
 
1777
1789
  module.exports = {
1778
1790
  hasErrors,
1779
- weakLocation,
1780
1791
  locationString,
1781
1792
  messageString,
1782
1793
  messageStringMultiline,
package/lib/base/model.js CHANGED
@@ -33,8 +33,8 @@ const availableBetaFlags = {
33
33
  enableUniversalCsn: true,
34
34
  odataTerms: true,
35
35
  optionalActionFunctionParameters: true, // not supported by runtime, yet.
36
- associationDefault: true,
37
36
  annotateForeignKeys: true,
37
+ effectiveCsn: true,
38
38
  // disabled by --beta-mode
39
39
  nestedServices: false,
40
40
  };
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const { requireForeignKeyAccess } = require('../checks/onConditions');
4
+ const { applyTransformationsOnNonDictionary } = require('../model/csnUtils');
5
+
6
+ /**
7
+ * Check that all paths in calculated elements-on write either access normal fields
8
+ * (structures are already rejected by the compiler) or access the foreign keys of
9
+ * associations. Filters and parameters are not allowed.
10
+ *
11
+ * @param {CSN.Element} calculatedElement
12
+ * @param {string} propOnParent
13
+ * @param {object} valueOfCalculatedElement
14
+ * @param {CSN.Path} path
15
+ */
16
+ function checkPathsInStoredCalcElement( calculatedElement, propOnParent, valueOfCalculatedElement, path ) {
17
+ if (calculatedElement.value.stored) {
18
+ applyTransformationsOnNonDictionary(calculatedElement, 'value', {
19
+ ref: (parent, prop, value, csnPath) => {
20
+ _checkPathsInStoredCalcElement.call(this, parent, value, csnPath);
21
+ },
22
+ }, {}, path);
23
+ }
24
+ }
25
+
26
+
27
+ /**
28
+ * See comment for calling function above.
29
+ *
30
+ * @param {object} parent
31
+ * @param {(string|object)[]} value
32
+ * @param {CSN.Path} csnPath
33
+ */
34
+ function _checkPathsInStoredCalcElement( parent, value, csnPath ) {
35
+ const { _links } = parent;
36
+
37
+ for (let i = 0; i < value.length; ++i) {
38
+ let hasPathError = false;
39
+ const step = value[i];
40
+ const stepArt = _links[i].art;
41
+
42
+ if (stepArt.value) {
43
+ applyTransformationsOnNonDictionary(stepArt, 'value', {
44
+ ref: (nestedParent, prop, nestedValue, path) => {
45
+ _checkPathsInStoredCalcElement.call(this, nestedParent, nestedValue, path);
46
+ },
47
+ }, {}, csnPath);
48
+ }
49
+ else if (stepArt.target) {
50
+ const id = step.id || step;
51
+ if (stepArt.on) {
52
+ // It's an unmanaged association - traversal is always forbidden
53
+ this.error('ref-unexpected-navigation', csnPath, { '#': 'calc-unmanaged', id, elemref: parent });
54
+ hasPathError = true;
55
+ }
56
+ else {
57
+ // It's a managed association - access of the foreign keys is allowed
58
+ requireForeignKeyAccess(parent, i, (errorIndex) => {
59
+ this.error('ref-unexpected-navigation', csnPath, {
60
+ '#': 'calc-non-fk', id, elemref: parent, name: value[errorIndex].id || value[errorIndex],
61
+ });
62
+ hasPathError = true;
63
+ });
64
+ }
65
+ }
66
+ if (typeof step === 'object') {
67
+ if (step.where) {
68
+ this.error('ref-unexpected-filter', csnPath, { '#': 'calc', elemref: parent });
69
+ hasPathError = true;
70
+ }
71
+ if (step.args) {
72
+ this.error('ref-unexpected-args', csnPath, { '#': 'calc', elemref: parent });
73
+ hasPathError = true;
74
+ }
75
+ }
76
+ if (hasPathError)
77
+ break; // avoid too many consequent errors
78
+ }
79
+ }
80
+
81
+ module.exports = {
82
+ value: checkPathsInStoredCalcElement,
83
+ };