@sap/cds-compiler 2.15.4 → 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 +33 -1590
- package/bin/cdsc.js +36 -33
- 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 +220 -103
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +60 -20
- package/lib/base/messages.js +65 -24
- package/lib/base/model.js +44 -2
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +1 -1
- 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 +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +87 -9
- package/lib/compiler/define.js +2 -2
- package/lib/compiler/extend.js +59 -11
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +13 -13
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +193 -218
- package/lib/compiler/shared.js +47 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/csn2edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +25 -30
- package/lib/edm/edmUtils.js +10 -24
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -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 +20632 -22313
- package/lib/json/from-csn.js +56 -49
- package/lib/json/to-csn.js +10 -8
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +303 -229
- package/lib/language/language.g4 +573 -629
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +1 -1
- package/lib/model/csnUtils.js +170 -283
- package/lib/model/revealInternalProperties.js +28 -8
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +12 -21
- 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 +334 -339
- package/lib/render/toHdbcds.js +19 -15
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +53 -51
- package/lib/render/utils/common.js +15 -1
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- package/lib/transform/db/views.js +3 -3
- 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 +19 -22
- package/lib/transform/forOdataNew.js +10 -12
- package/lib/transform/localized.js +22 -16
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +3 -3
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- 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/model/csnUtils.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
60
|
-
inspectRef,
|
|
61
|
-
artifactRef,
|
|
62
|
-
effectiveType,
|
|
64
|
+
getFinalBaseTypeWithProps,
|
|
63
65
|
get$combined,
|
|
64
|
-
getOrigin,
|
|
65
66
|
getQueryPrimarySource,
|
|
66
|
-
|
|
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 &&
|
|
262
|
+
(obj.type && ((getFinalTypeDef(obj.type).elements) || (obj.type.ref && getFinalBaseTypeWithProps(obj.type)?.elements)));
|
|
265
263
|
}
|
|
266
264
|
|
|
267
265
|
/**
|
|
@@ -486,97 +484,122 @@ 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
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
* Returns
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
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 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
|
+
* Caches type lookups. If the CSN changes drastically, you will need to re-call `csnUtils()`
|
|
503
|
+
* and use the newly returned `getFinalBaseTypeWithProps()`.
|
|
528
504
|
*
|
|
529
|
-
* @param {string|object} type Type
|
|
530
|
-
* @
|
|
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
|
|
505
|
+
* @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
|
|
506
|
+
* @returns {object|null}
|
|
534
507
|
*/
|
|
535
|
-
function
|
|
508
|
+
function getFinalBaseTypeWithProps(type) {
|
|
509
|
+
type = _normalizeTypeRef(type);
|
|
536
510
|
if (!type)
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
511
|
+
return null;
|
|
512
|
+
|
|
513
|
+
// Nothing to copy from builtin type name.
|
|
514
|
+
if (typeof type === 'string' && isBuiltinType( type )) {
|
|
515
|
+
return { type };
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// We differentiate between ref and type to avoid collisions due to dict key.
|
|
519
|
+
// Delimiter chosen arbitrarily; just one that is rarely used.
|
|
520
|
+
const resolvedKey = (typeof type === 'object') ? `ref:${type.ref.join('\\')}` : `type:${type}`;
|
|
521
|
+
|
|
522
|
+
if (finalBaseTypeCache[resolvedKey]) {
|
|
523
|
+
if (finalBaseTypeCache[resolvedKey] === true)
|
|
524
|
+
throw new ModelError(`Detected circular type reference; can't resolve: ${resolvedKey}`);
|
|
525
|
+
return finalBaseTypeCache[resolvedKey];
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
let typeRef = artifactRef(type); // throws if not found
|
|
529
|
+
const isNonScalar = _cacheNonScalar(typeRef);
|
|
530
|
+
if (isNonScalar)
|
|
531
|
+
return finalBaseTypeCache[resolvedKey];
|
|
532
|
+
|
|
533
|
+
const props = {};
|
|
534
|
+
_copyTypeProps(props, typeRef);
|
|
535
|
+
|
|
536
|
+
// If the resolved type is a builtin, stop and use its type arguments.
|
|
537
|
+
type = _normalizeTypeRef(typeRef.type);
|
|
538
|
+
if (typeof type === 'string' && isBuiltinType(type))
|
|
539
|
+
return _cacheResolved(props);
|
|
540
|
+
|
|
541
|
+
// Set to true (before the recursive call) to avoid cyclic issues.
|
|
542
|
+
finalBaseTypeCache[resolvedKey] = true;
|
|
543
|
+
|
|
544
|
+
// Continue the search
|
|
545
|
+
const finalBase = getFinalBaseTypeWithProps(type);
|
|
546
|
+
if (!finalBase) // Reference has no proper type, e.g. due to `type of View:calculated`.
|
|
547
|
+
return _cacheResolved(null);
|
|
548
|
+
const nonScalar = _cacheNonScalar(finalBase);
|
|
549
|
+
if (nonScalar)
|
|
550
|
+
return finalBaseTypeCache[resolvedKey];
|
|
551
|
+
|
|
552
|
+
// If not a non-scalar, must be resolved type.
|
|
553
|
+
_copyTypeProps(props, finalBase);
|
|
554
|
+
_cacheResolved(props);
|
|
555
|
+
return props;
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Cache/Store the type props under the current `resolvedKey` in the `resolved` cache.
|
|
559
|
+
*
|
|
560
|
+
* @param {object} typeProps
|
|
561
|
+
*/
|
|
562
|
+
function _cacheResolved(typeProps) {
|
|
563
|
+
finalBaseTypeCache[resolvedKey] = typeProps;
|
|
564
|
+
return typeProps;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Structured or arrayed types are not followed further, so cache them.
|
|
569
|
+
*
|
|
570
|
+
* @param obj
|
|
571
|
+
* @returns {boolean} True, if structured/arrayed/invalid, false if scalar.
|
|
572
|
+
*/
|
|
573
|
+
function _cacheNonScalar(obj) {
|
|
574
|
+
if (!obj) { // Reference has no proper type, e.g. due to `type of View:calculated`.
|
|
575
|
+
_cacheResolved(null);
|
|
576
|
+
return true;
|
|
547
577
|
}
|
|
548
|
-
|
|
549
|
-
|
|
578
|
+
if (obj.elements || obj.items) {
|
|
579
|
+
_cacheResolved(obj);
|
|
580
|
+
return true;
|
|
550
581
|
}
|
|
551
|
-
|
|
552
|
-
if (definedType && definedType.type)
|
|
553
|
-
return getFinalBaseType(definedType.type, path, resolved, cycleCheck);
|
|
554
|
-
else
|
|
555
|
-
return getFinalBaseType(definedType, path, resolved, cycleCheck);
|
|
582
|
+
return false;
|
|
556
583
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Copy type properties from source to target. Also copies `type`, `enum`,
|
|
587
|
+
* and `localized` (if keepLocalized is true). Only copies from source,
|
|
588
|
+
* if target does not have them.
|
|
589
|
+
*
|
|
590
|
+
* @param {object} target
|
|
591
|
+
* @param {object} source
|
|
592
|
+
*/
|
|
593
|
+
function _copyTypeProps(target, source) {
|
|
594
|
+
target.type = source.type;
|
|
595
|
+
const typeProps = [ ...typeParameters.list, 'enum', 'default', 'localized' ];
|
|
596
|
+
for (const param of typeProps) {
|
|
597
|
+
if (target[param] === undefined && source[param] !== undefined) {
|
|
598
|
+
target[param] = source[param];
|
|
567
599
|
}
|
|
568
600
|
}
|
|
569
|
-
|
|
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;
|
|
601
|
+
return target;
|
|
578
602
|
}
|
|
579
|
-
return type;
|
|
580
603
|
}
|
|
581
604
|
}
|
|
582
605
|
|
|
@@ -770,25 +793,6 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
|
|
|
770
793
|
}
|
|
771
794
|
}
|
|
772
795
|
|
|
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
796
|
/**
|
|
793
797
|
* @param {CSN.Query} mainQuery
|
|
794
798
|
* @param {queryCallback|queryCallback[]} queryCallback
|
|
@@ -896,12 +900,10 @@ function hasAnnotationValue(artifact, annotationName, expected = true, caseInsen
|
|
|
896
900
|
* @param {ODataOptions} options EDM specific options
|
|
897
901
|
*/
|
|
898
902
|
function isEdmPropertyRendered(elementCsn, options) {
|
|
899
|
-
if(options.toOdata)
|
|
900
|
-
options = options.toOdata;
|
|
901
903
|
// FKs are rendered in
|
|
902
904
|
// V2/V4 flat: always on
|
|
903
905
|
// V4 struct: on/off
|
|
904
|
-
const renderForeignKey = (options.
|
|
906
|
+
const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
|
|
905
907
|
const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
|
|
906
908
|
const isNavigable = elementCsn.target ?
|
|
907
909
|
(elementCsn['@odata.navigable'] === undefined ||
|
|
@@ -924,16 +926,19 @@ function isEdmPropertyRendered(elementCsn, options) {
|
|
|
924
926
|
* - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
|
|
925
927
|
* - For the 'quoted' naming mode, this means correctly replacing some '.' with '_'.
|
|
926
928
|
*
|
|
927
|
-
*
|
|
928
|
-
*
|
|
929
|
+
* The above rules might differ for different SQL dialects.
|
|
930
|
+
* Exceptions will be listed below.
|
|
929
931
|
*
|
|
930
932
|
* @param {string} artifactName The fully qualified name of the artifact
|
|
931
|
-
* @param {
|
|
932
|
-
* @param {CSN.Model
|
|
933
|
+
* @param {'plain'|'quoted'|'hdbcds'|string} sqlMapping The naming mode to use
|
|
934
|
+
* @param {CSN.Model} csn
|
|
935
|
+
* @param {('sqlite'|'hana'|'plain'|string)} [sqlDialect='plain'] The SQL dialect to use
|
|
933
936
|
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
|
|
934
937
|
*/
|
|
935
|
-
|
|
936
|
-
|
|
938
|
+
// eslint-disable-next-line no-unused-vars
|
|
939
|
+
function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect='plain') {
|
|
940
|
+
if(csn && typeof csn === 'object' && csn.definitions) {
|
|
941
|
+
isValidMappingDialectCombi(sqlDialect, sqlMapping)
|
|
937
942
|
if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
|
|
938
943
|
return getResultingName(csn, sqlMapping, artifactName);
|
|
939
944
|
}
|
|
@@ -942,24 +947,8 @@ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
|
|
|
942
947
|
} else {
|
|
943
948
|
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
944
949
|
}
|
|
945
|
-
else {
|
|
946
|
-
|
|
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
|
-
}
|
|
950
|
+
} else {
|
|
951
|
+
throw new Error('A valid CSN model is required to correctly calculate the database name of an artifact.');
|
|
963
952
|
}
|
|
964
953
|
}
|
|
965
954
|
|
|
@@ -1042,6 +1031,12 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1042
1031
|
return null;
|
|
1043
1032
|
}
|
|
1044
1033
|
|
|
1034
|
+
function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
|
|
1035
|
+
if(sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
|
|
1036
|
+
throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${sqlDialect}`);
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1045
1040
|
|
|
1046
1041
|
/**
|
|
1047
1042
|
* Return the resulting database element name for 'elemName', depending on the current
|
|
@@ -1051,11 +1046,17 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1051
1046
|
* - For the 'quoted' naming mode, it means converting all '.' to '_'.
|
|
1052
1047
|
* No other naming modes are accepted!
|
|
1053
1048
|
*
|
|
1049
|
+
* The above rules might differ for different SQL dialects.
|
|
1050
|
+
* Exceptions will be listed below.
|
|
1051
|
+
*
|
|
1054
1052
|
* @param {string} elemName The name of the element
|
|
1055
|
-
* @param {
|
|
1053
|
+
* @param {'plain'|'quoted'|'hdbcds'|string} sqlMapping The naming mode to use
|
|
1054
|
+
* @param {('sqlite'|'hana'|'plain'|string)} [sqlDialect='plain'] The SQL dialect to use
|
|
1056
1055
|
* @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
|
|
1057
1056
|
*/
|
|
1058
|
-
|
|
1057
|
+
// eslint-disable-next-line no-unused-vars
|
|
1058
|
+
function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect='plain') {
|
|
1059
|
+
isValidMappingDialectCombi(sqlMapping, sqlDialect)
|
|
1059
1060
|
if (sqlMapping === 'hdbcds') {
|
|
1060
1061
|
return elemName;
|
|
1061
1062
|
}
|
|
@@ -1170,85 +1171,6 @@ function getNormalizedQuery(art) {
|
|
|
1170
1171
|
return art;
|
|
1171
1172
|
}
|
|
1172
1173
|
|
|
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
1174
|
/**
|
|
1253
1175
|
* If the artifact with the name given is part of a context (or multiple), return the top-most context.
|
|
1254
1176
|
* Else, return the artifact itself. Namespaces are not of concern here.
|
|
@@ -1300,29 +1222,6 @@ function getLastPartOfRef(ref) {
|
|
|
1300
1222
|
return getLastPartOf(lastPathStep.id || lastPathStep);
|
|
1301
1223
|
}
|
|
1302
1224
|
|
|
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
1225
|
/**
|
|
1327
1226
|
* Copy all annotations from 'fromNode' to 'toNode'.
|
|
1328
1227
|
*
|
|
@@ -1373,49 +1272,53 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
|
|
|
1373
1272
|
}
|
|
1374
1273
|
|
|
1375
1274
|
/**
|
|
1376
|
-
* Applies annotations from `csn.extensions` to definitions
|
|
1377
|
-
*
|
|
1275
|
+
* Applies annotations from `csn.extensions` to definitions and their elements.
|
|
1276
|
+
*
|
|
1378
1277
|
* `config.filter` can be used to only copy annotations for those definitions,
|
|
1379
1278
|
* for which the filter returns true.
|
|
1380
1279
|
*
|
|
1280
|
+
* @todo Does _not_ apply param/action/... annotations.
|
|
1281
|
+
*
|
|
1381
1282
|
* @param {CSN.Model} csn
|
|
1382
1283
|
* @param {{overwrite?: boolean, filter?: (name: string) => boolean}} config
|
|
1383
1284
|
*/
|
|
1384
|
-
function
|
|
1285
|
+
function applyAnnotationsFromExtensions(csn, config) {
|
|
1385
1286
|
if (!csn.extensions)
|
|
1386
1287
|
return;
|
|
1387
1288
|
|
|
1388
1289
|
const filter = config.filter || ((_name) => true);
|
|
1389
1290
|
for (const ext of csn.extensions) {
|
|
1390
1291
|
const name = ext.annotate || ext.extend;
|
|
1391
|
-
|
|
1392
|
-
|
|
1292
|
+
const def = csn.definitions[name];
|
|
1293
|
+
if (name && def && filter(name)) {
|
|
1294
|
+
copyAnnotationsAndDoc(ext, def, config.overwrite);
|
|
1295
|
+
applyAnnotationsToElements(ext, def);
|
|
1393
1296
|
}
|
|
1394
1297
|
}
|
|
1395
|
-
}
|
|
1396
1298
|
|
|
1397
|
-
function
|
|
1398
|
-
|
|
1399
|
-
|
|
1299
|
+
function applyAnnotationsToElements(ext, def) {
|
|
1300
|
+
// Only the definition is arrayed but the extension is not since
|
|
1301
|
+
// `items` is not expected in `extensions` by the CSN frontend and not
|
|
1302
|
+
// generated by the CDL parser for `annotate E:arrayed.elem`.
|
|
1303
|
+
if (def.items)
|
|
1304
|
+
def = def.items;
|
|
1400
1305
|
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
if (name === 'path' && Object.getPrototypeOf(node)) {
|
|
1412
|
-
callback(node.path, node);
|
|
1413
|
-
}
|
|
1414
|
-
// Descend recursively
|
|
1415
|
-
forEachPath(node[name], callback);
|
|
1306
|
+
if (!ext.elements || !def.elements)
|
|
1307
|
+
return;
|
|
1308
|
+
|
|
1309
|
+
forEach(ext.elements, (key, sourceElem) => {
|
|
1310
|
+
const targetElem = def.elements[key];
|
|
1311
|
+
if (targetElem) {
|
|
1312
|
+
copyAnnotationsAndDoc(sourceElem, targetElem, config.overwrite);
|
|
1313
|
+
applyAnnotationsToElements(sourceElem, targetElem);
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1416
1316
|
}
|
|
1417
1317
|
}
|
|
1418
1318
|
|
|
1319
|
+
function isAspect(node) {
|
|
1320
|
+
return node && node.kind === 'aspect';
|
|
1321
|
+
}
|
|
1419
1322
|
|
|
1420
1323
|
/**
|
|
1421
1324
|
* Return true if the artifact has a valid, truthy persistence.exists/skip annotation
|
|
@@ -1492,16 +1395,6 @@ function getServiceNames(csn) {
|
|
|
1492
1395
|
return result;
|
|
1493
1396
|
}
|
|
1494
1397
|
|
|
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
1398
|
/**
|
|
1506
1399
|
* Walk path in the CSN and return the result.
|
|
1507
1400
|
*
|
|
@@ -1589,8 +1482,7 @@ module.exports = {
|
|
|
1589
1482
|
cloneCsnNonDict,
|
|
1590
1483
|
cloneCsnDictionary,
|
|
1591
1484
|
isBuiltinType,
|
|
1592
|
-
|
|
1593
|
-
applyDefinitionAnnotationsFromExtensions,
|
|
1485
|
+
applyAnnotationsFromExtensions,
|
|
1594
1486
|
forEachGeneric,
|
|
1595
1487
|
forEachDefinition,
|
|
1596
1488
|
forEachMember,
|
|
@@ -1610,21 +1502,16 @@ module.exports = {
|
|
|
1610
1502
|
isPersistedOnDatabase,
|
|
1611
1503
|
generatedByCompilerVersion,
|
|
1612
1504
|
getNormalizedQuery,
|
|
1613
|
-
mergeOptions,
|
|
1614
1505
|
getRootArtifactName,
|
|
1615
1506
|
getLastPartOfRef,
|
|
1616
|
-
getParentNamesOf,
|
|
1617
|
-
getParentNameOf,
|
|
1618
1507
|
getLastPartOf,
|
|
1619
1508
|
copyAnnotations,
|
|
1620
1509
|
copyAnnotationsAndDoc,
|
|
1621
1510
|
isAspect,
|
|
1622
|
-
forEachPath,
|
|
1623
1511
|
hasValidSkipOrExists,
|
|
1624
1512
|
getNamespace,
|
|
1625
1513
|
sortCsnDefinitionsForTests,
|
|
1626
1514
|
getServiceNames,
|
|
1627
|
-
isSkipped,
|
|
1628
1515
|
walkCsnPath,
|
|
1629
1516
|
getVariableReplacement,
|
|
1630
1517
|
implicitAs,
|