@sap/cds-compiler 2.13.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
package/lib/compiler/define.js
CHANGED
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
// References are resolved in the "resolve" phase of the compiler, see
|
|
78
78
|
// './resolver.js'. We then get the properties `type.absolute` and `length`.
|
|
79
79
|
|
|
80
|
-
// Sub phase 1 (addXYZ) - only for main
|
|
80
|
+
// Sub phase 1 (addXYZ) - only for main artifacts
|
|
81
81
|
// - set _block links
|
|
82
82
|
// - store definitions (including context extensions), NO duplicate check
|
|
83
83
|
// - artifact name check
|
|
@@ -505,13 +505,13 @@ function define( model ) {
|
|
|
505
505
|
initParentLink( parent, definitions );
|
|
506
506
|
}
|
|
507
507
|
if (art.kind !== 'namespace' &&
|
|
508
|
-
isDeprecatedEnabled( options, '
|
|
508
|
+
isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' )) {
|
|
509
509
|
let p = parent;
|
|
510
510
|
while (p && kindProperties[p.kind].artifacts)
|
|
511
511
|
p = p._parent;
|
|
512
512
|
if (p) {
|
|
513
513
|
error( 'subartifacts-not-supported', [ art.name.location, art ],
|
|
514
|
-
{ art: p, prop: 'deprecated.
|
|
514
|
+
{ art: p, prop: 'deprecated._generatedEntityNameWithUnderscore' },
|
|
515
515
|
// eslint-disable-next-line max-len
|
|
516
516
|
'With the option $(PROP), no sub artifact can be defined for a non-context/service $(ART)' );
|
|
517
517
|
}
|
|
@@ -572,7 +572,7 @@ function define( model ) {
|
|
|
572
572
|
if (query.on)
|
|
573
573
|
initExprForQuery( query.on, query );
|
|
574
574
|
// TODO: MIXIN with name = ...subquery (not yet supported anyway)
|
|
575
|
-
initSelectItems( query, query.columns );
|
|
575
|
+
initSelectItems( query, query.columns, query );
|
|
576
576
|
if (query.where)
|
|
577
577
|
initExprForQuery( query.where, query );
|
|
578
578
|
if (query.having)
|
|
@@ -580,12 +580,14 @@ function define( model ) {
|
|
|
580
580
|
initMembers( query, query, query._block );
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
function initSelectItems( parent, columns ) {
|
|
583
|
+
function initSelectItems( parent, columns, user ) {
|
|
584
584
|
// TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
585
585
|
let wildcard = null;
|
|
586
|
+
let hasItems = false;
|
|
586
587
|
for (const col of columns || parent.expand || parent.inline || []) {
|
|
587
588
|
if (!col) // parse error
|
|
588
589
|
continue;
|
|
590
|
+
hasItems = true;
|
|
589
591
|
if (!columns) {
|
|
590
592
|
if (parent.value)
|
|
591
593
|
setLink( col, '_pathHead', parent ); // also set for '*' in expand/inline
|
|
@@ -606,13 +608,27 @@ function define( model ) {
|
|
|
606
608
|
col.val = null; // do not consider it for expandWildcard()
|
|
607
609
|
}
|
|
608
610
|
}
|
|
609
|
-
|
|
611
|
+
// Either expression (value), expand or new association (target && type)
|
|
612
|
+
else if (col.value || col.expand || (col.target && col.type)) {
|
|
610
613
|
setLink( col, '_block', parent._block );
|
|
611
614
|
defineAnnotations( col, col, parent._block ); // TODO: complain with inline
|
|
612
615
|
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
613
616
|
if (columns)
|
|
614
617
|
initExprForQuery( col.value, parent );
|
|
615
|
-
initSelectItems( col );
|
|
618
|
+
initSelectItems( col, null, user );
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (hasItems && !wildcard && parent.excludingDict) {
|
|
623
|
+
// TODO: Better way to get source file?
|
|
624
|
+
let block = parent;
|
|
625
|
+
while (block._block)
|
|
626
|
+
block = block._block;
|
|
627
|
+
|
|
628
|
+
if (block.$frontend === 'cdl') {
|
|
629
|
+
warning('query-ignoring-exclude', [ parent.excludingDict[$location], user ],
|
|
630
|
+
{ prop: '*' },
|
|
631
|
+
'Excluding elements without wildcard $(PROP) has no effect');
|
|
616
632
|
}
|
|
617
633
|
}
|
|
618
634
|
}
|
|
@@ -898,15 +914,43 @@ function define( model ) {
|
|
|
898
914
|
delete obj.on; // continuation semantics: not specified
|
|
899
915
|
}
|
|
900
916
|
if (targetAspect.elements) {
|
|
917
|
+
const inEntity = parent._main && parent._main.kind === 'entity';
|
|
918
|
+
// TODO: also allow indirectly (component in component in entity)?
|
|
901
919
|
setLink( targetAspect, '_outer', obj );
|
|
902
920
|
setLink( targetAspect, '_parent', parent._parent );
|
|
903
921
|
setLink( targetAspect, '_main', null ); // for name resolution
|
|
904
|
-
|
|
905
|
-
parent =
|
|
922
|
+
|
|
923
|
+
parent = targetAspect;
|
|
906
924
|
construct = parent; // avoid extension behavior
|
|
907
|
-
|
|
908
|
-
setLink(
|
|
909
|
-
initDollarSelf(
|
|
925
|
+
targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
|
|
926
|
+
setLink( targetAspect, '_block', block );
|
|
927
|
+
initDollarSelf( targetAspect );
|
|
928
|
+
// allow ref of up_ in anonymous aspect inside entity
|
|
929
|
+
// (TODO: complain if used and the managed composition is included into
|
|
930
|
+
// another entity - might induce auto-redirection):
|
|
931
|
+
if (inEntity && !targetAspect.elements.up_) {
|
|
932
|
+
const up = {
|
|
933
|
+
name: {
|
|
934
|
+
id: 'up_',
|
|
935
|
+
alias: 'up_',
|
|
936
|
+
element: obj.name.element,
|
|
937
|
+
absolute: obj.name.absolute,
|
|
938
|
+
},
|
|
939
|
+
kind: '$navElement',
|
|
940
|
+
location: obj.location,
|
|
941
|
+
};
|
|
942
|
+
setLink( up, '_parent', targetAspect );
|
|
943
|
+
setLink( up, '_main', targetAspect ); // used on main artifact
|
|
944
|
+
// recompilation case: both target and targetAspect → allow up_ in that case, too:
|
|
945
|
+
const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
|
|
946
|
+
const entity = name && model.definitions[name];
|
|
947
|
+
if (entity && entity.elements)
|
|
948
|
+
setLink( up, '_origin', entity.elements.up_ );
|
|
949
|
+
// processAspectComposition/expand() sets _origin to element of
|
|
950
|
+
// generated target entity
|
|
951
|
+
targetAspect.$tableAliases.up_ = up;
|
|
952
|
+
}
|
|
953
|
+
obj = targetAspect;
|
|
910
954
|
}
|
|
911
955
|
}
|
|
912
956
|
if (obj !== parent && obj.elements && parent.enum) {
|
|
@@ -926,6 +970,7 @@ function define( model ) {
|
|
|
926
970
|
if (checkDefinitions( construct, parent, 'enum', obj.enum || false ))
|
|
927
971
|
forEachGeneric( obj, 'enum', init );
|
|
928
972
|
}
|
|
973
|
+
|
|
929
974
|
if (obj.foreignKeys) // cannot be extended or annotated - TODO: check anyway?
|
|
930
975
|
forEachInOrder( obj, 'foreignKeys', init );
|
|
931
976
|
if (checkDefinitions( construct, parent, 'actions' ))
|
|
@@ -1009,7 +1054,7 @@ function define( model ) {
|
|
|
1009
1054
|
// - artifacts (CDL-only anyway) only inside [extend] context|service
|
|
1010
1055
|
if (!dict)
|
|
1011
1056
|
return false;
|
|
1012
|
-
const names = Object.
|
|
1057
|
+
const names = Object.keys( dict );
|
|
1013
1058
|
if (!names.length) // TODO: re-check - really allow empty dict if no other?
|
|
1014
1059
|
return false;
|
|
1015
1060
|
const feature = kindProperties[parent.kind][prop];
|
|
@@ -1046,6 +1091,9 @@ function define( model ) {
|
|
|
1046
1091
|
error( 'unexpected-elements', [ location, construct ], {},
|
|
1047
1092
|
'Elements only exist in entities, types or typed constructs' );
|
|
1048
1093
|
}
|
|
1094
|
+
else if (prop === 'columns') {
|
|
1095
|
+
error( 'extend-columns', [ location, construct ], { art: construct } );
|
|
1096
|
+
}
|
|
1049
1097
|
else { // if (prop === 'enum') {
|
|
1050
1098
|
error( 'unexpected-enum', [ location, construct ], {},
|
|
1051
1099
|
'Enum symbols can only be defined for types or typed constructs' );
|
package/lib/compiler/extend.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
const { searchName, weakLocation } = require('../base/messages');
|
|
9
9
|
const {
|
|
10
|
-
isDeprecatedEnabled,
|
|
11
|
-
forEachGeneric, forEachInOrder,
|
|
10
|
+
isDeprecatedEnabled,
|
|
11
|
+
forEachGeneric, forEachInOrder, forEachDefinition,
|
|
12
12
|
} = require('../base/model');
|
|
13
13
|
const { dictAdd } = require('../base/dictionaries');
|
|
14
14
|
const { kindProperties, dictKinds } = require('./base');
|
|
@@ -47,15 +47,16 @@ function extend( model ) {
|
|
|
47
47
|
|
|
48
48
|
applyExtensions();
|
|
49
49
|
|
|
50
|
-
const commonLanguagesEntity
|
|
51
|
-
|
|
52
|
-
model.definitions['sap.common.Languages'];
|
|
50
|
+
const commonLanguagesEntity = options.addTextsLanguageAssoc &&
|
|
51
|
+
model.definitions['sap.common.Languages'];
|
|
53
52
|
const addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
|
|
54
53
|
commonLanguagesEntity.elements.code);
|
|
55
54
|
Object.keys( model.definitions ).forEach( processArtifact );
|
|
56
55
|
|
|
57
56
|
lateExtensions( false );
|
|
58
57
|
|
|
58
|
+
compositionChildPersistence();
|
|
59
|
+
|
|
59
60
|
/**
|
|
60
61
|
* Process "composition of" artifacts.
|
|
61
62
|
*
|
|
@@ -88,6 +89,25 @@ function extend( model ) {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Copy `@cds.persistence.skip` and `@cds.persistence.skip` from parent to child
|
|
94
|
+
* for managed compositions. This needs to be done after extensions, i.e. annotations,
|
|
95
|
+
* have been applied or `annotate E.comp` would not have an effect on `E.comp.subComp`.
|
|
96
|
+
*/
|
|
97
|
+
function compositionChildPersistence() {
|
|
98
|
+
const processed = new WeakSet();
|
|
99
|
+
forEachDefinition(model, processCompositionPersistence);
|
|
100
|
+
|
|
101
|
+
function processCompositionPersistence(def) {
|
|
102
|
+
if (def.$inferred === 'composition-entity' && !processed.has(def)) {
|
|
103
|
+
if (def._parent)
|
|
104
|
+
processCompositionPersistence(def._parent);
|
|
105
|
+
copyPersistenceAnnotations(def, def._parent, options);
|
|
106
|
+
processed.add(def);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
91
111
|
// extend ------------------------------------------------------------------
|
|
92
112
|
|
|
93
113
|
/**
|
|
@@ -158,6 +178,7 @@ function extend( model ) {
|
|
|
158
178
|
checkDefinitions( ext, art, 'enum');
|
|
159
179
|
checkDefinitions( ext, art, 'actions');
|
|
160
180
|
checkDefinitions( ext, art, 'params');
|
|
181
|
+
checkDefinitions( ext, art, 'columns');
|
|
161
182
|
defineAnnotations( ext, art, ext._block, ext.kind );
|
|
162
183
|
}
|
|
163
184
|
return true;
|
|
@@ -245,7 +266,7 @@ function extend( model ) {
|
|
|
245
266
|
while (obj.items)
|
|
246
267
|
obj = obj.items;
|
|
247
268
|
const validDict = obj[prop] || prop === 'elements' && obj.enum;
|
|
248
|
-
const member = validDict[name];
|
|
269
|
+
const member = validDict && validDict[name];
|
|
249
270
|
if (!member)
|
|
250
271
|
extendNothing( dict[name], prop, name, art, validDict );
|
|
251
272
|
else if (!(member.$duplicates))
|
|
@@ -262,6 +283,10 @@ function extend( model ) {
|
|
|
262
283
|
*/
|
|
263
284
|
function extendColumns( ext, art ) {
|
|
264
285
|
// TODO: consider reportUnstableExtensions
|
|
286
|
+
|
|
287
|
+
for (const col of ext.columns)
|
|
288
|
+
defineAnnotations( col, col, ext._block, ext.kind );
|
|
289
|
+
|
|
265
290
|
const { location } = ext.name;
|
|
266
291
|
const { query } = art;
|
|
267
292
|
if (!query) {
|
|
@@ -487,7 +512,7 @@ function extend( model ) {
|
|
|
487
512
|
const fioriAnno = art['@fiori.draft.enabled'];
|
|
488
513
|
const fioriEnabled = fioriAnno && (fioriAnno.val === undefined || fioriAnno.val);
|
|
489
514
|
|
|
490
|
-
const textsName = (isDeprecatedEnabled( options, '
|
|
515
|
+
const textsName = (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
|
|
491
516
|
? `${ art.name.absolute }_texts`
|
|
492
517
|
: `${ art.name.absolute }.texts`;
|
|
493
518
|
const textsEntity = model.definitions[textsName];
|
|
@@ -496,8 +521,9 @@ function extend( model ) {
|
|
|
496
521
|
return;
|
|
497
522
|
if (textsEntity) // expanded localized data in source
|
|
498
523
|
return; // -> make it idempotent
|
|
499
|
-
createTextsEntity( art, textsName, localized, fioriEnabled );
|
|
524
|
+
const newTextsEntity = createTextsEntity( art, textsName, localized, fioriEnabled );
|
|
500
525
|
addTextsAssociations( art, textsName, localized );
|
|
526
|
+
copyPersistenceAnnotations(newTextsEntity, art, options);
|
|
501
527
|
}
|
|
502
528
|
|
|
503
529
|
/**
|
|
@@ -602,13 +628,18 @@ function extend( model ) {
|
|
|
602
628
|
elements,
|
|
603
629
|
$inferred: 'localized-entity',
|
|
604
630
|
};
|
|
631
|
+
// If there is a type `sap.common.Locale`, then use it as the type for the element `locale`.
|
|
632
|
+
// If not, use the default `cds.String` with a length of 14.
|
|
633
|
+
const hasLocaleType = model.definitions['sap.common.Locale'] &&
|
|
634
|
+
model.definitions['sap.common.Locale'].kind === 'type';
|
|
605
635
|
const locale = {
|
|
606
636
|
name: { location, id: 'locale' },
|
|
607
637
|
kind: 'element',
|
|
608
|
-
type: augmentPath( location, 'cds.String' ),
|
|
609
|
-
length: { literal: 'number', val: 14, location },
|
|
638
|
+
type: augmentPath( location, hasLocaleType ? 'sap.common.Locale' : 'cds.String' ),
|
|
610
639
|
location,
|
|
611
640
|
};
|
|
641
|
+
if (!hasLocaleType)
|
|
642
|
+
locale.length = { literal: 'number', val: 14, location };
|
|
612
643
|
|
|
613
644
|
if (!fioriEnabled) {
|
|
614
645
|
locale.key = { val: true, location };
|
|
@@ -626,7 +657,7 @@ function extend( model ) {
|
|
|
626
657
|
};
|
|
627
658
|
dictAdd( art.elements, 'ID_texts', textId );
|
|
628
659
|
}
|
|
629
|
-
if (isDeprecatedEnabled( options, '
|
|
660
|
+
if (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
|
|
630
661
|
setLink( art, '_base', base );
|
|
631
662
|
|
|
632
663
|
dictAdd( art.elements, 'locale', locale );
|
|
@@ -685,6 +716,8 @@ function extend( model ) {
|
|
|
685
716
|
}
|
|
686
717
|
if (fioriEnabled)
|
|
687
718
|
annotateWith( art, '@assert.unique.locale', art.location, assertUniqueValue, 'array' );
|
|
719
|
+
|
|
720
|
+
return art;
|
|
688
721
|
}
|
|
689
722
|
|
|
690
723
|
/**
|
|
@@ -798,7 +831,7 @@ function extend( model ) {
|
|
|
798
831
|
target = resolvePath( origin.targetAspect, 'compositionTarget', origin );
|
|
799
832
|
if (!target || !target.elements)
|
|
800
833
|
return;
|
|
801
|
-
const entityName = (isDeprecatedEnabled( options, '
|
|
834
|
+
const entityName = (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
|
|
802
835
|
? `${ base.name.absolute }_${ elem.name.id }`
|
|
803
836
|
: `${ base.name.absolute }.${ elem.name.id }`;
|
|
804
837
|
const entity = allowAspectComposition( target, elem, keys, entityName ) &&
|
|
@@ -809,6 +842,13 @@ function extend( model ) {
|
|
|
809
842
|
};
|
|
810
843
|
setArtifactLink( elem.target, entity );
|
|
811
844
|
if (entity) {
|
|
845
|
+
// Support using the up_ element in the generated entity to be used
|
|
846
|
+
// inside the anonymous aspect:
|
|
847
|
+
const { up_ } = target.$tableAliases;
|
|
848
|
+
// TODO: invalidate "up_" alias (at least further navigation) if it
|
|
849
|
+
// already has an _origin (when the managed composition is included)
|
|
850
|
+
if (up_)
|
|
851
|
+
setLink( up_, '_origin', entity.elements.up_ );
|
|
812
852
|
model.$compositionTargets[entity.name.absolute] = true;
|
|
813
853
|
processAspectComposition( entity );
|
|
814
854
|
processLocalizedData( entity );
|
|
@@ -914,7 +954,7 @@ function extend( model ) {
|
|
|
914
954
|
// By default, 'up_' is a managed primary key association.
|
|
915
955
|
// If 'up_' shall be rendered unmanaged, infer the parent
|
|
916
956
|
// primary keys and add the ON condition
|
|
917
|
-
if (isDeprecatedEnabled( options, '
|
|
957
|
+
if (isDeprecatedEnabled( options, '_unmanagedUpInComponent' )) {
|
|
918
958
|
addProxyElements( art, keys, 'aspect-composition', target.name && location,
|
|
919
959
|
'up__', '@odata.containment.ignore' );
|
|
920
960
|
up.on = augmentEqual( location, 'up_', Object.values( keys ), 'up__' );
|
|
@@ -925,7 +965,7 @@ function extend( model ) {
|
|
|
925
965
|
// even if target cardinality is 1..1
|
|
926
966
|
up.notNull = { location, val: true };
|
|
927
967
|
}
|
|
928
|
-
if (isDeprecatedEnabled( options, '
|
|
968
|
+
if (isDeprecatedEnabled( options, '_generatedEntityNameWithUnderscore' ))
|
|
929
969
|
setLink( art, '_base', base._base || base );
|
|
930
970
|
|
|
931
971
|
dictAdd( art.elements, 'up_', up);
|
|
@@ -934,6 +974,10 @@ function extend( model ) {
|
|
|
934
974
|
setLink( art, '_block', model.$internal );
|
|
935
975
|
model.definitions[entityName] = art;
|
|
936
976
|
initArtifact( art );
|
|
977
|
+
|
|
978
|
+
// Copy persistence annotations from aspect.
|
|
979
|
+
copyPersistenceAnnotations(art, target, options);
|
|
980
|
+
|
|
937
981
|
return art;
|
|
938
982
|
}
|
|
939
983
|
|
|
@@ -955,6 +999,27 @@ function extend( model ) {
|
|
|
955
999
|
}
|
|
956
1000
|
}
|
|
957
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* Copy the annotations `@cds.persistence.skip`/`@cds.persistence.exists` from
|
|
1004
|
+
* source to target if present on source but not target.
|
|
1005
|
+
*
|
|
1006
|
+
* @param {object} target
|
|
1007
|
+
* @param {object} source
|
|
1008
|
+
* @param {CSN.Options} options
|
|
1009
|
+
*/
|
|
1010
|
+
function copyPersistenceAnnotations(target, source, options) {
|
|
1011
|
+
if (!source)
|
|
1012
|
+
return;
|
|
1013
|
+
// Copy @cds.persistence.skip/exists annotation.
|
|
1014
|
+
const noCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
|
|
1015
|
+
const existsAnno = '@cds.persistence.exists';
|
|
1016
|
+
const skipAnno = '@cds.persistence.skip';
|
|
1017
|
+
if (!noCopyExists && source[existsAnno] && !target[existsAnno])
|
|
1018
|
+
target[existsAnno] = source[existsAnno];
|
|
1019
|
+
if (source[skipAnno] && !target[skipAnno])
|
|
1020
|
+
target[skipAnno] = source[skipAnno];
|
|
1021
|
+
}
|
|
1022
|
+
|
|
958
1023
|
function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
959
1024
|
const args = relations.map( eq );
|
|
960
1025
|
return (args.length === 1)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
|
+
const { dictAddArray } = require('../base/dictionaries');
|
|
5
6
|
const { forEachGeneric, forEachMember } = require('../base/model');
|
|
6
7
|
const { setLink, setArtifactLink } = require('./utils');
|
|
7
8
|
|
|
@@ -16,7 +17,7 @@ function finalizeParseCdl( model ) {
|
|
|
16
17
|
const { message, error } = model.$messageFunctions;
|
|
17
18
|
const {
|
|
18
19
|
resolveUncheckedPath,
|
|
19
|
-
|
|
20
|
+
resolveTypeArgumentsUnchecked,
|
|
20
21
|
defineAnnotations,
|
|
21
22
|
initMembers,
|
|
22
23
|
extensionsDict,
|
|
@@ -26,26 +27,32 @@ function finalizeParseCdl( model ) {
|
|
|
26
27
|
return;
|
|
27
28
|
|
|
28
29
|
function resolveTypesAndExtensionsForParseCdl() {
|
|
29
|
-
|
|
30
|
-
model.extensions = [];
|
|
30
|
+
const extensions = [];
|
|
31
31
|
|
|
32
32
|
// TODO: probably better to loop over extensions of all sources (there is just one)
|
|
33
33
|
for (const name in extensionsDict) {
|
|
34
34
|
for (const ext of extensionsDict[name]) {
|
|
35
35
|
ext.name.absolute = resolveUncheckedPath( ext.name, 'extend', ext );
|
|
36
36
|
// Define annotations of this top-level extension
|
|
37
|
-
defineAnnotations( ext, ext, ext._block );
|
|
37
|
+
defineAnnotations( ext, ext, ext._block, 'extend' );
|
|
38
38
|
mergeAnnotatesForSameArtifact( ext );
|
|
39
39
|
// Initialize members and define annotations in sub-elements.
|
|
40
40
|
initMembers( ext, ext, ext._block, true );
|
|
41
|
-
|
|
41
|
+
extensions.push( ext );
|
|
42
|
+
for (const col of ext.columns || []) {
|
|
43
|
+
// Note, no `priority` argument, since we don't apply the extension in the end.
|
|
44
|
+
defineAnnotations( col, col, ext._block );
|
|
45
|
+
}
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
forEachGeneric(model, 'definitions', art => resolveTypesForParseCdl(art, art));
|
|
46
50
|
forEachGeneric(model, 'vocabularies', art => resolveTypesForParseCdl(art, art));
|
|
47
|
-
|
|
51
|
+
|
|
52
|
+
if (extensions.length > 0) {
|
|
53
|
+
model.extensions = extensions;
|
|
48
54
|
model.extensions.forEach(ext => resolveTypesForParseCdl(ext, ext));
|
|
55
|
+
}
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
/**
|
|
@@ -144,11 +151,13 @@ function finalizeParseCdl( model ) {
|
|
|
144
151
|
|
|
145
152
|
/**
|
|
146
153
|
* Resolves `artWithType.type` in an unchecked manner. Handles `type of` cases.
|
|
154
|
+
* `artWithType` has the `type` property, i.e. it could be an `items` object.
|
|
155
|
+
* `user` is the actual artifact, e.g. entity or element.
|
|
147
156
|
*
|
|
148
157
|
* @param {object} artWithType
|
|
149
|
-
* @param {XSN.Artifact}
|
|
158
|
+
* @param {XSN.Artifact} user
|
|
150
159
|
*/
|
|
151
|
-
function resolveTypeUnchecked(artWithType,
|
|
160
|
+
function resolveTypeUnchecked(artWithType, user) {
|
|
152
161
|
if (!artWithType.type)
|
|
153
162
|
return;
|
|
154
163
|
const root = artWithType.type.path && artWithType.type.path[0];
|
|
@@ -159,19 +168,18 @@ function finalizeParseCdl( model ) {
|
|
|
159
168
|
// without special treatment.
|
|
160
169
|
if (artWithType.type.scope !== 'typeOf') {
|
|
161
170
|
// elem: Type or elem: type of Artifact:elem
|
|
162
|
-
const name = resolveUncheckedPath(artWithType.type, 'type',
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
resolveTypeArguments( artWithType, def, artifact );
|
|
171
|
+
const name = resolveUncheckedPath( artWithType.type, 'type', user );
|
|
172
|
+
const type = name && model.definitions[name] || { name: { absolute: name } };
|
|
173
|
+
resolveTypeArgumentsUnchecked( artWithType, type, user );
|
|
166
174
|
return;
|
|
167
175
|
}
|
|
168
|
-
else if (!
|
|
169
|
-
error( 'ref-undefined-typeof', [ artWithType.type.location,
|
|
176
|
+
else if (!user._main) {
|
|
177
|
+
error( 'ref-undefined-typeof', [ artWithType.type.location, user ], {},
|
|
170
178
|
'Current artifact has no element to refer to as type' );
|
|
171
179
|
return;
|
|
172
180
|
}
|
|
173
181
|
else if (root.id === '$self' || root.id === '$projection') {
|
|
174
|
-
setArtifactLink( root,
|
|
182
|
+
setArtifactLink( root, user._main );
|
|
175
183
|
}
|
|
176
184
|
else {
|
|
177
185
|
// For better error messages, check for invalid TYPE OFs similarly
|
|
@@ -179,19 +187,18 @@ function finalizeParseCdl( model ) {
|
|
|
179
187
|
let struct = artWithType;
|
|
180
188
|
while (struct.kind === 'element')
|
|
181
189
|
struct = struct._parent;
|
|
182
|
-
if (struct.kind === 'select' || struct !==
|
|
183
|
-
message( 'type-unexpected-typeof', [ artWithType.type.location,
|
|
190
|
+
if (struct.kind === 'select' || struct !== user._main) {
|
|
191
|
+
message( 'type-unexpected-typeof', [ artWithType.type.location, user ],
|
|
184
192
|
{ keyword: 'type of', '#': struct.kind } );
|
|
185
193
|
return;
|
|
186
194
|
}
|
|
187
195
|
|
|
188
|
-
const fake = { name: { absolute:
|
|
196
|
+
const fake = { name: { absolute: user.name.absolute } };
|
|
189
197
|
// to-csn just needs a fake element whose absolute name and _parent/_main links are correct
|
|
190
|
-
setLink( fake, '_parent',
|
|
191
|
-
setLink( fake, '_main',
|
|
198
|
+
setLink( fake, '_parent', user._parent );
|
|
199
|
+
setLink( fake, '_main', user._main ); // value does not matter...
|
|
192
200
|
setArtifactLink( root, fake );
|
|
193
201
|
}
|
|
194
|
-
resolveTypeArguments( artifact, {}, artifact ); // issue error for type args
|
|
195
202
|
}
|
|
196
203
|
|
|
197
204
|
function chooseAndReportDuplicateAnnotation(artifact, annoName) {
|
|
@@ -215,17 +222,27 @@ function finalizeParseCdl( model ) {
|
|
|
215
222
|
|
|
216
223
|
forEachMember(ext, sub => mergeAnnotatesForSameArtifact(sub));
|
|
217
224
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
// do not do a complex merge:
|
|
226
|
+
if (isComplexExtension( ext ) ||
|
|
227
|
+
!Array.isArray( ext.$duplicates ) || ext.$duplicates.some( isComplexExtension ))
|
|
228
|
+
return;
|
|
229
|
+
for (const dup of ext.$duplicates) {
|
|
230
|
+
for (const prop in dup) {
|
|
231
|
+
if (prop.charAt(0) === '@')
|
|
232
|
+
dictAddArray( ext, prop, dup[prop] );
|
|
223
233
|
}
|
|
224
|
-
ext.$duplicates = ext.$duplicates.filter(val => (val.kind !== 'annotate'));
|
|
225
|
-
if (ext.$duplicates.length === 0)
|
|
226
|
-
delete ext.$duplicates;
|
|
227
234
|
}
|
|
235
|
+
delete ext.$duplicates;
|
|
228
236
|
}
|
|
229
237
|
}
|
|
230
238
|
|
|
239
|
+
/**
|
|
240
|
+
* We only de-duplicate an extend/annotate `ext` in function
|
|
241
|
+
* mergeAnnotatesForSameArtifact() if the extend/annotate is simple, i.e. has
|
|
242
|
+
* no members like elements.
|
|
243
|
+
*/
|
|
244
|
+
function isComplexExtension( ext ) {
|
|
245
|
+
return ext.kind !== 'annotate' || ext.elements || ext.parameters || ext.actions;
|
|
246
|
+
}
|
|
247
|
+
|
|
231
248
|
module.exports = finalizeParseCdl;
|