@sap/cds-compiler 4.0.2 → 4.1.2
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 +100 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_BETA.md +11 -0
- package/lib/api/main.js +31 -11
- package/lib/api/validate.js +1 -1
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +84 -38
- package/lib/base/messages.js +11 -10
- package/lib/base/model.js +6 -2
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/onConditions.js +17 -12
- package/lib/checks/queryNoDbArtifacts.js +132 -72
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +1 -1
- package/lib/compiler/assert-consistency.js +44 -16
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +7 -8
- package/lib/compiler/checks.js +274 -197
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +3 -3
- package/lib/compiler/define.js +63 -50
- package/lib/compiler/extend.js +38 -20
- package/lib/compiler/finalize-parse-cdl.js +2 -1
- package/lib/compiler/generate.js +0 -8
- package/lib/compiler/index.js +9 -7
- package/lib/compiler/kick-start.js +2 -0
- package/lib/compiler/populate.js +139 -110
- package/lib/compiler/propagator.js +4 -3
- package/lib/compiler/resolve.js +157 -126
- package/lib/compiler/shared.js +706 -404
- package/lib/compiler/tweak-assocs.js +21 -10
- package/lib/compiler/utils.js +228 -36
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/edm.js +4 -1
- package/lib/edm/edmPreprocessor.js +5 -4
- package/lib/edm/edmUtils.js +2 -4
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3987 -3963
- package/lib/json/from-csn.js +43 -47
- package/lib/json/to-csn.js +11 -11
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +59 -59
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +20 -16
- package/lib/model/revealInternalProperties.js +29 -21
- package/lib/modelCompare/compare.js +112 -39
- package/lib/modelCompare/utils/filter.js +54 -24
- package/lib/optionProcessor.js +6 -6
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +34 -20
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +77 -26
- package/lib/render/utils/common.js +3 -3
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +61 -20
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +8 -8
- package/lib/transform/db/expansion.js +17 -21
- package/lib/transform/db/flattening.js +23 -23
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +6 -6
- package/lib/transform/forRelationalDB.js +69 -75
- package/lib/transform/localized.js +6 -5
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/package.json +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -14,11 +14,13 @@ const {
|
|
|
14
14
|
setArtifactLink,
|
|
15
15
|
linkToOrigin,
|
|
16
16
|
copyExpr,
|
|
17
|
+
forEachUserArtifact,
|
|
18
|
+
forEachQueryExpr,
|
|
17
19
|
traverseQueryPost,
|
|
18
20
|
traverseQueryExtra,
|
|
19
21
|
setExpandStatus,
|
|
20
|
-
traverseExpr,
|
|
21
22
|
} = require('./utils');
|
|
23
|
+
const { CsnLocation } = require('./classes');
|
|
22
24
|
|
|
23
25
|
const $location = Symbol.for('cds.$location');
|
|
24
26
|
const $inferred = Symbol.for('cds.$inferred');
|
|
@@ -30,9 +32,11 @@ function tweakAssocs( model ) {
|
|
|
30
32
|
info, warning, error,
|
|
31
33
|
} = model.$messageFunctions;
|
|
32
34
|
const {
|
|
35
|
+
traverseExpr,
|
|
36
|
+
checkExpr,
|
|
37
|
+
checkOnCondition,
|
|
33
38
|
effectiveType,
|
|
34
39
|
getOrigin,
|
|
35
|
-
navigationEnv,
|
|
36
40
|
} = model.$functions;
|
|
37
41
|
|
|
38
42
|
// Phase 5: rewrite associations
|
|
@@ -40,6 +44,13 @@ function tweakAssocs( model ) {
|
|
|
40
44
|
// Think hard whether an on condition rewrite can lead to a new cyclic
|
|
41
45
|
// dependency. If so, we need other messages anyway. TODO: probably do
|
|
42
46
|
// another cyclic check with testMode.js
|
|
47
|
+
forEachUserArtifact( model, 'definitions', function check( art ) {
|
|
48
|
+
checkOnCondition( art.on, (art.kind !== 'mixin' ? 'on' : 'mixin-on'), art );
|
|
49
|
+
checkExpr( art.value, (art.$syntax === 'calc' ? 'calc' : 'column'), art );
|
|
50
|
+
|
|
51
|
+
if (art.kind === 'select')
|
|
52
|
+
forEachQueryExpr( art, checkExpr );
|
|
53
|
+
} );
|
|
43
54
|
return;
|
|
44
55
|
|
|
45
56
|
|
|
@@ -166,13 +177,9 @@ function tweakAssocs( model ) {
|
|
|
166
177
|
}
|
|
167
178
|
if (names.length) {
|
|
168
179
|
const loc = otherAssoc.foreignKeys[$location] || dictLocation( otherAssoc.foreignKeys );
|
|
169
|
-
const location = loc && (!loc.endCol
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
col: loc.endCol - 1,
|
|
173
|
-
endLine: loc.endLine,
|
|
174
|
-
endCol: loc.endCol,
|
|
175
|
-
} );
|
|
180
|
+
const location = loc && (!loc.endCol
|
|
181
|
+
? loc
|
|
182
|
+
: new CsnLocation( loc.file, loc.endLine, loc.endCol - 1, loc.endLine, loc.endCol ));
|
|
176
183
|
const baseAssoc = assocWithExplicitSpec( thisAssoc );
|
|
177
184
|
if (inferredForeignKeys( baseAssoc.foreignKeys )) { // still inferred = via target keys
|
|
178
185
|
error( 'rewrite-key-not-covered-implicit', [ location, otherAssoc ],
|
|
@@ -482,8 +489,12 @@ function tweakAssocs( model ) {
|
|
|
482
489
|
item.id = name;
|
|
483
490
|
}
|
|
484
491
|
}
|
|
485
|
-
|
|
492
|
+
let env = name && elem._effectiveType; // should have been computed
|
|
493
|
+
// refs in ON cannot navigate along `items`, no need to consider `items` here
|
|
494
|
+
if (env?.target)
|
|
495
|
+
env = env.target._artifact?._effectiveType;
|
|
486
496
|
elem = setArtifactLink( item, env?.elements?.[name] );
|
|
497
|
+
|
|
487
498
|
if (elem && !Array.isArray(elem))
|
|
488
499
|
return elem;
|
|
489
500
|
// TODO: better (extra message), TODO: do it
|
package/lib/compiler/utils.js
CHANGED
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
// Please do not add functions “for completeness”, this is not an API file for
|
|
7
7
|
// others but only by the core compiler.
|
|
8
8
|
|
|
9
|
-
// TODO:
|
|
9
|
+
// TODO: split this file into utils/….js, add some functions from lib/base/model.js
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
13
|
const { dictAdd, pushToDict, dictFirst } = require('../base/dictionaries');
|
|
14
14
|
const { kindProperties } = require('./base');
|
|
15
|
+
const { XsnName, XsnArtifact, CsnLocation } = require('./classes');
|
|
16
|
+
|
|
17
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
15
18
|
|
|
16
19
|
// for links, i.e., properties starting with an underscore '_':
|
|
17
20
|
|
|
@@ -108,15 +111,18 @@ function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
|
108
111
|
|
|
109
112
|
function proxyCopyMembers( art, dictProp, originDict, location, kind ) {
|
|
110
113
|
art[dictProp] = Object.create( null );
|
|
114
|
+
// TODO: set $inferred ? for dict?
|
|
111
115
|
for (const name in originDict) {
|
|
112
116
|
const origin = originDict[name];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if (origin !== undefined) {
|
|
118
|
+
const member = linkToOrigin( origin, name, art, dictProp,
|
|
119
|
+
location || origin.location, true );
|
|
120
|
+
member.$inferred = 'expanded';
|
|
121
|
+
if (kind)
|
|
122
|
+
member.kind = kind;
|
|
123
|
+
if (kind && origin.masked) // TODO: remove!
|
|
124
|
+
member.masked = Object.assign( { $inferred: 'nav' }, origin.masked );
|
|
125
|
+
}
|
|
120
126
|
}
|
|
121
127
|
}
|
|
122
128
|
|
|
@@ -132,7 +138,7 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
132
138
|
if (prop) { // extension or structure include
|
|
133
139
|
// TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
|
|
134
140
|
const p = parent.items || parent.targetAspect || parent;
|
|
135
|
-
if (
|
|
141
|
+
if (p[prop] === undefined)
|
|
136
142
|
p[prop] = Object.create(null);
|
|
137
143
|
dictAdd( p[prop], name, elem );
|
|
138
144
|
}
|
|
@@ -166,10 +172,12 @@ function setMemberParent( elem, name, parent, prop ) {
|
|
|
166
172
|
* @param {XSN.Artifact} art
|
|
167
173
|
* @param {XSN.Location} location
|
|
168
174
|
*/
|
|
169
|
-
function dependsOn( user, art, location ) {
|
|
175
|
+
function dependsOn( user, art, location, semanticLoc = undefined ) {
|
|
176
|
+
while (user._outer && !user.kind)
|
|
177
|
+
user = user._outer;
|
|
170
178
|
if (!user._deps)
|
|
171
179
|
setLink( user, '_deps', [] );
|
|
172
|
-
user._deps.push( { art, location } );
|
|
180
|
+
user._deps.push( { art, location, semanticLoc } );
|
|
173
181
|
}
|
|
174
182
|
|
|
175
183
|
/**
|
|
@@ -180,6 +188,8 @@ function dependsOn( user, art, location ) {
|
|
|
180
188
|
* @param {XSN.Artifact} art
|
|
181
189
|
*/
|
|
182
190
|
function dependsOnSilent( user, art ) {
|
|
191
|
+
while (user._outer && !user.kind)
|
|
192
|
+
user = user._outer;
|
|
183
193
|
if (!user._deps)
|
|
184
194
|
setLink( user, '_deps', [] );
|
|
185
195
|
user._deps.push( { art } );
|
|
@@ -253,7 +263,9 @@ function copyExpr( expr, location, skipUnderscored, rewritePath ) {
|
|
|
253
263
|
return expr.map( e => copyExpr( e, location, skipUnderscored, rewritePath ) );
|
|
254
264
|
|
|
255
265
|
const proto = Object.getPrototypeOf( expr );
|
|
256
|
-
if (proto && proto !== Object.prototype
|
|
266
|
+
if (proto && proto !== Object.prototype && proto !== XsnName.prototype &&
|
|
267
|
+
// do not copy object from special classes outside the compiler domain&&
|
|
268
|
+
proto !== XsnArtifact.prototype && proto !== CsnLocation.prototype)
|
|
257
269
|
return expr;
|
|
258
270
|
const r = Object.create( proto );
|
|
259
271
|
for (const prop of Object.getOwnPropertyNames( expr )) {
|
|
@@ -299,7 +311,7 @@ function testExpr( expr, pathTest, queryTest, user ) {
|
|
|
299
311
|
return expr.args.some( e => testExpr( e, pathTest, queryTest, user ) );
|
|
300
312
|
// named args => dictionary
|
|
301
313
|
for (const namedArg of Object.keys(expr.args)) {
|
|
302
|
-
if (testExpr(expr.args[namedArg], pathTest, queryTest, user))
|
|
314
|
+
if (testExpr( expr.args[namedArg], pathTest, queryTest, user ))
|
|
303
315
|
return true;
|
|
304
316
|
}
|
|
305
317
|
}
|
|
@@ -314,6 +326,129 @@ function targetMaxNotOne( assoc, item ) {
|
|
|
314
326
|
return cardinality && cardinality.targetMax && cardinality.targetMax.val !== 1;
|
|
315
327
|
}
|
|
316
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Call function `callback(art)` for each user-defined main artifact and member
|
|
331
|
+
* `art` of the model reachable from the dictionary `model[prop]`. User-defined
|
|
332
|
+
* artifacts are those with no or a falsy `art.$inferred` value, i.e. this
|
|
333
|
+
* function is useful for checks.
|
|
334
|
+
*
|
|
335
|
+
* The callback function is not called on the following artifacts:
|
|
336
|
+
* - `enum` symbol definitions (use forEachUserDict() yourself if needed)
|
|
337
|
+
* - the anonymous aspect in the `target`/`targetAspect` property (but the
|
|
338
|
+
* callback function is called on its elements).
|
|
339
|
+
* - table aliases
|
|
340
|
+
*
|
|
341
|
+
* The callback function is also called on duplicates. For example, if there are
|
|
342
|
+
* two entities named `E`, the callback function is called on both.
|
|
343
|
+
* It is also called on columns with `inline`.
|
|
344
|
+
*
|
|
345
|
+
* See also function forEachDefinition(), currently in lib/base/model.js.
|
|
346
|
+
*/
|
|
347
|
+
function forEachUserArtifact( model, prop, callback ) { // not enums
|
|
348
|
+
forEachUserDict( model, prop, function main( art ) {
|
|
349
|
+
callback( art );
|
|
350
|
+
forEachUserDict( art, 'params', function param( par ) {
|
|
351
|
+
callback( par );
|
|
352
|
+
forEachUserElementAndFKey( par, callback );
|
|
353
|
+
} );
|
|
354
|
+
if (art.$queries) {
|
|
355
|
+
for (const query of art.$queries) {
|
|
356
|
+
callback( query );
|
|
357
|
+
forEachUserDict( query, 'mixin', callback );
|
|
358
|
+
forEachUserElementAndFKey( query, callback );
|
|
359
|
+
if (query.$inlines) // e.g. not with `entity V as projection on V;`
|
|
360
|
+
query.$inlines.forEach( callback );
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
else if (art.returns) {
|
|
364
|
+
callback( art.returns );
|
|
365
|
+
forEachUserElementAndFKey( art.returns, callback );
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
forEachUserElementAndFKey( art, callback );
|
|
369
|
+
}
|
|
370
|
+
forEachUserArtifact( art, 'actions', callback );
|
|
371
|
+
} );
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Call function `callback(art)` for each user-defined element and foreign key
|
|
376
|
+
* reachable from artifact `art`. Do not (again) call the callback function on
|
|
377
|
+
* `art` itself, even if it is an element.
|
|
378
|
+
*
|
|
379
|
+
* Consider that we have (nested) `array of`/`many` types, but do not call the
|
|
380
|
+
* callback function on the array item itself (only on elements inside).
|
|
381
|
+
*/
|
|
382
|
+
function forEachUserElementAndFKey( art, callback ) {
|
|
383
|
+
while (art.items)
|
|
384
|
+
art = art.items;
|
|
385
|
+
if (art.target) {
|
|
386
|
+
forEachUserDict( art, 'foreignKeys', callback );
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (art.targetAspect)
|
|
390
|
+
art = art.targetAspect;
|
|
391
|
+
forEachUserDict( art, 'elements', function element( elem ) {
|
|
392
|
+
callback( elem );
|
|
393
|
+
forEachUserElementAndFKey( elem, callback );
|
|
394
|
+
} );
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function forEachUserDict( art, prop, callback ) {
|
|
398
|
+
const dict = art[prop];
|
|
399
|
+
if (!dict || dict[$inferred])
|
|
400
|
+
return;
|
|
401
|
+
for (const name in dict) {
|
|
402
|
+
const obj = dict[name];
|
|
403
|
+
if (obj.$inferred)
|
|
404
|
+
continue;
|
|
405
|
+
callback( obj, name, prop );
|
|
406
|
+
if (Array.isArray(obj.$duplicates)) // redefinitions
|
|
407
|
+
obj.$duplicates.forEach( o => callback( o, name, prop ) );
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Call `callback( expr, exprCtx, query )` on all direct expressions `expr` of
|
|
413
|
+
* `query`, where `exprCtx` is the expression context used as key for the
|
|
414
|
+
* `referenceSemantics` in shared.js.
|
|
415
|
+
*
|
|
416
|
+
* Indirect expressions are not called, these are:
|
|
417
|
+
* - the `from` reference (expression of the table alias)
|
|
418
|
+
* - the ON-condition of mixins (expression of the mixin)
|
|
419
|
+
* - the expressions in columns (expression of the column/element)
|
|
420
|
+
*/
|
|
421
|
+
function forEachQueryExpr( query, callback ) { // see resolveQuery()
|
|
422
|
+
forEachJoinOn( query, query.from, callback );
|
|
423
|
+
// TODO: run over $inlines ?
|
|
424
|
+
if (query.where)
|
|
425
|
+
callback( query.where, 'where', query );
|
|
426
|
+
if (query.groupBy)
|
|
427
|
+
forEachExprArray( query, query.groupBy, 'groupBy', 'groupBy', callback );
|
|
428
|
+
if (query.having)
|
|
429
|
+
callback( query.having, 'having', query );
|
|
430
|
+
if (query.$orderBy)
|
|
431
|
+
forEachExprArray( query, query.$orderBy, 'orderBy-set-ref', 'orderBy-set-expr', callback );
|
|
432
|
+
if (query.orderBy)
|
|
433
|
+
forEachExprArray( query, query.orderBy, 'orderBy-ref', 'orderBy-expr', callback );
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function forEachJoinOn( query, from, callback ) {
|
|
437
|
+
if (!from?.join)
|
|
438
|
+
return; // TODO: run over from.path here?
|
|
439
|
+
for (const tab of from.args)
|
|
440
|
+
forEachJoinOn( query, tab, callback );
|
|
441
|
+
if (from.on)
|
|
442
|
+
callback( from.on, 'join-on', query );
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function forEachExprArray( query, array, refContext, exprContext, callback ) {
|
|
446
|
+
for (const expr of array ) {
|
|
447
|
+
if (expr)
|
|
448
|
+
callback( expr, (expr.path ? refContext : exprContext), query );
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
317
452
|
// Query tree post-order traversal - called for everything which contributes to the query
|
|
318
453
|
// i.e. is necessary to calculate the elements of the query
|
|
319
454
|
// except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
|
|
@@ -442,28 +577,6 @@ function isDirectComposition( art ) {
|
|
|
442
577
|
return type && type[0] && type[0].id === 'cds.Composition';
|
|
443
578
|
}
|
|
444
579
|
|
|
445
|
-
function traverseExpr( expr, exprCtx, user, callback ) {
|
|
446
|
-
if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
|
|
447
|
-
return;
|
|
448
|
-
|
|
449
|
-
if (expr.path) {
|
|
450
|
-
callback( expr, exprCtx, user );
|
|
451
|
-
// TODO: move arguments and filter traversal to here
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
else if (expr.type || expr.query) {
|
|
455
|
-
callback( expr, exprCtx, user );
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (expr.args) {
|
|
459
|
-
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
460
|
-
// TODO: re-think $expected
|
|
461
|
-
args.forEach( e => traverseExpr( e, exprCtx, user, callback ) );
|
|
462
|
-
}
|
|
463
|
-
if (expr.suffix) // fn( … ) OVER …
|
|
464
|
-
expr.suffix.forEach( e => traverseExpr( e, exprCtx, user, callback ) );
|
|
465
|
-
}
|
|
466
|
-
|
|
467
580
|
function userQuery( user ) {
|
|
468
581
|
// TODO: we need _query links set by the definer
|
|
469
582
|
while (user._main) {
|
|
@@ -474,6 +587,63 @@ function userQuery( user ) {
|
|
|
474
587
|
return null;
|
|
475
588
|
}
|
|
476
589
|
|
|
590
|
+
function pathStartsWithSelf( ref ) {
|
|
591
|
+
const head = ref && !ref.scope && ref.path?.[0];
|
|
592
|
+
if (head?._navigation?.kind === '$self')
|
|
593
|
+
return true;
|
|
594
|
+
if (head?._artifact?.kind === 'builtin') // CDS variable
|
|
595
|
+
return false;
|
|
596
|
+
return undefined;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function columnRefStartsWithSelf( col ) {
|
|
600
|
+
for (; col; col = col._pathHead) {
|
|
601
|
+
const ref = col.value;
|
|
602
|
+
const head = ref && !ref.scope && ref.path?.[0];
|
|
603
|
+
if (head?._navigation?.kind === '$self')
|
|
604
|
+
return true;
|
|
605
|
+
if (head?._artifact?.kind === 'builtin') // CDS variable
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Remark: this function is based on an early check that no target element is
|
|
612
|
+
// covered more than once by a foreign key: then…
|
|
613
|
+
// we only need to check that all foreign key references are primary keys and
|
|
614
|
+
// that the number of foreign and primary keys are the same.
|
|
615
|
+
function isAssocToPrimaryKeys( assoc ) {
|
|
616
|
+
let fkeys = 0;
|
|
617
|
+
const { foreignKeys } = assoc;
|
|
618
|
+
if (!foreignKeys)
|
|
619
|
+
return undefined;
|
|
620
|
+
for (const name in foreignKeys) {
|
|
621
|
+
const fk = foreignKeys[name];
|
|
622
|
+
const elem = fk.targetElement._artifact;
|
|
623
|
+
if (!elem || fk.$duplicates)
|
|
624
|
+
return undefined;
|
|
625
|
+
if (!elem.key?.val)
|
|
626
|
+
return false;
|
|
627
|
+
++fkeys;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const elements = assoc.target._artifact?.elements;
|
|
631
|
+
if (!elements)
|
|
632
|
+
return undefined;
|
|
633
|
+
for (const name in elements) {
|
|
634
|
+
if (elements[name].key?.val)
|
|
635
|
+
--fkeys;
|
|
636
|
+
}
|
|
637
|
+
return fkeys === 0;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// only if _effectiveType has been computed:
|
|
641
|
+
function getUnderlyingBuiltinType( art ) {
|
|
642
|
+
while (art?._effectiveType && !art.builtin)
|
|
643
|
+
art = art._origin || art.type?._artifact;
|
|
644
|
+
return art;
|
|
645
|
+
}
|
|
646
|
+
|
|
477
647
|
function definedViaCdl( art ) {
|
|
478
648
|
// return !!art._block?.artifacts;
|
|
479
649
|
// TODO: the above code would work when _block links are correctly set on
|
|
@@ -483,6 +653,21 @@ function definedViaCdl( art ) {
|
|
|
483
653
|
return $frontend !== 'json' && $frontend !== '$internal';
|
|
484
654
|
}
|
|
485
655
|
|
|
656
|
+
// For error messages: ----------------------------------------------------------
|
|
657
|
+
|
|
658
|
+
// (To be) used for the location in error messages
|
|
659
|
+
function artifactRefLocation( ref ) {
|
|
660
|
+
return (ref._artifact?._main)
|
|
661
|
+
? ref.path[ref.path.length - 1].location
|
|
662
|
+
: ref.location;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function compositionTextVariant( art, composition, association = 'std' ) {
|
|
666
|
+
return (getUnderlyingBuiltinType( art )?.name.absolute === 'cds.Composition')
|
|
667
|
+
? composition
|
|
668
|
+
: association;
|
|
669
|
+
}
|
|
670
|
+
|
|
486
671
|
module.exports = {
|
|
487
672
|
pushLink,
|
|
488
673
|
annotationVal,
|
|
@@ -505,13 +690,20 @@ module.exports = {
|
|
|
505
690
|
copyExpr,
|
|
506
691
|
testExpr,
|
|
507
692
|
targetMaxNotOne,
|
|
693
|
+
forEachUserArtifact,
|
|
694
|
+
forEachQueryExpr,
|
|
508
695
|
traverseQueryPost,
|
|
509
696
|
traverseQueryExtra,
|
|
510
697
|
viewFromPrimary,
|
|
511
698
|
setExpandStatus,
|
|
512
699
|
setExpandStatusAnnotate,
|
|
513
700
|
isDirectComposition,
|
|
514
|
-
traverseExpr,
|
|
515
701
|
userQuery,
|
|
702
|
+
pathStartsWithSelf,
|
|
703
|
+
columnRefStartsWithSelf,
|
|
704
|
+
isAssocToPrimaryKeys,
|
|
705
|
+
getUnderlyingBuiltinType,
|
|
516
706
|
definedViaCdl,
|
|
707
|
+
artifactRefLocation,
|
|
708
|
+
compositionTextVariant,
|
|
517
709
|
};
|
|
@@ -1488,7 +1488,7 @@ function csn2annotationEdm(reqDefs, csnVocabularies, serviceName,
|
|
|
1488
1488
|
function handleEdmJson(obj, msg, exprDef=undefined) {
|
|
1489
1489
|
|
|
1490
1490
|
let edmNode = undefined;
|
|
1491
|
-
if(obj
|
|
1491
|
+
if(obj == null)
|
|
1492
1492
|
return edmNode;
|
|
1493
1493
|
|
|
1494
1494
|
const dynExprs = edmUtils.intersect(dynamicExpressionNames, Object.keys(obj));
|
package/lib/edm/edm.js
CHANGED
|
@@ -1220,13 +1220,16 @@ function getEdm(options, messageFunctions) {
|
|
|
1220
1220
|
// V4 referential constraints!
|
|
1221
1221
|
addReferentialConstraintNodes()
|
|
1222
1222
|
{
|
|
1223
|
+
// flip the constrains if this is a $self partner
|
|
1223
1224
|
let _constraints = this._csn._constraints;
|
|
1225
|
+
let [i,j] = [0,1];
|
|
1224
1226
|
if(this._csn._constraints._partnerCsn) {
|
|
1225
1227
|
_constraints = this._csn._constraints._partnerCsn._constraints;
|
|
1228
|
+
[i,j] = [1,0];
|
|
1226
1229
|
}
|
|
1227
1230
|
_constraints.constraints && Object.values(_constraints.constraints).forEach(c =>
|
|
1228
1231
|
this.append(new ReferentialConstraint(this._v,
|
|
1229
|
-
{ Property: c[
|
|
1232
|
+
{ Property: c[i].join(options.pathDelimiter), ReferencedProperty: c[j].join(options.pathDelimiter) } ) )
|
|
1230
1233
|
);
|
|
1231
1234
|
}
|
|
1232
1235
|
}
|
|
@@ -94,7 +94,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
94
94
|
as a step in the OData transformer with the goal to have a protocol agnostic OData CSN.
|
|
95
95
|
*/
|
|
96
96
|
if (csn.meta && csn.meta.options && csn.meta.options.odataVersion === 'v4' && options.isV2()) {
|
|
97
|
-
const { toFinalBaseType }= require('../transform/
|
|
97
|
+
const { toFinalBaseType }= require('../transform/transformUtils').getTransformers(csn, options);
|
|
98
98
|
expandCSNToFinalBaseType(csn, { toFinalBaseType }, csnUtils, serviceRootNames, options);
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -975,8 +975,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
975
975
|
edmUtils.foreach(struct.elements,
|
|
976
976
|
e =>
|
|
977
977
|
e['@odata.foreignKey4'] === element.name,
|
|
978
|
-
e => e
|
|
979
|
-
element
|
|
978
|
+
e => e.$ignore = true);
|
|
979
|
+
element.$ignore = true;
|
|
980
980
|
info(null, ['definitions', struct.name, 'elements', element.name]
|
|
981
981
|
`${element.type.replace('cds.', '')} "${element.name}" excluded,
|
|
982
982
|
target "${element._target.name}" is annotated '@cds.autoexpose: ${element._target['@cds.autoexpose']}'`
|
|
@@ -1588,7 +1588,8 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
1588
1588
|
setProp(elt, '$visited', true);
|
|
1589
1589
|
let newRefs = [];
|
|
1590
1590
|
// if the foreign keys are explicitly requested, ignore associations and use the flat foreign key instead
|
|
1591
|
-
|
|
1591
|
+
// ignore nested unmanaged associations
|
|
1592
|
+
if((!options.renderForeignKeys || (options.renderForeignKeys && !elt.target)) && !(elt.target && elt.on))
|
|
1592
1593
|
newRefs = produceKeyRefPaths(elt, prefix + options.pathDelimiter + eltName, path);
|
|
1593
1594
|
if(newRefs.length) {
|
|
1594
1595
|
keyPaths.push(...newRefs);
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -839,10 +839,8 @@ function mergeIntoNavPropEntry(annoPrefix, navPropEntry, prefix, props) {
|
|
|
839
839
|
// Assign but not overwrite annotation
|
|
840
840
|
function assignAnnotation(node, name, value) {
|
|
841
841
|
if(value !== undefined &&
|
|
842
|
-
name !== undefined && name[0] === '@'
|
|
843
|
-
|
|
844
|
-
node[name] = value;
|
|
845
|
-
}
|
|
842
|
+
name !== undefined && name[0] === '@')
|
|
843
|
+
node[name] ??= value;
|
|
846
844
|
}
|
|
847
845
|
|
|
848
846
|
// Set non enumerable property if it doesn't exist yet
|
package/lib/gen/Dictionary.json
CHANGED
|
@@ -787,8 +787,7 @@
|
|
|
787
787
|
]
|
|
788
788
|
},
|
|
789
789
|
"Common.ValueListRelevantQualifiers": {
|
|
790
|
-
"Type": "Collection(Core.SimpleIdentifier)"
|
|
791
|
-
"$experimental": true
|
|
790
|
+
"Type": "Collection(Core.SimpleIdentifier)"
|
|
792
791
|
},
|
|
793
792
|
"Common.ValueListWithFixedValues": {
|
|
794
793
|
"Type": "Core.Tag",
|
|
@@ -802,8 +801,7 @@
|
|
|
802
801
|
"AppliesTo": [
|
|
803
802
|
"Property",
|
|
804
803
|
"Parameter"
|
|
805
|
-
]
|
|
806
|
-
"$experimental": true
|
|
804
|
+
]
|
|
807
805
|
},
|
|
808
806
|
"Common.ValueListReferences": {
|
|
809
807
|
"Type": "Collection(Edm.String)",
|
|
@@ -1461,6 +1459,10 @@
|
|
|
1461
1459
|
"Record"
|
|
1462
1460
|
]
|
|
1463
1461
|
},
|
|
1462
|
+
"HTML5.LinkTarget": {
|
|
1463
|
+
"Type": "HTML5.LinkTargetType",
|
|
1464
|
+
"$experimental": true
|
|
1465
|
+
},
|
|
1464
1466
|
"JSON.Schema": {
|
|
1465
1467
|
"Type": "JSON.JSON",
|
|
1466
1468
|
"AppliesTo": [
|
|
@@ -1561,15 +1563,13 @@
|
|
|
1561
1563
|
"Type": "Edm.String",
|
|
1562
1564
|
"AppliesTo": [
|
|
1563
1565
|
"EntitySet"
|
|
1564
|
-
]
|
|
1565
|
-
"$experimental": true
|
|
1566
|
+
]
|
|
1566
1567
|
},
|
|
1567
1568
|
"PersonalData.DataSubjectRoleDescription": {
|
|
1568
1569
|
"Type": "Edm.String",
|
|
1569
1570
|
"AppliesTo": [
|
|
1570
1571
|
"EntitySet"
|
|
1571
|
-
]
|
|
1572
|
-
"$experimental": true
|
|
1572
|
+
]
|
|
1573
1573
|
},
|
|
1574
1574
|
"PersonalData.FieldSemantics": {
|
|
1575
1575
|
"Type": "PersonalData.FieldSemanticsType",
|
|
@@ -1834,8 +1834,7 @@
|
|
|
1834
1834
|
"Type": "Edm.String",
|
|
1835
1835
|
"AppliesTo": [
|
|
1836
1836
|
"Property"
|
|
1837
|
-
]
|
|
1838
|
-
"$experimental": true
|
|
1837
|
+
]
|
|
1839
1838
|
},
|
|
1840
1839
|
"UI.TextArrangement": {
|
|
1841
1840
|
"Type": "UI.TextArrangementType",
|
|
@@ -3435,6 +3434,31 @@
|
|
|
3435
3434
|
"width": "Edm.String"
|
|
3436
3435
|
}
|
|
3437
3436
|
},
|
|
3437
|
+
"HTML5.LinkTargetType": {
|
|
3438
|
+
"$kind": "TypeDefinition",
|
|
3439
|
+
"UnderlyingType": "Edm.String",
|
|
3440
|
+
"$Allowed": {
|
|
3441
|
+
"Values": {
|
|
3442
|
+
"_self": {
|
|
3443
|
+
"Value": "_self",
|
|
3444
|
+
"Type": "String"
|
|
3445
|
+
},
|
|
3446
|
+
"_blank": {
|
|
3447
|
+
"Value": "_blank",
|
|
3448
|
+
"Type": "String"
|
|
3449
|
+
},
|
|
3450
|
+
"_parent": {
|
|
3451
|
+
"Value": "_parent",
|
|
3452
|
+
"Type": "String"
|
|
3453
|
+
},
|
|
3454
|
+
"_top": {
|
|
3455
|
+
"Value": "_top",
|
|
3456
|
+
"Type": "String"
|
|
3457
|
+
}
|
|
3458
|
+
},
|
|
3459
|
+
"Symbols": {}
|
|
3460
|
+
}
|
|
3461
|
+
},
|
|
3438
3462
|
"JSON.JSON": {
|
|
3439
3463
|
"$kind": "TypeDefinition",
|
|
3440
3464
|
"UnderlyingType": "Edm.Stream"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2c5e5aa4e688aa88877916a4d40340bf
|