@sap/cds-compiler 4.0.0 → 4.1.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.
- package/CHANGELOG.md +115 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_BETA.md +11 -0
- package/lib/api/main.js +60 -12
- package/lib/api/validate.js +1 -1
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +84 -38
- package/lib/base/messages.js +11 -10
- package/lib/base/model.js +6 -2
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/onConditions.js +17 -12
- package/lib/checks/queryNoDbArtifacts.js +132 -72
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +1 -1
- package/lib/compiler/assert-consistency.js +44 -16
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +7 -8
- package/lib/compiler/checks.js +274 -197
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +3 -3
- package/lib/compiler/define.js +63 -50
- package/lib/compiler/extend.js +38 -20
- package/lib/compiler/finalize-parse-cdl.js +2 -1
- package/lib/compiler/generate.js +0 -8
- package/lib/compiler/index.js +9 -7
- package/lib/compiler/kick-start.js +2 -0
- package/lib/compiler/populate.js +139 -110
- package/lib/compiler/propagator.js +4 -3
- package/lib/compiler/resolve.js +157 -126
- package/lib/compiler/shared.js +706 -404
- package/lib/compiler/tweak-assocs.js +21 -10
- package/lib/compiler/utils.js +228 -36
- package/lib/edm/annotations/genericTranslation.js +30 -2
- package/lib/edm/edm.js +4 -1
- package/lib/edm/edmPreprocessor.js +12 -5
- package/lib/edm/edmUtils.js +2 -4
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3987 -3963
- package/lib/json/from-csn.js +43 -47
- package/lib/json/to-csn.js +11 -11
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +59 -59
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +20 -16
- package/lib/model/revealInternalProperties.js +29 -21
- package/lib/model/sortViews.js +4 -2
- package/lib/modelCompare/compare.js +112 -39
- package/lib/modelCompare/utils/filter.js +54 -24
- package/lib/optionProcessor.js +6 -6
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +34 -20
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +77 -26
- package/lib/render/utils/common.js +3 -3
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +61 -20
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +8 -8
- package/lib/transform/db/expansion.js +17 -21
- package/lib/transform/db/flattening.js +23 -23
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +56 -42
- package/lib/transform/forRelationalDB.js +69 -75
- package/lib/transform/localized.js +6 -5
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/package.json +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -52,6 +52,7 @@ const centralMessages = {
|
|
|
52
52
|
'anno-unstable-array': { severity: 'Warning' },
|
|
53
53
|
'anno-invalid-sql-element': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
54
54
|
'anno-invalid-sql-struct': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
55
|
+
'anno-invalid-sql-assoc': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
55
56
|
'anno-invalid-sql-calc': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
56
57
|
'anno-invalid-sql-kind': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
57
58
|
'anno-invalid-sql-view': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
@@ -63,46 +64,36 @@ const centralMessages = {
|
|
|
63
64
|
'anno-undefined-param': { severity: 'Warning' },
|
|
64
65
|
'anno-unexpected-ellipsis': { severity: 'Error', configurableFor: 'deprecated' },
|
|
65
66
|
|
|
66
|
-
'args-expecting-named': { severity: 'Error' },
|
|
67
|
-
'args-no-params': { severity: 'Error' },
|
|
68
|
-
'args-undefined-param': { severity: 'Error' },
|
|
69
|
-
|
|
70
67
|
'name-invalid-dollar-alias': { severity: 'Error', configurableFor: true },
|
|
71
68
|
|
|
72
69
|
'type-invalid-items': { severity: 'Error' }, // not supported yet
|
|
73
70
|
'assoc-as-type': { severity: 'Error' }, // TODO: allow more, but not all
|
|
71
|
+
'def-unexpected-nested-proj': { severity: 'Error', configurableFor: 'v4' },
|
|
74
72
|
'def-unexpected-paramview-assoc': { severity: 'Error' },
|
|
75
73
|
'def-unexpected-calcview-assoc': { severity: 'Error' },
|
|
76
74
|
'chained-array-of': { severity: 'Error' },
|
|
77
|
-
'
|
|
75
|
+
'def-missing-type': { severity: 'Error', configurableFor: [ 'compile' ] },
|
|
78
76
|
'check-proper-type-of': { severity: 'Info', errorFor: [ 'for.odata', 'to.edmx', 'to.hdbcds', 'to.sql', 'to.hdi', 'to.rename' ] },
|
|
79
77
|
|
|
80
78
|
'def-duplicate-autoexposed': { severity: 'Error' },
|
|
81
79
|
'def-unexpected-default': { severity: 'Error', configurableFor: 'test' },
|
|
82
80
|
|
|
83
|
-
'expr-
|
|
81
|
+
'expr-unexpected-filter': { severity: 'Error' },
|
|
84
82
|
|
|
85
83
|
'empty-type': { severity: 'Info' }, // only still an error in old transformers
|
|
86
84
|
|
|
87
85
|
'ref-deprecated-orderby': { severity: 'Error', configurableFor: true },
|
|
88
|
-
'ref-
|
|
89
|
-
'ref-sloppy-type': { severity: 'Error' },
|
|
86
|
+
'ref-invalid-type': { severity: 'Error' },
|
|
90
87
|
'ref-unexpected-self': { severity: 'Error' },
|
|
91
|
-
'ref-
|
|
88
|
+
'ref-invalid-include': { severity: 'Error' },
|
|
92
89
|
'type-unexpected-typeof': { severity: 'Error' },
|
|
93
90
|
'type-ignoring-argument': { severity: 'Error', configurableFor: true },
|
|
94
91
|
'type-expected-builtin': { severity: 'Error', configurableFor: true },
|
|
95
92
|
'type-expecting-service-target': { severity: 'Error', configurableFor: true },
|
|
96
|
-
'ref-expecting-action-param-type': { severity: 'Error' },
|
|
97
|
-
'ref-sloppy-actionparam-type': { severity: 'Error' },
|
|
98
|
-
'ref-expecting-event-type': { severity: 'Error' }, // TODO: Test coverage
|
|
99
|
-
'ref-sloppy-event-type': { severity: 'Error' },
|
|
100
|
-
'ref-expecting-struct': { severity: 'Error' },
|
|
101
93
|
'ref-expecting-const': { severity: 'Error' },
|
|
102
|
-
'ref-
|
|
103
|
-
'ref-
|
|
104
|
-
'ref-
|
|
105
|
-
'ref-sloppy-target': { severity: 'Warning' },
|
|
94
|
+
'ref-invalid-source': { severity: 'Error' },
|
|
95
|
+
'ref-invalid-target': { severity: 'Error' },
|
|
96
|
+
'ref-sloppy-target': { severity: 'Error', configurableFor: 'v4' },
|
|
106
97
|
|
|
107
98
|
'extend-repeated-intralayer': { severity: 'Warning' },
|
|
108
99
|
'extend-unrelated-layer': { severity: 'Info' },
|
|
@@ -166,15 +157,13 @@ const centralMessages = {
|
|
|
166
157
|
'syntax-unexpected-sql-clause': { severity: 'Error' }, // TODO: configurableFor:'tests'?
|
|
167
158
|
|
|
168
159
|
'type-managed-composition': { severity: 'Error' },
|
|
169
|
-
'type-unsupported-precision-change': { severity: 'Error'},
|
|
160
|
+
'type-unsupported-precision-change': { severity: 'Error' },
|
|
161
|
+
'type-unsupported-key-change': { severity: 'Error', configurableFor: true },
|
|
170
162
|
|
|
171
163
|
'def-missing-element': { severity: 'Error' },
|
|
172
164
|
|
|
173
165
|
'def-unsupported-calc-elem': { severity: 'Error', configurableFor: true },
|
|
174
166
|
|
|
175
|
-
'unexpected-keys-for-composition': { severity: 'Error' }, // TODO: more than 30 chars
|
|
176
|
-
'unmanaged-as-key': { severity: 'Error' }, // is confusing
|
|
177
|
-
'composition-as-key': { severity: 'Error' }, // is confusing and not supported
|
|
178
167
|
'def-invalid-key-cardinality': { severity: 'Error' },
|
|
179
168
|
// Published! Used in @cap-js-community/odata-v2-adapter; if renamed, add to oldMessageIds
|
|
180
169
|
'odata-spec-violation-array': { severity: 'Warning' }, // more than 30 chars
|
|
@@ -213,6 +202,8 @@ const oldMessageIds = createDict({
|
|
|
213
202
|
'old-anno-duplicate': 'anno-duplicate', // Example
|
|
214
203
|
'assoc-in-array': 'type-invalid-items',
|
|
215
204
|
'duplicate-autoexposed': 'def-duplicate-autoexposed',
|
|
205
|
+
'expr-no-filter': 'expr-unexpected-filter',
|
|
206
|
+
'check-proper-type': 'def-missing-type',
|
|
216
207
|
});
|
|
217
208
|
|
|
218
209
|
// Set up the old-to-new message ID mapping in the message registry.
|
|
@@ -511,7 +502,12 @@ const centralMessageTexts = {
|
|
|
511
502
|
// Messages for erroneous references -----------------------------------------
|
|
512
503
|
// location at erroneous reference (if possible)
|
|
513
504
|
'ref-deprecated-orderby': 'Replace source element reference $(ID) by $(NEWCODE); auto-corrected',
|
|
514
|
-
'ref-unexpected-self':
|
|
505
|
+
'ref-unexpected-self': {
|
|
506
|
+
std: 'Unexpected $(ID) reference; is valid only in ON-conditions of unmanaged associations',
|
|
507
|
+
on: 'Unexpected $(ID) reference; is valid only if compared to be equal to an association of the target side',
|
|
508
|
+
subQuery: 'Unexpected $(ID) reference in a sub query',
|
|
509
|
+
setQuery: 'Unexpected $(ID) reference in a query on the right side of $(OP)',
|
|
510
|
+
},
|
|
515
511
|
'ref-undefined-def': {
|
|
516
512
|
std: 'Artifact $(ART) has not been found',
|
|
517
513
|
// TODO: proposal 'No definition of $(NAME) found',
|
|
@@ -565,7 +561,11 @@ const centralMessageTexts = {
|
|
|
565
561
|
},
|
|
566
562
|
'ref-unexpected-assoc': {
|
|
567
563
|
std: 'Unexpected reference to association $(NAME)', // "std" currently unused
|
|
564
|
+
unmanaged: 'Unexpected reference to an unmanaged association',
|
|
565
|
+
'self-unmanaged': 'Unexpected column reference starting with $(ALIAS) to an unmanaged association',
|
|
566
|
+
self: 'A reference to an unmanaged association is only valid when compared via $(CODE)',
|
|
568
567
|
expr: 'Associations can\'t be used as values in expressions',
|
|
568
|
+
'expr-comp': 'Compositions can\'t be used as values in expressions',
|
|
569
569
|
cast: 'Casting to an association is not supported',
|
|
570
570
|
},
|
|
571
571
|
'ref-unexpected-calculated': {
|
|
@@ -575,7 +575,7 @@ const centralMessageTexts = {
|
|
|
575
575
|
},
|
|
576
576
|
'ref-unexpected-localized': {
|
|
577
577
|
std: 'Unexpected reference to localized element $(NAME)', // "std" currently unused
|
|
578
|
-
calc: 'Calculated elements can\'t refer to localized elements',
|
|
578
|
+
calc: 'Calculated elements "on-write" can\'t refer to localized elements',
|
|
579
579
|
},
|
|
580
580
|
|
|
581
581
|
'ref-unexpected-navigation': {
|
|
@@ -624,6 +624,14 @@ const centralMessageTexts = {
|
|
|
624
624
|
comp: 'Unexpected composition inside $(PROP)',
|
|
625
625
|
},
|
|
626
626
|
|
|
627
|
+
'type-unexpected-default': {
|
|
628
|
+
std: 'Unexpected $(KEYWORD) on an association/composition', // unused
|
|
629
|
+
multi: 'Unexpected $(KEYWORD); expected exactly one foreign key in combination with default value, but found $(COUNT)',
|
|
630
|
+
structured: 'Unexpected $(KEYWORD) in combination with structured foreign key $(NAME); $(KEYWORD) requires a non-structured foreign key',
|
|
631
|
+
'onCond': 'Unexpected $(KEYWORD) on an association/composition with ON-condition; $(KEYWORD) requires exactly one foreign key',
|
|
632
|
+
'targetAspect': 'Unexpected $(KEYWORD) on composition of aspect'
|
|
633
|
+
},
|
|
634
|
+
|
|
627
635
|
'anno-builtin': 'Builtin types should not be annotated. Use custom type instead',
|
|
628
636
|
'anno-undefined-def': 'Artifact $(ART) has not been found', // TODO: ext-
|
|
629
637
|
'anno-undefined-art': 'No artifact has been found with name $(ART)',
|
|
@@ -654,14 +662,25 @@ const centralMessageTexts = {
|
|
|
654
662
|
source: 'Unexpected definition of an association in an entity with parameters',
|
|
655
663
|
target: 'Expected association target to have no parameters',
|
|
656
664
|
},
|
|
665
|
+
'def-unexpected-nested-proj': {
|
|
666
|
+
std: 'Unexpected $(CODE)',
|
|
667
|
+
var: 'Unexpected $(CODE) after reference to CDS variable',
|
|
668
|
+
struct: 'Unexpected $(CODE); can only be used after a reference to a structure or association',
|
|
669
|
+
init: 'Unexpected $(CODE); can only be used after a reference to a structure, association or table alias',
|
|
670
|
+
},
|
|
657
671
|
'def-unexpected-calcview-assoc': {
|
|
658
672
|
std: 'unused',
|
|
659
673
|
'source': 'Unexpected definition of an association in an entity annotated with $(ANNO)',
|
|
660
674
|
'target': 'Expected association target not to be annotated with $(ANNO)',
|
|
661
675
|
},
|
|
676
|
+
'def-invalid-key': {
|
|
677
|
+
std: 'The current element can\'t be defined as primary key', // (unused)
|
|
678
|
+
unmanaged: 'Unmanaged associations/compositions can\'t be defined as primary key',
|
|
679
|
+
composition: 'Managed aspect compositions can\'t be defined as primary key',
|
|
680
|
+
},
|
|
662
681
|
'def-unexpected-key': {
|
|
663
682
|
std: '$(ART) can\'t have additional keys',
|
|
664
|
-
virtual: 'Unexpected $(PROP) for virtual element $(
|
|
683
|
+
virtual: 'Unexpected $(PROP) for virtual element $(ART)',
|
|
665
684
|
// TODO: Better message?
|
|
666
685
|
include: '$(ART) can\'t have additional keys (through include)',
|
|
667
686
|
},
|
|
@@ -725,19 +744,27 @@ const centralMessageTexts = {
|
|
|
725
744
|
missing: 'Expected element $(ID) to have at least all the same sub-elements as included artifacts, but it is missing $(NAME)'
|
|
726
745
|
},
|
|
727
746
|
|
|
728
|
-
'ref-expecting-
|
|
729
|
-
'ref-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
747
|
+
'ref-expecting-const': 'A constant expression or variable is expected here',
|
|
748
|
+
'ref-invalid-target': {
|
|
749
|
+
std: 'An entity, projection or view is expected here', // TODO: change text
|
|
750
|
+
composition: 'Expecting an entity or aspect as composition target',
|
|
751
|
+
bare: 'Expecting the target aspect to have elements',
|
|
752
|
+
aspect: 'Expecting the name of an aspect in property $(PROP)', // only CSN input
|
|
753
|
+
},
|
|
754
|
+
'ref-invalid-include': {
|
|
755
|
+
std: 'A type, entity, aspect or event with direct elements is expected here',
|
|
756
|
+
bare : 'An aspect without elements is expected here',
|
|
757
|
+
},
|
|
758
|
+
'ref-invalid-type': {
|
|
759
|
+
std: 'A type or an element is expected here',
|
|
760
|
+
param: 'A type, an element, or a service entity is expected here',
|
|
761
|
+
event: 'A type, an element, an event, or a service entity is expected here',
|
|
762
|
+
},
|
|
734
763
|
// TODO: text variant if the association does not start an entity
|
|
735
|
-
'ref-
|
|
736
|
-
'ref-expecting-target': 'An entity or an aspect is expected here', // TODO: coverage
|
|
764
|
+
'ref-invalid-source': 'A query source must be an entity or an association',
|
|
737
765
|
'extend-columns': 'Artifact $(ART) can\'t be extended with columns, only projections can',
|
|
738
766
|
'extend-repeated-intralayer': 'Unstable element order due to repeated extensions in same layer',
|
|
739
767
|
'extend-unexpected-include': 'Can\'t extend $(META) with includes',
|
|
740
|
-
'ref-expecting-bare-aspect': 'An aspect without elements is expected here',
|
|
741
768
|
|
|
742
769
|
'ext-duplicate-same-file': 'Duplicate extension with $(PROP) in same file',
|
|
743
770
|
'ext-duplicate-extend-type': 'Duplicate type extension for type $(TYPE)',
|
|
@@ -787,6 +814,8 @@ const centralMessageTexts = {
|
|
|
787
814
|
target: 'Expected target $(TARGET) of specified element $(NAME) to be the same as the inferred element\'s target $(ART)',
|
|
788
815
|
foreignKeys: 'Expected foreign keys of specified element $(NAME) to be the same as the inferred element\'s foreign keys',
|
|
789
816
|
prop: 'Value for $(PROP) of the specified element $(NAME) does not match the inferred element\'s value',
|
|
817
|
+
enumExtra: 'Specified element $(NAME) differs from inferred element: it has an additional enum element $(ID)',
|
|
818
|
+
enumVal: 'Specified element $(NAME) differs from inferred element: it has a different value for enum element $(ID)',
|
|
790
819
|
},
|
|
791
820
|
|
|
792
821
|
'query-unexpected-property': {
|
|
@@ -794,10 +823,7 @@ const centralMessageTexts = {
|
|
|
794
823
|
calculatedElement: 'Unexpected property $(PROP) in the specified element $(NAME); calculated elements are not supported in queries',
|
|
795
824
|
},
|
|
796
825
|
|
|
797
|
-
'ref-sloppy-type': 'A type or an element is expected here',
|
|
798
|
-
'ref-sloppy-actionparam-type': 'A type, an element, or a service entity is expected here',
|
|
799
826
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
800
|
-
'ref-sloppy-event-type': 'A type, an element, an event, or a service entity is expected here',
|
|
801
827
|
|
|
802
828
|
'ref-ambiguous': {
|
|
803
829
|
std: 'Replace ambiguous $(ID) by $(NAMES)',
|
|
@@ -812,6 +838,16 @@ const centralMessageTexts = {
|
|
|
812
838
|
entity: 'Entity $(ART) with managed compositions can\'t be used in types', // yet
|
|
813
839
|
},
|
|
814
840
|
|
|
841
|
+
'type-unsupported-key-change': {
|
|
842
|
+
std: 'Added element $(ID) is a primary key change and will not work if the table contains data',
|
|
843
|
+
changed: 'Changed element $(ID) is a primary key change and will not work if the table contains data'
|
|
844
|
+
},
|
|
845
|
+
|
|
846
|
+
'type-unsupported-key-sqlite': {
|
|
847
|
+
std: 'Added element $(ID) is a primary key change and will not work with dialect $(NAME)',
|
|
848
|
+
changed: 'Changed element $(ID) is a primary key change and will not work with dialect $(NAME)'
|
|
849
|
+
},
|
|
850
|
+
|
|
815
851
|
// -----------------------------------------------------------------------------------
|
|
816
852
|
// Expressions
|
|
817
853
|
// -----------------------------------------------------------------------------------
|
|
@@ -820,6 +856,16 @@ const centralMessageTexts = {
|
|
|
820
856
|
std: 'Expected a comparison with $(OP) when using $(ID) in an ON-condition',
|
|
821
857
|
},
|
|
822
858
|
|
|
859
|
+
'type-invalid-cardinality': {
|
|
860
|
+
std: 'Invalid value $(VALUE) for cardinality', // unused variant
|
|
861
|
+
sourceMax: 'Invalid value $(PROP) for maximum source cardinality, expecting a positive number or $(OTHERPROP)',
|
|
862
|
+
targetMax: 'Invalid value $(PROP) for maximum target cardinality, expecting a positive number or $(OTHERPROP)',
|
|
863
|
+
targetMin: 'Invalid value $(PROP) for minimum target cardinality, expecting a non-negative number',
|
|
864
|
+
sourceMin: 'Invalid value $(PROP) for minimum source cardinality, expecting a non-negative number',
|
|
865
|
+
sourceVal: 'Source minimum cardinality must not be greater than source maximum cardinality',
|
|
866
|
+
targetVal: 'Target minimum cardinality must not be greater than target maximum cardinality',
|
|
867
|
+
},
|
|
868
|
+
|
|
823
869
|
'i18n-different-value': 'Different translation for key $(PROP) of language $(OTHERPROP) in unrelated layers',
|
|
824
870
|
|
|
825
871
|
// OData version dependent messages
|
|
@@ -948,7 +994,7 @@ const centralMessageTexts = {
|
|
|
948
994
|
*
|
|
949
995
|
* @typedef {object} MessageConfig
|
|
950
996
|
* @property {MessageSeverity} severity Default severity for the message.
|
|
951
|
-
* @property {string[]|'deprecated'|'v4'|true} [configurableFor]
|
|
997
|
+
* @property {string[]|'deprecated'|'v4'|'test'|true} [configurableFor]
|
|
952
998
|
* Whether the error can be reclassified to a warning or lower.
|
|
953
999
|
* If not `true` then an array is expected with specified modules in which the error is downgradable.
|
|
954
1000
|
* Only has an effect if default severity is 'Error'.
|
package/lib/base/messages.js
CHANGED
|
@@ -17,6 +17,7 @@ const { cdlNewLineRegEx } = require('../language/textUtils');
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
const path = require('path');
|
|
19
19
|
const { inspect } = require('util')
|
|
20
|
+
const { CsnLocation } = require('../compiler/classes');
|
|
20
21
|
|
|
21
22
|
// term instance for messages
|
|
22
23
|
const colorTerm = term();
|
|
@@ -94,7 +95,7 @@ class CompilationError extends Error {
|
|
|
94
95
|
super( `CDS compilation failed\n${messages?.map( m => m.toString() ).join('\n') || ''}` );
|
|
95
96
|
/** @since v4.0.0 */
|
|
96
97
|
this.code = 'ERR_CDS_COMPILATION_FAILURE';
|
|
97
|
-
this.messages = messages;
|
|
98
|
+
this.messages = [ ...messages ].sort(compareMessageSeverityAware);
|
|
98
99
|
|
|
99
100
|
// TODO: remove this bin/cdsc.js specifics
|
|
100
101
|
Object.defineProperty( this, 'hasBeenReported', { value: false, configurable: true, writable: true, enumerable: false } );
|
|
@@ -128,7 +129,7 @@ class CompilationError extends Error {
|
|
|
128
129
|
class CompileMessage {
|
|
129
130
|
/**
|
|
130
131
|
* Creates an instance of CompileMessage.
|
|
131
|
-
* @param {
|
|
132
|
+
* @param {CSN.Location} location Location of the message
|
|
132
133
|
* @param {string} msg The message text
|
|
133
134
|
* @param {MessageSeverity} [severity='Error'] Severity: Debug, Info, Warning, Error
|
|
134
135
|
* @param {string} [id] The ID of the message - visible as property messageId
|
|
@@ -139,7 +140,7 @@ class CompileMessage {
|
|
|
139
140
|
*/
|
|
140
141
|
constructor(location, msg, severity = 'Error', id = null, home = null, moduleName = null) {
|
|
141
142
|
this.message = msg;
|
|
142
|
-
this.$location = { ...location, address: undefined };
|
|
143
|
+
this.$location = { __proto__: CsnLocation.prototype, ...location, address: undefined };
|
|
143
144
|
this.validNames = null;
|
|
144
145
|
this.home = home; // semantic location, e.g. 'entity:"E"/element:"x"'
|
|
145
146
|
this.severity = severity;
|
|
@@ -892,14 +893,14 @@ function replaceInString( text, params ) {
|
|
|
892
893
|
}
|
|
893
894
|
|
|
894
895
|
/**
|
|
895
|
-
* @param {
|
|
896
|
-
* @returns {
|
|
896
|
+
* @param {CsnLocation} loc
|
|
897
|
+
* @returns {CsnLocation}
|
|
897
898
|
*/
|
|
898
899
|
function weakLocation( loc ) {
|
|
899
900
|
if (!loc)
|
|
900
|
-
return
|
|
901
|
+
return new CsnLocation();
|
|
901
902
|
// no endLine/endCol
|
|
902
|
-
return
|
|
903
|
+
return new CsnLocation( loc.file, loc.line, loc.col, undefined, undefined );
|
|
903
904
|
}
|
|
904
905
|
|
|
905
906
|
/**
|
|
@@ -1310,14 +1311,14 @@ function artName( art, omit ) {
|
|
|
1310
1311
|
const r = (name.absolute) ? [ quoted( name.absolute ) ] : [];
|
|
1311
1312
|
if (name.select && name.select > 1 || name.select != null && art.kind !== 'element') // Yes, omit select:1 for element - TODO: re-check
|
|
1312
1313
|
r.push( (art.kind === 'extend' ? 'block:' : 'query:') + name.select ); // TODO: rename to 'select:1' and consider whether there are more selects
|
|
1313
|
-
if (name.action && omit !== 'action')
|
|
1314
|
+
if (name.action != null && omit !== 'action')
|
|
1314
1315
|
r.push( `${ memberActionName(art) }:${ quoted( name.action ) }` );
|
|
1315
|
-
if (name.alias && art.kind !== '$self' && name.$inferred !== '$internal')
|
|
1316
|
+
if (name.alias != null && art.kind !== '$self' && name.$inferred !== '$internal')
|
|
1316
1317
|
r.push( (art.kind === 'mixin' ? 'mixin:' : 'alias:') + quoted( name.alias ) );
|
|
1317
1318
|
if (name.param != null && omit !== 'param')
|
|
1318
1319
|
r.push( name.param ? `param:${ quoted( name.param ) }` : 'returns' ); // TODO: join
|
|
1319
1320
|
|
|
1320
|
-
if (name.element && omit !== 'element') {
|
|
1321
|
+
if (name.element != null && omit !== 'element') {
|
|
1321
1322
|
if (name.select != null && !art.$inferred)
|
|
1322
1323
|
r.push( 'column:' + quoted( name.element ) );
|
|
1323
1324
|
else
|
package/lib/base/model.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// module- and csn/XSN-independent definitions
|
|
2
|
+
|
|
3
|
+
// TODO: move XSN-specific things to lib/compiler/utils/
|
|
4
|
+
// TODO: move CSN-specific things to ???
|
|
5
|
+
|
|
1
6
|
'use strict';
|
|
2
7
|
|
|
3
8
|
const { forEach } = require('../utils/objectUtils');
|
|
@@ -22,14 +27,13 @@ const queryOps = {
|
|
|
22
27
|
const availableBetaFlags = {
|
|
23
28
|
// enabled by --beta-mode
|
|
24
29
|
annotationExpressions: true,
|
|
25
|
-
toRename: true, // Removes once it's publicly documented
|
|
26
30
|
assocsWithParams: true, // beta, because runtimes don't support it, yet.
|
|
27
31
|
hanaAssocRealCardinality: true,
|
|
28
32
|
mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it
|
|
29
33
|
enableUniversalCsn: true,
|
|
30
|
-
aspectWithoutElements: true, // TODO: do not just remove beta flag - remove elements, too!
|
|
31
34
|
odataTerms: true,
|
|
32
35
|
optionalActionFunctionParameters: true, // not supported by runtime, yet.
|
|
36
|
+
associationDefault: true,
|
|
33
37
|
// disabled by --beta-mode
|
|
34
38
|
nestedServices: false,
|
|
35
39
|
};
|
|
@@ -53,12 +53,12 @@ function rejectParamDefaultsInHanaCds( member, memberName, prop, path ) {
|
|
|
53
53
|
*/
|
|
54
54
|
function warnAboutDefaultOnAssociationForHanaCds( member, memberName, prop, path ) {
|
|
55
55
|
const art = this.csn.definitions[path[1]];
|
|
56
|
-
if (!art.query && !art.projection &&
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
if (this.options.transformation === 'hdbcds' && !art.query && !art.projection && member.target && member.default) {
|
|
57
|
+
const type = member._type?.type || member.type || 'cds.Association';
|
|
58
|
+
this.warning('type-invalid-default', path, { '#': type === 'cds.Association' ? 'std' : 'comp' }, {
|
|
59
|
+
std: 'Default on associations is not supported for HDBCDS',
|
|
60
|
+
comp: 'Default on compositions is not supported for HDBCDS',
|
|
61
|
+
});
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -16,8 +16,6 @@ const { setProp } = require('../base/model');
|
|
|
16
16
|
function validateForeignKeys( member, memberName ) {
|
|
17
17
|
// We have a managed association
|
|
18
18
|
const isManagedAssoc = mem => mem && mem.target && !mem.on;
|
|
19
|
-
// We have an unmanaged association
|
|
20
|
-
const isUnmanagedAssoc = mem => mem && mem.target && mem.on && !mem.keys;
|
|
21
19
|
|
|
22
20
|
// Declared as arrow-function to keep scope the same (this value)
|
|
23
21
|
const handleAssociation = (mem) => {
|
|
@@ -46,9 +44,6 @@ function validateForeignKeys( member, memberName ) {
|
|
|
46
44
|
if (mem.items) {
|
|
47
45
|
this.error(null, member.$path, {}, 'Array-like properties must not be foreign keys');
|
|
48
46
|
}
|
|
49
|
-
else if (isUnmanagedAssoc(mem)) {
|
|
50
|
-
this.error(null, member.$path, {}, 'Unmanaged association must not be a foreign key');
|
|
51
|
-
}
|
|
52
47
|
else if (mem.keys) {
|
|
53
48
|
handleAssociation(mem);
|
|
54
49
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { forEachGeneric } = require('../model/csnUtils');
|
|
4
4
|
const { otherSideIsExpandableStructure, resolveArtifactType } = require('./utils');
|
|
5
|
+
const { pathId } = require('../model/csnRefs');
|
|
5
6
|
|
|
6
7
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
7
8
|
|
|
@@ -93,7 +94,7 @@ function validateOnCondition( member, memberName, property, path ) {
|
|
|
93
94
|
}
|
|
94
95
|
else {
|
|
95
96
|
// It's a managed association - access of the foreign keys is allowed
|
|
96
|
-
|
|
97
|
+
requireForeignKeyAccess(member.on[i], j, (errorIndex) => {
|
|
97
98
|
this.error('ref-unexpected-navigation', csnPath, {
|
|
98
99
|
'#': 'std', id, elemref, name: ref[errorIndex].id || ref[errorIndex],
|
|
99
100
|
});
|
|
@@ -158,22 +159,26 @@ function validateOnCondition( member, memberName, property, path ) {
|
|
|
158
159
|
|
|
159
160
|
/**
|
|
160
161
|
* Ensure that only foreign keys of the association `parent.ref[refIndex]` are accessed in `parent.ref`.
|
|
161
|
-
* If a non-fk field is accessed, `
|
|
162
|
+
* If a non-fk field is accessed, `noForeignKeyCallback` is invoked.
|
|
162
163
|
*
|
|
163
|
-
* @param {object} parent
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* @param {
|
|
167
|
-
*
|
|
164
|
+
* @param {object} parent
|
|
165
|
+
* Object containing `ref` and `_links` from csnRefs.
|
|
166
|
+
*
|
|
167
|
+
* @param {number} refIndex
|
|
168
|
+
* Index of the to-be-checked association in `parent.ref`
|
|
169
|
+
*
|
|
170
|
+
* @param {(errorIndex: number) => void} noForeignKeyCallback
|
|
171
|
+
* Called if there are non-fk path steps. Argument is index in `parent.ref` that is faulty.
|
|
172
|
+
* If a fk-step is missing, `errorIndex` will be `> parent.ref.length`.
|
|
168
173
|
*/
|
|
169
|
-
function
|
|
174
|
+
function requireForeignKeyAccess( parent, refIndex, noForeignKeyCallback ) {
|
|
170
175
|
const { ref, _links } = parent;
|
|
171
176
|
const assoc = _links[refIndex].art;
|
|
172
177
|
|
|
173
|
-
const next = ref[refIndex + 1]
|
|
178
|
+
const next = pathId(ref[refIndex + 1]);
|
|
174
179
|
let possibleKeys = next && assoc.keys.filter(r => r.ref[0] === next);
|
|
175
180
|
if (!possibleKeys || possibleKeys.length === 0) {
|
|
176
|
-
|
|
181
|
+
noForeignKeyCallback(refIndex + 1);
|
|
177
182
|
}
|
|
178
183
|
else {
|
|
179
184
|
// For cases where `Association to T { struct.one, struct.two };` is used instead of `{ struct }`.
|
|
@@ -195,7 +200,7 @@ function checkForeignKeyAccess( parent, refIndex, csnPath, callback ) {
|
|
|
195
200
|
++fkIndex;
|
|
196
201
|
}
|
|
197
202
|
if (!success)
|
|
198
|
-
|
|
203
|
+
noForeignKeyCallback(refIndex + fkIndex);
|
|
199
204
|
}
|
|
200
205
|
}
|
|
201
206
|
|
|
@@ -211,4 +216,4 @@ function validateMixinOnCondition( query, path ) {
|
|
|
211
216
|
forEachGeneric( query.SELECT, 'mixin', validateOnCondition.bind(this), path );
|
|
212
217
|
}
|
|
213
218
|
|
|
214
|
-
module.exports = { validateOnCondition, validateMixinOnCondition,
|
|
219
|
+
module.exports = { validateOnCondition, validateMixinOnCondition, requireForeignKeyAccess };
|