@sap/cds-compiler 5.9.4 → 6.0.12
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 +117 -319
- package/README.md +1 -1
- package/bin/cds_update_identifiers.js +3 -5
- package/bin/cdsc.js +24 -9
- package/bin/cdshi.js +1 -1
- package/bin/cdsse.js +4 -4
- package/doc/CHANGELOG_BETA.md +11 -0
- package/doc/CHANGELOG_DEPRECATED.md +29 -0
- package/lib/api/main.js +8 -5
- package/lib/api/options.js +12 -10
- package/lib/base/builtins.js +1 -0
- package/lib/base/message-registry.js +191 -99
- package/lib/base/messages.js +35 -21
- package/lib/base/model.js +14 -24
- package/lib/checks/actionsFunctions.js +10 -20
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/elements.js +35 -10
- package/lib/checks/enums.js +31 -0
- package/lib/checks/foreignKeys.js +2 -2
- package/lib/checks/hasPersistedElements.js +5 -0
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/managedWithoutKeys.js +5 -4
- package/lib/checks/queryNoDbArtifacts.js +10 -8
- package/lib/checks/types.js +5 -5
- package/lib/checks/validator.js +6 -4
- package/lib/compiler/assert-consistency.js +13 -9
- package/lib/compiler/checks.js +20 -52
- package/lib/compiler/define.js +31 -6
- package/lib/compiler/extend.js +5 -1
- package/lib/compiler/generate.js +14 -17
- package/lib/compiler/populate.js +8 -31
- package/lib/compiler/propagator.js +21 -35
- package/lib/compiler/resolve.js +64 -29
- package/lib/compiler/shared.js +16 -4
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +1 -1
- package/lib/edm/annotations/edmJson.js +23 -20
- package/lib/edm/annotations/genericTranslation.js +12 -10
- package/lib/edm/csn2edm.js +50 -56
- package/lib/edm/edm.js +33 -28
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +54 -88
- package/lib/edm/edmUtils.js +9 -12
- package/lib/gen/BaseParser.js +63 -52
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1153 -1165
- package/lib/gen/Dictionary.json +21 -1
- package/lib/json/from-csn.js +70 -43
- package/lib/json/to-csn.js +6 -8
- package/lib/language/multiLineStringParser.js +3 -2
- package/lib/main.d.ts +58 -24
- package/lib/model/cloneCsn.js +3 -0
- package/lib/model/csnUtils.js +28 -39
- package/lib/model/xprAsTree.js +23 -9
- package/lib/modelCompare/compare.js +5 -4
- package/lib/optionProcessor.js +24 -17
- package/lib/parsers/AstBuildingParser.js +81 -25
- package/lib/parsers/XprTree.js +57 -3
- package/lib/parsers/identifiers.js +1 -1
- package/lib/parsers/index.js +0 -3
- package/lib/render/manageConstraints.js +25 -25
- package/lib/render/toCdl.js +173 -170
- package/lib/render/toHdbcds.js +126 -128
- package/lib/render/toRename.js +7 -7
- package/lib/render/toSql.js +128 -125
- package/lib/render/utils/common.js +47 -22
- package/lib/render/utils/delta.js +25 -25
- package/lib/render/utils/operators.js +2 -2
- package/lib/render/utils/pretty.js +5 -5
- package/lib/render/utils/sql.js +13 -13
- package/lib/render/utils/standardDatabaseFunctions.js +115 -103
- package/lib/render/utils/unique.js +4 -4
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +2 -2
- package/lib/transform/db/associations.js +6 -7
- package/lib/transform/db/assocsToQueries/utils.js +4 -5
- package/lib/transform/db/backlinks.js +12 -9
- package/lib/transform/db/cdsPersistence.js +8 -7
- package/lib/transform/db/constraints.js +13 -10
- package/lib/transform/db/expansion.js +7 -3
- package/lib/transform/db/flattening.js +4 -14
- package/lib/transform/db/processSqlServices.js +2 -1
- package/lib/transform/db/temporal.js +5 -7
- package/lib/transform/db/views.js +2 -4
- package/lib/transform/draft/db.js +8 -8
- package/lib/transform/draft/odata.js +10 -7
- package/lib/transform/forOdata.js +10 -5
- package/lib/transform/forRelationalDB.js +5 -75
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/createForeignKeys.js +11 -10
- package/lib/transform/odata/flattening.js +8 -4
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/transformUtils.js +4 -8
- package/lib/transform/translateAssocsToJoins.js +14 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
- package/lib/utils/objectUtils.js +0 -17
- package/package.json +10 -13
- package/share/messages/def-upcoming-virtual-change.md +1 -1
- package/LICENSE +0 -37
- package/bin/cds_remove_invalid_whitespace.js +0 -138
- package/doc/CHANGELOG_ARCHIVE.md +0 -3604
- package/lib/gen/genericAntlrParser.js +0 -3
- package/lib/gen/language.checksum +0 -1
- package/lib/gen/language.interp +0 -456
- package/lib/gen/language.tokens +0 -180
- package/lib/gen/languageLexer.interp +0 -439
- package/lib/gen/languageLexer.js +0 -1483
- package/lib/gen/languageLexer.tokens +0 -167
- package/lib/gen/languageParser.js +0 -24941
- package/lib/language/antlrParser.js +0 -205
- package/lib/language/errorStrategy.js +0 -646
- package/lib/language/genericAntlrParser.js +0 -1572
- package/lib/parsers/CdlGrammar.g4 +0 -2070
package/lib/compiler/checks.js
CHANGED
|
@@ -16,12 +16,10 @@ const {
|
|
|
16
16
|
forEachMember,
|
|
17
17
|
forEachMemberRecursively,
|
|
18
18
|
isDeprecatedEnabled,
|
|
19
|
-
isBetaEnabled,
|
|
20
19
|
} = require('../base/model');
|
|
21
20
|
const { typeParameters } = require('./builtins');
|
|
22
21
|
const { propagationRules } = require('../base/builtins');
|
|
23
22
|
const { annotationVal } = require('./utils');
|
|
24
|
-
const { functionsWithoutParentheses } = require('../parsers/identifiers');
|
|
25
23
|
|
|
26
24
|
const $location = Symbol.for( 'cds.$location' );
|
|
27
25
|
|
|
@@ -95,8 +93,6 @@ function check( model ) {
|
|
|
95
93
|
checkTypeStructure( art );
|
|
96
94
|
checkAssociation( art ); // type def could be assoc
|
|
97
95
|
checkDefaultValue( art );
|
|
98
|
-
if (art.kind === 'enum')
|
|
99
|
-
checkEnum( art );
|
|
100
96
|
checkEnumType( art );
|
|
101
97
|
}
|
|
102
98
|
|
|
@@ -332,26 +328,6 @@ function check( model ) {
|
|
|
332
328
|
}
|
|
333
329
|
}
|
|
334
330
|
|
|
335
|
-
/**
|
|
336
|
-
* The enumNode is a single enum element and not the whole type.
|
|
337
|
-
*
|
|
338
|
-
* @param {XSN.Artifact} enumNode
|
|
339
|
-
*/
|
|
340
|
-
function checkEnum( enumNode ) {
|
|
341
|
-
if (!enumNode.value)
|
|
342
|
-
return;
|
|
343
|
-
|
|
344
|
-
const type = enumNode.value.literal;
|
|
345
|
-
const loc = enumNode.value.location;
|
|
346
|
-
|
|
347
|
-
// Special handling to print a more detailed error message.
|
|
348
|
-
// Other cases like `null` as enum value are handled in `checkEnumValueType()`
|
|
349
|
-
if (type === 'enum') {
|
|
350
|
-
warning( 'ref-unexpected-enum', [ loc, enumNode ], {},
|
|
351
|
-
'References to other values are not allowed as enum values' );
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
331
|
function checkEnumType( enumNode ) {
|
|
356
332
|
// Either the type is an enum or an arrayed enum. We are only interested in
|
|
357
333
|
// the enum and don't care whether the enum is arrayed.
|
|
@@ -563,8 +539,7 @@ function check( model ) {
|
|
|
563
539
|
|
|
564
540
|
// Check that "not null" artifacts don't have `null` default values.
|
|
565
541
|
// At least one property must be written explicitly to avoid reporting on inferred elements.
|
|
566
|
-
if (
|
|
567
|
-
(art.default?.val === null || art.notNull?.val)) {
|
|
542
|
+
if (art.default?.val === null || art.notNull?.val) {
|
|
568
543
|
const notNullValue = getInheritedProp( art, 'notNull' );
|
|
569
544
|
if (notNullValue?.val && defaultValue?.val === null) {
|
|
570
545
|
const loc = (art.default || art.notNull)?.location || art.location;
|
|
@@ -585,6 +560,7 @@ function check( model ) {
|
|
|
585
560
|
} );
|
|
586
561
|
}
|
|
587
562
|
else if (art._effectiveType?.elements) {
|
|
563
|
+
// TODO: error for v7
|
|
588
564
|
warning( 'type-unexpected-default-struct', [ defaultValue.location, art ], {
|
|
589
565
|
'#': art.kind, keyword: 'default',
|
|
590
566
|
}, {
|
|
@@ -779,24 +755,29 @@ function check( model ) {
|
|
|
779
755
|
* Let users know about it.
|
|
780
756
|
*
|
|
781
757
|
* @param elem
|
|
758
|
+
*
|
|
759
|
+
* TODO: simplify if the old parser is gone (query-invalid-virtual-struct stays)
|
|
782
760
|
*/
|
|
783
761
|
function checkVirtualSelectItemChangeForV6( elem ) {
|
|
784
762
|
if (
|
|
785
763
|
!elem.virtual?.val || elem.virtual.$inferred || // not explicitly marked virtual
|
|
786
|
-
|
|
787
|
-
elem.
|
|
788
|
-
|
|
764
|
+
!elem.name.$inferred || // has explicit alias
|
|
765
|
+
elem._columnParent || // virtual inside expand/inline is already error
|
|
766
|
+
elem._parent.kind === 'element' || // dito (expand without ref)
|
|
767
|
+
!elem.value?.path && !elem.value?.func || // neither ref nor function call
|
|
768
|
+
elem.value.args || // arguments (with function call)
|
|
769
|
+
elem.value.path?.length > 1 || // multi-path step, i.e. no new definition
|
|
770
|
+
elem.value.path?.some( ps => ps.args || ps.where ) // not a simple reference
|
|
789
771
|
)
|
|
790
772
|
return;
|
|
791
773
|
|
|
792
|
-
if (
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
warning('def-upcoming-virtual-change', [ elem.virtual.location, elem ], { name: elem.name.id });
|
|
774
|
+
if (elem.expand) {
|
|
775
|
+
warning( 'query-invalid-virtual-struct', [ elem.expand[$location], elem ],
|
|
776
|
+
{ code: `as ${ elem.name.id }` } );
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
error( 'def-upcoming-virtual-change', [ elem.virtual.location, elem ] );
|
|
780
|
+
}
|
|
800
781
|
}
|
|
801
782
|
|
|
802
783
|
function checkCalculatedElementValue( elem ) {
|
|
@@ -991,7 +972,7 @@ function check( model ) {
|
|
|
991
972
|
|
|
992
973
|
function checkAnnotationAssignment1( art, anno ) {
|
|
993
974
|
const name = anno.name?.id;
|
|
994
|
-
if (art.$contains?.$annotation && anno.kind === '$annotation') {
|
|
975
|
+
if (art.$contains?.$annotation && anno.kind === '$annotation' && anno._outer) {
|
|
995
976
|
if (checkAnnotationAcceptsExpressions( anno, art ))
|
|
996
977
|
checkAnnotationExpressions( anno, art );
|
|
997
978
|
}
|
|
@@ -1144,7 +1125,7 @@ function check( model ) {
|
|
|
1144
1125
|
return;
|
|
1145
1126
|
}
|
|
1146
1127
|
|
|
1147
|
-
// Struct expected (can only happen within arrays)?
|
|
1128
|
+
// Struct expected (can only happen within arrays, or CSN input)?
|
|
1148
1129
|
if (elementDecl._effectiveType.elements) {
|
|
1149
1130
|
if (value.literal !== 'struct') {
|
|
1150
1131
|
warning( null, loc, { anno }, 'A struct value is required here for annotation $(ANNO)' );
|
|
@@ -1385,17 +1366,4 @@ function isComplexView( art ) {
|
|
|
1385
1366
|
return (!art.query.from?._artifact || art.query.from._artifact.kind === 'element');
|
|
1386
1367
|
}
|
|
1387
1368
|
|
|
1388
|
-
/**
|
|
1389
|
-
* @param {XSN.Element} elem
|
|
1390
|
-
* @returns {boolean}
|
|
1391
|
-
*/
|
|
1392
|
-
function isFunctionWithoutParentheses( elem ) {
|
|
1393
|
-
const func = elem.value?.func;
|
|
1394
|
-
if (!func?.path || elem.value.args)
|
|
1395
|
-
return false;
|
|
1396
|
-
if (func.path.length !== 1)
|
|
1397
|
-
return false;
|
|
1398
|
-
return functionsWithoutParentheses.includes(func.path[0].id.toUpperCase());
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
1369
|
module.exports = check;
|
package/lib/compiler/define.js
CHANGED
|
@@ -298,6 +298,7 @@ function define( model ) {
|
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
300
300
|
setLink( art, '_block', block );
|
|
301
|
+
initExprAnnoBlock( art, block );
|
|
301
302
|
// dictAdd might set $duplicates
|
|
302
303
|
dictAdd( model.definitions, absolute, art );
|
|
303
304
|
}
|
|
@@ -393,6 +394,7 @@ function define( model ) {
|
|
|
393
394
|
|
|
394
395
|
function addExtension( ext, block ) {
|
|
395
396
|
setLink( ext, '_block', block );
|
|
397
|
+
initExprAnnoBlock( ext, block );
|
|
396
398
|
const absolute = ext.name && resolveUncheckedPath( ext.name, '_extensions', ext );
|
|
397
399
|
if (!absolute) // broken path
|
|
398
400
|
return;
|
|
@@ -429,6 +431,7 @@ function define( model ) {
|
|
|
429
431
|
if (prop === 'params' && name === '') // RETURNS
|
|
430
432
|
sub.name = { id: '', location: sub.location };
|
|
431
433
|
setLink( sub, '_block', parent._block );
|
|
434
|
+
initExprAnnoBlock( sub, parent._block );
|
|
432
435
|
setLink( sub, '_parent', parent );
|
|
433
436
|
setLink( sub, '_main', parent._main || parent );
|
|
434
437
|
initExtension( sub );
|
|
@@ -446,6 +449,26 @@ function define( model ) {
|
|
|
446
449
|
}
|
|
447
450
|
}
|
|
448
451
|
|
|
452
|
+
function initExprAnnoBlock( art, block ) {
|
|
453
|
+
// remark: `art` could also be the extension
|
|
454
|
+
for (const prop in art) {
|
|
455
|
+
if (prop.charAt(0) !== '@')
|
|
456
|
+
continue;
|
|
457
|
+
const anno = art[prop];
|
|
458
|
+
// _block links needed for `cast( … as Type )`, `[ ..., cast( … as Type ) ]`
|
|
459
|
+
if (anno.literal === 'array') {
|
|
460
|
+
anno.kind = '$annotation';
|
|
461
|
+
for (const item of anno.val)
|
|
462
|
+
setLink( item, '_block', block );
|
|
463
|
+
}
|
|
464
|
+
else if (anno.$tokenTexts) {
|
|
465
|
+
// remark: it wouldn't hurt to set it always...
|
|
466
|
+
anno.kind = '$annotation';
|
|
467
|
+
setLink( anno, '_block', block );
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
449
472
|
function addVocabulary( vocab, block, prefix ) {
|
|
450
473
|
setLink( vocab, '_block', block );
|
|
451
474
|
const { name } = vocab;
|
|
@@ -982,11 +1005,11 @@ function define( model ) {
|
|
|
982
1005
|
col: 'You have provided a $(PROP) already at line $(LINE), column $(COL)',
|
|
983
1006
|
} );
|
|
984
1007
|
// TODO: extra text variants for expand/inline? - probably not
|
|
985
|
-
col.val =
|
|
1008
|
+
col.val = '**'; // do not consider it for expandWildcard()
|
|
986
1009
|
}
|
|
987
1010
|
}
|
|
988
|
-
// Either expression (value), expand or new association
|
|
989
|
-
else if (col.value || col.
|
|
1011
|
+
// Either expression (value), expand, new virtual or new association
|
|
1012
|
+
else if (col.value || col.name) {
|
|
990
1013
|
if (!col._block)
|
|
991
1014
|
setLink( col, '_block', parent._block );
|
|
992
1015
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
@@ -1009,6 +1032,7 @@ function define( model ) {
|
|
|
1009
1032
|
}
|
|
1010
1033
|
|
|
1011
1034
|
initItemsLinks( col, parent._block );
|
|
1035
|
+
initExprAnnoBlock( col, parent._block );
|
|
1012
1036
|
}
|
|
1013
1037
|
|
|
1014
1038
|
if (hasItems && !wildcard && parent.excludingDict && !options.$recompile) {
|
|
@@ -1069,6 +1093,7 @@ function define( model ) {
|
|
|
1069
1093
|
const main = parent._main || parent;
|
|
1070
1094
|
const isQueryExtension = construct.kind === 'extend' && main.query;
|
|
1071
1095
|
let obj = initItemsLinks( construct, block );
|
|
1096
|
+
initExprAnnoBlock( construct, block );
|
|
1072
1097
|
if (obj.target && targetIsTargetAspect( obj )) {
|
|
1073
1098
|
obj.targetAspect = obj.target;
|
|
1074
1099
|
delete obj.target;
|
|
@@ -1096,7 +1121,7 @@ function define( model ) {
|
|
|
1096
1121
|
forEachGeneric( obj, 'enum', init );
|
|
1097
1122
|
}
|
|
1098
1123
|
|
|
1099
|
-
if (obj.foreignKeys)
|
|
1124
|
+
if (obj.foreignKeys)
|
|
1100
1125
|
forEachInOrder( obj, 'foreignKeys', init );
|
|
1101
1126
|
if (checkDefinitions( construct, parent, 'actions' ))
|
|
1102
1127
|
forEachGeneric( construct, 'actions', init );
|
|
@@ -1136,8 +1161,8 @@ function define( model ) {
|
|
|
1136
1161
|
}
|
|
1137
1162
|
if (hasElement) {
|
|
1138
1163
|
// This message is similar to the one above. In v6, we could probably
|
|
1139
|
-
// turn this warning into an error, remove `$syntax: 'element'
|
|
1140
|
-
//
|
|
1164
|
+
// turn this warning into an error, remove `$syntax: 'element',
|
|
1165
|
+
// and use the above `ext-unexpected-element` only for CSN input.
|
|
1141
1166
|
warning( 'ext-expecting-enum', [ obj.elements[$location], construct ],
|
|
1142
1167
|
{ code: 'extend … with enum' }, 'Use $(CODE) when extending enums' );
|
|
1143
1168
|
}
|
package/lib/compiler/extend.js
CHANGED
|
@@ -74,7 +74,8 @@ function extend( model ) {
|
|
|
74
74
|
applyIncludes, // TODO: re-check
|
|
75
75
|
} );
|
|
76
76
|
|
|
77
|
-
const includesNonShadowedFirst
|
|
77
|
+
const includesNonShadowedFirst
|
|
78
|
+
= isDeprecatedEnabled( model.options, '_includesNonShadowedFirst' );
|
|
78
79
|
|
|
79
80
|
sortModelSources();
|
|
80
81
|
const extensionsDict = Object.create( null ); // TODO TMP
|
|
@@ -489,6 +490,7 @@ function extend( model ) {
|
|
|
489
490
|
const location = firstEllipsis.location || anno.name.location;
|
|
490
491
|
message( 'anno-unexpected-ellipsis', [ location, art ], { code: '...' } );
|
|
491
492
|
previousAnno = {
|
|
493
|
+
kind: '$annotation',
|
|
492
494
|
val: [],
|
|
493
495
|
literal: 'array',
|
|
494
496
|
name: { id: annoName.slice( 1 ) },
|
|
@@ -499,6 +501,7 @@ function extend( model ) {
|
|
|
499
501
|
// TODO: If we introduce sub-messages, point to the non-array base value.
|
|
500
502
|
error( 'anno-mismatched-ellipsis', [ anno.name.location, art ], { code: '...' } );
|
|
501
503
|
previousAnno = {
|
|
504
|
+
kind: '$annotation',
|
|
502
505
|
val: [],
|
|
503
506
|
literal: 'array',
|
|
504
507
|
name: previousAnno.name,
|
|
@@ -532,6 +535,7 @@ function extend( model ) {
|
|
|
532
535
|
}
|
|
533
536
|
// console.log('TP:',previousValue.map(se),anno.val.map(se),'->',result.map(se))
|
|
534
537
|
return {
|
|
538
|
+
kind: '$annotation',
|
|
535
539
|
val: result,
|
|
536
540
|
literal: 'array',
|
|
537
541
|
name: previousAnno.name,
|
package/lib/compiler/generate.js
CHANGED
|
@@ -696,7 +696,7 @@ function generate( model ) {
|
|
|
696
696
|
$inferred: 'composition-entity',
|
|
697
697
|
};
|
|
698
698
|
if (target.name) { // named target aspect
|
|
699
|
-
if (options
|
|
699
|
+
if (!isDeprecatedEnabled( options, 'noCompositionIncludes' ))
|
|
700
700
|
art.includes = [ createInclude( target.name.id, location ) ];
|
|
701
701
|
setLink( art, '_origin', target );
|
|
702
702
|
setLink( art, '_upperAspects', [ target, ...(elem._main._upperAspects || []) ] );
|
|
@@ -721,20 +721,11 @@ function generate( model ) {
|
|
|
721
721
|
location,
|
|
722
722
|
},
|
|
723
723
|
};
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
//
|
|
727
|
-
if
|
|
728
|
-
|
|
729
|
-
'up__', '@odata.containment.ignore' );
|
|
730
|
-
up.on = augmentEqual( location, 'up_', Object.values( keys ), 'up__' );
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
up.key = { location, val: true };
|
|
734
|
-
// managed associations must be explicitly set to not null
|
|
735
|
-
// even if target cardinality is 1..1
|
|
736
|
-
up.notNull = { location, val: true };
|
|
737
|
-
}
|
|
724
|
+
|
|
725
|
+
up.key = { location, val: true };
|
|
726
|
+
// managed associations must be explicitly set to not null
|
|
727
|
+
// even if target cardinality is 1..1
|
|
728
|
+
up.notNull = { location, val: true };
|
|
738
729
|
|
|
739
730
|
dictAdd( art.elements, 'up_', up );
|
|
740
731
|
// Only for named aspects, use a new location; otherwise use the origin's one.
|
|
@@ -753,7 +744,7 @@ function generate( model ) {
|
|
|
753
744
|
// Copy persistence annotations from aspect.
|
|
754
745
|
copyPersistenceAnnotations( art, target ); // after extendArtifactBefore()
|
|
755
746
|
|
|
756
|
-
if (options
|
|
747
|
+
if (!isDeprecatedEnabled( options, 'noCompositionIncludes' ) && art.includes)
|
|
757
748
|
applyIncludes( art, art ); // for actions
|
|
758
749
|
return art;
|
|
759
750
|
}
|
|
@@ -798,12 +789,18 @@ function generate( model ) {
|
|
|
798
789
|
if (!source)
|
|
799
790
|
return;
|
|
800
791
|
|
|
801
|
-
|
|
792
|
+
// Copied since v6
|
|
793
|
+
const copyJournal = !isDeprecatedEnabled( options, 'noPersistenceJournalForGeneratedEntities' );
|
|
794
|
+
if (copyJournal)
|
|
795
|
+
copy( '@cds.persistence.journal' );
|
|
796
|
+
|
|
797
|
+
const copyExists = !isDeprecatedEnabled( options, '_eagerPersistenceForGeneratedEntities' );
|
|
802
798
|
if (copyExists)
|
|
803
799
|
copy( '@cds.persistence.exists' );
|
|
804
800
|
copy( '@cds.persistence.skip' );
|
|
805
801
|
copy( '@cds.tenant.independent' );
|
|
806
802
|
|
|
803
|
+
/** @param {string} anno */
|
|
807
804
|
function copy( anno ) {
|
|
808
805
|
if ( source[anno] && !target[anno] )
|
|
809
806
|
target[anno] = { ...source[anno], $inferred: 'parent-origin' };
|
package/lib/compiler/populate.js
CHANGED
|
@@ -91,16 +91,6 @@ function populate( model ) {
|
|
|
91
91
|
/** @type {any} may also be a boolean */
|
|
92
92
|
let newAutoExposed = [];
|
|
93
93
|
|
|
94
|
-
const scopedRedirections
|
|
95
|
-
= !isDeprecatedEnabled( options, '_shortAutoexposed' ) &&
|
|
96
|
-
!isDeprecatedEnabled( options, '_longAutoexposed' ) &&
|
|
97
|
-
!isDeprecatedEnabled( options, '_noInheritedAutoexposeViaComposition' ) &&
|
|
98
|
-
!isDeprecatedEnabled( options, '_noScopedRedirections' );
|
|
99
|
-
const autoexposeViaComposition
|
|
100
|
-
= (isDeprecatedEnabled( options, '_noInheritedAutoexposeViaComposition' ))
|
|
101
|
-
? 'Composition'
|
|
102
|
-
: true;
|
|
103
|
-
const redirectInSubQueries = isDeprecatedEnabled( options, '_redirectInSubQueries' );
|
|
104
94
|
const ignoreSpecifiedElements
|
|
105
95
|
= isDeprecatedEnabled( model.options, 'ignoreSpecifiedQueryElements' );
|
|
106
96
|
|
|
@@ -413,7 +403,7 @@ function populate( model ) {
|
|
|
413
403
|
// console.log( message( null, location, art, {target:struct,art}, 'Info','EXPAND-ELEM')
|
|
414
404
|
// .toString(), Object.keys(struct.elements))
|
|
415
405
|
proxyCopyMembers( art, 'elements', struct.elements, location,
|
|
416
|
-
null, isDeprecatedEnabled( options, '
|
|
406
|
+
null, isDeprecatedEnabled( options, '_noKeyPropagationWithExpansions' ) );
|
|
417
407
|
// Set elements expansion status (the if condition is always true, as no
|
|
418
408
|
// elements expansion will take place on artifact with existing other
|
|
419
409
|
// member property):
|
|
@@ -750,8 +740,8 @@ function populate( model ) {
|
|
|
750
740
|
const siblings = wildcardSiblings( columns, query );
|
|
751
741
|
expandWildcard( col, siblings, inlineHead, query );
|
|
752
742
|
}
|
|
753
|
-
// If neither expression (value), expand nor new association.
|
|
754
|
-
if (!col.value && !col.
|
|
743
|
+
// If neither expression (value), expand, new virtual nor new association.
|
|
744
|
+
if (!col.value && !col.name)
|
|
755
745
|
continue; // error should have been reported by parser
|
|
756
746
|
if (col.inline) {
|
|
757
747
|
const q = userQuery( query );
|
|
@@ -794,7 +784,7 @@ function populate( model ) {
|
|
|
794
784
|
function ensureColumnName( col, colIndex, query, insideExpand ) {
|
|
795
785
|
if (col.name)
|
|
796
786
|
return col.name.id;
|
|
797
|
-
if (col.inline || col.val === '*')
|
|
787
|
+
if (col.inline || col.val === '*' || col.val === '**') // '**' = duplicate '*'
|
|
798
788
|
return '';
|
|
799
789
|
const path = col.value &&
|
|
800
790
|
(col.value.path || !col.value.args && col.value.func?.path);
|
|
@@ -1085,7 +1075,7 @@ function populate( model ) {
|
|
|
1085
1075
|
if (assoc.kind === 'mixin')
|
|
1086
1076
|
return false;
|
|
1087
1077
|
const query = userQuery( assoc );
|
|
1088
|
-
return
|
|
1078
|
+
return !query || query._main._leadingQuery === query;
|
|
1089
1079
|
}
|
|
1090
1080
|
|
|
1091
1081
|
function redirectImplicitlyDo( elem, assoc, target, service ) {
|
|
@@ -1096,8 +1086,7 @@ function populate( model ) {
|
|
|
1096
1086
|
// the current main artifact is a suitable auto-redirection target → return it
|
|
1097
1087
|
return elem._main;
|
|
1098
1088
|
}
|
|
1099
|
-
const elemScope =
|
|
1100
|
-
preferredElemScope( target, service, elem, assoc._main || assoc );
|
|
1089
|
+
const elemScope = preferredElemScope( target, service, elem, assoc._main || assoc );
|
|
1101
1090
|
const exposed = minimalExposure( target, service, elemScope );
|
|
1102
1091
|
|
|
1103
1092
|
if (!exposed.length) {
|
|
@@ -1304,24 +1293,12 @@ function populate( model ) {
|
|
|
1304
1293
|
return false;
|
|
1305
1294
|
}
|
|
1306
1295
|
// no @cds.autoexpose or @cds.autoexpose:null
|
|
1307
|
-
|
|
1308
|
-
art.$autoexpose = model.$compositionTargets[art.name.id]
|
|
1309
|
-
? autoexposeViaComposition
|
|
1310
|
-
: null;
|
|
1296
|
+
art.$autoexpose = model.$compositionTargets[art.name.id] ? true : null;
|
|
1311
1297
|
return true; // still check for inherited @cds.autoexpose
|
|
1312
1298
|
}
|
|
1313
1299
|
|
|
1314
1300
|
function autoExposedName( target, service, elemScope ) {
|
|
1315
1301
|
const absolute = target.name.id;
|
|
1316
|
-
if (isDeprecatedEnabled( options, '_shortAutoexposed' )) {
|
|
1317
|
-
const parent = definitionScope( target )._parent;
|
|
1318
|
-
const name = (parent) ? absolute.substring( parent.name.id.length + 1 ) : absolute;
|
|
1319
|
-
// no need for dedot here (as opposed to deprecated._longAutoexposed), as
|
|
1320
|
-
// the name for dependent entities have already been created using `_` then
|
|
1321
|
-
return `${ service.name.id }.${ name }`;
|
|
1322
|
-
}
|
|
1323
|
-
if (isDeprecatedEnabled( options, '_longAutoexposed' ))
|
|
1324
|
-
return `${ service.name.id }.${ absolute }`;
|
|
1325
1302
|
const base = definitionScope( target );
|
|
1326
1303
|
if (base === target)
|
|
1327
1304
|
return `${ service.name.id }.${ absolute.substring( absolute.lastIndexOf( '.' ) + 1 ) }`;
|
|
@@ -1338,7 +1315,7 @@ function populate( model ) {
|
|
|
1338
1315
|
function createAutoExposed( target, service, elemScope ) {
|
|
1339
1316
|
const absolute = autoExposedName( target, service, elemScope );
|
|
1340
1317
|
const autoexposed = model.definitions[absolute];
|
|
1341
|
-
if (autoexposed && (autoexposed.kind !== 'namespace'
|
|
1318
|
+
if (autoexposed && (autoexposed.kind !== 'namespace')) {
|
|
1342
1319
|
if (isDirectProjection( autoexposed, target )) {
|
|
1343
1320
|
const anno = autoexposed['@cds.redirection.target'];
|
|
1344
1321
|
if (annotationIsFalse( anno )) {
|
|
@@ -12,7 +12,6 @@ const {
|
|
|
12
12
|
forEachDefinition,
|
|
13
13
|
forEachMember,
|
|
14
14
|
forEachGeneric,
|
|
15
|
-
isDeprecatedEnabled,
|
|
16
15
|
} = require( '../base/model');
|
|
17
16
|
const {
|
|
18
17
|
setLink,
|
|
@@ -30,7 +29,7 @@ const { xprRewriteFns } = require('./xpr-rewrite');
|
|
|
30
29
|
function propagate( model ) {
|
|
31
30
|
const props = {
|
|
32
31
|
'@': annotation, // always except in 'items' (and parameters for entity return types)
|
|
33
|
-
doc:
|
|
32
|
+
doc: docComment, // like annotations, but guarded by option `propagateDocComments`
|
|
34
33
|
default: withKind, // always except in 'items'
|
|
35
34
|
virtual,
|
|
36
35
|
notNull,
|
|
@@ -72,19 +71,15 @@ function propagate( model ) {
|
|
|
72
71
|
for (const rule in propagationRules)
|
|
73
72
|
props[rule] = ruleToFunction[propagationRules[rule]];
|
|
74
73
|
|
|
75
|
-
const { options } = model;
|
|
76
74
|
const { rewriteAnnotationsRefs } = xprRewriteFns( model );
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
const { warning, throwWithError } = model.$messageFunctions;
|
|
75
|
+
|
|
76
|
+
const { message, throwWithError } = model.$messageFunctions;
|
|
80
77
|
|
|
81
78
|
forEachDefinition( model, run );
|
|
82
79
|
forEachGeneric( model, 'vocabularies', run );
|
|
83
80
|
|
|
84
|
-
// TODO: move 'virtual' handling/checks to resolver
|
|
85
|
-
|
|
86
|
-
if (!oldVirtualNotNullPropagation) // check would always be right, but to be ultra compatible…
|
|
87
|
-
forEachDefinition( model, checkVirtual );
|
|
81
|
+
// TODO: move 'virtual' handling/checks to resolver
|
|
82
|
+
forEachDefinition( model, checkVirtual );
|
|
88
83
|
throwWithError();
|
|
89
84
|
return model;
|
|
90
85
|
|
|
@@ -176,22 +171,6 @@ function propagate( model ) {
|
|
|
176
171
|
if (transformer)
|
|
177
172
|
transformer( prop, target, source, viaType );
|
|
178
173
|
}
|
|
179
|
-
// propagate NOT NULL and VIRTUAL from sub elements with
|
|
180
|
-
// 'deprecated.oldVirtualNotNullPropagation':
|
|
181
|
-
if (oldVirtualNotNullPropagation &&
|
|
182
|
-
target.$inferred !== 'proxy' &&
|
|
183
|
-
target.kind === 'element' && source.kind === 'element') {
|
|
184
|
-
let elem = source; // the outer element
|
|
185
|
-
while (elem._parent.kind === 'element')
|
|
186
|
-
elem = elem._parent;
|
|
187
|
-
if (elem !== source) {
|
|
188
|
-
if (target.notNull === undefined && elem.notNull !== undefined)
|
|
189
|
-
props.notNull( 'notNull', target, elem );
|
|
190
|
-
if (target.virtual === undefined && elem.virtual !== undefined)
|
|
191
|
-
props.virtual( 'virtual', target, elem );
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
// setLink( target, '_status', 'shallow-propagated' );
|
|
195
174
|
}
|
|
196
175
|
|
|
197
176
|
function never() { /* no-op: don't propagate */ }
|
|
@@ -252,12 +231,14 @@ function propagate( model ) {
|
|
|
252
231
|
return;
|
|
253
232
|
if (prop === 'params' && target.$inferred !== 'proxy' && target.$inferred !== 'include')
|
|
254
233
|
return;
|
|
255
|
-
|
|
256
|
-
|
|
234
|
+
// Remark: occurrences of `foreignKeys` which are not propagated already in
|
|
235
|
+
// tweak-assocs.js: inside `targetAspect` and parameters
|
|
236
|
+
const dict = source[prop];
|
|
237
|
+
if (prop === 'foreignKeys' && (!dict || target.on))
|
|
238
|
+
return; // e.g. published associations with filters, or `Association to many …`
|
|
257
239
|
const location = target.type && !target.type.$inferred && target.type.location ||
|
|
258
240
|
target.location ||
|
|
259
241
|
target._outer && target._outer.location;
|
|
260
|
-
const dict = source[prop];
|
|
261
242
|
target[prop] = Object.create( null ); // also propagate empty elements
|
|
262
243
|
const propagateKey = target.kind === 'aspect'; // anonymous aspect
|
|
263
244
|
for (const name in dict) {
|
|
@@ -318,6 +299,13 @@ function propagate( model ) {
|
|
|
318
299
|
withKind( prop, target, source );
|
|
319
300
|
}
|
|
320
301
|
|
|
302
|
+
function docComment( prop, target, source ) {
|
|
303
|
+
if (model.options.propagateDocComments)
|
|
304
|
+
annotation( prop, target, source );
|
|
305
|
+
else // TODO: Probably just "never"
|
|
306
|
+
onlyViaParent( prop, target, source );
|
|
307
|
+
}
|
|
308
|
+
|
|
321
309
|
function onlyViaArtifact( prop, target, source ) {
|
|
322
310
|
const from = viewFromPrimary( target )?.path;
|
|
323
311
|
// do not propagate from member / if follow assoc in from or into `returns` of actions (v4)
|
|
@@ -333,10 +321,8 @@ function propagate( model ) {
|
|
|
333
321
|
always( prop, target, source ); // not in 'items'
|
|
334
322
|
}
|
|
335
323
|
|
|
336
|
-
function notNull( prop, target, source,
|
|
324
|
+
function notNull( prop, target, source, _viaType ) {
|
|
337
325
|
// Really "reset" NOT NULL when ref has assoc with cardinality min: 0 (TODO: Universal CSN)
|
|
338
|
-
if (oldVirtualNotNullPropagation && viaType)
|
|
339
|
-
return; // strange propagation not supported with Universal CSN
|
|
340
326
|
if (target.value && withAssociation( target.value, targetMinZero ))
|
|
341
327
|
target[prop] = { $inferred: 'NULL', val: undefined }; // set null value in Universal CSN
|
|
342
328
|
// $inferred: 'NULL' is only an issue for sub elements with a 'value' property;
|
|
@@ -348,7 +334,7 @@ function propagate( model ) {
|
|
|
348
334
|
function virtual( prop, target, source, viaType ) {
|
|
349
335
|
if (!viaType)
|
|
350
336
|
always( prop, target, source );
|
|
351
|
-
else
|
|
337
|
+
else // NULL would block strange propagation to sub element
|
|
352
338
|
target[prop] = { $inferred: 'NULL', val: undefined }; // set null value in Universal CSN
|
|
353
339
|
}
|
|
354
340
|
|
|
@@ -360,14 +346,14 @@ function propagate( model ) {
|
|
|
360
346
|
function checkNonVirtualElement( elem ) {
|
|
361
347
|
// Not enough at all, but so are the current checks - a complete expression
|
|
362
348
|
// must be checked. Here we just check what might have worked before.
|
|
363
|
-
// TODO: Propagate 'virtual' in resolver
|
|
349
|
+
// TODO: Propagate 'virtual' in resolver.
|
|
364
350
|
const path = !elem.virtual && elem.value && elem.value.path;
|
|
365
351
|
if (!path || path.broken)
|
|
366
352
|
return;
|
|
367
353
|
for (const item of path) {
|
|
368
354
|
const art = item && item._artifact;
|
|
369
355
|
if (art?.virtual?.val) {
|
|
370
|
-
|
|
356
|
+
message( 'def-missing-virtual', [ item.location, elem ], { art, keyword: 'virtual' },
|
|
371
357
|
// eslint-disable-next-line @stylistic/js/max-len
|
|
372
358
|
'Prepend $(KEYWORD) to current select item - containing element $(ART) is virtual' );
|
|
373
359
|
return;
|