@sap/cds-compiler 5.0.6 → 5.1.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 +35 -0
- package/bin/cdsc.js +34 -8
- package/bin/cdshi.js +2 -1
- package/lib/api/main.js +10 -1
- package/lib/api/options.js +2 -3
- package/lib/base/message-registry.js +14 -0
- package/lib/base/messages.js +2 -1
- package/lib/base/meta.js +10 -0
- package/lib/base/optionProcessorHelper.js +11 -0
- package/lib/checks/dbFeatureFlags.js +5 -0
- package/lib/compiler/assert-consistency.js +1 -0
- package/lib/compiler/define.js +1 -1
- package/lib/compiler/extend.js +43 -7
- package/lib/compiler/index.js +4 -4
- package/lib/compiler/populate.js +58 -11
- package/lib/compiler/propagator.js +9 -4
- package/lib/compiler/resolve.js +115 -79
- package/lib/compiler/shared.js +2 -1
- package/lib/compiler/tweak-assocs.js +29 -5
- package/lib/edm/edm.js +8 -0
- package/lib/edm/edmPreprocessor.js +7 -3
- package/lib/gen/Dictionary.json +37 -0
- package/lib/json/to-csn.js +2 -0
- package/lib/main.js +2 -5
- package/lib/model/cloneCsn.js +1 -0
- package/lib/model/csnRefs.js +2 -1
- package/lib/model/csnUtils.js +0 -12
- package/lib/model/revealInternalProperties.js +0 -1
- package/lib/modelCompare/compare.js +12 -10
- package/lib/optionProcessor.js +2 -0
- package/lib/render/toCdl.js +8 -7
- package/lib/render/toHdbcds.js +1 -2
- package/lib/render/toSql.js +44 -8
- package/lib/transform/db/backlinks.js +20 -5
- package/lib/transform/db/killAnnotations.js +3 -0
- package/lib/transform/db/processSqlServices.js +63 -0
- package/lib/transform/draft/odata.js +6 -1
- package/lib/transform/forRelationalDB.js +9 -0
- package/lib/utils/file.js +77 -4
- package/package.json +1 -1
package/lib/compiler/resolve.js
CHANGED
|
@@ -67,6 +67,7 @@ const {
|
|
|
67
67
|
} = require('./utils');
|
|
68
68
|
|
|
69
69
|
const detectCycles = require('./cycle-detector');
|
|
70
|
+
const { CompilerAssertion } = require('../base/error');
|
|
70
71
|
|
|
71
72
|
const $location = Symbol.for( 'cds.$location' );
|
|
72
73
|
|
|
@@ -90,7 +91,6 @@ function resolve( model ) {
|
|
|
90
91
|
resolvePath,
|
|
91
92
|
resolveDefinitionName,
|
|
92
93
|
traverseExpr,
|
|
93
|
-
createRemainingAnnotateStatements,
|
|
94
94
|
effectiveType,
|
|
95
95
|
getOrigin,
|
|
96
96
|
getInheritedProp,
|
|
@@ -131,8 +131,6 @@ function resolve( model ) {
|
|
|
131
131
|
resolveDefinitionName( model.sources[name].namespace );
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
// create “super” ANNOTATE statements for annotations on unknown artifacts:
|
|
135
|
-
createRemainingAnnotateStatements();
|
|
136
134
|
// report cyclic dependencies:
|
|
137
135
|
detectCycles( model.definitions, ( user, art, location, semanticLoc ) => {
|
|
138
136
|
if (location) {
|
|
@@ -158,68 +156,102 @@ function resolve( model ) {
|
|
|
158
156
|
// Phase 2+3: calculate propagated KEYs
|
|
159
157
|
//--------------------------------------------------------------------------
|
|
160
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Set `_projection` links in navigation elements and creates $navElement hierarchy.
|
|
161
|
+
*
|
|
162
|
+
* @param {XSN.Artifact} view
|
|
163
|
+
*/
|
|
161
164
|
function setNavigationProjections( view ) {
|
|
162
165
|
if (!view.$queries)
|
|
163
166
|
return;
|
|
164
167
|
for (const query of view.$queries) {
|
|
165
|
-
|
|
166
|
-
|
|
168
|
+
// traversing sub-elements not necessary, since we're in a view
|
|
169
|
+
// TODO: Handle expand.
|
|
170
|
+
forEachGeneric( query, 'elements', function navProjectionsForElement( elem ) {
|
|
171
|
+
if (!elem._origin || elem.expand || !elem.value?.path)
|
|
167
172
|
return;
|
|
168
173
|
// TODO: what about elements where _origin is set without value?
|
|
169
174
|
// TODO: or should we push elems with `expand` sibling to extra list for
|
|
170
175
|
// better messages? (Whatever that means exactly.)
|
|
171
|
-
const nav = pathNavigation( elem.value );
|
|
172
|
-
const { path } = elem.value;
|
|
173
|
-
|
|
174
|
-
if (elem._pathHead?.kind === '$inline' && path.length === 1) {
|
|
175
|
-
const item = path[0];
|
|
176
|
-
const hpath = elem._pathHead.value?.path;
|
|
177
|
-
const head = hpath?.length === 1 && hpath[0]._navigation;
|
|
178
|
-
// Alias .{ elem } - but not with Alias.{ $magic }: consider for ON-rewrite
|
|
179
|
-
if (head?.kind === '$tableAlias' && item.id.charAt(0) !== '$')
|
|
180
|
-
pushLink( head.elements[item.id], '_projections', elem );
|
|
181
|
-
}
|
|
182
|
-
else if (nav.navigation) { // not set for $self.…
|
|
183
|
-
// Path could start with table alias; get start index
|
|
184
|
-
let index = path.indexOf(nav.item);
|
|
185
|
-
if (index === -1)
|
|
186
|
-
return; // should not happen
|
|
187
176
|
|
|
188
|
-
|
|
189
|
-
if (
|
|
190
|
-
|
|
177
|
+
if (elem._pathHead) {
|
|
178
|
+
if (elem._pathHead?.kind !== '$inline')
|
|
179
|
+
// we're traversing top-level elements of the query; other _pathHead kinds can't happen
|
|
180
|
+
throw new CompilerAssertion('found unexpected "expand", but expected "inline"');
|
|
191
181
|
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const step = path[index];
|
|
197
|
-
if (!step?.id || step.where || step.args)
|
|
198
|
-
break;
|
|
199
|
-
if (!navItem.elements?.[step.id]) {
|
|
200
|
-
const elements = navItem._origin?.elements ||
|
|
201
|
-
navItem._origin?.target?._artifact?.elements;
|
|
202
|
-
if (!elements)
|
|
203
|
-
break;
|
|
204
|
-
// Only link available path steps (navigation tree).
|
|
205
|
-
const origin = elements[step.id];
|
|
206
|
-
const member = linkToOrigin( origin, step.id, navItem, 'elements',
|
|
207
|
-
navItem.path?.location, true );
|
|
208
|
-
member.$inferred = 'expanded';
|
|
209
|
-
member.kind = '$navElement';
|
|
210
|
-
}
|
|
211
|
-
navItem = navItem.elements[step.id];
|
|
212
|
-
setLink( step, '_navigation', navItem );
|
|
213
|
-
++index;
|
|
182
|
+
if (!isPathBreakout( elem.value )) {
|
|
183
|
+
const fullPath = columnParentPath( elem );
|
|
184
|
+
if (fullPath)
|
|
185
|
+
setNavigationProjectionsForElementRef({ path: fullPath }, elem);
|
|
214
186
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
setNavigationProjectionsForElementRef( elem.value, elem );
|
|
218
190
|
}
|
|
219
191
|
} );
|
|
220
192
|
}
|
|
221
193
|
}
|
|
222
194
|
|
|
195
|
+
function setNavigationProjectionsForElementRef( ref, elem ) {
|
|
196
|
+
const { path } = ref;
|
|
197
|
+
const nav = pathNavigation( ref );
|
|
198
|
+
if (nav.navigation) { // not set for $self.…
|
|
199
|
+
// Path could start with table alias; get start index
|
|
200
|
+
let index = path.indexOf(nav.item);
|
|
201
|
+
if (index === -1)
|
|
202
|
+
return; // should not happen
|
|
203
|
+
|
|
204
|
+
let navItem = nav.navigation;
|
|
205
|
+
if (!nav.item._navigation) // first non-table-alias
|
|
206
|
+
setLink( nav.item, '_navigation', navItem );
|
|
207
|
+
|
|
208
|
+
if (path[index].where || path[index].args)
|
|
209
|
+
return;
|
|
210
|
+
++index;
|
|
211
|
+
while (navItem && index < path.length) {
|
|
212
|
+
const step = path[index];
|
|
213
|
+
if (!step?.id || step.where || step.args)
|
|
214
|
+
break;
|
|
215
|
+
if (!navItem.elements?.[step.id]) {
|
|
216
|
+
const elements = navItem._origin?.elements ||
|
|
217
|
+
navItem._origin?.target?._artifact?.elements;
|
|
218
|
+
if (!elements)
|
|
219
|
+
break;
|
|
220
|
+
// Only link available path steps (navigation tree).
|
|
221
|
+
const origin = elements[step.id];
|
|
222
|
+
const member = linkToOrigin( origin, step.id, navItem, 'elements',
|
|
223
|
+
navItem.path?.location, true );
|
|
224
|
+
member.$inferred = 'expanded';
|
|
225
|
+
member.kind = '$navElement';
|
|
226
|
+
}
|
|
227
|
+
navItem = navItem.elements[step.id];
|
|
228
|
+
setLink( step, '_navigation', navItem );
|
|
229
|
+
++index;
|
|
230
|
+
}
|
|
231
|
+
// Last path step, if found, is a simple projection
|
|
232
|
+
if (index === path.length && navItem)
|
|
233
|
+
pushLink( navItem, '_projections', elem );
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function columnParentPath( elem ) {
|
|
238
|
+
if (!elem._pathHead || !elem.value?.path || isPathBreakout( elem.value ))
|
|
239
|
+
return elem.value?.path;
|
|
240
|
+
|
|
241
|
+
const fullPath = [ ...elem.value.path ];
|
|
242
|
+
let pathHead = elem._pathHead;
|
|
243
|
+
while (pathHead) {
|
|
244
|
+
if (pathHead.kind !== '$inline' || !pathHead.value?.path ||
|
|
245
|
+
isPathBreakout( pathHead.value )) {
|
|
246
|
+
// path breakout for e.g. `$self.{ foo }`, `1 as a .{ foo }`
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
fullPath.unshift(...pathHead.value.path);
|
|
250
|
+
pathHead = pathHead._pathHead;
|
|
251
|
+
}
|
|
252
|
+
return fullPath;
|
|
253
|
+
}
|
|
254
|
+
|
|
223
255
|
function propagateKeyProps( view ) {
|
|
224
256
|
// Second argument true ensure that `key` is only propagated along simple
|
|
225
257
|
// view, i.e. ref or subquery in FROM, not UNION or JOIN.
|
|
@@ -256,9 +288,9 @@ function resolve( model ) {
|
|
|
256
288
|
function inheritedSourceKeyProp( { value, _pathHead } ) {
|
|
257
289
|
if (!value || !value.path)
|
|
258
290
|
return null;
|
|
259
|
-
const nav = pathNavigation( value );
|
|
291
|
+
const nav = !_pathHead && pathNavigation( value );
|
|
260
292
|
const item = value.path[value.path.length - 1];
|
|
261
|
-
if (nav
|
|
293
|
+
if (nav?.navigation && nav.item === item)
|
|
262
294
|
return item._artifact?.key;
|
|
263
295
|
if (value.path.length !== 1 || _pathHead?.kind !== '$inline')
|
|
264
296
|
return null;
|
|
@@ -282,13 +314,11 @@ function resolve( model ) {
|
|
|
282
314
|
const toMany = withAssociation( from, targetMaxNotOne, true );
|
|
283
315
|
if (toMany) {
|
|
284
316
|
propagateKeys = false;
|
|
285
|
-
info( 'query-from-many', [ toMany.location, query ], { art: toMany },
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
element: 'Key properties are not propagated because a to-many association $(MEMBER) of $(ART) is selected',
|
|
291
|
-
} );
|
|
317
|
+
info( 'query-from-many', [ toMany.location, query ], { art: toMany }, {
|
|
318
|
+
std: 'Key properties are not propagated because a to-many association $(ART) is selected',
|
|
319
|
+
// eslint-disable-next-line max-len
|
|
320
|
+
element: 'Key properties are not propagated because a to-many association $(MEMBER) of $(ART) is selected',
|
|
321
|
+
} );
|
|
292
322
|
}
|
|
293
323
|
// Check that all keys from the source are projected:
|
|
294
324
|
const notProjected = []; // we actually push to the array
|
|
@@ -298,7 +328,7 @@ function resolve( model ) {
|
|
|
298
328
|
if (nav.$duplicates)
|
|
299
329
|
continue;
|
|
300
330
|
const { key } = nav._origin;
|
|
301
|
-
if (key
|
|
331
|
+
if (key?.val && !nav._projections?.length)
|
|
302
332
|
notProjected.push( nav.name.id );
|
|
303
333
|
}
|
|
304
334
|
if (notProjected.length) {
|
|
@@ -312,9 +342,12 @@ function resolve( model ) {
|
|
|
312
342
|
// Check that there is no to-many assoc used in select item:
|
|
313
343
|
for (const name in query.elements) {
|
|
314
344
|
const elem = query.elements[name];
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
345
|
+
|
|
346
|
+
if (!elem.$inferred && elem.value?.path) {
|
|
347
|
+
const path = elem._pathHead ? columnParentPath( elem ) : elem.value.path;
|
|
348
|
+
if (testExpr({ path }, selectTest, () => false, elem))
|
|
349
|
+
propagateKeys = false;
|
|
350
|
+
}
|
|
318
351
|
}
|
|
319
352
|
return propagateKeys;
|
|
320
353
|
|
|
@@ -322,15 +355,13 @@ function resolve( model ) {
|
|
|
322
355
|
const art = withAssociation( expr, targetMaxNotOne );
|
|
323
356
|
if (art) {
|
|
324
357
|
// ID published! Used in stakeholder project; if renamed, add to oldMessageIds
|
|
325
|
-
info( 'query-navigate-many', [ art.location, user || query ], { art },
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
alias: 'Navigating along to-many mixin association $(MEMBER) - key properties are not propagated',
|
|
333
|
-
} );
|
|
358
|
+
info( 'query-navigate-many', [ art.location, user || query ], { art }, {
|
|
359
|
+
std: 'Navigating along to-many association $(ART) - key properties are not propagated',
|
|
360
|
+
// eslint-disable-next-line max-len
|
|
361
|
+
element: 'Navigating along to-many association $(MEMBER) of $(ART) - key properties are not propagated',
|
|
362
|
+
// eslint-disable-next-line max-len
|
|
363
|
+
alias: 'Navigating along to-many mixin association $(MEMBER) - key properties are not propagated',
|
|
364
|
+
} );
|
|
334
365
|
}
|
|
335
366
|
return art;
|
|
336
367
|
}
|
|
@@ -511,14 +542,12 @@ function resolve( model ) {
|
|
|
511
542
|
else if (obj.targetAspect && obj.targetAspect.elements) { // silent dependencies
|
|
512
543
|
forEachGeneric( obj.targetAspect, 'elements', elem => dependsOnSilent( art, elem ) );
|
|
513
544
|
}
|
|
545
|
+
|
|
514
546
|
if (obj.foreignKeys) { // silent dependencies
|
|
515
547
|
// Avoid strange ref-cyclic if managed composition is key (check comes later)
|
|
516
|
-
//
|
|
517
|
-
if (obj.$inferred !== 'aspect-composition')
|
|
518
|
-
forEachGeneric( obj, 'foreignKeys', (elem)
|
|
519
|
-
dependsOnSilent( art, elem );
|
|
520
|
-
} );
|
|
521
|
-
}
|
|
548
|
+
// Done by addImplicitForeignKeys() for implicit keys.
|
|
549
|
+
if (!art.foreignKeys?.[$inferred] && obj.$inferred !== 'aspect-composition')
|
|
550
|
+
forEachGeneric( obj, 'foreignKeys', elem => dependsOnSilent( art, elem ) );
|
|
522
551
|
addForeignKeyNavigations( art );
|
|
523
552
|
}
|
|
524
553
|
|
|
@@ -1552,8 +1581,6 @@ function resolve( model ) {
|
|
|
1552
1581
|
function pathNavigation( ref ) {
|
|
1553
1582
|
// currently, indirectly projectable elements are not included - we might
|
|
1554
1583
|
// keep it this way! If we want them to be included - be aware: cycles
|
|
1555
|
-
if (!ref._artifact)
|
|
1556
|
-
return {};
|
|
1557
1584
|
let item = ref.path && ref.path[0];
|
|
1558
1585
|
const root = item && item._navigation;
|
|
1559
1586
|
if (!root)
|
|
@@ -1570,4 +1597,13 @@ function pathNavigation( ref ) {
|
|
|
1570
1597
|
return { navigation: root.elements?.[item.id], item, tableAlias: root };
|
|
1571
1598
|
}
|
|
1572
1599
|
|
|
1600
|
+
function isPathBreakout( ref ) {
|
|
1601
|
+
if (!ref.path?.[0])
|
|
1602
|
+
return false;
|
|
1603
|
+
if (ref.scope === 'param')
|
|
1604
|
+
return true;
|
|
1605
|
+
const nav = (ref.path[0]._navigation || ref.path[0]._artifact);
|
|
1606
|
+
return nav && (nav.kind === '$self' || ref.path[0].id.charAt(0) === '$');
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1573
1609
|
module.exports = resolve;
|
package/lib/compiler/shared.js
CHANGED
|
@@ -1738,7 +1738,8 @@ function fns( model ) {
|
|
|
1738
1738
|
* @param {string} msgId
|
|
1739
1739
|
* @param {any} location
|
|
1740
1740
|
* @param {object[]} valid
|
|
1741
|
-
* @param
|
|
1741
|
+
* @param {object} [textParams]
|
|
1742
|
+
* @param {object} [semantics]
|
|
1742
1743
|
*/
|
|
1743
1744
|
function signalNotFound( msgId, location, valid, textParams, semantics ) {
|
|
1744
1745
|
if (location.$notFound) // TODO: still necessary?
|
|
@@ -38,6 +38,9 @@ function tweakAssocs( model ) {
|
|
|
38
38
|
checkOnCondition,
|
|
39
39
|
effectiveType,
|
|
40
40
|
getOrigin,
|
|
41
|
+
extendForeignKeys,
|
|
42
|
+
createRemainingAnnotateStatements,
|
|
43
|
+
mergeSpecifiedForeignKeys,
|
|
41
44
|
} = model.$functions;
|
|
42
45
|
|
|
43
46
|
Object.assign(model.$functions, {
|
|
@@ -45,7 +48,7 @@ function tweakAssocs( model ) {
|
|
|
45
48
|
});
|
|
46
49
|
|
|
47
50
|
// Phase 5: rewrite associations
|
|
48
|
-
model._entities.forEach( rewriteArtifact );
|
|
51
|
+
model._entities.forEach( rewriteArtifact ); // _entities contains all definitions, sorted.
|
|
49
52
|
// Think hard whether an on condition rewrite can lead to a new cyclic
|
|
50
53
|
// dependency. If so, we need other messages anyway. TODO: probably dox
|
|
51
54
|
// another cyclic check with testMode.js
|
|
@@ -56,6 +59,11 @@ function tweakAssocs( model ) {
|
|
|
56
59
|
if (art.kind === 'select')
|
|
57
60
|
forEachQueryExpr( art, checkExpr );
|
|
58
61
|
} );
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// create “super” ANNOTATE statements for annotations on unknown artifacts:
|
|
65
|
+
createRemainingAnnotateStatements();
|
|
66
|
+
|
|
59
67
|
return;
|
|
60
68
|
|
|
61
69
|
|
|
@@ -68,7 +76,6 @@ function tweakAssocs( model ) {
|
|
|
68
76
|
// return;
|
|
69
77
|
if (!art.query) {
|
|
70
78
|
rewriteAssociation( art );
|
|
71
|
-
forEachGeneric( art, 'elements', rewriteAssociation );
|
|
72
79
|
}
|
|
73
80
|
else {
|
|
74
81
|
traverseQueryExtra( art, ( query ) => {
|
|
@@ -78,9 +85,11 @@ function tweakAssocs( model ) {
|
|
|
78
85
|
if (art._service)
|
|
79
86
|
forEachGeneric( art, 'elements', complainAboutTargetOutsideService );
|
|
80
87
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
if (art.query) {
|
|
89
|
+
traverseQueryPost(art.query, false, (query) => {
|
|
90
|
+
forEachGeneric( query, 'elements', rewriteAssociationCheck );
|
|
91
|
+
});
|
|
92
|
+
}
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
// function rewriteView( view ) {
|
|
@@ -231,6 +240,16 @@ function tweakAssocs( model ) {
|
|
|
231
240
|
}
|
|
232
241
|
|
|
233
242
|
function rewriteAssociation( element ) {
|
|
243
|
+
doRewriteAssociation( element );
|
|
244
|
+
if (element.target) {
|
|
245
|
+
extendForeignKeys( element );
|
|
246
|
+
if (element.foreignKeys$)
|
|
247
|
+
mergeSpecifiedForeignKeys( element );
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// only to be used by rewriteAssociation()
|
|
252
|
+
function doRewriteAssociation( element ) {
|
|
234
253
|
let elem = element.items || element; // TODO v6: nested items
|
|
235
254
|
if (elem.elements)
|
|
236
255
|
forEachGeneric( elem, 'elements', rewriteAssociation );
|
|
@@ -238,6 +257,7 @@ function tweakAssocs( model ) {
|
|
|
238
257
|
forEachGeneric( elem.targetAspect, 'elements', rewriteAssociation );
|
|
239
258
|
if (!originTarget( elem ))
|
|
240
259
|
return;
|
|
260
|
+
|
|
241
261
|
// console.log(message( null, elem.location, elem,
|
|
242
262
|
// {art:assoc,target,ftype:JSON.stringify(ftype)}, 'Info','RA').toString())
|
|
243
263
|
|
|
@@ -295,6 +315,7 @@ function tweakAssocs( model ) {
|
|
|
295
315
|
}
|
|
296
316
|
}
|
|
297
317
|
|
|
318
|
+
/** Returns the element's origin's target artifact. */
|
|
298
319
|
function originTarget( elem ) {
|
|
299
320
|
const assoc = !elem.expand && getOrigin( elem );
|
|
300
321
|
const ftype = assoc && effectiveType( assoc );
|
|
@@ -373,6 +394,9 @@ function tweakAssocs( model ) {
|
|
|
373
394
|
traverseExpr( elem.on, 'rewrite-on', elem,
|
|
374
395
|
expr => rewriteExpr( expr, elem, nav.tableAlias ) );
|
|
375
396
|
}
|
|
397
|
+
else if (elem._pathHead) {
|
|
398
|
+
error( 'rewrite-not-supported', [ elem.target.location, elem ] );
|
|
399
|
+
}
|
|
376
400
|
else {
|
|
377
401
|
// TODO: support that, now that the ON condition is rewritten in the right order
|
|
378
402
|
error( null, [ elem.value.location, elem ], {},
|
package/lib/edm/edm.js
CHANGED
|
@@ -883,12 +883,20 @@ function getEdm( options, messageFunctions ) {
|
|
|
883
883
|
class ReturnType extends PropertyBase {
|
|
884
884
|
constructor(version, csn) {
|
|
885
885
|
super(version, {}, csn);
|
|
886
|
+
// CSDL 12.8: If the return type is a collection of entity types,
|
|
887
|
+
// the Nullable attribute has no meaning and MUST NOT be specified.
|
|
888
|
+
if (csn.$NoNullableProperty)
|
|
889
|
+
delete this._edmAttributes.Nullable;
|
|
886
890
|
}
|
|
887
891
|
|
|
888
892
|
// we need Name but NO $kind, can't use standard to JSON()
|
|
889
893
|
toJSON() {
|
|
890
894
|
const json = Object.create(null);
|
|
891
895
|
this.toJSONattributes(json);
|
|
896
|
+
// CSDL 12.8: If the return type is a collection of entity types,
|
|
897
|
+
// the Nullable attribute has no meaning and MUST NOT be specified.
|
|
898
|
+
if (this._csn.$NoNullableProperty)
|
|
899
|
+
delete json.$Nullable;
|
|
892
900
|
return json;
|
|
893
901
|
}
|
|
894
902
|
}
|
|
@@ -1886,7 +1886,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1886
1886
|
mapCdsToEdmProp(def);
|
|
1887
1887
|
annotateAllowedValues(def, defLocation);
|
|
1888
1888
|
if (def.returns) {
|
|
1889
|
-
markCollection(def.returns);
|
|
1889
|
+
markCollection(def.returns, true);
|
|
1890
1890
|
mapCdsToEdmProp(def.returns);
|
|
1891
1891
|
annotateAllowedValues(def.returns, [ ...defLocation, 'returns' ]);
|
|
1892
1892
|
}
|
|
@@ -1899,16 +1899,20 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
|
|
|
1899
1899
|
rewriteAnnotationExpressions(member);
|
|
1900
1900
|
if (member.returns) {
|
|
1901
1901
|
edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
|
|
1902
|
-
markCollection(member.returns);
|
|
1902
|
+
markCollection(member.returns, true);
|
|
1903
1903
|
mapCdsToEdmProp(member.returns);
|
|
1904
1904
|
annotateAllowedValues(member.returns, [ ...location, 'returns' ]);
|
|
1905
1905
|
rewriteAnnotationExpressions(member.returns);
|
|
1906
1906
|
}
|
|
1907
1907
|
}, defLocation);
|
|
1908
1908
|
// mark members that need to be rendered as collections
|
|
1909
|
-
function markCollection( obj ) {
|
|
1909
|
+
function markCollection( obj, isReturns ) {
|
|
1910
1910
|
const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
|
|
1911
1911
|
if (items) {
|
|
1912
|
+
edmUtils.assignProp(obj, '$NoNullableProperty',
|
|
1913
|
+
isReturns && items.type &&
|
|
1914
|
+
!isBuiltinType(items.type) &&
|
|
1915
|
+
csn.definitions[items.type]?.kind === 'entity');
|
|
1912
1916
|
edmUtils.assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : false);
|
|
1913
1917
|
edmUtils.assignProp(obj, '$isCollection', true);
|
|
1914
1918
|
}
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -1508,6 +1508,13 @@
|
|
|
1508
1508
|
"Type": "HTML5.LinkTargetType",
|
|
1509
1509
|
"$experimental": true
|
|
1510
1510
|
},
|
|
1511
|
+
"HTML5.RowSpanForDuplicateValues": {
|
|
1512
|
+
"Type": "Core.Tag",
|
|
1513
|
+
"AppliesTo": [
|
|
1514
|
+
"Record"
|
|
1515
|
+
],
|
|
1516
|
+
"$experimental": true
|
|
1517
|
+
},
|
|
1511
1518
|
"JSON.Schema": {
|
|
1512
1519
|
"Type": "JSON.JSON",
|
|
1513
1520
|
"AppliesTo": [
|
|
@@ -1934,6 +1941,16 @@
|
|
|
1934
1941
|
],
|
|
1935
1942
|
"$experimental": true
|
|
1936
1943
|
},
|
|
1944
|
+
"UI.IsAIOperation": {
|
|
1945
|
+
"Type": "Core.Tag",
|
|
1946
|
+
"AppliesTo": [
|
|
1947
|
+
"Action",
|
|
1948
|
+
"Function",
|
|
1949
|
+
"ActionImport",
|
|
1950
|
+
"FunctionImport"
|
|
1951
|
+
],
|
|
1952
|
+
"$experimental": true
|
|
1953
|
+
},
|
|
1937
1954
|
"UI.CreateHidden": {
|
|
1938
1955
|
"Type": "Core.Tag",
|
|
1939
1956
|
"AppliesTo": [
|
|
@@ -2019,6 +2036,13 @@
|
|
|
2019
2036
|
"Parameter"
|
|
2020
2037
|
]
|
|
2021
2038
|
},
|
|
2039
|
+
"UI.Recommendations": {
|
|
2040
|
+
"Type": "Edm.ComplexType",
|
|
2041
|
+
"AppliesTo": [
|
|
2042
|
+
"EntityType"
|
|
2043
|
+
],
|
|
2044
|
+
"$experimental": true
|
|
2045
|
+
},
|
|
2022
2046
|
"UI.ExcludeFromNavigationContext": {
|
|
2023
2047
|
"Type": "Core.Tag",
|
|
2024
2048
|
"AppliesTo": [
|
|
@@ -3235,6 +3259,7 @@
|
|
|
3235
3259
|
"Properties": {
|
|
3236
3260
|
"Property": "Edm.PropertyPath",
|
|
3237
3261
|
"DynamicProperty": "Edm.AnnotationPath",
|
|
3262
|
+
"Expression": "Edm.PrimitiveType",
|
|
3238
3263
|
"Descending": "Edm.Boolean"
|
|
3239
3264
|
}
|
|
3240
3265
|
},
|
|
@@ -4230,6 +4255,7 @@
|
|
|
4230
4255
|
"IncludeGrandTotal": "Edm.Boolean",
|
|
4231
4256
|
"InitialExpansionLevel": "Edm.Int32",
|
|
4232
4257
|
"Visualizations": "Collection(Edm.AnnotationPath)",
|
|
4258
|
+
"RecursiveHierarchyQualifier": "Aggregation.HierarchyQualifier",
|
|
4233
4259
|
"RequestAtLeast": "Collection(Edm.PropertyPath)",
|
|
4234
4260
|
"SelectionFields": "Collection(Edm.PropertyPath)"
|
|
4235
4261
|
}
|
|
@@ -4479,6 +4505,17 @@
|
|
|
4479
4505
|
"ValueListProperty": "Edm.String"
|
|
4480
4506
|
}
|
|
4481
4507
|
},
|
|
4508
|
+
"UI.PropertyRecommendationType": {
|
|
4509
|
+
"$kind": "ComplexType",
|
|
4510
|
+
"Abstract": "true",
|
|
4511
|
+
"Properties": {
|
|
4512
|
+
"RecommendedFieldValue": "Edm.PrimitiveType",
|
|
4513
|
+
"RecommendedFieldDescription": "Edm.String",
|
|
4514
|
+
"RecommendedFieldScoreValue": "Edm.Decimal",
|
|
4515
|
+
"RecommendedFieldIsSuggestion": "Edm.Boolean"
|
|
4516
|
+
},
|
|
4517
|
+
"$experimental": true
|
|
4518
|
+
},
|
|
4482
4519
|
"UI.VisualizationType": {
|
|
4483
4520
|
"$kind": "EnumType",
|
|
4484
4521
|
"Members": [
|
package/lib/json/to-csn.js
CHANGED
|
@@ -384,6 +384,8 @@ function attachAnnotations( annotate, prop, dict, inferred, insideReturns = fals
|
|
|
384
384
|
attachAnnotations( sub, 'elements', elems, inf, entry.returns );
|
|
385
385
|
else if (many.enum) // make 'enum' annotations appear in 'elements' annotate
|
|
386
386
|
attachAnnotations( sub, 'elements', many.enum, inf, entry.returns );
|
|
387
|
+
else if (entry.foreignKeys) // make 'foreignKeys' annotations appear in 'elements' annotate
|
|
388
|
+
attachAnnotations( sub, 'elements', entry.foreignKeys, inf );
|
|
387
389
|
}
|
|
388
390
|
if (Object.keys( sub ).length)
|
|
389
391
|
annoDict[name] = sub;
|
package/lib/main.js
CHANGED
|
@@ -30,11 +30,8 @@ const builtins = lazyload('./base/builtins');
|
|
|
30
30
|
const base = lazyload('./compiler/base');
|
|
31
31
|
const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
|
|
32
32
|
const lsp = lazyload('./compiler/lsp-api');
|
|
33
|
+
const meta = lazyload('./base/meta');
|
|
33
34
|
|
|
34
|
-
// The compiler version (taken from package.json)
|
|
35
|
-
function version() {
|
|
36
|
-
return require('../package.json').version;
|
|
37
|
-
}
|
|
38
35
|
|
|
39
36
|
const toCsn = lazyload('./json/to-csn')
|
|
40
37
|
|
|
@@ -76,7 +73,7 @@ function parseExpr( cdlSource, filename = '<expr>.cds', options = {} ) {
|
|
|
76
73
|
// ATTENTION: Keep in sync with main.d.ts!
|
|
77
74
|
module.exports = {
|
|
78
75
|
// Compiler
|
|
79
|
-
version,
|
|
76
|
+
version: () => meta.version(),
|
|
80
77
|
compile: (filenames, dir, options, fileCache) => { // main function
|
|
81
78
|
traceApi( 'compile', options );
|
|
82
79
|
return compiler.compileX(filenames, dir, options, fileCache).then(toCsn.compactModel);
|
package/lib/model/cloneCsn.js
CHANGED
|
@@ -30,6 +30,7 @@ const internalCsnProps = {
|
|
|
30
30
|
$tableConstraints: shallowCopy,
|
|
31
31
|
$default: shallowCopy, // used for HANA CSN migrations
|
|
32
32
|
$notNull: shallowCopy, // used for HANA CSN migrations
|
|
33
|
+
$sqlService: shallowCopy,
|
|
33
34
|
};
|
|
34
35
|
const internalEnumerableCsnProps = {
|
|
35
36
|
__proto__: null,
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -448,7 +448,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
448
448
|
if (!step)
|
|
449
449
|
return null;
|
|
450
450
|
if (!effectiveType( art ))
|
|
451
|
-
throw new
|
|
451
|
+
throw new ModelError( 'Cyclic type definition' );
|
|
452
452
|
if (typeof step === 'string')
|
|
453
453
|
return navigationEnv( art, true ).elements[step];
|
|
454
454
|
|
|
@@ -1324,6 +1324,7 @@ module.exports = {
|
|
|
1324
1324
|
traverseQuery,
|
|
1325
1325
|
artifactProperties,
|
|
1326
1326
|
implicitAs,
|
|
1327
|
+
getKeysDict,
|
|
1327
1328
|
analyseCsnPath,
|
|
1328
1329
|
pathId,
|
|
1329
1330
|
columnAlias,
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -12,7 +12,6 @@ const { isBuiltinType, isAnnotationExpression } = require('../base/builtins');
|
|
|
12
12
|
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
13
13
|
const { typeParameters } = require('../compiler/builtins');
|
|
14
14
|
const { forEach } = require('../utils/objectUtils');
|
|
15
|
-
const { version } = require('../../package.json');
|
|
16
15
|
const { cloneAnnotationValue } = require('./cloneCsn');
|
|
17
16
|
|
|
18
17
|
// Low-level utility functions to work with compact CSN.
|
|
@@ -946,16 +945,6 @@ function isPersistedAsTable( artifact ) {
|
|
|
946
945
|
!hasAnnotationValue(artifact, '@cds.persistence.exists');
|
|
947
946
|
}
|
|
948
947
|
|
|
949
|
-
/**
|
|
950
|
-
* Central generated by cds-compiler string generator function without further decoration
|
|
951
|
-
* for unified tagging of generated content
|
|
952
|
-
*
|
|
953
|
-
* @returns {string} String containing compiler version that was used to generate content
|
|
954
|
-
*/
|
|
955
|
-
function generatedByCompilerVersion() {
|
|
956
|
-
return `generated by cds-compiler version ${ version }`;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
948
|
/**
|
|
960
949
|
* Return the projection to look like a query.
|
|
961
950
|
*
|
|
@@ -1438,7 +1427,6 @@ module.exports = {
|
|
|
1438
1427
|
isPersistedOnDatabase,
|
|
1439
1428
|
isPersistedAsView,
|
|
1440
1429
|
isPersistedAsTable,
|
|
1441
|
-
generatedByCompilerVersion,
|
|
1442
1430
|
getNormalizedQuery,
|
|
1443
1431
|
getRootArtifactName,
|
|
1444
1432
|
getLastPartOfRef,
|
|
@@ -257,17 +257,19 @@ function getExtensionAndMigrations(beforeModel, options, {
|
|
|
257
257
|
function getDeletions(afterModel, options, { deletions }) {
|
|
258
258
|
return function compareArtifacts(artifact, name) {
|
|
259
259
|
const otherArtifact = afterModel.definitions[name];
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
const isPersistedTable = isPersistedAsTable(artifact);
|
|
261
|
+
const isPersistedView = isPersistedAsView(artifact);
|
|
262
|
+
const isPersistedTableOther = otherArtifact && isPersistedAsTable(otherArtifact);
|
|
263
|
+
const isPersistedViewOther = otherArtifact && isPersistedAsView(otherArtifact);
|
|
264
|
+
|
|
265
|
+
// Looking for deleted entities or table -> view / view -> table
|
|
266
|
+
if (
|
|
267
|
+
(isPersistedTable && isPersistedViewOther) || // table -> view
|
|
268
|
+
(isPersistedView && isPersistedTableOther) || // view -> table
|
|
269
|
+
((isPersistedTable || isPersistedView) && // deleted
|
|
270
|
+
!(isPersistedTableOther || isPersistedViewOther))
|
|
271
|
+
) // view turned into table - need to render a drop for the view
|
|
265
272
|
deletions[name] = artifact;
|
|
266
|
-
}
|
|
267
|
-
// eslint-disable-next-line sonarjs/no-duplicated-branches
|
|
268
|
-
else if (isPersistedAsView(artifact) && isPersistedOther) { // view turned into table - need to render a drop for the view
|
|
269
|
-
deletions[name] = artifact;
|
|
270
|
-
}
|
|
271
273
|
};
|
|
272
274
|
}
|
|
273
275
|
|