@sap/cds-compiler 5.9.4 → 6.0.10
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 +104 -319
- package/README.md +1 -1
- package/bin/cds_update_identifiers.js +3 -5
- package/bin/cdsc.js +22 -8
- 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 +190 -96
- package/lib/base/messages.js +29 -20
- 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 +30 -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 +12 -9
- package/lib/compiler/checks.js +18 -50
- package/lib/compiler/define.js +6 -6
- package/lib/compiler/extend.js +2 -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 +35 -22
- package/lib/compiler/shared.js +7 -1
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +1 -1
- package/lib/edm/annotations/edmJson.js +20 -15
- package/lib/edm/annotations/genericTranslation.js +7 -8
- package/lib/edm/csn2edm.js +46 -50
- package/lib/edm/edm.js +8 -7
- package/lib/edm/edmPreprocessor.js +33 -83
- package/lib/edm/edmUtils.js +2 -2
- package/lib/gen/BaseParser.js +55 -44
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1133 -1150
- 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/csnUtils.js +28 -39
- package/lib/model/xprAsTree.js +23 -9
- package/lib/modelCompare/compare.js +5 -4
- package/lib/optionProcessor.js +21 -17
- package/lib/parsers/AstBuildingParser.js +63 -11
- 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 ) {
|
|
@@ -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
|
@@ -982,11 +982,11 @@ function define( model ) {
|
|
|
982
982
|
col: 'You have provided a $(PROP) already at line $(LINE), column $(COL)',
|
|
983
983
|
} );
|
|
984
984
|
// TODO: extra text variants for expand/inline? - probably not
|
|
985
|
-
col.val =
|
|
985
|
+
col.val = '**'; // do not consider it for expandWildcard()
|
|
986
986
|
}
|
|
987
987
|
}
|
|
988
|
-
// Either expression (value), expand or new association
|
|
989
|
-
else if (col.value || col.
|
|
988
|
+
// Either expression (value), expand, new virtual or new association
|
|
989
|
+
else if (col.value || col.name) {
|
|
990
990
|
if (!col._block)
|
|
991
991
|
setLink( col, '_block', parent._block );
|
|
992
992
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
@@ -1096,7 +1096,7 @@ function define( model ) {
|
|
|
1096
1096
|
forEachGeneric( obj, 'enum', init );
|
|
1097
1097
|
}
|
|
1098
1098
|
|
|
1099
|
-
if (obj.foreignKeys)
|
|
1099
|
+
if (obj.foreignKeys)
|
|
1100
1100
|
forEachInOrder( obj, 'foreignKeys', init );
|
|
1101
1101
|
if (checkDefinitions( construct, parent, 'actions' ))
|
|
1102
1102
|
forEachGeneric( construct, 'actions', init );
|
|
@@ -1136,8 +1136,8 @@ function define( model ) {
|
|
|
1136
1136
|
}
|
|
1137
1137
|
if (hasElement) {
|
|
1138
1138
|
// This message is similar to the one above. In v6, we could probably
|
|
1139
|
-
// turn this warning into an error, remove `$syntax: 'element'
|
|
1140
|
-
//
|
|
1139
|
+
// turn this warning into an error, remove `$syntax: 'element',
|
|
1140
|
+
// and use the above `ext-unexpected-element` only for CSN input.
|
|
1141
1141
|
warning( 'ext-expecting-enum', [ obj.elements[$location], construct ],
|
|
1142
1142
|
{ code: 'extend … with enum' }, 'Use $(CODE) when extending enums' );
|
|
1143
1143
|
}
|
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
|
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;
|
package/lib/compiler/resolve.js
CHANGED
|
@@ -143,10 +143,6 @@ function resolve( model ) {
|
|
|
143
143
|
const msg = semanticLoc && 'target';
|
|
144
144
|
error( 'ref-cyclic', [ location, semanticLoc || user ], {
|
|
145
145
|
art, '#': msg,
|
|
146
|
-
}, {
|
|
147
|
-
std: 'Illegal circular reference to $(ART)',
|
|
148
|
-
element: 'Illegal circular reference to element $(MEMBER) of $(ART)',
|
|
149
|
-
target: 'Illegal circular reference to target $(ART)',
|
|
150
146
|
} );
|
|
151
147
|
}
|
|
152
148
|
} );
|
|
@@ -1172,12 +1168,24 @@ function resolve( model ) {
|
|
|
1172
1168
|
return [ null, '$navElement', '$tableAlias' ][path.length] === (kind || true) && elem._origin;
|
|
1173
1169
|
}
|
|
1174
1170
|
|
|
1175
|
-
function
|
|
1171
|
+
function isQuasiVirtualAssociation( art ) {
|
|
1172
|
+
if (art.on)
|
|
1173
|
+
return false;
|
|
1174
|
+
if (art.foreignKeys != null)
|
|
1175
|
+
return !art.foreignKeys;
|
|
1176
1176
|
const max = art.cardinality?.targetMax;
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1177
|
+
return max && (typeof max.val !== 'number' || max.val > 1);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
function addImplicitForeignKeys( art, obj, target ) {
|
|
1181
|
+
if (!art.$inferred && !art.virtual?.val && isQuasiVirtualAssociation( obj )) {
|
|
1182
|
+
if (!isDeprecatedEnabled( options, 'noQuasiVirtualAssocs' )) {
|
|
1183
|
+
// TODO: set `foreignKeys`, `ON` or `virtual` to false or similar to
|
|
1184
|
+
// indicate that it is already handled (and that we might not be able to
|
|
1185
|
+
// follow the assoc)? Let us set foreignKeys to false
|
|
1186
|
+
obj.foreignKeys = false;
|
|
1187
|
+
return; // no foreign keys for `Association to many Target`;
|
|
1188
|
+
}
|
|
1181
1189
|
}
|
|
1182
1190
|
obj.foreignKeys = Object.create( null );
|
|
1183
1191
|
forEachInOrder( target, 'elements', ( elem, name ) => {
|
|
@@ -1306,6 +1314,8 @@ function resolve( model ) {
|
|
|
1306
1314
|
'The redirected target is the original $(ART)' );
|
|
1307
1315
|
}
|
|
1308
1316
|
|
|
1317
|
+
// No check with user-provided ON/fKeys (remark: compiler deduceds ON/fKeys
|
|
1318
|
+
// _after_ redirecting the target = no $inferred test necessary):
|
|
1309
1319
|
if (elem.foreignKeys || elem.on)
|
|
1310
1320
|
return; // TODO: or should we still bring an msg if nothing in common?
|
|
1311
1321
|
|
|
@@ -1562,9 +1572,10 @@ function resolve( model ) {
|
|
|
1562
1572
|
const art = type && (type.kind === 'entity' ? type : type.target?._artifact);
|
|
1563
1573
|
if (!art)
|
|
1564
1574
|
return ref; // error already reported via resolvePathItem()
|
|
1565
|
-
const unexpectedFilter = expected !== 'column' && expected !== 'calc'
|
|
1575
|
+
const unexpectedFilter = expected !== 'column' && expected !== 'calc' && 'std' ||
|
|
1576
|
+
isQuasiVirtualAssociation( type ) && 'model-only';
|
|
1566
1577
|
if (last.args || last.where || last.cardinality)
|
|
1567
|
-
reportUnexpectedArgsAndFilter( last, expected, user, art, unexpectedFilter
|
|
1578
|
+
reportUnexpectedArgsAndFilter( last, expected, user, art, unexpectedFilter );
|
|
1568
1579
|
// TODO: we should have different message-ids for the "last" stuff: adding
|
|
1569
1580
|
// `.item` likely corrects the ref, probably with location at end of ref
|
|
1570
1581
|
return ref;
|
|
@@ -1622,12 +1633,12 @@ function resolve( model ) {
|
|
|
1622
1633
|
function resolveEnumSymbol( expr, expected, user, type ) {
|
|
1623
1634
|
const { sym } = expr;
|
|
1624
1635
|
// CSN input with both '#'+val (recompilation) → do not resolve
|
|
1625
|
-
if (!sym || expr.val !== undefined
|
|
1636
|
+
if (!sym || expr.val !== undefined)
|
|
1626
1637
|
return;
|
|
1627
1638
|
// The parameter def for an argument, and the annotation def for an assignment
|
|
1628
1639
|
// is in `type._artifact`, it is not `type` itself:
|
|
1629
|
-
type = effectiveType( type.id ? type._artifact : type );
|
|
1630
|
-
// Remark:
|
|
1640
|
+
type = type && effectiveType( type.id ? type._artifact : type );
|
|
1641
|
+
// Remark: type could be 0 for cyclic parameter type
|
|
1631
1642
|
if (type && type.foreignKeys) {
|
|
1632
1643
|
const keys = Object.values( type.foreignKeys );
|
|
1633
1644
|
if (keys.length === 1) {
|
|
@@ -1636,11 +1647,14 @@ function resolve( model ) {
|
|
|
1636
1647
|
}
|
|
1637
1648
|
}
|
|
1638
1649
|
const symbols = type && type.enum;
|
|
1639
|
-
if (!symbols)
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1650
|
+
if (!symbols) {
|
|
1651
|
+
if (user.kind !== '$annotation') { // TODO: better type deduction for annotations
|
|
1652
|
+
const msg = (user.kind === 'enum') ? 'symbolDef' : type && 'invalidType';
|
|
1653
|
+
warning( 'ref-unexpected-enum', [ expr.location, user ],
|
|
1654
|
+
{ '#': msg || 'untyped', enum: sym.id, type: type || '' } );
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
else if (symbols[sym.id]) {
|
|
1644
1658
|
setLink( sym, '_artifact', symbols[sym.id] );
|
|
1645
1659
|
}
|
|
1646
1660
|
else {
|
|
@@ -1649,8 +1663,7 @@ function resolve( model ) {
|
|
|
1649
1663
|
type = getOrigin( type );
|
|
1650
1664
|
const err = message( 'ref-undefined-enum', [ sym.location, user ],
|
|
1651
1665
|
{ id: sym.id, type } );
|
|
1652
|
-
|
|
1653
|
-
attachAndEmitValidNames( err, symbols );
|
|
1666
|
+
attachAndEmitValidNames( err, symbols );
|
|
1654
1667
|
}
|
|
1655
1668
|
}
|
|
1656
1669
|
|
|
@@ -1662,7 +1675,7 @@ function resolve( model ) {
|
|
|
1662
1675
|
if ((step.where || step.cardinality) && variant) {
|
|
1663
1676
|
const location = combinedLocation( step.where, step.cardinality );
|
|
1664
1677
|
// XSN TODO: filter$location including […]
|
|
1665
|
-
|
|
1678
|
+
error( 'expr-unexpected-filter', [ location, user ], { '#': variant } );
|
|
1666
1679
|
}
|
|
1667
1680
|
return variant && traverseExpr.SKIP;
|
|
1668
1681
|
}
|
package/lib/compiler/shared.js
CHANGED
|
@@ -1286,6 +1286,8 @@ function fns( model ) {
|
|
|
1286
1286
|
if (!user.elements && !user.actions && !user.enum && !user.params &&
|
|
1287
1287
|
couldBeDraftsEntity( item.id, valid, prev, path ))
|
|
1288
1288
|
return;
|
|
1289
|
+
if (couldBeDraftAdminDataEntity( item ) )
|
|
1290
|
+
return;
|
|
1289
1291
|
signalNotFound( (valid.length > 1 ? 'ext-undefined-art' : 'ext-undefined-def'),
|
|
1290
1292
|
// TODO: ext-undefined-xyz
|
|
1291
1293
|
[ item.location, user ], valid, { art } );
|
|
@@ -1293,11 +1295,15 @@ function fns( model ) {
|
|
|
1293
1295
|
|
|
1294
1296
|
function couldBeDraftsEntity( id, valid, prev, path ) {
|
|
1295
1297
|
const entity = prev
|
|
1296
|
-
? prev === path[path.length - 2]._artifact && prev
|
|
1298
|
+
? prev === path[path.length - 2]._artifact && prev // TODO: Should check for '.drafts'?
|
|
1297
1299
|
: path.length === 1 && id.endsWith( '.drafts' ) && model.definitions[id.slice( 0, -7 )];
|
|
1298
1300
|
return entity?.kind === 'entity' && !!entity._service;
|
|
1299
1301
|
}
|
|
1300
1302
|
|
|
1303
|
+
function couldBeDraftAdminDataEntity( item ) {
|
|
1304
|
+
return item.id === 'DraftAdministrativeData' && !item._artifact;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1301
1307
|
function undefinedParam( user, head, valid, _dict, _art, _path, semantics ) {
|
|
1302
1308
|
// TODO: text variant if there are no parameters, or in artifactParameters()
|
|
1303
1309
|
// TODO: use prepared message variants
|
|
@@ -274,7 +274,7 @@ function tweakAssocs( model ) {
|
|
|
274
274
|
// target -> avoid infloop ourselves with _status.
|
|
275
275
|
// TODO: this should be good now
|
|
276
276
|
const chain = [];
|
|
277
|
-
while (!elem.on &&
|
|
277
|
+
while (!elem.on && elem.foreignKeys == null) {
|
|
278
278
|
chain.push( elem );
|
|
279
279
|
if (elem._status === 'rewrite') { // circular dependency (already reported)
|
|
280
280
|
for (const e of chain)
|
package/lib/compiler/utils.js
CHANGED
|
@@ -126,7 +126,7 @@ function proxyCopyMembers( art, dictProp, originDict, location, kind, tmpDepreca
|
|
|
126
126
|
if (kind)
|
|
127
127
|
member.kind = kind;
|
|
128
128
|
else if (origin.key && !tmpDeprecated)
|
|
129
|
-
// TODO(v6): remove tmpDeprecated once `
|
|
129
|
+
// TODO(v6): remove tmpDeprecated once `_noKeyPropagationWithExpansions` is removed
|
|
130
130
|
member.key = Object.assign( { $inferred: 'expanded' }, origin.key );
|
|
131
131
|
if (kind && origin.masked) // TODO: remove!
|
|
132
132
|
member.masked = Object.assign( { $inferred: 'nav' }, origin.masked );
|