@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.
- package/CHANGELOG.md +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -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 +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- 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 +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +74 -69
- package/lib/json/to-csn.js +17 -14
- 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 +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- 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 +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- 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/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- 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 +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- 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 +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- 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/checks/unknownMagic.js +0 -41
- 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,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
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
* Returns
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
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
|
|
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
|
|
508
|
+
* @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
|
|
509
|
+
* @returns {object|null}
|
|
534
510
|
*/
|
|
535
|
-
function
|
|
511
|
+
function getFinalBaseTypeWithProps(type) {
|
|
512
|
+
type = _normalizeTypeRef(type);
|
|
536
513
|
if (!type)
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
|
|
549
|
-
|
|
581
|
+
if (obj.elements || obj.items) {
|
|
582
|
+
_cacheResolved(obj);
|
|
583
|
+
return true;
|
|
550
584
|
}
|
|
551
|
-
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
-
|
|
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(
|
|
795
|
+
callback.forEach(cb => cb( o, name, prop, path.concat(p), construct ));
|
|
768
796
|
else
|
|
769
|
-
callback( o, name, prop, path.concat(
|
|
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.
|
|
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
|
-
*
|
|
928
|
-
*
|
|
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 {
|
|
932
|
-
* @param {CSN.Model
|
|
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
|
-
|
|
936
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
1377
|
-
*
|
|
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 {{
|
|
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
|
|
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 (
|
|
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 &&
|
|
1392
|
-
|
|
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
|
|
1398
|
-
|
|
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
|
-
|
|
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);
|
|
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
|
-
|
|
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,
|