@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.
Files changed (127) hide show
  1. package/CHANGELOG.md +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -296
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -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 artifats
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, 'generatedEntityNameWithUnderscore' )) {
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.generatedEntityNameWithUnderscore' },
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
- else if (col.value || col.expand) {
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
- obj = targetAspect;
905
- parent = obj;
922
+
923
+ parent = targetAspect;
906
924
  construct = parent; // avoid extension behavior
907
- obj.kind = 'aspect'; // TODO: probably '$aspect' to detect
908
- setLink( obj, '_block', block );
909
- initDollarSelf( obj );
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.getOwnPropertyNames( dict );
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' );
@@ -7,8 +7,8 @@
7
7
 
8
8
  const { searchName, weakLocation } = require('../base/messages');
9
9
  const {
10
- isDeprecatedEnabled, isBetaEnabled,
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 // TODO: remove beta after a grace period
51
- = (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
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, 'generatedEntityNameWithUnderscore' ))
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, 'generatedEntityNameWithUnderscore' ))
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, 'generatedEntityNameWithUnderscore' ))
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, 'unmanagedUpInComponent' )) {
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, 'generatedEntityNameWithUnderscore' ))
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
- resolveTypeArguments,
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
- if (!model.extensions)
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
- model.extensions.push( ext );
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
- if (model.extensions)
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} artifact
158
+ * @param {XSN.Artifact} user
150
159
  */
151
- function resolveTypeUnchecked(artWithType, artifact) {
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', artifact);
163
- const def = name && model.definitions[name];
164
- if (def)
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 (!artifact._main) {
169
- error( 'ref-undefined-typeof', [ artWithType.type.location, artifact ], {},
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, artifact._main );
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 !== artifact._main) {
183
- message( 'type-unexpected-typeof', [ artWithType.type.location, artifact ],
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: artifact.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', artifact._parent );
191
- setLink( fake, '_main', artifact._main ); // value does not matter...
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
- if (ext.$annotations && Array.isArray(ext.$duplicates)) {
219
- const annotates = ext.$duplicates.filter(val => (val.kind === 'annotate'));
220
- for (const dup of annotates) {
221
- ext.$annotations.push(...dup.$annotations);
222
- delete dup.$annotations;
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;