@sap/cds-compiler 4.7.4 → 4.8.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 +47 -2
- package/bin/cdsc.js +15 -1
- package/bin/cdshi.js +13 -3
- package/doc/CHANGELOG_BETA.md +5 -1
- package/lib/api/main.js +61 -23
- package/lib/api/options.js +40 -0
- package/lib/base/builtins.js +89 -0
- package/lib/base/keywords.js +5 -1
- package/lib/base/location.js +91 -14
- package/lib/base/message-registry.js +50 -33
- package/lib/base/messages.js +71 -16
- package/lib/base/model.js +0 -2
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/elements.js +2 -1
- package/lib/checks/enricher.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +6 -22
- package/lib/compiler/assert-consistency.js +3 -5
- package/lib/compiler/builtins.js +0 -74
- package/lib/compiler/checks.js +61 -11
- package/lib/compiler/define.js +3 -3
- package/lib/compiler/extend.js +2 -2
- package/lib/compiler/index.js +9 -9
- package/lib/compiler/populate.js +13 -5
- package/lib/compiler/propagator.js +3 -0
- package/lib/compiler/resolve.js +6 -20
- package/lib/compiler/shared.js +1 -1
- package/lib/compiler/tweak-assocs.js +2 -2
- package/lib/compiler/utils.js +3 -3
- package/lib/compiler/{classes.js → xsn-model.js} +0 -16
- package/lib/edm/annotations/edmJson.js +7 -5
- package/lib/edm/annotations/genericTranslation.js +113 -55
- package/lib/edm/csn2edm.js +25 -9
- package/lib/edm/edm.js +3 -3
- package/lib/edm/edmInboundChecks.js +24 -5
- package/lib/edm/edmPreprocessor.js +46 -20
- package/lib/edm/edmUtils.js +3 -16
- package/lib/gen/Dictionary.json +9 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1941 -1850
- package/lib/json/csnVersion.js +7 -4
- package/lib/json/from-csn.js +8 -7
- package/lib/json/to-csn.js +12 -7
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/genericAntlrParser.js +9 -10
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +1 -1
- package/lib/main.d.ts +23 -0
- package/lib/main.js +8 -1
- package/lib/model/cloneCsn.js +15 -6
- package/lib/model/csnRefs.js +141 -35
- package/lib/model/csnUtils.js +1 -4
- package/lib/model/enrichCsn.js +1 -1
- package/lib/modelCompare/compare.js +106 -92
- package/lib/optionProcessor.js +23 -1
- package/lib/render/toCdl.js +3 -2
- package/lib/render/toHdbcds.js +4 -48
- package/lib/render/toSql.js +6 -3
- package/lib/transform/addTenantFields.js +58 -35
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/expansion.js +3 -0
- package/lib/transform/db/flattening.js +71 -46
- package/lib/transform/db/views.js +1 -4
- package/lib/transform/draft/odata.js +16 -17
- package/lib/transform/effective/main.js +6 -3
- package/lib/transform/effective/misc.js +18 -8
- package/lib/transform/effective/types.js +4 -3
- package/lib/transform/forOdata.js +8 -7
- package/lib/transform/forRelationalDB.js +103 -112
- package/lib/transform/odata/flattening.js +82 -44
- package/lib/transform/odata/toFinalBaseType.js +9 -25
- package/lib/transform/odata/typesExposure.js +28 -15
- package/lib/transform/parseExpr.js +0 -3
- package/lib/transform/transformUtils.js +12 -8
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/coreComputed.js +2 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -1
- package/package.json +2 -2
- package/share/messages/README.md +4 -0
- package/share/messages/anno-duplicate-unrelated-layer.md +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/def-duplicate-autoexposed.md +1 -1
- package/share/messages/extend-repeated-intralayer.md +3 -16
- package/share/messages/extend-unrelated-layer.md +1 -1
- package/share/messages/message-explanations.json +1 -0
- package/share/messages/redirected-to-ambiguous.md +1 -1
- package/share/messages/redirected-to-complex.md +1 -1
- package/share/messages/redirected-to-unrelated.md +1 -1
- package/share/messages/rewrite-not-supported.md +1 -1
- package/share/messages/syntax-expecting-unsigned-int.md +2 -2
- package/share/messages/type-missing-enum-value.md +59 -0
- package/share/messages/wildcard-excluding-one.md +1 -1
|
@@ -29,8 +29,9 @@ const tenantDef = {
|
|
|
29
29
|
'@cds.api.ignore': true, // and/or $generated: 'tenant' for the full Universal CSN?
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
function addTenantFields( csn, options ) {
|
|
33
|
-
const { error, throwWithError }
|
|
32
|
+
function addTenantFields( csn, options, messageFunctions ) {
|
|
33
|
+
const { error, throwWithError }
|
|
34
|
+
= messageFunctions ?? createMessageFunctions( options, 'tenant', csn );
|
|
34
35
|
const { tenantDiscriminator } = options;
|
|
35
36
|
const tenantName = tenantDiscriminator === true ? 'tenant' : tenantDiscriminator;
|
|
36
37
|
if (tenantName !== 'tenant') {
|
|
@@ -47,17 +48,21 @@ function addTenantFields( csn, options ) {
|
|
|
47
48
|
const { definitions } = csn;
|
|
48
49
|
if (!definitions)
|
|
49
50
|
return csn;
|
|
50
|
-
const {
|
|
51
|
+
const {
|
|
52
|
+
initDefinition,
|
|
53
|
+
artifactRef,
|
|
54
|
+
effectiveType,
|
|
55
|
+
msgLocations,
|
|
56
|
+
} = csnRefs( csn, true );
|
|
51
57
|
|
|
52
58
|
const typeCache = new WeakMap();
|
|
53
|
-
const csnPath = [
|
|
59
|
+
const csnPath = [ null ];
|
|
54
60
|
let independent;
|
|
55
61
|
let projection;
|
|
56
62
|
|
|
57
63
|
for (const name in definitions) {
|
|
58
|
-
const art =
|
|
59
|
-
|
|
60
|
-
csnPath[1] = name;
|
|
64
|
+
const art = initDefinition( name );
|
|
65
|
+
csnPath[0] = art;
|
|
61
66
|
independent = art[annoTenantIndep];
|
|
62
67
|
projection = art.query || art.projection && art;
|
|
63
68
|
|
|
@@ -68,9 +73,11 @@ function addTenantFields( csn, options ) {
|
|
|
68
73
|
handleElements( art );
|
|
69
74
|
if (projection)
|
|
70
75
|
traverseQuery( projection, null, null, handleQuery );
|
|
76
|
+
// Note: handleQuery sets csnPath[0]; store if needed afterwards
|
|
71
77
|
}
|
|
72
78
|
else if (!independent && independent != null) {
|
|
73
|
-
error( 'tenant-invalid-anno-value', csnPath
|
|
79
|
+
error( 'tenant-invalid-anno-value', msgLocations( csnPath ),
|
|
80
|
+
{ anno: annoTenantIndep, value: independent },
|
|
74
81
|
// eslint-disable-next-line max-len
|
|
75
82
|
'Can\'t add $(ANNO) with value $(VALUE) to a non-entity, which is always tenant-independent' );
|
|
76
83
|
}
|
|
@@ -88,16 +95,15 @@ function addTenantFields( csn, options ) {
|
|
|
88
95
|
// the cache of csnRefs):
|
|
89
96
|
for (const name in definitions) {
|
|
90
97
|
const art = definitions[name];
|
|
91
|
-
|
|
92
|
-
art.elements = { [tenantName]: { ...tenantDef }, ...art.elements };
|
|
93
|
-
// consider non-enumerable `elements` of subqueries if that is supported
|
|
98
|
+
addTenantFieldToArt(art, options);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
(csn.extensions || []).forEach( ( ext, idx ) => {
|
|
97
102
|
const tenant = ext.elements?.[tenantName];
|
|
98
103
|
const name = ext.annotate || ext.extend; // extend should not happen
|
|
99
104
|
if (tenant && isTenantDepEntity( definitions[name] )) {
|
|
100
|
-
error( 'tenant-unexpected-ext',
|
|
105
|
+
error( 'tenant-unexpected-ext',
|
|
106
|
+
msgLocations( [ 'extensions', idx, 'elements', 'tenant' ] ),
|
|
101
107
|
{ name: tenantName },
|
|
102
108
|
'Can\'t annotate element $(NAME) of a tenant-dependent entity' );
|
|
103
109
|
}
|
|
@@ -112,7 +118,7 @@ function addTenantFields( csn, options ) {
|
|
|
112
118
|
const names = art.includes
|
|
113
119
|
.filter( name => isTenantDepEntity( csn.definitions[name] ) );
|
|
114
120
|
if (names.length) {
|
|
115
|
-
error( 'tenant-invalid-include', csnPath, { names }, {
|
|
121
|
+
error( 'tenant-invalid-include', msgLocations( csnPath ), { names }, {
|
|
116
122
|
// eslint-disable-next-line max-len
|
|
117
123
|
std: 'Can\'t include the tenant-dependent entities $(NAMES) into a tenant-independent definition',
|
|
118
124
|
// eslint-disable-next-line max-len
|
|
@@ -125,12 +131,13 @@ function addTenantFields( csn, options ) {
|
|
|
125
131
|
function handleElements( art ) {
|
|
126
132
|
const { elements } = art;
|
|
127
133
|
if (elements[tenantName]) {
|
|
128
|
-
error( 'tenant-unexpected-element',
|
|
134
|
+
error( 'tenant-unexpected-element',
|
|
135
|
+
msgLocations( [ ...csnPath, 'elements', tenantName ] ),
|
|
129
136
|
{ name: tenantName, option: 'tenantDiscriminator' },
|
|
130
137
|
'Can\'t have entity with element $(NAME) when using option $(OPTION)' );
|
|
131
138
|
}
|
|
132
139
|
else if (!independent && !Object.values( elements ).some( e => e.key )) {
|
|
133
|
-
error( 'tenant-expecting-key', csnPath, {},
|
|
140
|
+
error( 'tenant-expecting-key', msgLocations( csnPath ), {},
|
|
134
141
|
'There must be a key in a tenant-dependent entity' );
|
|
135
142
|
}
|
|
136
143
|
else {
|
|
@@ -147,18 +154,16 @@ function addTenantFields( csn, options ) {
|
|
|
147
154
|
return;
|
|
148
155
|
|
|
149
156
|
if (query !== projection && !independent) {
|
|
150
|
-
error( 'tenant-unsupported-query', csnPath, {},
|
|
157
|
+
error( 'tenant-unsupported-query', msgLocations( csnPath ), {},
|
|
151
158
|
'Can\'t yet have tenant-dependent non-simple query entities' );
|
|
152
159
|
projection = null;
|
|
153
160
|
return;
|
|
154
161
|
}
|
|
155
162
|
|
|
156
|
-
if (query.projection)
|
|
157
|
-
csnPath.push( 'projection' );
|
|
158
|
-
else if (query.SELECT)
|
|
159
|
-
csnPath.push( 'query', 'SELECT' );
|
|
160
|
-
else
|
|
163
|
+
if (!query.projection && !query.SELECT)
|
|
161
164
|
return; // query.SET or query.join
|
|
165
|
+
csnPath[0] = query;
|
|
166
|
+
csnPath.push( query.SELECT ? 'SELECT' : 'projection' );
|
|
162
167
|
|
|
163
168
|
const select = query.SELECT || query.projection;
|
|
164
169
|
if (select.mixin)
|
|
@@ -177,7 +182,7 @@ function addTenantFields( csn, options ) {
|
|
|
177
182
|
else if (query !== projection && select.columns) {
|
|
178
183
|
checkColumnCasts( select.columns );
|
|
179
184
|
}
|
|
180
|
-
csnPath.length =
|
|
185
|
+
csnPath.length = 1;
|
|
181
186
|
}
|
|
182
187
|
|
|
183
188
|
function handleQuerySource( query ) {
|
|
@@ -185,7 +190,7 @@ function addTenantFields( csn, options ) {
|
|
|
185
190
|
const art = query.ref[0]; // yes, the base
|
|
186
191
|
if (csn.definitions[art][annoTenantIndep])
|
|
187
192
|
return true;
|
|
188
|
-
error( 'tenant-invalid-query-source', csnPath, { art, '#': independent }, {
|
|
193
|
+
error( 'tenant-invalid-query-source', msgLocations( csnPath ), { art, '#': independent }, {
|
|
189
194
|
std: 'Can\'t use a tenant-dependent query source $(ART) in a tenant-independent entity',
|
|
190
195
|
event: 'Can\'t use a tenant-dependent query source $(ART) in an event',
|
|
191
196
|
} );
|
|
@@ -194,12 +199,12 @@ function addTenantFields( csn, options ) {
|
|
|
194
199
|
if (query !== (projection.SELECT || projection.projection)?.from) // with `join`
|
|
195
200
|
return false;
|
|
196
201
|
if ((query.as || implicitAs( query.ref )) === tenantName) {
|
|
197
|
-
error( 'tenant-invalid-alias-name', csnPath,
|
|
202
|
+
error( 'tenant-invalid-alias-name', msgLocations( csnPath ),
|
|
198
203
|
{ name: tenantName, '#': (query.as ? 'std' : 'implicit') } );
|
|
199
204
|
}
|
|
200
205
|
const art = artifactRef.from( query );
|
|
201
206
|
if (art[annoTenantIndep]) {
|
|
202
|
-
error( 'tenant-expecting-tenant-source', csnPath, { art: query },
|
|
207
|
+
error( 'tenant-expecting-tenant-source', msgLocations( csnPath ), { art: query },
|
|
203
208
|
// TODO: better the final entity name of assoc navigation in FROM
|
|
204
209
|
// eslint-disable-next-line max-len
|
|
205
210
|
'Expecting the query source $(ART) to be tenant-dependent for a tenant-dependent query entity' );
|
|
@@ -212,7 +217,7 @@ function addTenantFields( csn, options ) {
|
|
|
212
217
|
for (const name in mixin) {
|
|
213
218
|
csnPath[csnPath.length - 1] = name;
|
|
214
219
|
if (name === tenantName && !independent)
|
|
215
|
-
error( 'tenant-invalid-alias-name', csnPath, { name, '#': 'mixin' } );
|
|
220
|
+
error( 'tenant-invalid-alias-name', msgLocations( csnPath ), { name, '#': 'mixin' } );
|
|
216
221
|
handleAssociations( mixin[name], null );
|
|
217
222
|
}
|
|
218
223
|
csnPath.length -= 2;
|
|
@@ -220,7 +225,7 @@ function addTenantFields( csn, options ) {
|
|
|
220
225
|
|
|
221
226
|
function checkExcluding( excludeList ) {
|
|
222
227
|
if (excludeList.includes( tenantName )) {
|
|
223
|
-
error( 'tenant-invalid-excluding', csnPath, { name: tenantName },
|
|
228
|
+
error( 'tenant-invalid-excluding', msgLocations( csnPath ), { name: tenantName },
|
|
224
229
|
'Can\'t exclude $(NAME) from the query source of a tenant-dependent entity' );
|
|
225
230
|
}
|
|
226
231
|
}
|
|
@@ -241,7 +246,7 @@ function addTenantFields( csn, options ) {
|
|
|
241
246
|
for (const col of columns) {
|
|
242
247
|
++csnPath[csnPath.length - 1];
|
|
243
248
|
if (col.expand || col.inline) {
|
|
244
|
-
error( 'tenant-unsupported-expand-inline', csnPath, {},
|
|
249
|
+
error( 'tenant-unsupported-expand-inline', msgLocations( csnPath ), {},
|
|
245
250
|
'Can\'t use expand/inline in a tenant-dependent entity' );
|
|
246
251
|
}
|
|
247
252
|
if (col.key != null) // yes, also with key: false
|
|
@@ -275,12 +280,20 @@ function addTenantFields( csn, options ) {
|
|
|
275
280
|
return null;
|
|
276
281
|
|
|
277
282
|
if (elem.target) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
283
|
+
const { target } = elem;
|
|
284
|
+
if (csn.definitions[target][annoTenantIndep]) {
|
|
285
|
+
if (!independent && isComposition( elem ))
|
|
286
|
+
error( 'tenant-invalid-composition', msgLocations( csnPath ), { target } );
|
|
281
287
|
}
|
|
282
|
-
else if (
|
|
283
|
-
|
|
288
|
+
else if (independent) {
|
|
289
|
+
if (target.endsWith( '.DraftAdministrativeData' ) && csnPath.length === 3 &&
|
|
290
|
+
csnPath[1] === 'elements' && csnPath[2] === 'DraftAdministrativeData') {
|
|
291
|
+
error( 'tenant-invalid-draft', msgLocations( csnPath ), {},
|
|
292
|
+
'A tenant-independent entity can\'t be draft-enabled' );
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
error( 'tenant-invalid-target', msgLocations( csnPath ), { target } );
|
|
296
|
+
}
|
|
284
297
|
}
|
|
285
298
|
}
|
|
286
299
|
else if (elem.type && (independent || !elem.elements && !elem.items)) {
|
|
@@ -290,10 +303,11 @@ function addTenantFields( csn, options ) {
|
|
|
290
303
|
if (independent) {
|
|
291
304
|
if (!dep || dep === 'Composition')
|
|
292
305
|
return true; // check elements (assocs could be redirected)
|
|
293
|
-
error( 'tenant-invalid-target', csnPath, { type: elem.type, '#': 'type' } );
|
|
306
|
+
error( 'tenant-invalid-target', msgLocations( csnPath ), { type: elem.type, '#': 'type' } );
|
|
294
307
|
}
|
|
295
308
|
else if (dep && dep !== 'dependent') {
|
|
296
|
-
error( 'tenant-invalid-composition', csnPath
|
|
309
|
+
error( 'tenant-invalid-composition', msgLocations( csnPath ),
|
|
310
|
+
{ type: elem.type, '#': 'type' } );
|
|
297
311
|
}
|
|
298
312
|
}
|
|
299
313
|
else {
|
|
@@ -398,6 +412,15 @@ function isTenantDepEntity( art ) {
|
|
|
398
412
|
return art?.kind === 'entity' && !art[annoTenantIndep];
|
|
399
413
|
}
|
|
400
414
|
|
|
415
|
+
function addTenantFieldToArt( art, options ) {
|
|
416
|
+
const tenantName = options.tenantDiscriminator === true ? 'tenant' : options.tenantDiscriminator;
|
|
417
|
+
|
|
418
|
+
if (isTenantDepEntity( art ))
|
|
419
|
+
art.elements = { [tenantName]: { ...tenantDef }, ...art.elements };
|
|
420
|
+
// consider non-enumerable `elements` of subqueries if that is supported
|
|
421
|
+
}
|
|
422
|
+
|
|
401
423
|
module.exports = {
|
|
402
424
|
addTenantFields,
|
|
425
|
+
addTenantFieldToArt,
|
|
403
426
|
};
|
|
@@ -58,6 +58,9 @@ function expandStructureReferences( csn, options, pathDelimiter, messageFunction
|
|
|
58
58
|
orderBy: (parent, name, orderBy, path) => {
|
|
59
59
|
parent.orderBy = expand(orderBy, path.concat('orderBy'));
|
|
60
60
|
},
|
|
61
|
+
list: (parent, name, list, path) => {
|
|
62
|
+
parent.list = expand(list, path.concat('list'));
|
|
63
|
+
},
|
|
61
64
|
};
|
|
62
65
|
|
|
63
66
|
// To not have a whole model loop for such a "small" thing, we kill all non-sql-backend relevant annotations here
|
|
@@ -1,38 +1,42 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
applyTransformations,
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
applyTransformations,
|
|
5
|
+
applyTransformationsOnNonDictionary,
|
|
6
|
+
cardinality2str,
|
|
7
|
+
copyAnnotations,
|
|
8
|
+
implicitAs,
|
|
9
|
+
isDeepEqual,
|
|
10
|
+
findAnnotationExpression,
|
|
7
11
|
} = require('../../model/csnUtils');
|
|
12
|
+
const { isBuiltinType, isMagicVariable } = require('../../base/builtins');
|
|
8
13
|
const transformUtils = require('../transformUtils');
|
|
9
14
|
const { csnRefs } = require('../../model/csnRefs');
|
|
10
15
|
const { setProp, isBetaEnabled } = require('../../base/model');
|
|
11
16
|
const { forEach } = require('../../utils/objectUtils');
|
|
12
17
|
const { transformExpression } = require('./applyTransformations');
|
|
13
18
|
const { cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
19
|
+
|
|
14
20
|
/**
|
|
15
|
-
* Strip off leading $self from refs where applicable
|
|
21
|
+
* Strip off leading $self from refs where applicable.
|
|
22
|
+
* Only relevant for HDBCDS, because handling of `$self` is not implemented there.
|
|
16
23
|
*
|
|
17
|
-
* @param {
|
|
24
|
+
* @param {object} parent
|
|
25
|
+
* @param {string} prop
|
|
26
|
+
* @param {CSN.Elements} elements
|
|
18
27
|
*/
|
|
19
|
-
function removeLeadingSelf(
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
ref
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}, /* only for kind entity and view */ /* do not go into .actions */
|
|
35
|
-
}, [], { skipIgnore: false, allowArtifact: artifact => (artifact.kind === 'entity'), skipDict: { actions: true } });
|
|
28
|
+
function removeLeadingSelf( parent, prop, elements ) {
|
|
29
|
+
for (const [ elementName, element ] of Object.entries(elements)) {
|
|
30
|
+
if (element.on) {
|
|
31
|
+
applyTransformationsOnNonDictionary(elements, elementName, {
|
|
32
|
+
ref: (root, name, ref) => {
|
|
33
|
+
// HDBCDS renderers seem to expect it to not be there...
|
|
34
|
+
if (ref[0] === '$self' && ref.length > 1 && !isMagicVariable(ref[1]) && ref[1] !== '$projection' && ref[1] !== '$self')
|
|
35
|
+
root.ref.shift();
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/**
|
|
@@ -79,34 +83,49 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
|
|
|
79
83
|
|
|
80
84
|
const ignoreOdataKinds = { aspect: 1, event: 1, type: 1 };
|
|
81
85
|
const replaceWithDummyKinds = { action: 1, function: 1, event: 1 };
|
|
86
|
+
const stripItems = options.transformation === 'hdbcds' || options.transformation === 'sql';
|
|
87
|
+
const removeItems = new Set();
|
|
82
88
|
applyTransformations(csn, {
|
|
83
89
|
type: (node, prop, type, path, parent, parentProp) => {
|
|
84
90
|
if (options.toOdata && node.kind && node.kind in ignoreOdataKinds)
|
|
85
91
|
return;
|
|
86
92
|
if (parentProp === 'cast') {
|
|
87
93
|
const e = csnUtils.getFinalTypeInfo(type, t => resolved.get(t)?.art || csnUtils.artifactRef(t));
|
|
94
|
+
if (e.items && stripItems)
|
|
95
|
+
removeItems.add(node);
|
|
88
96
|
if (!e || e.items || e.elements)
|
|
89
97
|
return;
|
|
90
98
|
}
|
|
91
99
|
if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
|
|
92
100
|
toFinalBaseType(node, resolved, true);
|
|
93
101
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
|
|
103
|
+
if (node.items && stripItems) {
|
|
104
|
+
removeItems.add(node);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
if (node.items) // items could have unresolved types
|
|
108
|
+
toFinalBaseType(node.items, resolved, true);
|
|
109
|
+
|
|
110
|
+
// structured types might not have the child-types replaced.
|
|
111
|
+
// Drill down to ensure this.
|
|
112
|
+
const nextElements = node.elements || node.items?.elements;
|
|
113
|
+
const stack = nextElements ? [ nextElements ] : [];
|
|
114
|
+
while (stack.length > 0) {
|
|
115
|
+
const elements = stack.pop();
|
|
116
|
+
for (const e of Object.values(elements)) {
|
|
117
|
+
toFinalBaseType(e, resolved, true);
|
|
118
|
+
if (stripItems && e.items) {
|
|
119
|
+
removeItems.add(e);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
if (!options.toOdata && e.items) // items could have unresolved types
|
|
123
|
+
toFinalBaseType(e.items, resolved, true);
|
|
124
|
+
const next = e.elements || e.items?.elements;
|
|
125
|
+
if (next)
|
|
126
|
+
stack.push(next);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
110
129
|
}
|
|
111
130
|
}
|
|
112
131
|
|
|
@@ -115,6 +134,7 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
|
|
|
115
134
|
removeLocalized(node);
|
|
116
135
|
}
|
|
117
136
|
},
|
|
137
|
+
items: node => removeItems.add(node),
|
|
118
138
|
}, [ (definitions, artifactName, artifact) => {
|
|
119
139
|
// Replace events, actions and functions with simple dummies - they don't have effect on forRelationalDB stuff
|
|
120
140
|
// and that way they contain no references and don't hurt.
|
|
@@ -123,6 +143,8 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
|
|
|
123
143
|
// TODO:factor out somewhere else
|
|
124
144
|
if (!options.toOdata && artifact.kind in replaceWithDummyKinds) {
|
|
125
145
|
const dummy = { kind: artifact.kind };
|
|
146
|
+
if (artifact.kind === 'event')
|
|
147
|
+
dummy.elements = {}; // events must be structured for recompilation
|
|
126
148
|
if (artifact.$location)
|
|
127
149
|
setProp(dummy, '$location', artifact.$location);
|
|
128
150
|
|
|
@@ -131,6 +153,13 @@ function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDe
|
|
|
131
153
|
// TODO: skipDict options as default function arguments not via Object.assign
|
|
132
154
|
} ], iterateOptions);
|
|
133
155
|
|
|
156
|
+
// no support for array-of - turn into CLOB/Text
|
|
157
|
+
for (const node of removeItems) {
|
|
158
|
+
node.type = 'cds.LargeString';
|
|
159
|
+
delete node.items;
|
|
160
|
+
}
|
|
161
|
+
removeItems.clear();
|
|
162
|
+
|
|
134
163
|
|
|
135
164
|
/**
|
|
136
165
|
* OData V4 only:
|
|
@@ -439,17 +468,13 @@ function linkForeignKeyAnnotationExtensionsToAssociation( csn, options ) {
|
|
|
439
468
|
*/
|
|
440
469
|
function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFunctions, pathDelimiter, flattenKeyRefs, csnUtils, iterateOptions = {} ) {
|
|
441
470
|
const { error, warning } = messageFunctions;
|
|
442
|
-
const {
|
|
471
|
+
const { inspectRef, isStructured } = csnUtils;
|
|
443
472
|
const { flattenStructStepsInRef, flattenStructuredElement } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
|
|
444
473
|
if (flattenKeyRefs) {
|
|
445
474
|
applyTransformations(csn, {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
// replace foreign keys that are managed associations by their respective foreign keys
|
|
450
|
-
flattenFKs(element, elementName, [ ...path, 'elements', elementName ]);
|
|
451
|
-
}
|
|
452
|
-
});
|
|
475
|
+
keys: (element, prop, keys, path) => {
|
|
476
|
+
// replace foreign keys that are managed associations by their respective foreign keys
|
|
477
|
+
flattenFKs(element, path.at(-1), path);
|
|
453
478
|
},
|
|
454
479
|
}, [], Object.assign({
|
|
455
480
|
skipIgnore: false,
|
|
@@ -62,10 +62,9 @@ function usesMixinAssociation( query, association, associationName ) {
|
|
|
62
62
|
* @param {CSN.Model} csn
|
|
63
63
|
* @param {CSN.Options} options
|
|
64
64
|
* @param {{error: Function, info: Function}} messageFunctions
|
|
65
|
-
* @param {Function} transformCommon For the time being: Pass from outside
|
|
66
65
|
* @returns {(query: CSN.Query, artifact: CSN.Artifact, artName: string, path: CSN.Path) => void} Transformer function for views
|
|
67
66
|
*/
|
|
68
|
-
function getViewTransformer( csn, options, messageFunctions
|
|
67
|
+
function getViewTransformer( csn, options, messageFunctions ) {
|
|
69
68
|
const csnUtils = getUtils(csn);
|
|
70
69
|
const {
|
|
71
70
|
get$combined, isAssocOrComposition,
|
|
@@ -237,8 +236,6 @@ function getViewTransformer( csn, options, messageFunctions, transformCommon ) {
|
|
|
237
236
|
// Copy the association element to the MIXIN clause under its alias name
|
|
238
237
|
// Needs to be a deep copy, as we transform the on-condition
|
|
239
238
|
const mixinElem = cloneCsnNonDict(elem, options);
|
|
240
|
-
// Perform common transformations on the newly generated MIXIN element (won't be reached otherwise)
|
|
241
|
-
transformCommon(mixinElem, mixinElemName);
|
|
242
239
|
|
|
243
240
|
if (query.SELECT && !query.SELECT.mixin)
|
|
244
241
|
query.SELECT.mixin = Object.create(null);
|
|
@@ -80,9 +80,24 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
80
80
|
*/
|
|
81
81
|
function generateDraftForOdata( artifact, artifactName, rootArtifact ) {
|
|
82
82
|
// Nothing to do if already draft-enabled (composition traversal may have circles)
|
|
83
|
-
if (
|
|
83
|
+
if ((artifact['@Common.DraftRoot.PreparationAction'] || artifact['@Common.DraftNode.PreparationAction']) &&
|
|
84
|
+
artifact.actions && artifact.actions.draftPrepare)
|
|
84
85
|
return;
|
|
85
86
|
|
|
87
|
+
const draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');
|
|
88
|
+
assignAction(draftPrepare, artifact);
|
|
89
|
+
// Generate the actions into the draft-enabled artifact (only draft roots can be activated/edited)
|
|
90
|
+
|
|
91
|
+
// action draftPrepare (SideEffectsQualifier: String) return <artifact>;
|
|
92
|
+
if (artifact === rootArtifact) {
|
|
93
|
+
// action draftActivate() return <artifact>;
|
|
94
|
+
const draftActivate = createAction('draftActivate', artifactName);
|
|
95
|
+
assignAction(draftActivate, artifact);
|
|
96
|
+
|
|
97
|
+
// action draftEdit (PreserveChanges: Boolean) return <artifact>;
|
|
98
|
+
const draftEdit = createAction('draftEdit', artifactName, 'PreserveChanges', 'cds.Boolean');
|
|
99
|
+
assignAction(draftEdit, artifact);
|
|
100
|
+
}
|
|
86
101
|
|
|
87
102
|
// Generate the DraftAdministrativeData projection into the service, unless there is already one
|
|
88
103
|
// @ts-ignore
|
|
@@ -195,22 +210,6 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
195
210
|
});
|
|
196
211
|
}
|
|
197
212
|
}
|
|
198
|
-
|
|
199
|
-
// Generate the actions into the draft-enabled artifact (only draft roots can be activated/edited)
|
|
200
|
-
|
|
201
|
-
// action draftPrepare (SideEffectsQualifier: String) return <artifact>;
|
|
202
|
-
const draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');
|
|
203
|
-
assignAction(draftPrepare, artifact);
|
|
204
|
-
|
|
205
|
-
if (artifact === rootArtifact) {
|
|
206
|
-
// action draftActivate() return <artifact>;
|
|
207
|
-
const draftActivate = createAction('draftActivate', artifactName);
|
|
208
|
-
assignAction(draftActivate, artifact);
|
|
209
|
-
|
|
210
|
-
// action draftEdit (PreserveChanges: Boolean) return <artifact>;
|
|
211
|
-
const draftEdit = createAction('draftEdit', artifactName, 'PreserveChanges', 'cds.Boolean');
|
|
212
|
-
assignAction(draftEdit, artifact);
|
|
213
|
-
}
|
|
214
213
|
}
|
|
215
214
|
}
|
|
216
215
|
|
|
@@ -38,7 +38,7 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
38
38
|
messageFunctions.setModel(csn);
|
|
39
39
|
|
|
40
40
|
const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, messageFunctions, '_');
|
|
41
|
-
queries.projectionToSELECTAndAddColumns(csn);
|
|
41
|
+
const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
|
|
42
42
|
|
|
43
43
|
let csnUtils = getUtils(csn, 'init-all');
|
|
44
44
|
|
|
@@ -63,7 +63,7 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
63
63
|
// Expand a structured thing in: keys, columns, order by, group by
|
|
64
64
|
expansion.expandStructureReferences(csn, options, '_', messageFunctions, csnUtils);
|
|
65
65
|
|
|
66
|
-
const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils);
|
|
66
|
+
const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils, options);
|
|
67
67
|
|
|
68
68
|
// Remove properties attached by validator - they do not "grow" as the model grows.
|
|
69
69
|
cleanup();
|
|
@@ -81,7 +81,10 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
81
81
|
associations.transformBacklinks(csn, options, csnUtils, messageFunctions);
|
|
82
82
|
generateDrafts(csn, options, '_', messageFunctions);
|
|
83
83
|
misc.attachPersistenceName(csn, options, csnUtils);
|
|
84
|
-
misc.removeDefinitionsAndProperties(csn);
|
|
84
|
+
misc.removeDefinitionsAndProperties(csn, options);
|
|
85
|
+
|
|
86
|
+
if (!options.resolveProjections)
|
|
87
|
+
redoProjections.forEach(fn => fn());
|
|
85
88
|
|
|
86
89
|
messageFunctions.throwWithError();
|
|
87
90
|
|
|
@@ -14,9 +14,11 @@ function attachPersistenceName( csn, options, csnUtils ) {
|
|
|
14
14
|
const { addStringAnnotationTo } = csnUtils;
|
|
15
15
|
|
|
16
16
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
17
|
-
|
|
17
|
+
if (artifact.kind === 'entity') {
|
|
18
|
+
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
forEachMemberRecursively(artifact, (member, memberName) => addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member), [ 'definitions', artifactName ]);
|
|
21
|
+
}
|
|
20
22
|
});
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -39,20 +41,28 @@ function killProp( parent, prop ) {
|
|
|
39
41
|
* - includes
|
|
40
42
|
* - localized
|
|
41
43
|
* @param {CSN.Model} csn
|
|
44
|
+
* @param {CSN.Options} options
|
|
42
45
|
* @todo Callback-like architecture and merge with persistence name?
|
|
43
46
|
*/
|
|
44
|
-
function _removeDefinitionsAndProperties( csn ) {
|
|
47
|
+
function _removeDefinitionsAndProperties( csn, options ) {
|
|
45
48
|
const killers = {
|
|
46
49
|
$ignore: (a, b, c, path, parentParent) => {
|
|
47
50
|
const tail = path[path.length - 1];
|
|
48
51
|
delete parentParent[tail];
|
|
49
52
|
},
|
|
50
53
|
kind: (artifact, a, b, path) => {
|
|
51
|
-
if (artifact.kind === 'aspect' || artifact.kind === 'type')
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
if (artifact.kind === 'aspect' || artifact.kind === 'type') {
|
|
55
|
+
if (artifact.elements || artifact.items || options.resolveSimpleTypes)
|
|
56
|
+
delete csn.definitions[path[1]];
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
if (artifact.kind === 'event') {
|
|
60
|
+
delete artifact.projection;
|
|
61
|
+
delete artifact.query;
|
|
62
|
+
}
|
|
63
|
+
if (artifact['@cds.persistence.skip'] === 'if-unused')
|
|
64
|
+
artifact['@cds.persistence.skip'] = false;
|
|
65
|
+
}
|
|
56
66
|
},
|
|
57
67
|
// Still used in flattenStructuredElements - in db/flattening.js
|
|
58
68
|
_flatElementNameWithDots: killProp,
|
|
@@ -17,10 +17,11 @@ const { cloneCsnDict, cloneCsnNonDict } = require('../../model/cloneCsn');
|
|
|
17
17
|
* @todo What about annotations on the type?
|
|
18
18
|
* @param {CSN.Model} csn will be transformed
|
|
19
19
|
* @param {object} csnUtils
|
|
20
|
+
* @param {CSN.Options} options
|
|
20
21
|
* @returns {Function} Callback to resolve things (actions and their returns) later - as for them, $self would lead to unresolvable constructs at this point
|
|
21
22
|
* so we can call this callback after flattening is done - then we can safely resolve their types.
|
|
22
23
|
*/
|
|
23
|
-
function resolveTypes( csn, csnUtils ) {
|
|
24
|
+
function resolveTypes( csn, csnUtils, options ) {
|
|
24
25
|
const { getFinalTypeInfo } = csnUtils;
|
|
25
26
|
const later = [];
|
|
26
27
|
applyTransformations(csn, {
|
|
@@ -51,7 +52,7 @@ function resolveTypes( csn, csnUtils ) {
|
|
|
51
52
|
*
|
|
52
53
|
* Drill down into .elements and .items
|
|
53
54
|
*
|
|
54
|
-
* @param {object} parent Object with a .type
|
|
55
|
+
* @param {object} parent Object with a .type property
|
|
55
56
|
*/
|
|
56
57
|
function resolveType( parent ) {
|
|
57
58
|
// TODO: I assume there can be cases with a type ref but still having .elements already? Subelement anno?
|
|
@@ -67,7 +68,7 @@ function resolveTypes( csn, csnUtils ) {
|
|
|
67
68
|
parent.items = cloneCsnNonDict(final.items);
|
|
68
69
|
delete parent.type;
|
|
69
70
|
}
|
|
70
|
-
else if (final?.type) {
|
|
71
|
+
else if (final?.type && (options.resolveSimpleTypes || parent.type.ref?.length > 1)) {
|
|
71
72
|
forEachKey(final, (key) => { // copy `type` + properties (default, etc.)
|
|
72
73
|
if (parent[key] === undefined || key === 'type')
|
|
73
74
|
parent[key] = final[key];
|