@sap/cds-compiler 5.1.2 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/bin/cdsc.js +7 -2
- package/bin/cdshi.js +24 -17
- package/bin/cdsse.js +17 -18
- package/doc/CHANGELOG_BETA.md +9 -4
- package/lib/api/main.js +19 -2
- package/lib/api/options.js +4 -1
- package/lib/api/validate.js +5 -0
- package/lib/base/builtins.js +1 -0
- package/lib/base/message-registry.js +40 -3
- package/lib/base/messages.js +1 -1
- package/lib/base/model.js +0 -11
- package/lib/checks/actionsFunctions.js +0 -12
- package/lib/checks/structuredAnnoExpressions.js +10 -14
- package/lib/compiler/assert-consistency.js +21 -13
- package/lib/compiler/builtins.js +2 -2
- package/lib/compiler/checks.js +25 -6
- package/lib/compiler/define.js +27 -31
- package/lib/compiler/extend.js +16 -18
- package/lib/compiler/generate.js +3 -3
- package/lib/compiler/populate.js +22 -16
- package/lib/compiler/propagator.js +3 -2
- package/lib/compiler/resolve.js +87 -94
- package/lib/compiler/shared.js +12 -13
- package/lib/compiler/tweak-assocs.js +390 -86
- package/lib/compiler/utils.js +41 -33
- package/lib/compiler/xpr-rewrite.js +45 -58
- package/lib/edm/annotations/genericTranslation.js +17 -13
- package/lib/edm/csn2edm.js +28 -4
- package/lib/edm/edm.js +68 -28
- package/lib/edm/edmInboundChecks.js +5 -8
- package/lib/edm/edmPreprocessor.js +66 -40
- package/lib/edm/edmUtils.js +1 -1
- package/lib/gen/BaseParser.js +778 -0
- package/lib/gen/CdlParser.js +4477 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +4072 -4024
- package/lib/inspect/inspectPropagation.js +1 -1
- package/lib/json/from-csn.js +5 -3
- package/lib/json/to-csn.js +7 -10
- package/lib/language/antlrParser.js +96 -0
- package/lib/language/errorStrategy.js +1 -1
- package/lib/language/genericAntlrParser.js +32 -4
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/model/cloneCsn.js +22 -13
- 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 +16 -10
- package/lib/parsers/AstBuildingParser.js +1290 -0
- package/lib/parsers/CdlGrammar.g4 +2013 -0
- package/lib/parsers/Lexer.js +249 -0
- package/lib/render/toCdl.js +46 -45
- package/lib/render/toSql.js +5 -5
- package/lib/transform/addTenantFields.js +4 -4
- 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/forRelationalDB.js +7 -6
- 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/lib/utils/moduleResolve.js +1 -1
- package/package.json +2 -2
- package/share/messages/redirected-to-ambiguous.md +5 -4
- package/share/messages/redirected-to-complex.md +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,12 +7,63 @@
|
|
|
7
7
|
Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
|
|
10
|
+
## Version 5.3.0 - 2024-09-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- compiler:
|
|
15
|
+
- A warning is emitted if a string enum's values are longer than the specified length.
|
|
16
|
+
- ON-condition rewriting has been improved and now supports secondary associations.
|
|
17
|
+
- to.edm(x): Support optional action and function parameters in OData V4. The following rules apply:
|
|
18
|
+
+ A parameter declared `not null` without default value is mandatory.
|
|
19
|
+
+ A **function** parameter declared `null` without default value is mandatory.
|
|
20
|
+
+ An **action** parameter declared `null` without default value is optional as it is equivalent to
|
|
21
|
+
`@Core.OptionalParameter { DefaultValue: null }`.
|
|
22
|
+
+ A parameter with a default value is optional and the default value is rendered as
|
|
23
|
+
`@Core.OptionalParameter { DefaultValue: <value> }` regardless of its nullability.
|
|
24
|
+
+ `@Core.OptionalParameter: true` can be used to turn a mandatory parameter into an optional parameter
|
|
25
|
+
(especially function parameters) and to signal an unspecified default value in the API if the parameter
|
|
26
|
+
has no default clause.
|
|
27
|
+
+ `@Core.OptionalParameter: false` turns the creation of a `@Core.OptionalParameter: { Default: ... }`
|
|
28
|
+
annotation off.
|
|
29
|
+
+ A default clause or `@Core.OptionalParameter: <bool>` have no effect on an explicit binding parameter.
|
|
30
|
+
+ Mandatory and optional **action** parameters may appear in any order.
|
|
31
|
+
+ Optional **function** parameters must not be followed by mandatory parameters.
|
|
32
|
+
- to.edm: Forward `@OpenAPI {...}` into EDM Json with option `--odata-openapi-hints`.
|
|
33
|
+
- cdsc: Option `--transitive-localized-views` was added.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- CDL parser: Issue warning if annotation assignments have been written
|
|
38
|
+
at an invalid position inside a type expressions.
|
|
39
|
+
- CDL parser: Issue warning for arrayed parameter with default value.
|
|
40
|
+
- to.cdl: Arrayed parameters with default values were not rendered correctly.
|
|
41
|
+
|
|
42
|
+
## Version 5.2.0 - 2024-08-27
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- to.edm(x): Add `@Core.Links { rel: 'author', href: 'https://cap.cloud.sap' };` as watermark to lead schema.
|
|
47
|
+
- to.sql.migration: Introduce option `script` to aid in generation of manual migration scripts by not aborting when encountering lossy changes.
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- for.odata: No longer reject default values on action/function parameters.
|
|
52
|
+
- to.edm(x): Raise warning for default values on action/function parameters that they are ignored.
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- compiler: Fix extensions with bound actions using an explicit binding parameter in `parseCdl` CSN.
|
|
57
|
+
- for.odata, to.edm(x): Fix some issues with resolving annotation expressions in nested objects and
|
|
58
|
+
reliably replace value of `=` attribute with `true` after processing.
|
|
59
|
+
|
|
10
60
|
## Version 5.1.2 - 2024-08-05
|
|
11
61
|
|
|
12
62
|
### Fixed
|
|
13
63
|
|
|
14
64
|
- compiler: In parseCdl mode, bound actions specifying the binding parameter with `$self` did not work.
|
|
15
65
|
|
|
66
|
+
|
|
16
67
|
## Version 5.1.0 - 2024-07-25
|
|
17
68
|
|
|
18
69
|
### Added
|
|
@@ -105,6 +156,13 @@ This is a preview version for the major release and contains breaking changes. I
|
|
|
105
156
|
Use `to.edm(x)` instead.
|
|
106
157
|
|
|
107
158
|
|
|
159
|
+
## Version 4.9.8 - 2024-07-29
|
|
160
|
+
|
|
161
|
+
### Fixed
|
|
162
|
+
|
|
163
|
+
- compiler: Fix extensions with bound actions using an explicit binding parameter in `parseCdl` CSN.
|
|
164
|
+
- 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)
|
|
165
|
+
|
|
108
166
|
## Version 4.9.6 - 2024-07-15
|
|
109
167
|
|
|
110
168
|
### Fixed
|
package/bin/cdsc.js
CHANGED
|
@@ -217,6 +217,11 @@ function cdsc_main() {
|
|
|
217
217
|
if (cmdLine.options.stdin)
|
|
218
218
|
cmdLine.options.fallbackParser ??= 'auto!';
|
|
219
219
|
|
|
220
|
+
if (cmdLine.options[cmdLine.command]?.transitiveLocalizedViews) {
|
|
221
|
+
cmdLine.options.fewerLocalizedViews = !cmdLine.options[cmdLine.command].transitiveLocalizedViews
|
|
222
|
+
delete cmdLine.options[cmdLine.command].transitiveLocalizedViews;
|
|
223
|
+
}
|
|
224
|
+
|
|
220
225
|
parseSeverityOptions(cmdLine);
|
|
221
226
|
|
|
222
227
|
// Do the work for the selected command
|
|
@@ -306,7 +311,7 @@ async function executeCommandLine( command, options, args ) {
|
|
|
306
311
|
manageConstraints,
|
|
307
312
|
toSql,
|
|
308
313
|
inspect,
|
|
309
|
-
|
|
314
|
+
forEffective,
|
|
310
315
|
forSeal,
|
|
311
316
|
};
|
|
312
317
|
const commandsWithoutCompilation = {
|
|
@@ -352,7 +357,7 @@ async function executeCommandLine( command, options, args ) {
|
|
|
352
357
|
return model;
|
|
353
358
|
}
|
|
354
359
|
|
|
355
|
-
function
|
|
360
|
+
function forEffective( model ) {
|
|
356
361
|
const features = [ 'resolveSimpleTypes', 'resolveProjections', 'remapOdataAnnotations', 'keepLocalized' ];
|
|
357
362
|
for (const feature of features) {
|
|
358
363
|
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 { tokenStream } = compiler.parseX( buf, 'hi.cds', options );
|
|
51
|
+
const { tokens, lexer } = tokenStream;
|
|
52
|
+
if (!buf.length || !tokens || !tokens.length)
|
|
49
53
|
return;
|
|
54
|
+
|
|
50
55
|
const chars = [ ...buf ];
|
|
51
|
-
for (const tok of
|
|
52
|
-
if (tok.
|
|
56
|
+
for (const tok of tokens) {
|
|
57
|
+
if (tok.type === 'Comment') // but interpret DocComment!
|
|
53
58
|
continue;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const { location, start } = tok;
|
|
60
|
+
if (start < 0)
|
|
61
|
+
continue;
|
|
62
|
+
const stop = lexer.characterPos( location.endLine, location.endCol ) - 1;
|
|
63
|
+
const cat = tok.parsedAs;
|
|
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)
|
|
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/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,11 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
|
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
**Don't use `beta` fixes, changes and features in productive mode.**
|
|
10
10
|
|
|
11
|
+
## Version 5.3.0 - 2024-09-25
|
|
12
|
+
|
|
13
|
+
### Removed `optionalActionFunctionParameters`
|
|
14
|
+
|
|
15
|
+
It is now enabled by default.
|
|
11
16
|
|
|
12
17
|
## Version 5.0.0 - 2024-05-29
|
|
13
18
|
|
|
@@ -17,19 +22,19 @@ It is now enabled by default.
|
|
|
17
22
|
|
|
18
23
|
## Version 4.9.0 - 2024-04-25
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
### Removed `odataAnnotationExpressions`
|
|
21
26
|
|
|
22
27
|
It is now enabled by default.
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
### Removed `odataPathsInAnnotationExpressions`
|
|
25
30
|
|
|
26
31
|
It is now enabled by default.
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
### Removed `annotationExpressions`
|
|
29
34
|
|
|
30
35
|
It is now enabled by default.
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
### Added `temporalRawProjection`
|
|
33
38
|
|
|
34
39
|
Enables revocation of temporal where clause in projections of temporal entities if the
|
|
35
40
|
`@cds.valid { from, to }` annotations on the projection elements have falsy values.
|
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/api/validate.js
CHANGED
|
@@ -118,6 +118,11 @@ const validators = {
|
|
|
118
118
|
expected: () => 'type boolean|number',
|
|
119
119
|
found: val => `type ${ typeof val }`,
|
|
120
120
|
},
|
|
121
|
+
withLocations: {
|
|
122
|
+
validate: val => typeof val === 'boolean' || val === 'withEndPosition',
|
|
123
|
+
expected: () => 'type boolean|"withEndPosition"',
|
|
124
|
+
found: val => `type ${ typeof val }`,
|
|
125
|
+
},
|
|
121
126
|
dictionaryPrototype: {
|
|
122
127
|
validate: () => true,
|
|
123
128
|
},
|
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,9 @@ 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-after': { severity: 'Warning', errorFor: [ 'v6' ] },
|
|
162
|
+
'syntax-unexpected-many-one': { severity: 'Error', configurableFor: true }, // TODO: remove `configurableFor` soon, latest v6
|
|
160
163
|
'syntax-unexpected-null': { severity: 'Error', configurableFor: true },
|
|
161
164
|
'syntax-unexpected-reserved-word': { severity: 'Error', configurableFor: true },
|
|
162
165
|
'syntax-unknown-escape': { severity: 'Error', configurableFor: true },
|
|
@@ -260,7 +263,8 @@ const centralMessageTexts = {
|
|
|
260
263
|
std: 'Invalid option combination found: $(OPTION) and $(PROP)', // unused
|
|
261
264
|
'valid-structured': 'Structured OData is only supported with OData version v4',
|
|
262
265
|
'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)'
|
|
266
|
+
'tenant-and-naming': 'Option $(OPTION) can\'t be combined with sqlMapping $(PROP) - expected sqlMapping $(VALUE)',
|
|
267
|
+
'dry-and-script': 'script:true must be combined with dry:true, found $(VALUE)',
|
|
264
268
|
},
|
|
265
269
|
'api-unexpected-combination': {
|
|
266
270
|
std: 'Unexpected option combination: $(OPTION) and $(PROP)', // unused
|
|
@@ -356,6 +360,12 @@ const centralMessageTexts = {
|
|
|
356
360
|
std: 'Annotations can\'t be used in a column with $(CODE)',
|
|
357
361
|
doc: 'Doc comments can\'t be used in a column with $(CODE)',
|
|
358
362
|
},
|
|
363
|
+
'syntax-unexpected-after': {
|
|
364
|
+
std: 'Unexpected $(KEYWORD) after annotation assignment',
|
|
365
|
+
many: 'Unexpected $(KEYWORD) after array type',
|
|
366
|
+
enum: 'Unexpected annotation assignment after enum type',
|
|
367
|
+
},
|
|
368
|
+
'syntax-unexpected-many-one': 'Replace $(CODE) with $(DELIMITED) to avoid an ambiguity with managed compositions of anonymous aspects',
|
|
359
369
|
'syntax-invalid-name': {
|
|
360
370
|
std: 'Identifier must consist of at least one character', // only via delimited id
|
|
361
371
|
// as: 'String in property $(PROP) must not be empty', // expecting non-empty string is ok
|
|
@@ -694,8 +704,16 @@ const centralMessageTexts = {
|
|
|
694
704
|
'annotation': 'Variable $(NAME) can only be used in annotation values',
|
|
695
705
|
},
|
|
696
706
|
|
|
697
|
-
|
|
698
|
-
|
|
707
|
+
'rewrite-not-supported': {
|
|
708
|
+
// TODO: Better text ?
|
|
709
|
+
std: 'The ON-condition is not rewritten here - provide an explicit ON-condition',
|
|
710
|
+
'inline-expand': 'The ON-condition is not rewritten in nested projections - provide an explicit ON-condition',
|
|
711
|
+
'secondary': 'The ON-condition is not rewritten due to multiple associations in this path - provide an explicit ON-condition',
|
|
712
|
+
},
|
|
713
|
+
'rewrite-not-projected': {
|
|
714
|
+
std: 'Projected association $(NAME) uses non-projected element $(ELEMREF)',
|
|
715
|
+
element: 'Projected association $(NAME) uses non-projected element $(ELEMREF) of $(ART)',
|
|
716
|
+
},
|
|
699
717
|
'type-unsupported-rewrite': {
|
|
700
718
|
std: 'Rewriting the ON-condition not supported here', // unused: merge with 'rewrite-not-supported'
|
|
701
719
|
'sub-element': 'Rewriting the ON-condition of unmanaged association in sub element is not supported'
|
|
@@ -745,6 +763,13 @@ const centralMessageTexts = {
|
|
|
745
763
|
'onCond': 'Unexpected $(KEYWORD) on an association/composition with ON-condition; $(KEYWORD) requires exactly one foreign key',
|
|
746
764
|
'targetAspect': 'Unexpected $(KEYWORD) on composition of aspect'
|
|
747
765
|
},
|
|
766
|
+
'type-expecting-service-target': {
|
|
767
|
+
std: 'Expecting service entity $(TARGET)',
|
|
768
|
+
ref: 'Expecting service entity $(TARGET); its element $(ID) referred to at line $(LINE), column $(COL) is not from an element with the same name in the provided model target',
|
|
769
|
+
key: 'Expecting service entity $(TARGET); its key element $(ID) is not from a key element with the same name in the provided model target',
|
|
770
|
+
missing: 'Expecting service entity $(TARGET); it does not have the key element $(ID) of the provided model target',
|
|
771
|
+
order: 'Expecting service entity $(TARGET); its key elements are in a different order than those of the provided model target',
|
|
772
|
+
},
|
|
748
773
|
|
|
749
774
|
'anno-builtin': 'Builtin types should not be annotated nor extended. Use custom type instead',
|
|
750
775
|
'ext-undefined-def': 'Artifact $(ART) has not been found',
|
|
@@ -980,6 +1005,13 @@ const centralMessageTexts = {
|
|
|
980
1005
|
foreignKeys: 'Foreign key $(ID) does not result from the query',
|
|
981
1006
|
},
|
|
982
1007
|
|
|
1008
|
+
// ID published! Used in stakeholder project; if renamed, add to oldMessageIds
|
|
1009
|
+
'redirected-to-complex': {
|
|
1010
|
+
std: 'Redirection involves the complex view $(ART); add an explicit ON-condition/foreign keys to redirection',
|
|
1011
|
+
target: 'The redirected target $(ART) is a complex view; add an explicit ON-condition/foreign keys to redirection',
|
|
1012
|
+
targetOp: 'The redirected target $(ART) is a complex view with $(KEYWORD); add an explicit ON-condition/foreign keys to redirection',
|
|
1013
|
+
},
|
|
1014
|
+
|
|
983
1015
|
'ref-sloppy-target': 'An entity or an aspect (not type) is expected here',
|
|
984
1016
|
|
|
985
1017
|
'ref-ambiguous': {
|
|
@@ -1109,6 +1141,11 @@ const centralMessageTexts = {
|
|
|
1109
1141
|
'odata-parameter-order': 'Unexpected mandatory after optional parameter',
|
|
1110
1142
|
'odata-key-recursive': 'Unexpected recursive key $(NAME)',
|
|
1111
1143
|
'odata-key-uuid-default-anno': 'Expected element of type $(TYPE) to be annotated with $(ANNO) when used as primary key in $(ID)',
|
|
1144
|
+
'odata-ignoring-param-default': {
|
|
1145
|
+
'std': 'Ignoring default value',
|
|
1146
|
+
'xpr': 'Ignoring unexpected expression as default value',
|
|
1147
|
+
'colitem': 'Ignoring unexpected default value for a structured or collection like parameter',
|
|
1148
|
+
},
|
|
1112
1149
|
// -----------------------------------------------------------------------------------
|
|
1113
1150
|
// All odata-anno MUST have a '$(ANNO)' parameter to indicate error location
|
|
1114
1151
|
// -----------------------------------------------------------------------------------
|
package/lib/base/messages.js
CHANGED
|
@@ -908,7 +908,7 @@ function transformArg( arg, r, args, texts ) {
|
|
|
908
908
|
return quoted( arg );
|
|
909
909
|
if (arg._artifact)
|
|
910
910
|
arg = arg._artifact;
|
|
911
|
-
|
|
911
|
+
while (arg._outer) // nested 'items'
|
|
912
912
|
arg = arg._outer;
|
|
913
913
|
if (args['#'] || args.member )
|
|
914
914
|
return shortArtName( arg );
|
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.
|
|
@@ -30,7 +21,6 @@ const availableBetaFlags = {
|
|
|
30
21
|
mapAssocToJoinCardinality: true, // only SAP HANA HEX engine supports it
|
|
31
22
|
enableUniversalCsn: true,
|
|
32
23
|
odataTerms: true,
|
|
33
|
-
optionalActionFunctionParameters: true, // not supported by runtime, yet.
|
|
34
24
|
effectiveCsn: true,
|
|
35
25
|
tenantVariable: true,
|
|
36
26
|
calcAssoc: true,
|
|
@@ -224,7 +214,6 @@ module.exports = {
|
|
|
224
214
|
availableBetaFlags,
|
|
225
215
|
isDeprecatedEnabled,
|
|
226
216
|
checkRemovedDeprecatedFlags,
|
|
227
|
-
queryOps,
|
|
228
217
|
forEachDefinition,
|
|
229
218
|
forEachMember,
|
|
230
219
|
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
|
|