@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +61 -46
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  6. package/doc/CHANGELOG_BETA.md +26 -5
  7. package/doc/CHANGELOG_DEPRECATED.md +55 -1
  8. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  9. package/doc/Versioning.md +20 -1
  10. package/lib/api/.eslintrc.json +2 -2
  11. package/lib/api/main.js +282 -156
  12. package/lib/api/options.js +17 -88
  13. package/lib/api/validate.js +6 -10
  14. package/lib/base/keywords.js +280 -110
  15. package/lib/base/message-registry.js +85 -25
  16. package/lib/base/messages.js +119 -89
  17. package/lib/base/model.js +46 -2
  18. package/lib/base/optionProcessorHelper.js +53 -21
  19. package/lib/checks/actionsFunctions.js +15 -12
  20. package/lib/checks/annotationsOData.js +1 -1
  21. package/lib/checks/cdsPersistence.js +1 -0
  22. package/lib/checks/elements.js +6 -6
  23. package/lib/checks/invalidTarget.js +1 -1
  24. package/lib/checks/nonexpandableStructured.js +1 -1
  25. package/lib/checks/queryNoDbArtifacts.js +2 -1
  26. package/lib/checks/selectItems.js +101 -15
  27. package/lib/checks/types.js +7 -8
  28. package/lib/checks/utils.js +2 -2
  29. package/lib/checks/validator.js +3 -3
  30. package/lib/compiler/assert-consistency.js +78 -21
  31. package/lib/compiler/base.js +6 -4
  32. package/lib/compiler/builtins.js +177 -10
  33. package/lib/compiler/checks.js +1 -1
  34. package/lib/compiler/define.js +28 -23
  35. package/lib/compiler/extend.js +75 -18
  36. package/lib/compiler/finalize-parse-cdl.js +25 -18
  37. package/lib/compiler/index.js +27 -11
  38. package/lib/compiler/moduleLayers.js +7 -0
  39. package/lib/compiler/populate.js +26 -39
  40. package/lib/compiler/propagator.js +12 -7
  41. package/lib/compiler/resolve.js +207 -236
  42. package/lib/compiler/shared.js +100 -93
  43. package/lib/compiler/tweak-assocs.js +13 -20
  44. package/lib/compiler/utils.js +20 -6
  45. package/lib/edm/annotations/preprocessAnnotations.js +12 -13
  46. package/lib/edm/csn2edm.js +35 -37
  47. package/lib/edm/edm.js +22 -13
  48. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  49. package/lib/edm/edmInboundChecks.js +85 -0
  50. package/lib/edm/edmPreprocessor.js +338 -689
  51. package/lib/edm/edmUtils.js +97 -67
  52. package/lib/gen/Dictionary.json +29 -9
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +8 -31
  55. package/lib/gen/language.tokens +105 -114
  56. package/lib/gen/languageLexer.interp +1 -34
  57. package/lib/gen/languageLexer.js +892 -1007
  58. package/lib/gen/languageLexer.tokens +95 -106
  59. package/lib/gen/languageParser.js +20629 -22474
  60. package/lib/inspect/.eslintrc.json +4 -0
  61. package/lib/inspect/index.js +14 -0
  62. package/lib/inspect/inspectModelStatistics.js +81 -0
  63. package/lib/inspect/inspectPropagation.js +189 -0
  64. package/lib/inspect/inspectUtils.js +44 -0
  65. package/lib/json/from-csn.js +74 -69
  66. package/lib/json/to-csn.js +17 -14
  67. package/lib/language/antlrParser.js +2 -2
  68. package/lib/language/docCommentParser.js +61 -38
  69. package/lib/language/errorStrategy.js +52 -40
  70. package/lib/language/genericAntlrParser.js +424 -292
  71. package/lib/language/language.g4 +604 -687
  72. package/lib/language/multiLineStringParser.js +14 -42
  73. package/lib/language/textUtils.js +44 -0
  74. package/lib/main.d.ts +28 -42
  75. package/lib/main.js +104 -81
  76. package/lib/model/api.js +1 -1
  77. package/lib/model/csnRefs.js +57 -30
  78. package/lib/model/csnUtils.js +189 -287
  79. package/lib/model/revealInternalProperties.js +32 -10
  80. package/lib/model/sortViews.js +32 -31
  81. package/lib/modelCompare/compare.js +3 -0
  82. package/lib/optionProcessor.js +91 -57
  83. package/lib/render/.eslintrc.json +1 -1
  84. package/lib/render/DuplicateChecker.js +4 -7
  85. package/lib/render/manageConstraints.js +70 -2
  86. package/lib/render/toCdl.js +387 -367
  87. package/lib/render/toHdbcds.js +20 -16
  88. package/lib/render/toRename.js +44 -22
  89. package/lib/render/toSql.js +81 -59
  90. package/lib/render/utils/common.js +16 -3
  91. package/lib/render/utils/sql.js +20 -19
  92. package/lib/sql-identifier.js +6 -0
  93. package/lib/transform/db/.eslintrc.json +3 -2
  94. package/lib/transform/db/associations.js +43 -35
  95. package/lib/transform/db/cdsPersistence.js +5 -16
  96. package/lib/transform/db/constraints.js +1 -1
  97. package/lib/transform/db/expansion.js +7 -6
  98. package/lib/transform/db/flattening.js +16 -18
  99. package/lib/transform/db/transformExists.js +7 -5
  100. package/lib/transform/db/views.js +3 -3
  101. package/lib/transform/draft/.eslintrc.json +2 -2
  102. package/lib/transform/draft/db.js +6 -6
  103. package/lib/transform/draft/odata.js +6 -7
  104. package/lib/transform/forHanaNew.js +30 -24
  105. package/lib/transform/forOdataNew.js +14 -16
  106. package/lib/transform/localized.js +35 -25
  107. package/lib/transform/odata/toFinalBaseType.js +10 -10
  108. package/lib/transform/odata/typesExposure.js +17 -8
  109. package/lib/transform/odata/utils.js +1 -38
  110. package/lib/transform/transformUtilsNew.js +63 -77
  111. package/lib/transform/translateAssocsToJoins.js +2 -2
  112. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  113. package/lib/transform/universalCsn/coreComputed.js +11 -6
  114. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  115. package/lib/utils/file.js +31 -21
  116. package/lib/utils/moduleResolve.js +0 -1
  117. package/lib/utils/timetrace.js +20 -21
  118. package/package.json +34 -4
  119. package/share/messages/syntax-expected-integer.md +9 -8
  120. package/doc/ApiMigration.md +0 -237
  121. package/doc/CommandLineMigration.md +0 -58
  122. package/doc/ErrorMessages.md +0 -175
  123. package/doc/FioriAnnotations.md +0 -94
  124. package/doc/ODataTransformation.md +0 -273
  125. package/lib/backends.js +0 -529
  126. package/lib/checks/unknownMagic.js +0 -41
  127. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -5,6 +5,8 @@ const { applyTransformations, applyTransformationsOnNonDictionary } = require('.
5
5
  const { isBuiltinType } = require('../compiler/builtins.js')
6
6
  const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
7
7
  const { ModelError } = require("../base/error");
8
+ const { typeParameters } = require("../compiler/builtins");
9
+ const { forEach } = require('../utils/objectUtils');
8
10
  const version = require('../../package.json').version;
9
11
 
10
12
  // Low-level utility functions to work with compact CSN.
@@ -34,11 +36,14 @@ const version = require('../../package.json').version;
34
36
  */
35
37
 
36
38
  /**
37
- * Get utility functions for a given CSN.
39
+ * Get utility functions for a given CSN. Re-exports functions of `csnRefs()`.
38
40
  * @param {CSN.Model} model (Compact) CSN model
39
41
  */
40
42
  function getUtils(model, universalReady) {
41
- const { artifactRef, inspectRef, effectiveType, getOrigin, targetAspect, getColumn, getElement, initDefinition } = csnRefs(model, universalReady);
43
+ const _csnRefs = csnRefs(model, universalReady);
44
+ const { artifactRef } = _csnRefs;
45
+ /** Cache for getFinalBaseTypeWithProps(). Specific to the current model. */
46
+ const finalBaseTypeCache = Object.create(null);
42
47
 
43
48
  return {
44
49
  getCsnDef,
@@ -56,17 +61,10 @@ function getUtils(model, universalReady) {
56
61
  getServiceName,
57
62
  hasAnnotationValue,
58
63
  cloneWithTransformations,
59
- getFinalBaseType,
60
- inspectRef,
61
- artifactRef,
62
- effectiveType,
64
+ getFinalBaseTypeWithProps,
63
65
  get$combined,
64
- getOrigin,
65
66
  getQueryPrimarySource,
66
- targetAspect,
67
- getColumn,
68
- getElement,
69
- initDefinition
67
+ ..._csnRefs,
70
68
  };
71
69
 
72
70
  /**
@@ -261,7 +259,7 @@ function getUtils(model, universalReady) {
261
259
  */
262
260
  function isStructured(obj) {
263
261
  return obj.elements ||
264
- (obj.type && ((getFinalTypeDef(obj.type).elements) || (obj.type.ref && getFinalBaseType(obj.type).elements)));
262
+ (obj.type && ((getFinalTypeDef(obj.type).elements) || (obj.type.ref && getFinalBaseTypeWithProps(obj.type)?.elements)));
265
263
  }
266
264
 
267
265
  /**
@@ -486,97 +484,125 @@ function getUtils(model, universalReady) {
486
484
  }
487
485
  }
488
486
 
487
+ function _normalizeTypeRef(type) {
488
+ if (type && typeof type === 'object' && type.ref?.length === 1)
489
+ type = type.ref[0]; // simplify type: no element -> simple string can be used
490
+ return type;
491
+ }
489
492
 
490
493
  /**
491
- * Resolve to the final type of a type, that means follow type chains, references to other types or
492
- * elements a.s.o
493
- * Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
494
- * Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
495
- * else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
496
- * e.g. given the following model
497
- * type bar: S.foo;
498
- * type s1 {
499
- * s: s2;
500
- * };
501
- * type s2 {
502
- * u: type of S.e:t;
503
- * }
504
- * service S {
505
- * type foo: type of S.e:i.j1;
506
- * entity e {
507
- * key i: { j1: Integer };
508
- * t: bar;
509
- * v: s1;
510
- * x: blutz.s.u;
511
- * };
512
- * type blutz: S.e.v;
513
- * view V as select from e {
514
- * 1+1 as i: bar,
515
- * };
516
- * type tt: type of V:i;
517
- * }
518
- * the following calls will all return 'cds.Integer'
519
- * getFinalBaseType('S.tt')
520
- * getFinalBaseType('S.e',['i','j1'])
521
- * getFinalBaseType('S.e',['t'])
522
- * getFinalBaseType('S.e',['x'])
523
- * getFinalBaseType('S.blutz',['s', 'u'])
524
- * Types are always resolved as far as possible. A type name which has no further definition is simply returned.
525
- * Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
526
- * the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
527
- * no type (e.g. expr in a view without explicit type) returns 'null'
494
+ * Resolve to the final type of a type, that means follow type chains, references, etc.
495
+ * Input is a fully qualified type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
496
+ *
497
+ * Returns `null` if the type can't be resolved or if the referenced element has no type,
498
+ * e.g. `typeof V:calculated`.
499
+ * Otherwise, if scalar, returns an object that has a `type` property and all collected type
500
+ * properties, or the type object with `elements` or `items` property if structured/arrayed.
501
+ *
502
+ * Notes:
503
+ * - Caches type lookups. If the CSN changes drastically, you will need to re-call
504
+ * `csnUtils()` and use the newly returned `getFinalBaseTypeWithProps()`.
505
+ * - Does _not_ return the underlying type definition! It is an object with all relevant
506
+ * type properties collected while traversing the type chain!
528
507
  *
529
- * @param {string|object} type Type - either string or ref
530
- * @param {CSN.Path} path
531
- * @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
532
- * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
533
- * @returns
508
+ * @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
509
+ * @returns {object|null}
534
510
  */
535
- function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
511
+ function getFinalBaseTypeWithProps(type) {
512
+ type = _normalizeTypeRef(type);
536
513
  if (!type)
537
- return type;
538
- if (typeof(type) === 'string') {
539
- if (isBuiltinType(type)) // built-in type
540
- return type;
541
- if (cycleCheck) {
542
- let visited = path.length? type + ':' + path.join('.') : type;
543
- if (cycleCheck[visited])
544
- throw new ModelError('Circular type chain on type ' + type);
545
- else
546
- cycleCheck[visited] = true;
514
+ return null;
515
+
516
+ // Nothing to copy from builtin type name.
517
+ if (typeof type === 'string' && isBuiltinType( type )) {
518
+ return { type };
519
+ }
520
+
521
+ // We differentiate between ref and type to avoid collisions due to dict key.
522
+ // Delimiter chosen arbitrarily; just one that is rarely used.
523
+ const resolvedKey = (typeof type === 'object') ? `ref[${type.ref.length}]:${type.ref.join('\\')}` : `type:${type}`;
524
+
525
+ if (finalBaseTypeCache[resolvedKey]) {
526
+ if (finalBaseTypeCache[resolvedKey] === true)
527
+ throw new ModelError(`Detected circular type reference; can't resolve: ${resolvedKey}`);
528
+ return finalBaseTypeCache[resolvedKey];
529
+ }
530
+
531
+ let typeRef = artifactRef(type); // throws if not found
532
+ const isNonScalar = _cacheNonScalar(typeRef);
533
+ if (isNonScalar)
534
+ return finalBaseTypeCache[resolvedKey];
535
+
536
+ const props = {};
537
+ _copyTypeProps(props, typeRef);
538
+
539
+ // If the resolved type is a builtin, stop and use its type arguments.
540
+ type = _normalizeTypeRef(typeRef.type);
541
+ if (typeof type === 'string' && isBuiltinType(type))
542
+ return _cacheResolved(props);
543
+
544
+ // Set to true (before the recursive call) to avoid cyclic issues.
545
+ finalBaseTypeCache[resolvedKey] = true;
546
+
547
+ // Continue the search
548
+ const finalBase = getFinalBaseTypeWithProps(type);
549
+ if (!finalBase) // Reference has no proper type, e.g. due to `type of View:calculated`.
550
+ return _cacheResolved(null);
551
+ const nonScalar = _cacheNonScalar(finalBase);
552
+ if (nonScalar)
553
+ return finalBaseTypeCache[resolvedKey];
554
+
555
+ // If not a non-scalar, must be resolved type.
556
+ _copyTypeProps(props, finalBase);
557
+ _cacheResolved(props);
558
+ return props;
559
+
560
+ /**
561
+ * Cache/Store the type props under the current `resolvedKey` in the `resolved` cache.
562
+ *
563
+ * @param {object} typeProps
564
+ */
565
+ function _cacheResolved(typeProps) {
566
+ finalBaseTypeCache[resolvedKey] = typeProps;
567
+ return typeProps;
568
+ }
569
+
570
+ /**
571
+ * Structured or arrayed types are not followed further, so cache them.
572
+ *
573
+ * @param obj
574
+ * @returns {boolean} True, if structured/arrayed/invalid, false if scalar.
575
+ */
576
+ function _cacheNonScalar(obj) {
577
+ if (!obj) { // Reference has no proper type, e.g. due to `type of View:calculated`.
578
+ _cacheResolved(null);
579
+ return true;
547
580
  }
548
- else {
549
- cycleCheck = Object.create(null);
581
+ if (obj.elements || obj.items) {
582
+ _cacheResolved(obj);
583
+ return true;
550
584
  }
551
- let definedType = model.definitions[type];
552
- if (definedType && definedType.type)
553
- return getFinalBaseType(definedType.type, path, resolved, cycleCheck);
554
- else
555
- return getFinalBaseType(definedType, path, resolved, cycleCheck);
585
+ return false;
556
586
  }
557
- else if (typeof(type) === 'object') {
558
- if (type.ref) {
559
- // assert type.ref instanceof Array && type.ref.length >= 1
560
- const ref = resolved.has(type) ? resolved.get(type).art : artifactRef(type);
561
- return getFinalBaseType(ref, path, resolved, cycleCheck);
562
- }
563
- else if (type.elements) {
564
- if (path.length) {
565
- let [e, ...p] = path;
566
- return getFinalBaseType(type.elements[e], p, resolved, cycleCheck);
587
+
588
+ /**
589
+ * Copy type properties from source to target. Also copies `type`, `enum`,
590
+ * and `localized` (if keepLocalized is true). Only copies from source,
591
+ * if target does not have them.
592
+ *
593
+ * @param {object} target
594
+ * @param {object} source
595
+ */
596
+ function _copyTypeProps(target, source) {
597
+ target.type = source.type;
598
+ const typeProps = [ ...typeParameters.list, 'enum', 'default', 'localized' ];
599
+ for (const param of typeProps) {
600
+ if (target[param] === undefined && source[param] !== undefined) {
601
+ target[param] = source[param];
567
602
  }
568
603
  }
569
- else if (type.type)
570
- return (getFinalBaseType(type.type, path, resolved, cycleCheck));
571
- else if (type.items)
572
- return type;
573
- else
574
- // TODO: this happens if we don't have a type, e.g. an expression in a select list
575
- // in a view without explicit type. Instead of returning null we might want to return
576
- // the object instead?
577
- return null;
604
+ return target;
578
605
  }
579
- return type;
580
606
  }
581
607
  }
582
608
 
@@ -763,32 +789,15 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
763
789
  executeCallbacks( dictObj, name );
764
790
  }
765
791
  function executeCallbacks(o, name ) {
792
+ const p = iterateOptions.pathWithoutProp ? [ name ] : [ prop, name ];
793
+
766
794
  if (Array.isArray(callback))
767
- callback.forEach(cb => cb( o, name, prop, path.concat([prop, name]), construct ));
795
+ callback.forEach(cb => cb( o, name, prop, path.concat(p), construct ));
768
796
  else
769
- callback( o, name, prop, path.concat([prop, name]), construct )
797
+ callback( o, name, prop, path.concat(p), construct )
770
798
  }
771
799
  }
772
800
 
773
- // Like Object.assign() but copies also non enumerable properties
774
- function assignAll(target, ...sources) {
775
- sources.forEach(source => {
776
- const descriptors = Object.getOwnPropertyNames(source).reduce((propertyDescriptors, current) => {
777
- propertyDescriptors[current] = Object.getOwnPropertyDescriptor(source, current);
778
- return propertyDescriptors;
779
- }, {});
780
- // by default, Object.assign copies enumerable Symbols too
781
- Object.getOwnPropertySymbols(source).forEach(sym => {
782
- const descriptor = Object.getOwnPropertyDescriptor(source, sym);
783
- if (descriptor.enumerable) {
784
- descriptors[sym] = descriptor;
785
- }
786
- });
787
- Object.defineProperties(target, descriptors);
788
- });
789
- return target;
790
- }
791
-
792
801
  /**
793
802
  * @param {CSN.Query} mainQuery
794
803
  * @param {queryCallback|queryCallback[]} queryCallback
@@ -896,12 +905,10 @@ function hasAnnotationValue(artifact, annotationName, expected = true, caseInsen
896
905
  * @param {ODataOptions} options EDM specific options
897
906
  */
898
907
  function isEdmPropertyRendered(elementCsn, options) {
899
- if(options.toOdata)
900
- options = options.toOdata;
901
908
  // FKs are rendered in
902
909
  // V2/V4 flat: always on
903
910
  // V4 struct: on/off
904
- const renderForeignKey = (options.version === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
911
+ const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
905
912
  const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
906
913
  const isNavigable = elementCsn.target ?
907
914
  (elementCsn['@odata.navigable'] === undefined ||
@@ -924,16 +931,19 @@ function isEdmPropertyRendered(elementCsn, options) {
924
931
  * - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
925
932
  * - For the 'quoted' naming mode, this means correctly replacing some '.' with '_'.
926
933
  *
927
- * If the old function signature is used - with a namespace as the third argument - the result might be wrong,
928
- * since the '.' -> '_' conversion for quoted/hdbcds is missing.
934
+ * The above rules might differ for different SQL dialects.
935
+ * Exceptions will be listed below.
929
936
  *
930
937
  * @param {string} artifactName The fully qualified name of the artifact
931
- * @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
932
- * @param {CSN.Model|string|undefined} csn
938
+ * @param {'plain'|'quoted'|'hdbcds'|string} sqlMapping The naming mode to use
939
+ * @param {CSN.Model} csn
940
+ * @param {('sqlite'|'hana'|'plain'|string)} [sqlDialect='plain'] The SQL dialect to use
933
941
  * @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
934
942
  */
935
- function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
936
- if(csn && typeof csn === 'object' && csn.definitions)
943
+ // eslint-disable-next-line no-unused-vars
944
+ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect='plain') {
945
+ if(csn && typeof csn === 'object' && csn.definitions) {
946
+ isValidMappingDialectCombi(sqlDialect, sqlMapping)
937
947
  if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
938
948
  return getResultingName(csn, sqlMapping, artifactName);
939
949
  }
@@ -942,24 +952,8 @@ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
942
952
  } else {
943
953
  throw new Error('Unknown naming mode: ' + sqlMapping);
944
954
  }
945
- else {
946
- console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
947
- if (sqlMapping === 'hdbcds') {
948
- if (csn) {
949
- const namespace = String(csn);
950
- return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
951
- }
952
- return artifactName;
953
- }
954
- else if (sqlMapping === 'plain') {
955
- return artifactName.replace(/\./g, '_').toUpperCase();
956
- }
957
- else if (sqlMapping === 'quoted') {
958
- return artifactName;
959
- }
960
- else {
961
- throw new Error('Unknown naming mode: ' + sqlMapping);
962
- }
955
+ } else {
956
+ throw new Error('A valid CSN model is required to correctly calculate the database name of an artifact.');
963
957
  }
964
958
  }
965
959
 
@@ -1042,6 +1036,12 @@ function getUnderscoredName(startIndex, parts, csn) {
1042
1036
  return null;
1043
1037
  }
1044
1038
 
1039
+ function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
1040
+ if(sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
1041
+ throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${sqlDialect}`);
1042
+ return true;
1043
+ }
1044
+
1045
1045
 
1046
1046
  /**
1047
1047
  * Return the resulting database element name for 'elemName', depending on the current
@@ -1051,11 +1051,17 @@ function getUnderscoredName(startIndex, parts, csn) {
1051
1051
  * - For the 'quoted' naming mode, it means converting all '.' to '_'.
1052
1052
  * No other naming modes are accepted!
1053
1053
  *
1054
+ * The above rules might differ for different SQL dialects.
1055
+ * Exceptions will be listed below.
1056
+ *
1054
1057
  * @param {string} elemName The name of the element
1055
- * @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
1058
+ * @param {'plain'|'quoted'|'hdbcds'|string} sqlMapping The naming mode to use
1059
+ * @param {('sqlite'|'hana'|'plain'|string)} [sqlDialect='plain'] The SQL dialect to use
1056
1060
  * @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
1057
1061
  */
1058
- function getElementDatabaseNameOf(elemName, sqlMapping) {
1062
+ // eslint-disable-next-line no-unused-vars
1063
+ function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect='plain') {
1064
+ isValidMappingDialectCombi(sqlDialect, sqlMapping)
1059
1065
  if (sqlMapping === 'hdbcds') {
1060
1066
  return elemName;
1061
1067
  }
@@ -1170,85 +1176,6 @@ function getNormalizedQuery(art) {
1170
1176
  return art;
1171
1177
  }
1172
1178
 
1173
- /**
1174
- * Merge multiple 'options' objects (from right to left, i.e. rightmost wins). Structured option values are
1175
- * merged deeply. Structured option value from the right may override corresponding bool options on the left,
1176
- * but no other combination of struct/scalar values is allowed. Array options are not merged, i.e. their
1177
- * content is treated like scalars.
1178
- * Returns a new options object.
1179
- *
1180
- * @param {...CSN.Options} optionsObjects
1181
- * @return {CSN.Options}
1182
- */
1183
- function mergeOptions(...optionsObjects) {
1184
- let result = {};
1185
- for (const options of optionsObjects) {
1186
- if (options)
1187
- result = mergeTwo(result, options, 'options');
1188
- }
1189
-
1190
- // Reverse the array to ensure that the rightmost option has priority
1191
- const reversedOptions = [...optionsObjects].reverse(); // de-structure and create a new array, so reverse doesn't impact optionsObject
1192
- const msgOptions = reversedOptions.find(opt => opt && Array.isArray(opt.messages));
1193
- if (msgOptions) {
1194
- result.messages = msgOptions.messages;
1195
- }
1196
-
1197
- return result;
1198
-
1199
- // Recursively used for scalars, too
1200
- function mergeTwo(left, right, name) {
1201
- let intermediateResult;
1202
- // Copy left as far as required
1203
- if (Array.isArray(left)) {
1204
- // Shallow-copy left array
1205
- intermediateResult = left.slice();
1206
- } else if (isObject(left)) {
1207
- // Deep-copy left object (unless empty)
1208
- intermediateResult = Object.keys(left).length ? mergeTwo({}, left, name) : {};
1209
- } else {
1210
- // Just use left scalar
1211
- intermediateResult = left;
1212
- }
1213
- // Check against improper overwriting
1214
- if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
1215
- throw new ModelError(`Cannot overwrite structured option "${name}" with array or scalar value`);
1216
- }
1217
- if ((isScalar(left) && typeof left !== 'boolean' || Array.isArray(left)) && isObject(right) && !Array.isArray(right)) {
1218
- throw new ModelError(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
1219
- }
1220
-
1221
- // Copy or overwrite properties from right to left
1222
- if (Array.isArray(right)) {
1223
- // Shallow-copy right array
1224
- intermediateResult = right.slice();
1225
- } else if (isObject(right)) {
1226
- // Object overwrites undefined, scalars and arrays
1227
- if (intermediateResult === undefined || isScalar(intermediateResult) || Array.isArray(intermediateResult)) {
1228
- intermediateResult = {};
1229
- }
1230
- // Deep-copy right object into result
1231
- for (let key of Object.keys(right)) {
1232
- intermediateResult[key] = mergeTwo(intermediateResult[key], right[key], `${name}.${key}`);
1233
- }
1234
- } else {
1235
- // Right scalar wins (unless undefined)
1236
- intermediateResult = (right !== undefined) ? right : intermediateResult;
1237
- }
1238
- return intermediateResult;
1239
- }
1240
-
1241
- // Return true if 'o' is a non-null object or array
1242
- function isObject(o) {
1243
- return typeof o === 'object' && o !== null
1244
- }
1245
-
1246
- // Return true if 'o' is a non-undefined scalar
1247
- function isScalar(o) {
1248
- return o !== undefined && !isObject(o);
1249
- }
1250
- }
1251
-
1252
1179
  /**
1253
1180
  * If the artifact with the name given is part of a context (or multiple), return the top-most context.
1254
1181
  * Else, return the artifact itself. Namespaces are not of concern here.
@@ -1300,29 +1227,6 @@ function getLastPartOfRef(ref) {
1300
1227
  return getLastPartOf(lastPathStep.id || lastPathStep);
1301
1228
  }
1302
1229
 
1303
- // Return the name of the parent artifact of the artifact 'name' or
1304
- // '' if there is no parent.
1305
- function getParentNameOf(name) {
1306
- return name.substring(0, name.lastIndexOf('.'));
1307
- }
1308
-
1309
- // Return an array of parent names of 'name' (recursing into grand-parents)
1310
- // Examples:
1311
- // 'foo.bar.wiz' => [ 'foo.bar', 'foo' ]
1312
- // 'foo' => []
1313
- // 'foo::bar.wiz' => 'foo::bar'
1314
- // 'foo::bar' => []
1315
- function getParentNamesOf(name) {
1316
- let remainder = name.slice(0, -getLastPartOf(name).length);
1317
- if (remainder.endsWith('.')) {
1318
- let parentName = remainder.slice(0, -1);
1319
- return [parentName, ...getParentNamesOf(parentName)];
1320
- } else {
1321
- return [];
1322
- }
1323
- }
1324
-
1325
-
1326
1230
  /**
1327
1231
  * Copy all annotations from 'fromNode' to 'toNode'.
1328
1232
  *
@@ -1373,49 +1277,63 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
1373
1277
  }
1374
1278
 
1375
1279
  /**
1376
- * Applies annotations from `csn.extensions` to definitions, i.e. top-level artifacts.
1377
- * Does _not_ apply element/param/action/... annotations.
1280
+ * Applies annotations from `csn.extensions` to definitions and their elements.
1281
+ *
1378
1282
  * `config.filter` can be used to only copy annotations for those definitions,
1379
1283
  * for which the filter returns true.
1380
1284
  *
1285
+ * @todo Does _not_ apply param/action/... annotations.
1286
+ *
1381
1287
  * @param {CSN.Model} csn
1382
- * @param {{overwrite?: boolean, filter?: (name: string) => boolean}} config
1288
+ * @param {{notFound?: (name: string, index: number) => void, override?: boolean, filter?: (name: string) => boolean}} config
1289
+ * notFound: Function that is called if the referenced definition can't be found.
1290
+ * Second argument is index in `csn.extensions` array.
1291
+ * override: Whether to ignore existing annotations.
1292
+ * filter: Positive filter. If it returns true, annotations for the referenced artifact
1293
+ * will be applied.
1383
1294
  */
1384
- function applyDefinitionAnnotationsFromExtensions(csn, config) {
1295
+ function applyAnnotationsFromExtensions(csn, config) {
1385
1296
  if (!csn.extensions)
1386
1297
  return;
1387
1298
 
1388
1299
  const filter = config.filter || ((_name) => true);
1389
- for (const ext of csn.extensions) {
1300
+ for (let i = 0; i < csn.extensions.length; ++i) {
1301
+ const ext = csn.extensions[i];
1390
1302
  const name = ext.annotate || ext.extend;
1391
- if (name && csn.definitions[name] && filter(name)) {
1392
- copyAnnotationsAndDoc(ext, csn.definitions[name], config.overwrite);
1303
+ if (name && filter(name)) {
1304
+ const def = csn.definitions[name];
1305
+ if (def) {
1306
+ copyAnnotationsAndDoc(ext, def, config.override);
1307
+ applyAnnotationsToElements(ext, def);
1308
+ } else if (config.notFound) {
1309
+ config.notFound(name, i);
1310
+ }
1393
1311
  }
1394
1312
  }
1395
- }
1396
1313
 
1397
- function isAspect(node) {
1398
- return node && node.kind === 'aspect';
1399
- }
1314
+ function applyAnnotationsToElements(ext, def) {
1315
+ // Only the definition is arrayed but the extension is not since
1316
+ // `items` is not expected in `extensions` by the CSN frontend and not
1317
+ // generated by the CDL parser for `annotate E:arrayed.elem`.
1318
+ if (def.items)
1319
+ def = def.items;
1400
1320
 
1401
- // For each property named 'path' in 'node' (recursively), call callback(path, node)
1402
- function forEachPath(node, callback) {
1403
- if (node === null || typeof node !== 'object') {
1404
- // Primitive node
1405
- return;
1406
- }
1407
- for (let name in node) {
1408
- if (!Object.hasOwnProperty.call( node, name ))
1409
- continue;
1410
- // If path found within a non-dictionary, call callback
1411
- if (name === 'path' && Object.getPrototypeOf(node)) {
1412
- callback(node.path, node);
1413
- }
1414
- // Descend recursively
1415
- forEachPath(node[name], callback);
1321
+ if (!ext.elements || !def.elements)
1322
+ return;
1323
+
1324
+ forEach(ext.elements, (key, sourceElem) => {
1325
+ const targetElem = def.elements[key];
1326
+ if (targetElem) {
1327
+ copyAnnotationsAndDoc(sourceElem, targetElem, config.override);
1328
+ applyAnnotationsToElements(sourceElem, targetElem);
1329
+ }
1330
+ });
1416
1331
  }
1417
1332
  }
1418
1333
 
1334
+ function isAspect(node) {
1335
+ return node && node.kind === 'aspect';
1336
+ }
1419
1337
 
1420
1338
  /**
1421
1339
  * Return true if the artifact has a valid, truthy persistence.exists/skip annotation
@@ -1492,16 +1410,6 @@ function getServiceNames(csn) {
1492
1410
  return result;
1493
1411
  }
1494
1412
 
1495
- /**
1496
- * Check whether the artifact is @cds.persistence.skip
1497
- *
1498
- * @param {CSN.Artifact} artifact
1499
- * @returns {Boolean}
1500
- */
1501
- function isSkipped(artifact) {
1502
- return hasAnnotationValue(artifact, '@cds.persistence.skip', true)
1503
- }
1504
-
1505
1413
  /**
1506
1414
  * Walk path in the CSN and return the result.
1507
1415
  *
@@ -1589,8 +1497,7 @@ module.exports = {
1589
1497
  cloneCsnNonDict,
1590
1498
  cloneCsnDictionary,
1591
1499
  isBuiltinType,
1592
- assignAll,
1593
- applyDefinitionAnnotationsFromExtensions,
1500
+ applyAnnotationsFromExtensions,
1594
1501
  forEachGeneric,
1595
1502
  forEachDefinition,
1596
1503
  forEachMember,
@@ -1610,21 +1517,16 @@ module.exports = {
1610
1517
  isPersistedOnDatabase,
1611
1518
  generatedByCompilerVersion,
1612
1519
  getNormalizedQuery,
1613
- mergeOptions,
1614
1520
  getRootArtifactName,
1615
1521
  getLastPartOfRef,
1616
- getParentNamesOf,
1617
- getParentNameOf,
1618
1522
  getLastPartOf,
1619
1523
  copyAnnotations,
1620
1524
  copyAnnotationsAndDoc,
1621
1525
  isAspect,
1622
- forEachPath,
1623
1526
  hasValidSkipOrExists,
1624
1527
  getNamespace,
1625
1528
  sortCsnDefinitionsForTests,
1626
1529
  getServiceNames,
1627
- isSkipped,
1628
1530
  walkCsnPath,
1629
1531
  getVariableReplacement,
1630
1532
  implicitAs,