@sap/cds-compiler 5.1.2 → 5.2.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.
- package/CHANGELOG.md +26 -0
- package/bin/cdsc.js +2 -2
- package/bin/cdshi.js +24 -17
- package/bin/cdsse.js +17 -18
- package/lib/api/main.js +19 -2
- package/lib/api/options.js +4 -1
- package/lib/base/builtins.js +1 -0
- package/lib/base/message-registry.js +16 -3
- package/lib/base/model.js +0 -10
- package/lib/checks/actionsFunctions.js +0 -12
- package/lib/checks/structuredAnnoExpressions.js +10 -14
- package/lib/compiler/assert-consistency.js +19 -11
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/define.js +3 -3
- package/lib/compiler/extend.js +5 -5
- package/lib/compiler/populate.js +9 -9
- package/lib/compiler/propagator.js +1 -0
- package/lib/compiler/resolve.js +29 -34
- package/lib/compiler/shared.js +7 -8
- package/lib/compiler/tweak-assocs.js +155 -64
- package/lib/compiler/utils.js +1 -1
- package/lib/compiler/xpr-rewrite.js +4 -3
- package/lib/edm/annotations/genericTranslation.js +13 -9
- package/lib/edm/csn2edm.js +26 -2
- package/lib/edm/edm.js +23 -8
- package/lib/edm/edmInboundChecks.js +5 -7
- package/lib/edm/edmPreprocessor.js +43 -30
- package/lib/gen/BaseParser.js +720 -0
- package/lib/gen/CdlParser.js +4421 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +4006 -4001
- package/lib/language/antlrParser.js +62 -0
- package/lib/language/genericAntlrParser.js +28 -0
- package/lib/model/csnUtils.js +2 -0
- package/lib/model/revealInternalProperties.js +2 -0
- package/lib/modelCompare/utils/filter.js +70 -42
- package/lib/optionProcessor.js +9 -3
- package/lib/parsers/AstBuildingParser.js +1172 -0
- package/lib/parsers/CdlGrammar.g4 +1940 -0
- package/lib/parsers/Lexer.js +239 -0
- package/lib/render/toCdl.js +23 -27
- package/lib/render/toSql.js +5 -5
- package/lib/transform/db/applyTransformations.js +54 -16
- package/lib/transform/draft/odata.js +10 -11
- package/lib/transform/effective/flattening.js +10 -14
- package/lib/transform/odata/flattening.js +42 -31
- package/lib/transform/odata/toFinalBaseType.js +7 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +2 -2
- package/share/messages/redirected-to-ambiguous.md +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,12 +7,31 @@
|
|
|
7
7
|
Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
|
|
10
|
+
## Version 5.2.0 - 2024-08-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- to.edm(x): Add `@Core.Links { rel: 'author', href: 'https://cap.cloud.sap' };` as watermark to lead schema.
|
|
15
|
+
- to.sql.migration: Introduce option `script` to aid in generation of manual migration scripts by not aborting when encountering lossy changes.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- for.odata: No longer reject default values on action/function parameters.
|
|
20
|
+
- to.edm(x): Raise warning for default values on action/function parameters that they are ignored.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- compiler: Fix extensions with bound actions using an explicit binding parameter in `parseCdl` CSN.
|
|
25
|
+
- for.odata, to.edm(x): Fix some issues with resolving annotation expressions in nested objects and
|
|
26
|
+
reliably replace value of `=` attribute with `true` after processing.
|
|
27
|
+
|
|
10
28
|
## Version 5.1.2 - 2024-08-05
|
|
11
29
|
|
|
12
30
|
### Fixed
|
|
13
31
|
|
|
14
32
|
- compiler: In parseCdl mode, bound actions specifying the binding parameter with `$self` did not work.
|
|
15
33
|
|
|
34
|
+
|
|
16
35
|
## Version 5.1.0 - 2024-07-25
|
|
17
36
|
|
|
18
37
|
### Added
|
|
@@ -105,6 +124,13 @@ This is a preview version for the major release and contains breaking changes. I
|
|
|
105
124
|
Use `to.edm(x)` instead.
|
|
106
125
|
|
|
107
126
|
|
|
127
|
+
## Version 4.9.8 - 2024-07-29
|
|
128
|
+
|
|
129
|
+
### Fixed
|
|
130
|
+
|
|
131
|
+
- compiler: Fix extensions with bound actions using an explicit binding parameter in `parseCdl` CSN.
|
|
132
|
+
- to.edm(x): No `Nullable` attribute for `$ReturnType` of `Collection(<entity type>)` [OData V4 CSDL, section 12.8 Return Type](https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#sec_ReturnType)
|
|
133
|
+
|
|
108
134
|
## Version 4.9.6 - 2024-07-15
|
|
109
135
|
|
|
110
136
|
### Fixed
|
package/bin/cdsc.js
CHANGED
|
@@ -306,7 +306,7 @@ async function executeCommandLine( command, options, args ) {
|
|
|
306
306
|
manageConstraints,
|
|
307
307
|
toSql,
|
|
308
308
|
inspect,
|
|
309
|
-
|
|
309
|
+
forEffective,
|
|
310
310
|
forSeal,
|
|
311
311
|
};
|
|
312
312
|
const commandsWithoutCompilation = {
|
|
@@ -352,7 +352,7 @@ async function executeCommandLine( command, options, args ) {
|
|
|
352
352
|
return model;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
function
|
|
355
|
+
function forEffective( model ) {
|
|
356
356
|
const features = [ 'resolveSimpleTypes', 'resolveProjections', 'remapOdataAnnotations', 'keepLocalized' ];
|
|
357
357
|
for (const feature of features) {
|
|
358
358
|
if (options[feature]) // map to boolean equivalent
|
package/bin/cdshi.js
CHANGED
|
@@ -34,40 +34,47 @@ const categoryChars = { // default: first char of category name
|
|
|
34
34
|
// ExtElement: 'E', // using the first letter is the default
|
|
35
35
|
ExtBoundAction: 'B', // highlight like bound action definition
|
|
36
36
|
ExtParam: 'P', // highlight like entity/action parameter definition
|
|
37
|
+
FromImplicit: 'W',
|
|
37
38
|
Event: 'Y',
|
|
38
39
|
KeyImplicit: 'r', // handle as normal ref
|
|
39
40
|
// Remark: do not use `x`/`X` (hex literal `x'1e3d'`)
|
|
40
41
|
};
|
|
41
42
|
|
|
43
|
+
const options = { newParser: true, attachTokens: true, messages: [] };
|
|
44
|
+
|
|
42
45
|
function highlight( err, buf ) {
|
|
43
46
|
if (err) {
|
|
44
47
|
console.error( 'ERROR:', err.toString() );
|
|
45
48
|
return;
|
|
46
49
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
50
|
+
const parser = compiler.parseX( buf, 'hi.cds', options ).tokenStream;
|
|
51
|
+
// ts is parser with new parser
|
|
52
|
+
const { tokens, lexer } = parser;
|
|
53
|
+
if (!buf.length || !tokens || !tokens.length)
|
|
49
54
|
return;
|
|
50
55
|
const chars = [ ...buf ];
|
|
51
|
-
for (const tok of
|
|
52
|
-
|
|
56
|
+
for (const tok of tokens) {
|
|
57
|
+
const { location } = tok;
|
|
58
|
+
const start = lexer.characterPos( location.line, location.col );
|
|
59
|
+
if (start < 0)
|
|
53
60
|
continue;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
const stop = lexer.characterPos( location.endLine, location.endCol ) - 1;
|
|
62
|
+
const cat = tok.parsed;
|
|
63
|
+
// console.log(tok.location.toString(),tok.text,tok.parsed,stop > start)
|
|
64
|
+
if (!cat) {
|
|
65
|
+
if (stop > start) {
|
|
66
|
+
chars[start] = (cat !== 0 ? '\x0f' : '\x16'); // ^O / ^V (ERROR)
|
|
67
|
+
chars[stop] = '\x17'; // ^W
|
|
58
68
|
}
|
|
59
69
|
else {
|
|
60
|
-
chars[
|
|
70
|
+
chars[start] = (cat !== 0 ? '\x0e' : '\x15'); // ^N / ^U (ERROR)
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
|
-
else {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (tok.stop > tok.start) // stop in ANTLR at last char, not behind
|
|
69
|
-
chars[tok.start + 1] = '_';
|
|
70
|
-
}
|
|
73
|
+
else if (cat !== 'keyword' && cat !== 'token') {
|
|
74
|
+
if (cat !== 'ref' || chars[start] !== '$')
|
|
75
|
+
chars[start] = categoryChars[cat] || cat.charAt(0);
|
|
76
|
+
if (stop > start) // stop in ANTLR at last char, not behind
|
|
77
|
+
chars[start + 1] = '_';
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
80
|
for (const c of chars)
|
package/bin/cdsse.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// corrupted, incomplete or erroneous CDL sources.
|
|
8
8
|
//
|
|
9
9
|
// The output could be used directly by some editors, e.g. Emacs. The
|
|
10
|
-
// capabilities supported at the moments is: complete
|
|
11
|
-
//
|
|
10
|
+
// capabilities supported at the moments is: complete, find, lint.
|
|
11
|
+
// Syntax highlighting is supported by ./cdshi.js.
|
|
12
12
|
|
|
13
13
|
/* eslint no-console:off */
|
|
14
14
|
// @ts-nocheck
|
|
@@ -52,6 +52,7 @@ function usage( err ) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function complete( err, buf ) {
|
|
55
|
+
const messages = [];
|
|
55
56
|
if (err)
|
|
56
57
|
return usage( err );
|
|
57
58
|
const off = offset( buf );
|
|
@@ -74,16 +75,13 @@ function complete( err, buf ) {
|
|
|
74
75
|
const src = `${buf.substring( 0, off.prefix )}__NO_SUCH_ID__${buf.substring( off.cursor )}`;
|
|
75
76
|
const fname = path.resolve( '', file );
|
|
76
77
|
compiler.compileX( [ file ], '', {
|
|
77
|
-
attachValidNames: true, lintMode: true, beta, messages
|
|
78
|
-
}, { [fname]: src } )
|
|
78
|
+
newParser: true, attachValidNames: true, lintMode: true, beta, messages }, { [fname]: src } )
|
|
79
79
|
.then( ident, ident );
|
|
80
80
|
}
|
|
81
81
|
return true;
|
|
82
82
|
|
|
83
|
-
function ident(
|
|
84
|
-
|
|
85
|
-
return usage( xsnOrErr );
|
|
86
|
-
const vn = messageAt( xsnOrErr, 'validNames', off.col ) || Object.create(null);
|
|
83
|
+
function ident() {
|
|
84
|
+
const vn = messageAt( messages, 'validNames', off.col ) || Object.create(null);
|
|
87
85
|
// TODO: if there is no such message, use console.log( 'arbitrary identifier' )
|
|
88
86
|
// if we want to avoid that the editor switches to fuzzy completion match
|
|
89
87
|
// against the prefix (not yet done anyway)
|
|
@@ -107,18 +105,17 @@ function find( err, buf ) {
|
|
|
107
105
|
return usage();
|
|
108
106
|
if (off.prefix === off.cursor) // not at name
|
|
109
107
|
return true;
|
|
108
|
+
const messages = [];
|
|
110
109
|
const src = `${buf.substring( 0, off.prefix )}__NO_SUCH_ID__${buf.substring( off.cursor )}`;
|
|
111
110
|
const fname = path.resolve( '', file );
|
|
112
111
|
compiler.compileX( [ file ], '', {
|
|
113
|
-
attachValidNames: true, lintMode: true, beta, messages
|
|
112
|
+
newParser: true, attachValidNames: true, lintMode: true, beta, messages,
|
|
114
113
|
}, { [fname]: src } )
|
|
115
114
|
.then( show, show );
|
|
116
115
|
return true;
|
|
117
116
|
|
|
118
|
-
function show(
|
|
119
|
-
|
|
120
|
-
return usage( xsnOrErr );
|
|
121
|
-
const vn = messageAt( xsnOrErr, 'validNames', off.col ) || Object.create(null);
|
|
117
|
+
function show() {
|
|
118
|
+
const vn = messageAt( messages, 'validNames', off.col ) || Object.create(null);
|
|
122
119
|
const art = vn[buf.substring( off.prefix, off.cursor )];
|
|
123
120
|
if (art)
|
|
124
121
|
console.log( `${locationString( art.name.location || art.location )}: Definition` );
|
|
@@ -129,13 +126,13 @@ function find( err, buf ) {
|
|
|
129
126
|
function lint( err, buf ) {
|
|
130
127
|
if (err)
|
|
131
128
|
return usage( err );
|
|
129
|
+
const messages = [];
|
|
132
130
|
const fname = path.resolve( '', file );
|
|
133
|
-
compiler.compileX( [ file ], '', { lintMode: true, beta, messages
|
|
131
|
+
compiler.compileX( [ file ], '', { newParser: true, lintMode: true, beta, messages }, { [fname]: buf } )
|
|
134
132
|
.then( display, display );
|
|
135
133
|
return true;
|
|
136
134
|
|
|
137
135
|
function display( xsnOrErr ) {
|
|
138
|
-
const messages = xsnOrErr.messages || xsnOrErr.options && xsnOrErr.options.messages;
|
|
139
136
|
if (!messages)
|
|
140
137
|
return usage( xsnOrErr );
|
|
141
138
|
for (const msg of messages)
|
|
@@ -145,8 +142,10 @@ function lint( err, buf ) {
|
|
|
145
142
|
}
|
|
146
143
|
|
|
147
144
|
function tokensAt( buf, _offset, col, symbol ) {
|
|
145
|
+
const messages = [];
|
|
148
146
|
const src = `${buf.substring( 0, _offset )}≠${buf.substring( _offset )}`;
|
|
149
|
-
|
|
147
|
+
compiler.parseX( src, frel, { newParser: true, messages } );
|
|
148
|
+
const et = messageAt( messages, 'expectedTokens', col ) || [];
|
|
150
149
|
for (const n of et) {
|
|
151
150
|
if (typeof symbol === 'string') {
|
|
152
151
|
if (n.length > 3 && n.charAt(0) === "'" && n.charAt(1) === symbol)
|
|
@@ -166,8 +165,8 @@ function tokensAt( buf, _offset, col, symbol ) {
|
|
|
166
165
|
return et.includes( 'Identifier' );
|
|
167
166
|
}
|
|
168
167
|
|
|
169
|
-
function messageAt(
|
|
170
|
-
const msg =
|
|
168
|
+
function messageAt( messages, prop, col ) {
|
|
169
|
+
const msg = messages.find(
|
|
171
170
|
m => m[prop] && m.$location.line === line && m.$location.col === col && m.$location.file === frel
|
|
172
171
|
);
|
|
173
172
|
return msg && msg[prop];
|
package/lib/api/main.js
CHANGED
|
@@ -468,8 +468,19 @@ function remapName( key, csn, filter = () => true ) {
|
|
|
468
468
|
*/
|
|
469
469
|
function sqlMigration( csn, options, messageFunctions, beforeImage ) {
|
|
470
470
|
const internalOptions = prepareOptions.to.sql(options);
|
|
471
|
+
|
|
471
472
|
messageFunctions.setOptions( internalOptions );
|
|
472
473
|
handleTenantDiscriminator(options, internalOptions, messageFunctions);
|
|
474
|
+
if (!options.dry && internalOptions.script) {
|
|
475
|
+
messageFunctions.error('api-invalid-combination', null, { '#': 'dry-and-script', value: options.dry || 'undefined' });
|
|
476
|
+
messageFunctions.throwWithError();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (internalOptions.script && !internalOptions.severities?.['type-unsupported-key-change']) {
|
|
480
|
+
internalOptions.severities ??= {};
|
|
481
|
+
internalOptions.severities['type-unsupported-key-change'] = 'warning';
|
|
482
|
+
}
|
|
483
|
+
|
|
473
484
|
const { error, throwWithError } = messageFunctions;
|
|
474
485
|
|
|
475
486
|
// Prepare after-image.
|
|
@@ -479,7 +490,7 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
|
|
|
479
490
|
// Compare both images.
|
|
480
491
|
const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
|
|
481
492
|
messageFunctions.setModel(diff);
|
|
482
|
-
const diffFilterObj = diffFilter
|
|
493
|
+
const diffFilterObj = diffFilter.getFilter(internalOptions);
|
|
483
494
|
|
|
484
495
|
if (diffFilterObj) {
|
|
485
496
|
diff.extensions = diff.extensions.filter(ex => diffFilterObj.extension(ex, messageFunctions));
|
|
@@ -487,6 +498,9 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
|
|
|
487
498
|
Object.entries(diff.deletions).forEach(entry => diffFilterObj.deletion(entry, error));
|
|
488
499
|
diff.changedPrimaryKeys = diff.changedPrimaryKeys
|
|
489
500
|
.filter(an => diffFilterObj.changedPrimaryKeys(an));
|
|
501
|
+
|
|
502
|
+
if (internalOptions.script && diffFilterObj.hasLossyChanges())
|
|
503
|
+
messageFunctions.warning('def-unsupported-changes', null, null, 'Found potentially lossy changes - check generated SQL statements');
|
|
490
504
|
}
|
|
491
505
|
|
|
492
506
|
const identifierUtils = sqlUtils.getIdentifierUtils(csn, internalOptions);
|
|
@@ -494,7 +508,10 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
|
|
|
494
508
|
const drops = {
|
|
495
509
|
creates: {},
|
|
496
510
|
final: Object.entries(diff.deletions).reduce((previous, [ name, artifact ]) => {
|
|
497
|
-
|
|
511
|
+
if (artifact.query || artifact.projection)
|
|
512
|
+
previous[name] = `DROP VIEW ${ identifierUtils.renderArtifactName(name) };`;
|
|
513
|
+
else
|
|
514
|
+
previous[name] = `-- [WARNING] this statement is lossy\nDROP TABLE ${ identifierUtils.renderArtifactName(name) };`;
|
|
498
515
|
return previous;
|
|
499
516
|
}, {}),
|
|
500
517
|
};
|
package/lib/api/options.js
CHANGED
|
@@ -44,6 +44,7 @@ const publicOptionsNewAPI = [
|
|
|
44
44
|
'odataXServiceRefs',
|
|
45
45
|
'odataV2PartialConstr',
|
|
46
46
|
'odataVocabularies',
|
|
47
|
+
'odataNoCreator',
|
|
47
48
|
'service',
|
|
48
49
|
'serviceNames',
|
|
49
50
|
//
|
|
@@ -53,6 +54,8 @@ const publicOptionsNewAPI = [
|
|
|
53
54
|
'resolveProjections',
|
|
54
55
|
'remapOdataAnnotations',
|
|
55
56
|
'keepLocalized',
|
|
57
|
+
// to.sql.migration
|
|
58
|
+
'script',
|
|
56
59
|
];
|
|
57
60
|
|
|
58
61
|
// Internal options used for testing/debugging etc.
|
|
@@ -195,7 +198,7 @@ module.exports = {
|
|
|
195
198
|
return translateOptions(options, defaultOptions, hardOptions, undefined, [ 'valid-structured' ], 'to.odata');
|
|
196
199
|
},
|
|
197
200
|
},
|
|
198
|
-
for: {
|
|
201
|
+
for: {
|
|
199
202
|
odata: (options) => {
|
|
200
203
|
const hardOptions = { toOdata: true };
|
|
201
204
|
const defaultOptions = { odataVersion: 'v4', odataFormat: 'flat' };
|
package/lib/base/builtins.js
CHANGED
|
@@ -17,6 +17,7 @@ const propagationRules = {
|
|
|
17
17
|
'@cds.autoexpose': 'onlyViaArtifact',
|
|
18
18
|
'@cds.autoexposed': 'never',
|
|
19
19
|
'@cds.external': 'never',
|
|
20
|
+
'@cds.java.this.name': 'onlyViaParent',
|
|
20
21
|
'@cds.persistence.calcview': 'never',
|
|
21
22
|
'@cds.persistence.exists': 'never',
|
|
22
23
|
'@cds.persistence.skip': 'notWithPersistenceTable',
|
|
@@ -157,6 +157,8 @@ const centralMessages = {
|
|
|
157
157
|
'syntax-duplicate-equal-clause': { severity: 'Warning' },
|
|
158
158
|
'syntax-invalid-name': { severity: 'Error', configurableFor: 'deprecated' },
|
|
159
159
|
'syntax-missing-as': { severity: 'Error', configurableFor: true },
|
|
160
|
+
'syntax-missing-proj-semicolon': { severity: 'Warning', errorFor: [ 'v6' ] },
|
|
161
|
+
'syntax-unexpected-many-one': { severity: 'Error', configurableFor: true }, // TODO: remove `configurableFor` soon, latest v6
|
|
160
162
|
'syntax-unexpected-null': { severity: 'Error', configurableFor: true },
|
|
161
163
|
'syntax-unexpected-reserved-word': { severity: 'Error', configurableFor: true },
|
|
162
164
|
'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
|
|
@@ -260,7 +262,8 @@ const centralMessageTexts = {
|
|
|
260
262
|
std: 'Invalid option combination found: $(OPTION) and $(PROP)', // unused
|
|
261
263
|
'valid-structured': 'Structured OData is only supported with OData version v4',
|
|
262
264
|
'sql-dialect-and-naming': 'sqlDialect $(NAME) can\'t be combined with sqlMapping $(PROP)',
|
|
263
|
-
'tenant-and-naming': 'Option $(OPTION) can\'t be combined with sqlMapping $(PROP) - expected sqlMapping $(VALUE)'
|
|
265
|
+
'tenant-and-naming': 'Option $(OPTION) can\'t be combined with sqlMapping $(PROP) - expected sqlMapping $(VALUE)',
|
|
266
|
+
'dry-and-script': 'script:true must be combined with dry:true, found $(VALUE)',
|
|
264
267
|
},
|
|
265
268
|
'api-unexpected-combination': {
|
|
266
269
|
std: 'Unexpected option combination: $(OPTION) and $(PROP)', // unused
|
|
@@ -356,6 +359,7 @@ const centralMessageTexts = {
|
|
|
356
359
|
std: 'Annotations can\'t be used in a column with $(CODE)',
|
|
357
360
|
doc: 'Doc comments can\'t be used in a column with $(CODE)',
|
|
358
361
|
},
|
|
362
|
+
'syntax-unexpected-many-one': 'Replace $(CODE) with $(DELIMITED) to avoid an ambiguity with managed compositions of anonymous aspects',
|
|
359
363
|
'syntax-invalid-name': {
|
|
360
364
|
std: 'Identifier must consist of at least one character', // only via delimited id
|
|
361
365
|
// as: 'String in property $(PROP) must not be empty', // expecting non-empty string is ok
|
|
@@ -694,8 +698,12 @@ const centralMessageTexts = {
|
|
|
694
698
|
'annotation': 'Variable $(NAME) can only be used in annotation values',
|
|
695
699
|
},
|
|
696
700
|
|
|
697
|
-
|
|
698
|
-
|
|
701
|
+
'rewrite-not-supported': {
|
|
702
|
+
// TODO: Better text ?
|
|
703
|
+
std: 'The ON-condition is not rewritten here - provide an explicit ON-condition',
|
|
704
|
+
'inline-expand': 'The ON-condition is not rewritten in nested projections - provide an explicit ON-condition',
|
|
705
|
+
'secondary': 'The ON-condition is not rewritten due to multiple associations in this path - provide an explicit ON-condition',
|
|
706
|
+
},
|
|
699
707
|
'type-unsupported-rewrite': {
|
|
700
708
|
std: 'Rewriting the ON-condition not supported here', // unused: merge with 'rewrite-not-supported'
|
|
701
709
|
'sub-element': 'Rewriting the ON-condition of unmanaged association in sub element is not supported'
|
|
@@ -1109,6 +1117,11 @@ const centralMessageTexts = {
|
|
|
1109
1117
|
'odata-parameter-order': 'Unexpected mandatory after optional parameter',
|
|
1110
1118
|
'odata-key-recursive': 'Unexpected recursive key $(NAME)',
|
|
1111
1119
|
'odata-key-uuid-default-anno': 'Expected element of type $(TYPE) to be annotated with $(ANNO) when used as primary key in $(ID)',
|
|
1120
|
+
'odata-ignoring-param-default': {
|
|
1121
|
+
'std': 'Ignoring default value on parameter',
|
|
1122
|
+
'xpr': 'Ignoring unexpected expression as default value',
|
|
1123
|
+
'colitem': 'Ignoring unexpected default value for a structured or collection like parameter',
|
|
1124
|
+
},
|
|
1112
1125
|
// -----------------------------------------------------------------------------------
|
|
1113
1126
|
// All odata-anno MUST have a '$(ANNO)' parameter to indicate error location
|
|
1114
1127
|
// -----------------------------------------------------------------------------------
|
package/lib/base/model.js
CHANGED
|
@@ -7,15 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
const { forEach } = require('../utils/objectUtils');
|
|
9
9
|
|
|
10
|
-
const queryOps = {
|
|
11
|
-
query: 'select', // TODO: rename to SELECT
|
|
12
|
-
union: 'union',
|
|
13
|
-
intersect: 'union',
|
|
14
|
-
except: 'union',
|
|
15
|
-
minus: 'union',
|
|
16
|
-
subquery: 'union', // for (subquery) with ORDER BY or LIMIT/OFFSET
|
|
17
|
-
};
|
|
18
|
-
|
|
19
10
|
/**
|
|
20
11
|
* Object of all available beta flags that will be enabled/disabled by `--beta-mode`
|
|
21
12
|
* through cdsc. Only intended for INTERNAL USE.
|
|
@@ -224,7 +215,6 @@ module.exports = {
|
|
|
224
215
|
availableBetaFlags,
|
|
225
216
|
isDeprecatedEnabled,
|
|
226
217
|
checkRemovedDeprecatedFlags,
|
|
227
|
-
queryOps,
|
|
228
218
|
forEachDefinition,
|
|
229
219
|
forEachMember,
|
|
230
220
|
forEachMemberRecursively,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { isBuiltinType } = require('../base/builtins');
|
|
4
|
-
const { isBetaEnabled } = require('../base/model');
|
|
5
4
|
|
|
6
5
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
7
6
|
|
|
@@ -21,8 +20,6 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
21
20
|
// (this.options.odataProxies || this.options.odataXServiceRefs);
|
|
22
21
|
|
|
23
22
|
const serviceName = this.csnUtils.getServiceName(artName);
|
|
24
|
-
if (!serviceName && art.kind !== 'aspect')
|
|
25
|
-
this.warning(null, path, {}, 'Functions and actions must be declared in a service');
|
|
26
23
|
|
|
27
24
|
if (art.kind === 'entity') {
|
|
28
25
|
for (const [ actName, act ] of Object.entries(art.actions)) {
|
|
@@ -71,15 +68,6 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
71
68
|
if (!paramType)
|
|
72
69
|
return; // no type could be resolved
|
|
73
70
|
|
|
74
|
-
// "default" is always propagated to params
|
|
75
|
-
if (param.default && !isBetaEnabled(this.options, 'optionalActionFunctionParameters')) {
|
|
76
|
-
this.message('param-default', currPath, { '#': actKind }, {
|
|
77
|
-
std: 'Artifact parameters can\'t have a default value', // Not used
|
|
78
|
-
action: 'Action parameters can\'t have a default value',
|
|
79
|
-
function: 'Function parameters can\'t have a default value',
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
71
|
if (param.type && this.csnUtils.isAssocOrComposition(param)) {
|
|
84
72
|
this.error(null, currPath, { '#': actKind }, {
|
|
85
73
|
std: 'An association is not allowed as this artifact\'s parameter type', // Not used
|
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { isBuiltinType } = require('../base/builtins');
|
|
4
|
-
const {
|
|
4
|
+
const { transformAnnotationExpression } = require('../model/csnUtils');
|
|
5
5
|
/**
|
|
6
6
|
*
|
|
7
7
|
* @param {object} member
|
|
8
8
|
*/
|
|
9
9
|
function checkAnnotationExpression( member, _memberName, _prop, path ) {
|
|
10
10
|
Object.keys(member).filter(pn => pn[0] === '@').forEach((anno) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.error('odata-anno-xpr-ref', refPath, { anno, elemref, '#': 'flatten_builtin_type' });
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
}, xprPath);
|
|
11
|
+
transformAnnotationExpression(member, anno, {
|
|
12
|
+
ref: (elemref, __prop, _ref, refPath) => {
|
|
13
|
+
const { art, scope } = this.csnUtils.inspectRef(refPath);
|
|
14
|
+
if (scope !== '$magic' && art) {
|
|
15
|
+
const ft = this.csnUtils.getFinalTypeInfo(art.type);
|
|
16
|
+
if (!isBuiltinType(ft?.type))
|
|
17
|
+
this.error('odata-anno-xpr-ref', refPath, { anno, elemref, '#': 'flatten_builtin_type' });
|
|
18
|
+
}
|
|
23
19
|
},
|
|
24
|
-
},
|
|
20
|
+
}, path);
|
|
25
21
|
});
|
|
26
22
|
}
|
|
27
23
|
|
|
@@ -289,7 +289,8 @@ function assertConsistency( model, stage ) {
|
|
|
289
289
|
'$calcDepElement',
|
|
290
290
|
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit', '$limit',
|
|
291
291
|
'_origin', '_block', '$contains',
|
|
292
|
-
'_projections', '
|
|
292
|
+
'_projections', '_complexProjections',
|
|
293
|
+
'_parent', '_main', '_effectiveType', '$effectiveSeqNo', '$expand',
|
|
293
294
|
'$tableAliases', 'kind', '_$next', '_combined', '$inlines', '_status',
|
|
294
295
|
],
|
|
295
296
|
},
|
|
@@ -313,7 +314,7 @@ function assertConsistency( model, stage ) {
|
|
|
313
314
|
'kind', 'name', '$syntax', '_block', '_parent', '_main',
|
|
314
315
|
'elements', '_origin', '_joinParent', '$joinArgsIndex', '$syntax',
|
|
315
316
|
'$parens', '_status', // TODO: only in from
|
|
316
|
-
'scope', '_artifact', '$inferred', 'kind',
|
|
317
|
+
'scope', '_artifact', '_originalArtifact', '$inferred', 'kind',
|
|
317
318
|
'_effectiveType', '$effectiveSeqNo', // TODO:check this
|
|
318
319
|
'$duplicates', // In JOIN if both sides are the same.
|
|
319
320
|
],
|
|
@@ -370,7 +371,8 @@ function assertConsistency( model, stage ) {
|
|
|
370
371
|
requires: [ 'location' ],
|
|
371
372
|
optional: [
|
|
372
373
|
'path', 'elements', '_outer', '_parent', '_main', '_block', 'kind',
|
|
373
|
-
'scope', '_artifact', '$inferred', '$expand', '$inCycle',
|
|
374
|
+
'scope', '_artifact', '$inferred', '$expand', '$inCycle',
|
|
375
|
+
'$tableAliases', '_$next',
|
|
374
376
|
'_origin', '_effectiveType', '$effectiveSeqNo', '_extensions', '$contains',
|
|
375
377
|
],
|
|
376
378
|
},
|
|
@@ -390,7 +392,8 @@ function assertConsistency( model, stage ) {
|
|
|
390
392
|
'args', '$syntax',
|
|
391
393
|
'where', 'groupBy', 'limit', 'orderBy', 'having',
|
|
392
394
|
'cardinality',
|
|
393
|
-
'_artifact', '
|
|
395
|
+
'_artifact', '_originalArtifact',
|
|
396
|
+
'_navigation', '_user',
|
|
394
397
|
'$inferred',
|
|
395
398
|
],
|
|
396
399
|
},
|
|
@@ -443,7 +446,10 @@ function assertConsistency( model, stage ) {
|
|
|
443
446
|
test: expression, // properties below are "sub specifications"
|
|
444
447
|
ref: {
|
|
445
448
|
requires: [ 'location', 'path' ],
|
|
446
|
-
optional: [
|
|
449
|
+
optional: [
|
|
450
|
+
'scope', 'variant', '_artifact', '_originalArtifact',
|
|
451
|
+
'$inferred', '$parens', 'sort', 'nulls',
|
|
452
|
+
],
|
|
447
453
|
},
|
|
448
454
|
none: { optional: () => true }, // parse error
|
|
449
455
|
// TODO: why optional / enough in name?
|
|
@@ -488,7 +494,7 @@ function assertConsistency( model, stage ) {
|
|
|
488
494
|
'literal', 'val', 'sym', 'struct', 'variant', 'path', 'name', '$duplicates', 'upTo',
|
|
489
495
|
// expressions as annotation values
|
|
490
496
|
'$tokenTexts', 'op', 'args', 'func', '_artifact', 'type', '$typeArgs',
|
|
491
|
-
'scale', 'srid', 'length', 'precision', 'scope',
|
|
497
|
+
'scale', 'srid', 'length', 'precision', 'scope', '$parens',
|
|
492
498
|
],
|
|
493
499
|
// TODO: restrict path to #simplePath
|
|
494
500
|
},
|
|
@@ -524,7 +530,7 @@ function assertConsistency( model, stage ) {
|
|
|
524
530
|
'_effectiveType', '$effectiveSeqNo', '_origin', '_deps',
|
|
525
531
|
// CSN parser may let these properties slip through to XSN, even if input is invalid.
|
|
526
532
|
'args', 'op', 'func', 'suffix',
|
|
527
|
-
'$invalidPaths',
|
|
533
|
+
'$invalidPaths', '$parens',
|
|
528
534
|
],
|
|
529
535
|
// TODO: name requires if not in parser?
|
|
530
536
|
},
|
|
@@ -538,7 +544,7 @@ function assertConsistency( model, stage ) {
|
|
|
538
544
|
schema: {
|
|
539
545
|
id: { test: isStringOrNumber },
|
|
540
546
|
select: { test: TODO }, // TODO: remove
|
|
541
|
-
},
|
|
547
|
+
},
|
|
542
548
|
requires: [ 'location' ],
|
|
543
549
|
optional: [
|
|
544
550
|
'path', 'id', '$delimited', 'variant', // TODO: req path, opt id for main, req id for member
|
|
@@ -607,6 +613,7 @@ function assertConsistency( model, stage ) {
|
|
|
607
613
|
// - on a path item with a filter condition to the user of the ref (not nested)
|
|
608
614
|
// - on a JOIN node to the query (TODO: _outer?)
|
|
609
615
|
_artifact: { test: TODO },
|
|
616
|
+
_originalArtifact: { test: TODO },
|
|
610
617
|
_navigation: { test: TODO },
|
|
611
618
|
_effectiveType: { kind: true, test: TODO },
|
|
612
619
|
$effectiveSeqNo: { kind: true, test: isNumber },
|
|
@@ -641,7 +648,7 @@ function assertConsistency( model, stage ) {
|
|
|
641
648
|
$replacement: { kind: true, test: TODO }, // for smart * in queries
|
|
642
649
|
_origin: { kind: true, test: TODO },
|
|
643
650
|
_calcOrigin: { kind: true, test: TODO },
|
|
644
|
-
|
|
651
|
+
_columnParent: { kind: [ 'element', undefined ], test: TODO }, // column or * (wildcard)
|
|
645
652
|
_from: { kind: true, test: TODO }, // TODO: not necessary anymore ?
|
|
646
653
|
// array of $tableAlias (or includes) for explicit and implicit redirection:
|
|
647
654
|
_redirected: { kind: true, test: TODO },
|
|
@@ -658,7 +665,8 @@ function assertConsistency( model, stage ) {
|
|
|
658
665
|
_scc: { kind: true, test: TODO }, // for cyclic calculation
|
|
659
666
|
_sccCaller: { kind: true, test: TODO }, // for cyclic calculation
|
|
660
667
|
_status: { kind: true, test: TODO }, // TODO: $status
|
|
661
|
-
_projections: { kind: true, test: TODO },
|
|
668
|
+
_projections: { kind: true, test: TODO },
|
|
669
|
+
_complexProjections: { kind: true, test: TODO }, // for projected paths with filters
|
|
662
670
|
$entity: { kind: true, test: TODO },
|
|
663
671
|
_entities: { test: TODO },
|
|
664
672
|
$compositionTargets: { test: isDictionary( isBoolean ) },
|
|
@@ -972,7 +980,7 @@ function assertConsistency( model, stage ) {
|
|
|
972
980
|
return function valWithLocation( node, parent, prop, spec, name ) {
|
|
973
981
|
const valSchema = { val: Object.assign( {}, spec, { test: func } ) };
|
|
974
982
|
const requires = [ 'val', 'location' ];
|
|
975
|
-
const optional = [ 'literal', '$inferred', '$priority', '
|
|
983
|
+
const optional = [ 'literal', '$inferred', '$priority', '_columnParent' ];
|
|
976
984
|
standard( node, parent, prop, {
|
|
977
985
|
schema: valSchema, requires, optional, instanceOf: spec.instanceOf,
|
|
978
986
|
}, name );
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -77,6 +77,7 @@ typeParameters.list = Object.keys( typeParameters.expectedLiteralsFor );
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
const specialFunctions = compileFunctions( {
|
|
80
|
+
// TODO: use lower-case
|
|
80
81
|
'': [ // the default
|
|
81
82
|
{
|
|
82
83
|
intro: [ 'ALL', 'DISTINCT' ],
|
|
@@ -231,7 +232,6 @@ const numberRegEx = /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?[ \t]*$/i;
|
|
|
231
232
|
* - `unexpected_msg`: error message which is issued if `unexpected_char` matches
|
|
232
233
|
* - `unexpected_char`: regular expression matching an illegal character in `value`,
|
|
233
234
|
* the error location is only correct for a literal <prefix>'<value>'
|
|
234
|
-
* - `literal`: the value which is used instead of `prefix` in the AST
|
|
235
235
|
* TODO: we might do a range check (consider leap seconds, i.e. max value 60),
|
|
236
236
|
* but always allow Feb 29 (no leap year computation)
|
|
237
237
|
* Notes:
|
package/lib/compiler/define.js
CHANGED
|
@@ -903,9 +903,9 @@ function define( model ) {
|
|
|
903
903
|
hasItems = true;
|
|
904
904
|
if (!columns) { // expand or inline
|
|
905
905
|
if (parent.value)
|
|
906
|
-
setLink( col, '
|
|
907
|
-
else if (parent.
|
|
908
|
-
setLink( col, '
|
|
906
|
+
setLink( col, '_columnParent', parent ); // also set for '*' in expand/inline
|
|
907
|
+
else if (parent._columnParent)
|
|
908
|
+
setLink( col, '_columnParent', parent._columnParent );
|
|
909
909
|
}
|
|
910
910
|
if (col.val === '*') {
|
|
911
911
|
if (!wildcard) {
|
package/lib/compiler/extend.js
CHANGED
|
@@ -441,8 +441,8 @@ function extend( model ) {
|
|
|
441
441
|
function applySingleExtension( art, ext, prop ) {
|
|
442
442
|
if (prop === 'includes') {
|
|
443
443
|
if (ext.kind === 'extend' && art.$inferred) {
|
|
444
|
-
error( 'extend-for-generated', [ ext.name.location, ext ], { art },
|
|
445
|
-
'You can\'t use
|
|
444
|
+
error( 'extend-for-generated', [ ext.name.location, ext ], { art, keyword: 'extend' },
|
|
445
|
+
'You can\'t use $(KEYWORD) on the generated $(ART)' );
|
|
446
446
|
}
|
|
447
447
|
else if (art.kind !== 'annotate' && !art._outer) { // not with elem extension in targetAspect
|
|
448
448
|
const { id } = art.name;
|
|
@@ -797,7 +797,7 @@ function extend( model ) {
|
|
|
797
797
|
const dict = parent[prop];
|
|
798
798
|
if (!dict) {
|
|
799
799
|
// TODO: check - for each name? - better locations
|
|
800
|
-
const location = ext._parent[prop]?.[$location] || ext.name.location;
|
|
800
|
+
const location = ext._parent?.[prop]?.[$location] || ext.name.location;
|
|
801
801
|
// Remark: no `elements` dict location with `annotate Main:elem`
|
|
802
802
|
switch (prop) {
|
|
803
803
|
// TODO: change texts, somehow similar to checkDefinitions() ?
|
|
@@ -1052,8 +1052,8 @@ function extend( model ) {
|
|
|
1052
1052
|
if (ext.name._artifact === undefined) { // not already applied
|
|
1053
1053
|
setArtifactLink( ext.name, art );
|
|
1054
1054
|
if (noExtend && ext.kind === 'extend') {
|
|
1055
|
-
error( 'extend-for-generated', [ ext.name.location, ext ], { art },
|
|
1056
|
-
'You can\'t use
|
|
1055
|
+
error( 'extend-for-generated', [ ext.name.location, ext ], { art, keyword: 'extend' },
|
|
1056
|
+
'You can\'t use $(KEYWORD) on the generated $(ART)' );
|
|
1057
1057
|
continue;
|
|
1058
1058
|
}
|
|
1059
1059
|
if (ext.includes) {
|