@sap/cds-compiler 6.8.0 → 6.9.1
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 +55 -0
- package/README.md +4 -0
- package/bin/cdshi.js +1 -0
- package/lib/api/main.js +8 -1
- package/lib/api/options.js +3 -1
- package/lib/base/builtins.js +13 -9
- package/lib/base/csnRefs.js +8 -10
- package/lib/base/message-registry.js +61 -3
- package/lib/base/messages.js +2 -0
- package/lib/base/optionProcessor.js +2 -0
- package/lib/base/specialOptions.js +1 -1
- package/lib/compiler/assert-consistency.js +11 -9
- package/lib/compiler/base.js +5 -1
- package/lib/compiler/define.js +1 -1
- package/lib/compiler/dictionaries.js +2 -3
- package/lib/compiler/extend.js +137 -27
- package/lib/compiler/lsp-api.js +3 -3
- package/lib/compiler/populate.js +4 -5
- package/lib/compiler/resolve.js +50 -35
- package/lib/compiler/shared.js +33 -14
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +26 -23
- package/lib/compiler/xpr-rewrite.js +2 -2
- package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
- package/lib/edm/annotations/genericTranslation.js +49 -6
- package/lib/edm/csn2edm.js +4 -2
- package/lib/edm/edm.js +6 -3
- package/lib/gen/BaseParser.js +59 -97
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2055 -1969
- package/lib/gen/Dictionary.json +67 -7
- package/lib/json/from-csn.js +7 -12
- package/lib/json/to-csn.js +59 -35
- package/lib/parsers/AstBuildingParser.js +43 -30
- package/lib/render/toCdl.js +46 -27
- package/lib/render/toSql.js +9 -0
- package/lib/render/utils/common.js +3 -2
- package/lib/tool-lib/enrichCsn.js +1 -0
- package/lib/transform/effective/flattening.js +6 -5
- package/lib/transform/effective/main.js +5 -0
- package/lib/transform/forOdata.js +20 -2
- package/lib/transform/forRelationalDB.js +8 -4
- package/lib/transform/tupleExpansion.js +40 -0
- package/package.json +3 -40
package/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,61 @@ we might not list every change in its behavior here.
|
|
|
13
13
|
Productive code should never require a `beta` flag to be set, and
|
|
14
14
|
might use a deprecated flag only for a limited period of time.
|
|
15
15
|
|
|
16
|
+
## Version 6.9.1 - 2026-05-05
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
- **compiler:**
|
|
21
|
+
+ make an element added via `extend` correctly shadow an element from an include
|
|
22
|
+
+ do not issue a warning for a correct use of `$projection`
|
|
23
|
+
- **odata:**
|
|
24
|
+
+ do not generate wrong ReferentialConstraints for unmanaged Composition without a partner
|
|
25
|
+
+ render Partner attribute on forward association correctly
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Version 6.9.0 - 2026-04-21
|
|
30
|
+
|
|
31
|
+
### Features
|
|
32
|
+
|
|
33
|
+
- **compiler:**
|
|
34
|
+
+ add support for extending views/projections with several SQL clauses:
|
|
35
|
+
`where`, `group by`, `having`, `order by`, `limit`
|
|
36
|
+
+ allow extending derived enum types/elements by adding enum symbols
|
|
37
|
+
+ inside `extend … with enum {…}`, it is now possible to use `extend existingEnumSymbol with @Anno`,
|
|
38
|
+
like it is already possible inside `extend … with {…}`
|
|
39
|
+
+ when the new options `v7KeyPropagation` is set, the propagation of the `key` property in queries is simplified,
|
|
40
|
+
and the `key` property is not propagated when including structures into types
|
|
41
|
+
- **odata:** set `meta.compilerCsnFlavor` in the OData transformed CSN
|
|
42
|
+
- **seal**: set `meta.compilerCsnFlavor` in the SEAL transformed CSN
|
|
43
|
+
- **effective:** set `meta.compilerCsnFlavor` in the effective CSN
|
|
44
|
+
- **sql:**
|
|
45
|
+
+ set `meta.compilerCsnFlavor` in SQL transformed CSN
|
|
46
|
+
+ allow single-leafed structures within a `list` on the left-hand side of the `IN` operator.
|
|
47
|
+
For example `(author, struct) in (...)` becomes `(author_ID, struct_leaf) in (...)`.
|
|
48
|
+
+ support `cds.Vector` on Postgres and H2
|
|
49
|
+
|
|
50
|
+
### Bug Fixes
|
|
51
|
+
|
|
52
|
+
- **compiler:**
|
|
53
|
+
+ don't introduce a strange `cast` property in the CSN for a column with an expand on a to-many association
|
|
54
|
+
+ while crawling tokens for LSP, allow extensions without name
|
|
55
|
+
- **effective:** don't absolutify paths in filters in annotations
|
|
56
|
+
- **sql:** properly resolve references in annotation values and `on` conditions for a column inside an expand without base reference
|
|
57
|
+
|
|
58
|
+
### Improvements
|
|
59
|
+
|
|
60
|
+
- **compiler:**
|
|
61
|
+
+ add `compilerVersion` to CSN meta
|
|
62
|
+
+ issue a warning if a structure in an annotation value would have the same CSN representation as an expression
|
|
63
|
+
+ issue warnings for extends on built-in types
|
|
64
|
+
+ warn on duplicate members from includes via extend
|
|
65
|
+
- **odata:**
|
|
66
|
+
+ update OData vocabularies: Common, Core, UI
|
|
67
|
+
+ using `@readonly` with an expression value on entities or aspects is no longer silently accepted and now produces an error
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
16
71
|
## Version 6.8.0 - 2026-03-05
|
|
17
72
|
|
|
18
73
|
### Features
|
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Getting started
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
## Installation and Usage
|
|
5
6
|
|
|
6
7
|
Install with npm:
|
|
@@ -20,16 +21,19 @@ Or maintain your package.json dependencies as follows:
|
|
|
20
21
|
If your project already has a dependency to `@sap/cds`, nothing has to be done.
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
|
|
23
25
|
## Documentation
|
|
24
26
|
|
|
25
27
|
Please refer to the [official CDS documentation](https://cap.cloud.sap/docs/cds/).
|
|
26
28
|
|
|
27
29
|
|
|
30
|
+
|
|
28
31
|
## How to Obtain Support
|
|
29
32
|
|
|
30
33
|
In case you find a bug, please report an [incident](https://cap.cloud.sap/docs/resources/#reporting-incidents) on SAP Support Portal.
|
|
31
34
|
|
|
32
35
|
|
|
36
|
+
|
|
33
37
|
## History and License
|
|
34
38
|
|
|
35
39
|
The cds-compiler uses [Semantic Versioning](./doc/Versioning.md) for its version numbers.
|
package/bin/cdshi.js
CHANGED
|
@@ -32,6 +32,7 @@ const categoryChars = { // default: first char of category name
|
|
|
32
32
|
ExtService: 'S', // highlight like service definition
|
|
33
33
|
ExtContext: 'C', // highlight like context definition
|
|
34
34
|
// ExtElement: 'E', // using the first letter is the default
|
|
35
|
+
ExtEnum: 'H', // highlight like enum symbol definition
|
|
35
36
|
ExtBoundAction: 'B', // highlight like bound action definition
|
|
36
37
|
ExtParam: 'P', // highlight like entity/action parameter definition
|
|
37
38
|
FromImplicit: 'W',
|
package/lib/api/main.js
CHANGED
|
@@ -307,7 +307,14 @@ function forEffectiveInternal( csn, options, internalOptions, messageFunctions )
|
|
|
307
307
|
function forSeal( csn, options, messageFunctions ) {
|
|
308
308
|
const internalOptions = prepareOptions.for.seal(options);
|
|
309
309
|
internalOptions.transformation = 'effective';
|
|
310
|
-
|
|
310
|
+
const result = forEffectiveInternal(csn, options, internalOptions, messageFunctions);
|
|
311
|
+
|
|
312
|
+
if (!options.testMode) {
|
|
313
|
+
result.meta ??= {};
|
|
314
|
+
result.meta.compilerCsnFlavor = 'seal';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return result;
|
|
311
318
|
}
|
|
312
319
|
|
|
313
320
|
/**
|
package/lib/api/options.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// The options are specified in ../base/optionProcessor.js (and some other files).
|
|
4
4
|
// Some backends feel the need to "translate" option, which require to list
|
|
5
|
-
// all options also here (as
|
|
5
|
+
// all options also here (as "public" or "private" option).
|
|
6
|
+
// Parser and core compiler options might not have been listed here.
|
|
6
7
|
|
|
7
8
|
const { validate, generateStringValidator } = require('./validate');
|
|
8
9
|
const { makeMessageFunction } = require('../base/messages');
|
|
@@ -25,6 +26,7 @@ const publicOptionsNewAPI = [
|
|
|
25
26
|
'defaultStringLength',
|
|
26
27
|
'csnFlavor',
|
|
27
28
|
'noDollarCalc',
|
|
29
|
+
'v7KeyPropagation',
|
|
28
30
|
// DB
|
|
29
31
|
'sqlDialect',
|
|
30
32
|
'sqlMapping',
|
package/lib/base/builtins.js
CHANGED
|
@@ -89,29 +89,33 @@ function isMagicVariable( name ) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* Properties
|
|
93
|
-
* and not some foreign structure.
|
|
92
|
+
* Properties (without those from CSN v0.1.0) that make an annotation object value
|
|
93
|
+
* an actual expression and not some foreign structure.
|
|
94
94
|
*
|
|
95
95
|
* @type {string[]}
|
|
96
96
|
*/
|
|
97
|
-
const
|
|
98
|
-
'ref', 'xpr', 'list', '
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
const primaryExprProperties = [
|
|
98
|
+
'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET',
|
|
99
|
+
];
|
|
100
|
+
// only with 'ref'/'val'/'func'/misc:
|
|
101
|
+
const exprProperties = [
|
|
102
|
+
...primaryExprProperties,
|
|
103
|
+
'param', 'literal', 'args', 'cast',
|
|
101
104
|
];
|
|
102
|
-
|
|
103
105
|
|
|
104
106
|
/**
|
|
105
107
|
* Return whether JSON object `val` is a representation for an annotation expression
|
|
106
108
|
*/
|
|
107
109
|
function isAnnotationExpression( val ) {
|
|
108
|
-
return val?.['='] !== undefined &&
|
|
110
|
+
return val?.['='] !== undefined && // TODO: truthy
|
|
111
|
+
primaryExprProperties.some( prop => val[prop] !== undefined );
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
module.exports = {
|
|
112
115
|
propagationRules,
|
|
113
116
|
acceptsExprValues,
|
|
114
|
-
|
|
117
|
+
primaryExprProperties,
|
|
118
|
+
exprProperties,
|
|
115
119
|
isInReservedNamespace,
|
|
116
120
|
isBuiltinType,
|
|
117
121
|
isMagicVariable,
|
package/lib/base/csnRefs.js
CHANGED
|
@@ -784,14 +784,15 @@ function csnRefs( csn, universalReady ) {
|
|
|
784
784
|
const target = assocTarget( parent, refCtx );
|
|
785
785
|
return resolvePath( path, target.elements[head], target, 'target' );
|
|
786
786
|
}
|
|
787
|
-
if (
|
|
788
|
-
|
|
789
|
-
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
790
|
-
// in an ON condition of an association inside inner expand/inline:
|
|
787
|
+
if (semantics.dynamic === 'query') {
|
|
788
|
+
// in an anno, or an ON condition of an association inside inner expand/inline:
|
|
791
789
|
const elemParent = getCache( parent, '_element' );
|
|
792
|
-
if (elemParent)
|
|
790
|
+
if (elemParent?.elements) // expand in expand
|
|
793
791
|
return resolvePath( path, elemParent.elements[head], null, 'query' );
|
|
794
792
|
}
|
|
793
|
+
else if (baseEnv) { // ref-target (filter condition), expand, inline
|
|
794
|
+
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
795
|
+
}
|
|
795
796
|
if (!query) { // outside queries - TODO: items?
|
|
796
797
|
// refs in annos on foreign keys use fk name, not fk ref name:
|
|
797
798
|
const dict = parent.elements ?? getCache( parent, '_keys' );
|
|
@@ -810,11 +811,9 @@ function csnRefs( csn, universalReady ) {
|
|
|
810
811
|
if (!qcache)
|
|
811
812
|
throw new CompilerAssertion( `For semantics '${ refCtx }', query not in cache at: ${ locationString(query.$location) }` );
|
|
812
813
|
|
|
813
|
-
if (semantics.dynamic === 'query')
|
|
814
|
-
// TODO: for ON condition in expand, would need to use cached _element
|
|
815
|
-
// TODO: test and implement - Issue #11792!
|
|
814
|
+
if (semantics.dynamic === 'query')
|
|
816
815
|
return resolvePath( path, qcache.elements[head], null, 'query' );
|
|
817
|
-
|
|
816
|
+
|
|
818
817
|
for (const name in qcache.$aliases) {
|
|
819
818
|
const alias = qcache.$aliases[name];
|
|
820
819
|
const found = alias.elements[head];
|
|
@@ -1144,7 +1143,6 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
1144
1143
|
baseEnv = resolve.expandInline( obj, baseCtx, main, query, parent, baseEnv );
|
|
1145
1144
|
refCtx = prop;
|
|
1146
1145
|
}
|
|
1147
|
-
// TODO: for on condition in expand, also set an environment
|
|
1148
1146
|
isName = prop;
|
|
1149
1147
|
}
|
|
1150
1148
|
else if (prop === 'on') {
|
|
@@ -89,6 +89,7 @@ const centralMessages = {
|
|
|
89
89
|
'anno-invalid-sql-kind': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
90
90
|
'anno-invalid-sql-view': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
91
91
|
'anno-invalid-sql-view-element': { severity: 'Error', configurableFor: true }, // @sql.prepend/append - configurable for "I know what I'm doing"
|
|
92
|
+
'ext-duplicate-include': { severity: 'Warning', errorFor: [ 'v7' ] },
|
|
92
93
|
'ext-undefined-action': { severity: 'Warning' },
|
|
93
94
|
'ext-undefined-art': { severity: 'Warning' }, // for annotate statement (for CDL path root)
|
|
94
95
|
'ext-undefined-def': { severity: 'Warning' }, // for annotate statement (for CSN or CDL path cont)
|
|
@@ -100,6 +101,7 @@ const centralMessages = {
|
|
|
100
101
|
'ext-undefined-element-sec': { severity: 'Error', configurableFor: true }, // for security-relevant…
|
|
101
102
|
'ext-undefined-action-sec': { severity: 'Error', configurableFor: true }, // for security-relevant…
|
|
102
103
|
'ext-undefined-param-sec': { severity: 'Error', configurableFor: true }, // … annotate statement
|
|
104
|
+
'ext-unexpected-type-property': { severity: 'Warning', errorFor: [ 'v7' ] },
|
|
103
105
|
'ext-unexpected-returns': { severity: 'Warning' },
|
|
104
106
|
'ext-unexpected-returns-sec': { severity: 'Error', configurableFor: true }, // … annotate statement
|
|
105
107
|
'anno-unexpected-ellipsis': { severity: 'Error', configurableFor: 'deprecated' },
|
|
@@ -125,8 +127,11 @@ const centralMessages = {
|
|
|
125
127
|
'empty-type': { severity: 'Info' }, // only still an error in old transformers
|
|
126
128
|
|
|
127
129
|
'ref-deprecated-orderby': { severity: 'Error', configurableFor: true },
|
|
128
|
-
'ref-deprecated-self-element': { severity: 'Error', configurableFor:
|
|
130
|
+
'ref-deprecated-self-element': { severity: 'Error', configurableFor: 'v7' },
|
|
129
131
|
'ref-deprecated-variable': { severity: 'Warning' },
|
|
132
|
+
'ref-deprecated-in-extend': { severity: 'Warning' },
|
|
133
|
+
'ref-special-in-extend': { severity: 'Warning' },
|
|
134
|
+
'ref-unexpected-in-extend': { severity: 'Error' },
|
|
130
135
|
'ref-invalid-type': { severity: 'Error' },
|
|
131
136
|
'ref-unexpected-self': { severity: 'Error' },
|
|
132
137
|
'ref-invalid-include': { severity: 'Error' },
|
|
@@ -204,6 +209,8 @@ const centralMessages = {
|
|
|
204
209
|
// remark: a hard syntax error in new parser for `null` together with `not null`
|
|
205
210
|
'syntax-duplicate-equal-clause': { severity: 'Warning' },
|
|
206
211
|
'syntax-invalid-name': { severity: 'Error' },
|
|
212
|
+
'syntax-invalid-anno-struct': { severity: 'Warning', errorFor: [ 'v7' ] },
|
|
213
|
+
'syntax-invalid-nested-proj': { severity: 'Warning', errorFor: [ 'v7' ] },
|
|
207
214
|
'syntax-missing-as': { severity: 'Error', configurableFor: true },
|
|
208
215
|
'syntax-missing-proj-semicolon': { severity: 'Warning' },
|
|
209
216
|
'syntax-unexpected-after': { severity: 'Error' },
|
|
@@ -248,6 +255,7 @@ const centralMessages = {
|
|
|
248
255
|
'odata-unexpected-nullable-key': { severity: 'Error', configurableFor: true },
|
|
249
256
|
'odata-invalid-key-type': { severity: 'Warning' },
|
|
250
257
|
'odata-invalid-property-name': { severity: 'Warning' },
|
|
258
|
+
'odata-unexpected-xpr-anno': { severity: 'Error', configurableFor: true },
|
|
251
259
|
'odata-anno-preproc': { severity: 'Warning' },
|
|
252
260
|
'odata-anno-dict': { severity: 'Warning' },
|
|
253
261
|
'odata-anno-vocref': { severity: 'Warning' },
|
|
@@ -535,6 +543,10 @@ const centralMessageTexts = {
|
|
|
535
543
|
dot: 'Use a $(NEWCODE), not a $(CODE) after the arguments or filter on an entity',
|
|
536
544
|
colon: 'Use a $(NEWCODE), not a $(CODE) between the element names in a reference',
|
|
537
545
|
},
|
|
546
|
+
'syntax-invalid-anno-struct': {
|
|
547
|
+
std: 'Most CSN processors interpret a structure with property $(PROP) as expression',
|
|
548
|
+
ref: 'Most CSN processors interpret a structure with property $(PROP) as reference',
|
|
549
|
+
},
|
|
538
550
|
// 'syntax-ignoring-doc-comment' (Info)
|
|
539
551
|
'syntax-unexpected-reserved-word': '$(CODE) is a reserved word - write $(DELIMITED) instead if you want to use it as name',
|
|
540
552
|
'syntax-invalid-text-block': 'Missing newline in text block',
|
|
@@ -710,6 +722,14 @@ const centralMessageTexts = {
|
|
|
710
722
|
type: 'Illegal recursive type definition to $(TYPE)',
|
|
711
723
|
},
|
|
712
724
|
'ref-deprecated-orderby': 'Replace source element reference $(ID) by $(NEWCODE); auto-corrected',
|
|
725
|
+
'ref-deprecated-in-extend': {
|
|
726
|
+
std: 'In an extension, do not use the table alias $(ID) to refer to source elements',
|
|
727
|
+
columns: 'In an added column, do not use the table alias $(ID) to refer to source elements',
|
|
728
|
+
where: 'In an added WHERE, do not use the table alias $(ID) to refer to source elements',
|
|
729
|
+
groupBy: 'In an added GROUP BY, do not use the table alias $(ID) to refer to source elements',
|
|
730
|
+
having: 'In an added HAVING, do not use the table alias $(ID) to refer to source elements',
|
|
731
|
+
orderBy: 'In an added ORDER BY, do not use the table alias $(ID) to refer to source elements',
|
|
732
|
+
},
|
|
713
733
|
'ref-missing-self-counterpart' : {
|
|
714
734
|
std: 'Expected to find a matching element in $self-comparison for foreign key $(PROP) of association $(NAME)',
|
|
715
735
|
unmanaged: 'Expected to find a matching element in $self-comparison for $(PROP) of association $(NAME)',
|
|
@@ -1080,6 +1100,12 @@ const centralMessageTexts = {
|
|
|
1080
1100
|
|
|
1081
1101
|
'def-expected-structured': 'Events must either be structured or be projections',
|
|
1082
1102
|
|
|
1103
|
+
'ext-duplicate-include': {
|
|
1104
|
+
std: 'Duplicate $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
1105
|
+
elements: 'Duplicate element $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
1106
|
+
actions: 'Duplicate action or function $(NAME) through multiple includes $(SORTED_ARTS)',
|
|
1107
|
+
},
|
|
1108
|
+
|
|
1083
1109
|
'duplicate-definition': {
|
|
1084
1110
|
std: 'Duplicate definition of $(NAME)',
|
|
1085
1111
|
absolute: 'Duplicate definition of artifact $(NAME)',
|
|
@@ -1112,7 +1138,6 @@ const centralMessageTexts = {
|
|
|
1112
1138
|
'old-not-target': 'Expected element $(NAME) not to be an association, because it overrides the included element from $(ART)',
|
|
1113
1139
|
},
|
|
1114
1140
|
|
|
1115
|
-
'ref-expecting-$self': 'Use $(NEWCODE) instead of $(CODE) here or remove $(CODE) altogether if possible; the compiler has rewritten it to $(NEWCODE) in CSN',
|
|
1116
1141
|
'ref-expecting-assoc': {
|
|
1117
1142
|
std: 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition',
|
|
1118
1143
|
'with-type': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
|
|
@@ -1157,6 +1182,32 @@ const centralMessageTexts = {
|
|
|
1157
1182
|
join: 'Artifact $(ART) can\'t be extended with columns, because it contains a JOIN',
|
|
1158
1183
|
union: 'Artifact $(ART) can\'t be extended with columns, because it contains a UNION',
|
|
1159
1184
|
},
|
|
1185
|
+
'extend-where': {
|
|
1186
|
+
std: 'Artifact $(ART) can\'t be extended with WHERE, only simple views/projections without JOINs and UNIONs can',
|
|
1187
|
+
join: 'Artifact $(ART) can\'t be extended with WHERE, because it contains a JOIN',
|
|
1188
|
+
union: 'Artifact $(ART) can\'t be extended with WHERE, because it contains a UNION',
|
|
1189
|
+
},
|
|
1190
|
+
'extend-groupby': {
|
|
1191
|
+
std: 'Artifact $(ART) can\'t be extended with GROUP BY, only simple views/projections without JOINs and UNIONs can',
|
|
1192
|
+
join: 'Artifact $(ART) can\'t be extended with GROUP BY, because it contains a JOIN',
|
|
1193
|
+
union: 'Artifact $(ART) can\'t be extended with GROUP BY, because it contains a UNION',
|
|
1194
|
+
},
|
|
1195
|
+
'extend-having': {
|
|
1196
|
+
std: 'Artifact $(ART) can\'t be extended with HAVING, only simple views/projections without JOINs and UNIONs can',
|
|
1197
|
+
join: 'Artifact $(ART) can\'t be extended with HAVING, because it contains a JOIN',
|
|
1198
|
+
union: 'Artifact $(ART) can\'t be extended with HAVING, because it contains a UNION',
|
|
1199
|
+
},
|
|
1200
|
+
'extend-orderby': {
|
|
1201
|
+
std: 'Artifact $(ART) can\'t be extended with ORDER BY, only simple views/projections without JOINs and UNIONs can',
|
|
1202
|
+
join: 'Artifact $(ART) can\'t be extended with ORDER BY, because it contains a JOIN',
|
|
1203
|
+
union: 'Artifact $(ART) can\'t be extended with ORDER BY, because it contains a UNION',
|
|
1204
|
+
},
|
|
1205
|
+
'extend-limit': {
|
|
1206
|
+
std: 'Artifact $(ART) can\'t be extended with LIMIT, only simple views/projections without JOINs and UNIONs can',
|
|
1207
|
+
join: 'Artifact $(ART) can\'t be extended with LIMIT, because it contains a JOIN',
|
|
1208
|
+
union: 'Artifact $(ART) can\'t be extended with LIMIT, because it contains a UNION',
|
|
1209
|
+
},
|
|
1210
|
+
'ext-unexpected-sql-clause': 'Artifact $(ART) can\'t be extended with a $(KEYWORD) clause, because it already exists',
|
|
1160
1211
|
'extend-repeated-intralayer': 'Unstable element order due to repeated extensions in same layer',
|
|
1161
1212
|
'extend-unexpected-include': 'Can\'t extend $(META) with includes',
|
|
1162
1213
|
|
|
@@ -1257,11 +1308,17 @@ const centralMessageTexts = {
|
|
|
1257
1308
|
},
|
|
1258
1309
|
|
|
1259
1310
|
'ref-special-in-extend': {
|
|
1260
|
-
std: 'In an
|
|
1311
|
+
std: 'In an extension, $(ID) refers to the element of the projection source $(ART), not the table alias or mixin',
|
|
1261
1312
|
alias: 'In an added column, $(ID) refers to the element of the projection source $(ART), not the table alias',
|
|
1262
1313
|
mixin: 'In an added column, $(ID) refers to the element of the projection source $(ART), not the mixin',
|
|
1263
1314
|
},
|
|
1264
1315
|
|
|
1316
|
+
'ref-unexpected-in-extend': {
|
|
1317
|
+
std: 'Unexpected $(ID) in an extension; it matches both a source element of $(ART) and the table alias or mixin',
|
|
1318
|
+
alias: 'Unexpected $(ID) in an added $(KEYWORD); it matches both a source element of $(ART) and the table alias',
|
|
1319
|
+
mixin: 'Unexpected $(ID) in an added $(KEYWORD); it matches both a source element of $(ART) and the mixin',
|
|
1320
|
+
},
|
|
1321
|
+
|
|
1265
1322
|
'type-managed-composition': {
|
|
1266
1323
|
std: 'Managed compositions can\'t be used in types', // yet
|
|
1267
1324
|
sub: 'Managed compositions can\'t be used in sub elements',
|
|
@@ -1401,6 +1458,7 @@ const centralMessageTexts = {
|
|
|
1401
1458
|
xpr: 'Ignoring unexpected expression as default value',
|
|
1402
1459
|
colitem: 'Ignoring unexpected default value for a structured or collection like parameter',
|
|
1403
1460
|
},
|
|
1461
|
+
'odata-unexpected-xpr-anno': 'Annotation $(ANNO) with expression value is not allowed for kind $(KIND)',
|
|
1404
1462
|
// -----------------------------------------------------------------------------------
|
|
1405
1463
|
// All odata-anno MUST have a '$(ANNO)' parameter to indicate error location
|
|
1406
1464
|
// -----------------------------------------------------------------------------------
|
package/lib/base/messages.js
CHANGED
|
@@ -53,6 +53,7 @@ optionProcessor
|
|
|
53
53
|
.option(' --test-sort-csn')
|
|
54
54
|
.option(' --doc-comment')
|
|
55
55
|
.option(' --propagate-doc-comments')
|
|
56
|
+
.option(' --v7-key-propagation')
|
|
56
57
|
.option(' --add-texts-language-assoc')
|
|
57
58
|
.option(' --localized-without-coalesce')
|
|
58
59
|
.option(' --tenant-discriminator')
|
|
@@ -156,6 +157,7 @@ optionProcessor
|
|
|
156
157
|
option is implicitly enabled as well.
|
|
157
158
|
--doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
|
|
158
159
|
--propagate-doc-comments Propagate doc comments ('--doc-comment')
|
|
160
|
+
--v7-key-propagation Use simplified propagation of 'key' in query entities
|
|
159
161
|
--add-texts-language-assoc In generated texts entities, add association "language"
|
|
160
162
|
to "sap.common.Languages" if it exists
|
|
161
163
|
--localized-without-coalesce Omit coalesce in localized convenience views
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Definitions for beta and deprecated options
|
|
2
2
|
|
|
3
|
-
// Normal options are in ../
|
|
3
|
+
// Normal options are in ../base/optionProcessor.js (and some other files),
|
|
4
4
|
// unfortunately partly non-grep-able (option `fooBar` is defined via `--foo-bar`)
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
@@ -283,7 +283,7 @@ function assertConsistency( model, stage ) {
|
|
|
283
283
|
'quantifier', 'orderBy', 'limit', 'name', '$parens', 'kind',
|
|
284
284
|
'_origin', '$contains', // TODO tmp, see TODO in getOriginRaw()
|
|
285
285
|
'_parent', '_main', '_leadingQuery', '_effectiveType', '$effectiveSeqNo', // in FROM
|
|
286
|
-
'_$next', // parsing error: tableTerm with UNION on rhs.
|
|
286
|
+
'$expand', '_$next', // parsing error: tableTerm with UNION on rhs.
|
|
287
287
|
],
|
|
288
288
|
},
|
|
289
289
|
select: { // sub query
|
|
@@ -309,7 +309,7 @@ function assertConsistency( model, stage ) {
|
|
|
309
309
|
'on', '$parens', 'cardinality',
|
|
310
310
|
'kind', 'name', '_block', '_parent', '_main', '_user',
|
|
311
311
|
'$tableAliases', '_combined', '_joinParent', '$joinArgsIndex',
|
|
312
|
-
'_leadingQuery', '_$next', '_deps',
|
|
312
|
+
'_leadingQuery', '_$next', '_deps', '$expand',
|
|
313
313
|
],
|
|
314
314
|
},
|
|
315
315
|
ref: {
|
|
@@ -329,7 +329,7 @@ function assertConsistency( model, stage ) {
|
|
|
329
329
|
'$parens',
|
|
330
330
|
'kind', 'name', '_block', '_parent', '_main', 'elements',
|
|
331
331
|
'_effectiveType', '$effectiveSeqNo', '_origin', '_joinParent', '$joinArgsIndex',
|
|
332
|
-
'$duplicates', // duplicate query in FROM clause
|
|
332
|
+
'$expand', '$duplicates', // duplicate query in FROM clause
|
|
333
333
|
],
|
|
334
334
|
},
|
|
335
335
|
none: { optional: () => true }, // parse error
|
|
@@ -352,13 +352,13 @@ function assertConsistency( model, stage ) {
|
|
|
352
352
|
requires: [ 'location', 'name' ],
|
|
353
353
|
optional: [ '$duplicates' ],
|
|
354
354
|
},
|
|
355
|
-
orderBy: { inherits: 'value', test: isArray( expression ) },
|
|
355
|
+
orderBy: { kind: [ 'extend' ], inherits: 'value', test: isArray( expression ) },
|
|
356
356
|
sort: { test: locationVal( isString ), enum: [ 'asc', 'desc' ] },
|
|
357
357
|
nulls: { test: locationVal( isString ), enum: [ 'first', 'last' ] },
|
|
358
358
|
$orderBy: { inherits: 'orderBy' },
|
|
359
|
-
groupBy: { inherits: 'value', test: isArray( expression ) },
|
|
359
|
+
groupBy: { kind: [ 'extend' ], inherits: 'value', test: isArray( expression ) },
|
|
360
360
|
$limit: { test: TODO },
|
|
361
|
-
limit: { requires: [ 'rows' ], optional: [ 'offset', 'location' ] },
|
|
361
|
+
limit: { kind: [ 'extend' ], requires: [ 'rows' ], optional: [ 'offset', 'location' ] },
|
|
362
362
|
rows: { inherits: 'value' },
|
|
363
363
|
offset: { inherits: 'value' },
|
|
364
364
|
_combined: { test: TODO },
|
|
@@ -438,6 +438,8 @@ function assertConsistency( model, stage ) {
|
|
|
438
438
|
'location', '$inferred', 'sort', 'nulls',
|
|
439
439
|
'param', 'scope', // for dynamic parameter '?'
|
|
440
440
|
'args', 'op', 'func', 'suffix',
|
|
441
|
+
// needed for groupBy extensions - TODO: outside `value`:
|
|
442
|
+
'$extended', '_block', '_outer',
|
|
441
443
|
// calculated elements on-write - TODO: outside `value`
|
|
442
444
|
'stored',
|
|
443
445
|
],
|
|
@@ -512,8 +514,8 @@ function assertConsistency( model, stage ) {
|
|
|
512
514
|
test: args,
|
|
513
515
|
},
|
|
514
516
|
on: { kind: true, inherits: 'value', test: expression },
|
|
515
|
-
where: { inherits: 'value' },
|
|
516
|
-
having: { inherits: 'value' },
|
|
517
|
+
where: { kind: [ 'extend' ], inherits: 'value' },
|
|
518
|
+
having: { kind: [ 'extend' ], inherits: 'value' },
|
|
517
519
|
op: { test: locationVal( isString ) },
|
|
518
520
|
join: { test: locationVal( isString ) },
|
|
519
521
|
quantifier: { test: locationVal( isString ) },
|
|
@@ -736,7 +738,7 @@ function assertConsistency( model, stage ) {
|
|
|
736
738
|
$expand: {
|
|
737
739
|
kind: true,
|
|
738
740
|
// See description of `setExpandStatus()` of in `lib/compiler/utils.js`.
|
|
739
|
-
test: isOneOf( [ 'origin', 'annotate', '
|
|
741
|
+
test: isOneOf( [ 'origin', 'target', 'annotate', 'extend' ] ),
|
|
740
742
|
},
|
|
741
743
|
$inCycle: { kind: true, test: isBoolean },
|
|
742
744
|
|
package/lib/compiler/base.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
const { CompilerAssertion } = require( '../base/error' );
|
|
6
|
+
const $inferred = Symbol.for( 'cds.$inferred' );
|
|
6
7
|
|
|
7
8
|
const dictKinds = {
|
|
8
9
|
definitions: 'absolute',
|
|
@@ -67,7 +68,8 @@ const kindProperties = {
|
|
|
67
68
|
|
|
68
69
|
function propExists( prop, parent ) {
|
|
69
70
|
const obj = parent.returns || parent;
|
|
70
|
-
|
|
71
|
+
const members = (obj.items || obj.targetAspect || obj)[prop];
|
|
72
|
+
return members && !members[$inferred];
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -90,6 +92,8 @@ function getArtifactName( art ) {
|
|
|
90
92
|
const namePath = [];
|
|
91
93
|
let parent = art._outer || art;
|
|
92
94
|
while (parent._main || parent.kind === 'builtin') { // until we hit the main artifact
|
|
95
|
+
if (!parent.name)
|
|
96
|
+
return '<error>';
|
|
93
97
|
if (parent.name.$inferred !== '$internal' || parent.kind === '$inline')
|
|
94
98
|
namePath.push( parent );
|
|
95
99
|
if (parent.kind === 'select')
|
package/lib/compiler/define.js
CHANGED
|
@@ -1007,7 +1007,7 @@ function define( model ) {
|
|
|
1007
1007
|
}
|
|
1008
1008
|
else if ((col.expand || col.value) &&
|
|
1009
1009
|
!path && // no parse error (path without last item)
|
|
1010
|
-
(insideExpand || query._parent
|
|
1010
|
+
(insideExpand || query._parent?.kind !== 'select')) { // not sub-selects
|
|
1011
1011
|
error( 'query-req-name',
|
|
1012
1012
|
// TODO: message function: `query` should work directly
|
|
1013
1013
|
[ (col.value || col).location, (query.name ? query : query._parent ) ],
|
|
@@ -86,9 +86,8 @@ function dictAddArray( dict, name, entry, messageCallback ) {
|
|
|
86
86
|
function dictFirst( dict ) {
|
|
87
87
|
if (!dict)
|
|
88
88
|
return dict;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return undefined;
|
|
89
|
+
const names = Object.keys( dict );
|
|
90
|
+
return names.length ? dict[names[0]] : null;
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
// Push `entry` to the array value with key `name` in the dictionary `dict`.
|