@sap/cds-compiler 2.13.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
const { hasErrors, makeMessageFunction } = require('../base/messages');
|
|
8
8
|
const { setProp } = require('../base/model');
|
|
9
|
-
const { csnRefs } = require('../model/csnRefs');
|
|
10
9
|
|
|
11
10
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
-
const {
|
|
11
|
+
const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
|
|
12
|
+
const { typeParameters, isBuiltinType } = require('../compiler/builtins');
|
|
13
13
|
const { ModelError } = require("../base/error");
|
|
14
|
+
const { forEach } = require('../utils/objectUtils');
|
|
14
15
|
|
|
15
16
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
16
17
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
@@ -19,20 +20,18 @@ const { ModelError } = require("../base/error");
|
|
|
19
20
|
// TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
|
|
20
21
|
function getTransformers(model, options, pathDelimiter = '_') {
|
|
21
22
|
const { error, warning, info } = makeMessageFunction(model, options);
|
|
23
|
+
const csnUtils = getUtils(model);
|
|
22
24
|
const {
|
|
23
25
|
getCsnDef,
|
|
24
|
-
|
|
26
|
+
getFinalBaseTypeWithProps,
|
|
25
27
|
hasAnnotationValue,
|
|
26
28
|
inspectRef,
|
|
27
29
|
isStructured,
|
|
28
|
-
} = getUtils(model);
|
|
29
|
-
|
|
30
|
-
const {
|
|
31
30
|
effectiveType,
|
|
32
|
-
} =
|
|
33
|
-
|
|
31
|
+
} = csnUtils;
|
|
34
32
|
|
|
35
33
|
return {
|
|
34
|
+
csnUtils,
|
|
36
35
|
resolvePath,
|
|
37
36
|
flattenPath,
|
|
38
37
|
addDefaultTypeFacets,
|
|
@@ -44,7 +43,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
44
43
|
copyTypeProperties,
|
|
45
44
|
isAssociationOperand,
|
|
46
45
|
isDollarSelfOrProjectionOperand,
|
|
47
|
-
getFinalBaseType,
|
|
48
46
|
createExposingProjection,
|
|
49
47
|
createAndAddDraftAdminDataProjection,
|
|
50
48
|
createScalarElement,
|
|
@@ -242,23 +240,21 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
242
240
|
// for type of 'x' -> elem.type is an object, not a string -> use directly
|
|
243
241
|
let elemType;
|
|
244
242
|
if (!elem.elements) // structures do not have final base type
|
|
245
|
-
elemType =
|
|
243
|
+
elemType = getFinalBaseTypeWithProps(elem.type);
|
|
246
244
|
|
|
247
245
|
const struct = elemType ? elemType.elements : elem.elements;
|
|
248
246
|
|
|
249
247
|
// Collect all child elements (recursively) into 'result'
|
|
250
248
|
// TODO: Do not report collisions in the generated elements here, but instead
|
|
251
|
-
//
|
|
249
|
+
// leave that work to the receiver of this result
|
|
252
250
|
let result = Object.create(null);
|
|
253
251
|
const addGeneratedFlattenedElement = (e, eName) => {
|
|
254
|
-
if(result[eName])
|
|
255
|
-
error(
|
|
256
|
-
|
|
257
|
-
} else {
|
|
252
|
+
if (result[eName])
|
|
253
|
+
error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
|
|
254
|
+
else
|
|
258
255
|
result[eName] = e;
|
|
259
|
-
}
|
|
260
256
|
}
|
|
261
|
-
|
|
257
|
+
forEach(struct, (childName, childElem) => {
|
|
262
258
|
if (isStructured(childElem)) {
|
|
263
259
|
// Descend recursively into structured children
|
|
264
260
|
let grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
|
|
@@ -276,7 +272,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
276
272
|
} else {
|
|
277
273
|
// Primitive child - clone it and restore its cross references
|
|
278
274
|
let flatElemName = elemName + pathDelimiter + childName;
|
|
279
|
-
let flatElem =
|
|
275
|
+
let flatElem = cloneCsnNonDict(childElem, options);
|
|
280
276
|
// Don't take over notNull from leaf elements
|
|
281
277
|
delete flatElem.notNull;
|
|
282
278
|
setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
|
|
@@ -285,8 +281,12 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
285
281
|
});
|
|
286
282
|
|
|
287
283
|
// Fix all collected flat elements (names, annotations, properties, origin ..)
|
|
288
|
-
|
|
289
|
-
// Copy annotations from struct (not overwriting, because deep annotations should have precedence)
|
|
284
|
+
forEach(result, (name, flatElem) => {
|
|
285
|
+
// Copy annotations from struct (not overwriting, because deep annotations should have precedence).
|
|
286
|
+
// Attention:
|
|
287
|
+
// This has historic reasons. We don't copy doc-comments because copying annotations
|
|
288
|
+
// is questionable to begin with. Only selected annotations should have been copied,
|
|
289
|
+
// if at all.
|
|
290
290
|
copyAnnotations(elem, flatElem, false);
|
|
291
291
|
// Copy selected type properties
|
|
292
292
|
const props = ['key', 'virtual', 'masked', 'viaAll'];
|
|
@@ -394,82 +394,65 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
/**
|
|
397
|
-
* Replace the type of '
|
|
398
|
-
*
|
|
399
|
-
* Similar with associations and compositions (we probably need a _baseType link)
|
|
397
|
+
* Replace the type of 'nodeWithType' with its final base type, i.e. copy relevant type properties and
|
|
398
|
+
* set the `type` property to the builtin if scalar or delete it if structured/arrayed.
|
|
400
399
|
*
|
|
401
|
-
* @param {
|
|
400
|
+
* @param {object} nodeWithType
|
|
402
401
|
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
403
402
|
* @param {boolean} [keepLocalized=false] Whether to clone .localized from a type def
|
|
404
|
-
* @returns {void}
|
|
405
403
|
*/
|
|
406
|
-
function toFinalBaseType(
|
|
407
|
-
|
|
408
|
-
if (!
|
|
409
|
-
// In case of a ref -> Follow the ref
|
|
410
|
-
if (node.type && node.type.ref) {
|
|
411
|
-
const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
|
|
412
|
-
if(finalBaseType === null)
|
|
413
|
-
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
|
|
414
|
-
if(finalBaseType.elements) {
|
|
415
|
-
// This changes the order - to be discussed!
|
|
416
|
-
node.elements = cloneCsn(finalBaseType, options).elements; // copy elements
|
|
417
|
-
delete node.type; // delete the type reference as edm processing does not expect it
|
|
418
|
-
} else if(finalBaseType.items) {
|
|
419
|
-
// This changes the order - to be discussed!
|
|
420
|
-
node.items = cloneCsn(finalBaseType.items, options); // copy items
|
|
421
|
-
delete node.type;
|
|
422
|
-
} else {
|
|
423
|
-
node.type=finalBaseType;
|
|
424
|
-
}
|
|
404
|
+
function toFinalBaseType(nodeWithType, resolved = new WeakMap(), keepLocalized = false) {
|
|
405
|
+
const type = nodeWithType?.type;
|
|
406
|
+
if (!type || nodeWithType.elements || nodeWithType.items || resolved.has(nodeWithType)) {
|
|
425
407
|
return;
|
|
426
408
|
}
|
|
427
|
-
//
|
|
428
|
-
|
|
409
|
+
// The caller may use `{ art }` syntax for `{ ref }` objects, but we only use
|
|
410
|
+
// it to indicate that an artifact has been processed.
|
|
411
|
+
resolved.set(nodeWithType, nodeWithType);
|
|
429
412
|
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
413
|
+
// Nothing to copy from builtin.
|
|
414
|
+
if (typeof type === 'string' && isBuiltinType(type))
|
|
415
|
+
return;
|
|
416
|
+
|
|
417
|
+
let typeRef = null;
|
|
418
|
+
if (resolved.has(type)) {
|
|
419
|
+
typeRef = resolved.get(type)?.art
|
|
420
|
+
// The cached entry may not be resolved, yet.
|
|
421
|
+
if (typeRef.type && !isBuiltinType(typeRef.type))
|
|
422
|
+
typeRef = getFinalBaseTypeWithProps(typeRef.type);
|
|
423
|
+
} else {
|
|
424
|
+
typeRef = getFinalBaseTypeWithProps(type);
|
|
425
|
+
}
|
|
437
426
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
//
|
|
441
|
-
if(
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
427
|
+
if (typeRef.elements || typeRef.items) {
|
|
428
|
+
// Copy elements/items and we're finished. No need to look up actual base type,
|
|
429
|
+
// since it must also be structured and must contain at least as many elements,
|
|
430
|
+
// if not more (in client style CSN).
|
|
431
|
+
if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
432
|
+
nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
|
|
433
|
+
delete nodeWithType.type;
|
|
445
434
|
}
|
|
446
|
-
if
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if(!node.elements)
|
|
450
|
-
Object.assign(node, {elements: cloneTypeDef.elements});
|
|
435
|
+
if (typeRef.items) {
|
|
436
|
+
nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
|
|
437
|
+
delete nodeWithType.type;
|
|
451
438
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
439
|
return;
|
|
455
440
|
}
|
|
456
|
-
|
|
457
|
-
if (
|
|
458
|
-
|
|
459
|
-
|
|
441
|
+
|
|
442
|
+
if (typeRef.enum && nodeWithType.enum === undefined)
|
|
443
|
+
nodeWithType.enum = cloneCsnDictionary(typeRef.enum, options);
|
|
444
|
+
|
|
445
|
+
// Copy type and type arguments (+ localized)
|
|
446
|
+
|
|
447
|
+
for (const param of typeParameters.list) {
|
|
448
|
+
if (nodeWithType[param] === undefined && typeRef[param] !== undefined &&!typeRef.$default) {
|
|
449
|
+
nodeWithType[param] = typeRef[param];
|
|
450
|
+
}
|
|
460
451
|
}
|
|
461
|
-
if (
|
|
462
|
-
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
if (node.scale === undefined && typeDef.scale !== undefined)
|
|
466
|
-
Object.assign(node, { scale: typeDef.scale });
|
|
467
|
-
if (node.srid === undefined && typeDef.srid !== undefined)
|
|
468
|
-
Object.assign(node, { srid: typeDef.srid });
|
|
469
|
-
if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
|
|
470
|
-
Object.assign(node, { localized: typeDef.localized });
|
|
471
|
-
node.type = typeDef.type;
|
|
472
|
-
toFinalBaseType(node);
|
|
452
|
+
if (keepLocalized && nodeWithType.localized === undefined && typeRef.localized !== undefined)
|
|
453
|
+
nodeWithType.localized = typeRef.localized;
|
|
454
|
+
if (typeRef.type)
|
|
455
|
+
nodeWithType.type = typeRef.type;
|
|
473
456
|
}
|
|
474
457
|
|
|
475
458
|
// Return a full projection 'projectionId' of artifact 'art' for exposure in 'service'.
|
|
@@ -899,7 +882,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
899
882
|
let elements = artifact.elements;
|
|
900
883
|
if (elements) {
|
|
901
884
|
path.push('elements', null);
|
|
902
|
-
|
|
885
|
+
forEach(elements, (name, obj) => {
|
|
903
886
|
path[path.length - 1] = name;
|
|
904
887
|
recurseElements(obj, path, callback);
|
|
905
888
|
});
|
|
@@ -44,7 +44,7 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// If A2J reports error - end! Continuing with a broken CSN makes no sense
|
|
47
|
-
makeMessageFunction(model, options).
|
|
47
|
+
makeMessageFunction(model, options).throwWithAnyError();
|
|
48
48
|
// FIXME: Move this somewhere more appropriate
|
|
49
49
|
const compact = compactModel(model, compileOptions);
|
|
50
50
|
return compact;
|
|
@@ -57,10 +57,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
57
57
|
const options = model.options || inputOptions;
|
|
58
58
|
|
|
59
59
|
// create JOINs for foreign key paths
|
|
60
|
-
const noJoinForFK = options.forHana ? !options.
|
|
60
|
+
const noJoinForFK = options.forHana ? !options.joinfk : true;
|
|
61
61
|
|
|
62
62
|
// Note: This is called from the 'forHana' transformations, so it is controlled by its options)
|
|
63
|
-
const pathDelimiter = (options.forHana && options.
|
|
63
|
+
const pathDelimiter = (options.forHana && options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
64
64
|
|
|
65
65
|
forEachDefinition(model, prepareAssociations);
|
|
66
66
|
forEachDefinition(model, transformQueries);
|
|
@@ -1818,6 +1818,10 @@ function walkPath(node, env)
|
|
|
1818
1818
|
// or that are parameters ($parameters or escaped paths (':')
|
|
1819
1819
|
//path.length && path[ path.length-1 ]._artifact
|
|
1820
1820
|
const art = path && path.length && path[path.length-1]._artifact;
|
|
1821
|
+
|
|
1822
|
+
// regardless of the position in the query, ignore paths that have virtual path steps
|
|
1823
|
+
if(art && path.some(ps => ps._artifact && ps._artifact.virtual && ps._artifact.virtual.val))
|
|
1824
|
+
return path;
|
|
1821
1825
|
if(art && !internalArtifactKinds.includes(art.kind))
|
|
1822
1826
|
{
|
|
1823
1827
|
if(env.callback)
|
|
@@ -12,7 +12,7 @@ const { setAnnotationIfNotDefined } = require('./utils');
|
|
|
12
12
|
*/
|
|
13
13
|
function setCoreComputedOnViews(csn) {
|
|
14
14
|
const {
|
|
15
|
-
artifactRef, getColumn, getElement,
|
|
15
|
+
artifactRef, getColumn, getElement, getOrigin,
|
|
16
16
|
} = getUtils(csn, 'init-all');
|
|
17
17
|
|
|
18
18
|
forEachDefinition(csn, (artifact) => {
|
|
@@ -57,7 +57,15 @@ function setCoreComputedOnViews(csn) {
|
|
|
57
57
|
const column = getColumn(element);
|
|
58
58
|
if (column)
|
|
59
59
|
return column;
|
|
60
|
-
|
|
60
|
+
const from = getElementFromFrom(name, base.from);
|
|
61
|
+
if (from)
|
|
62
|
+
return from;
|
|
63
|
+
// For .expand/.inline, we can find it via origin
|
|
64
|
+
// Although I would have expected to find it via getColumn...
|
|
65
|
+
const origin = getOrigin(element);
|
|
66
|
+
if (origin)
|
|
67
|
+
return origin;
|
|
68
|
+
throw new Error(`Could not find ancestor for ${JSON.stringify(element)} named ${name}`);
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/**
|
|
@@ -87,10 +95,7 @@ function setCoreComputedOnViews(csn) {
|
|
|
87
95
|
return getElementFromFrom(name, base.SET.args[0]);
|
|
88
96
|
}
|
|
89
97
|
else if (base.args && base.join) {
|
|
90
|
-
|
|
91
|
-
if (!result)
|
|
92
|
-
throw new Error(`Could not find ${name} in ${JSON.stringify(base.args)}`);
|
|
93
|
-
return result;
|
|
98
|
+
return checkJoinSources(base.args, name);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
throw new Error(JSON.stringify(base));
|
|
@@ -137,9 +142,11 @@ function setCoreComputedOnViews(csn) {
|
|
|
137
142
|
* @returns {boolean}
|
|
138
143
|
*/
|
|
139
144
|
function needsCoreComputed(column) {
|
|
140
|
-
return column &&
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
return column &&
|
|
146
|
+
(
|
|
147
|
+
column.xpr || column.func || column.val !== undefined || column.param || column.SELECT || column.SET ||
|
|
148
|
+
column.ref && [ '$at', '$now', '$user', '$session' ].includes(column.ref[0])
|
|
149
|
+
);
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
/**
|
|
@@ -29,10 +29,7 @@ module.exports = (csn, options) => {
|
|
|
29
29
|
const definitionPropagationRules = {
|
|
30
30
|
'@cds.autoexpose': onlyViaArtifact,
|
|
31
31
|
'@fiori.draft.enabled': onlyViaArtifact,
|
|
32
|
-
'@':
|
|
33
|
-
if (source[prop] !== null)
|
|
34
|
-
target[prop] = source[prop];
|
|
35
|
-
},
|
|
32
|
+
'@': nullStopsPropagation,
|
|
36
33
|
// Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
|
|
37
34
|
elements: onlyTypeDef,
|
|
38
35
|
'@cds.persistence.exists': skip,
|
|
@@ -48,7 +45,7 @@ module.exports = (csn, options) => {
|
|
|
48
45
|
'@cds.autoexposed': skip,
|
|
49
46
|
'@cds.redirection.target': skip,
|
|
50
47
|
type: always,
|
|
51
|
-
doc:
|
|
48
|
+
doc: nullStopsPropagation,
|
|
52
49
|
length: always,
|
|
53
50
|
precision: always,
|
|
54
51
|
scale: always,
|
|
@@ -94,6 +91,10 @@ module.exports = (csn, options) => {
|
|
|
94
91
|
}, // overwrite from defProps
|
|
95
92
|
kind: skip,
|
|
96
93
|
val: always,
|
|
94
|
+
type: notWithItemsOrElements,
|
|
95
|
+
target: notWithItemsOrElements,
|
|
96
|
+
keys: notWithItemsOrElements,
|
|
97
|
+
cardinality: notWithItemsOrElements,
|
|
97
98
|
};
|
|
98
99
|
|
|
99
100
|
generate();
|
|
@@ -288,7 +289,11 @@ module.exports = (csn, options) => {
|
|
|
288
289
|
if (returns.target)
|
|
289
290
|
calculateForeignKeys(returns);
|
|
290
291
|
},
|
|
291
|
-
items: (parent, prop, items) =>
|
|
292
|
+
items: (parent, prop, items) => {
|
|
293
|
+
// items in items must be propagated
|
|
294
|
+
// `specialItemsRule()` does not cover this case
|
|
295
|
+
propagateMemberPropsFromOrigin(items, { '@': true, doc: true }, { items: onlyWithTypeRef });
|
|
296
|
+
},
|
|
292
297
|
elements: (parent, prop, elements) => {
|
|
293
298
|
forEachValue(elements, (e) => {
|
|
294
299
|
// within query elements we have to propagate the `enum` prop
|
|
@@ -358,13 +363,31 @@ module.exports = (csn, options) => {
|
|
|
358
363
|
return !origin;
|
|
359
364
|
}
|
|
360
365
|
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Some properties must only be copied over if the target
|
|
369
|
+
* is a type reference pointing to an element of a type or
|
|
370
|
+
* an aspect.
|
|
371
|
+
*
|
|
372
|
+
* @param {string} prop
|
|
373
|
+
* @param {CSN.Element} target
|
|
374
|
+
* @param {CSN.Element} source
|
|
375
|
+
*/
|
|
376
|
+
function onlyWithTypeRef(prop, target, source) {
|
|
377
|
+
const typeIsTypeRef = Boolean(typeof target.type === 'object' && target.type.ref);
|
|
378
|
+
if (typeIsTypeRef) {
|
|
379
|
+
const referencedArtifact = csn.definitions[target.type.ref[0]];
|
|
380
|
+
if (referencedArtifact.kind in { type: true, aspect: true })
|
|
381
|
+
target[prop] = source[prop];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
361
384
|
}
|
|
362
385
|
|
|
363
386
|
/**
|
|
364
387
|
* Identify the sources of the passed object and propagate the relevant
|
|
365
388
|
* properties/annotations along it's $origin chain.
|
|
366
389
|
*
|
|
367
|
-
* @param {
|
|
390
|
+
* @param {object} art Target object for propagation
|
|
368
391
|
*/
|
|
369
392
|
function propagateOnArtifactLevel(art) {
|
|
370
393
|
// check if art was already processed by the status flag
|
|
@@ -415,7 +438,7 @@ module.exports = (csn, options) => {
|
|
|
415
438
|
copyProperties(definition.$origin, definition, getDefinitionPropagationRuleFor);
|
|
416
439
|
|
|
417
440
|
// We need to propagate .elements to type artifacts - but our direct origin might not have .elements,
|
|
418
|
-
// because they are not propagated to members. We check if our root had elements (so we know that we should have some
|
|
441
|
+
// because they are not propagated to members. We check if our root had elements (so we know that we should have some as well)
|
|
419
442
|
// and then walk the origin-chain until we find the first .elements
|
|
420
443
|
if (definition.kind === 'type' && root.elements && !definition.elements) {
|
|
421
444
|
const firstOriginWithElements = getFirstOriginWithElements(source);
|
|
@@ -580,7 +603,7 @@ module.exports = (csn, options) => {
|
|
|
580
603
|
* @param {Function} getCustomRule getter for the `memberProps` or `defProps`
|
|
581
604
|
* which shall be used for retrieving custom rules
|
|
582
605
|
* @param {object} [except=null] array of properties which should not be propagated
|
|
583
|
-
* @param {object} [force=null] Force propagation of the contained keys via rule
|
|
606
|
+
* @param {object} [force=null] Force propagation of the contained keys via a custom rule.
|
|
584
607
|
*/
|
|
585
608
|
function copyProperties(from, to, getCustomRule, except = null, force = null) {
|
|
586
609
|
const keys = Object.keys(from);
|
|
@@ -589,7 +612,7 @@ function copyProperties(from, to, getCustomRule, except = null, force = null) {
|
|
|
589
612
|
if (except && !(force && force[key]) && (key.charAt(0) in except || key in except))
|
|
590
613
|
continue;
|
|
591
614
|
if (!(key in to)) {
|
|
592
|
-
const func = force && force[key] ?
|
|
615
|
+
const func = force && force[key] ? force[key] : getCustomRule(key);
|
|
593
616
|
if (func)
|
|
594
617
|
func(key, to, from);
|
|
595
618
|
}
|
|
@@ -680,6 +703,19 @@ function notWithTypeOrigin(prop, target, source) {
|
|
|
680
703
|
target[prop] = source[prop];
|
|
681
704
|
}
|
|
682
705
|
|
|
706
|
+
/**
|
|
707
|
+
* The value `null` tells us to skip the propagation of the property.
|
|
708
|
+
* This is the case e.g. for `doc` or for annotations.
|
|
709
|
+
*
|
|
710
|
+
* @param {string} prop
|
|
711
|
+
* @param {CSN.Element} target
|
|
712
|
+
* @param {CSN.Element} source
|
|
713
|
+
*/
|
|
714
|
+
function nullStopsPropagation(prop, target, source) {
|
|
715
|
+
if (source[prop] !== null)
|
|
716
|
+
target[prop] = source[prop];
|
|
717
|
+
}
|
|
718
|
+
|
|
683
719
|
/**
|
|
684
720
|
* Special propagation rules for .items - depending on the exact type of .items and the
|
|
685
721
|
* way it was referenced (type of, direct type, direct many), we need to propagate (or not).
|
|
@@ -700,6 +736,20 @@ function specialItemsRules(prop, target, source) {
|
|
|
700
736
|
target[prop] = source[prop];
|
|
701
737
|
}
|
|
702
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Don't propagate certain properties if the target already has a .items or .elements
|
|
741
|
+
*
|
|
742
|
+
* This happens with .expand/.inline
|
|
743
|
+
*
|
|
744
|
+
* @param {string} prop
|
|
745
|
+
* @param {CSN.Element} target
|
|
746
|
+
* @param {CSN.Element} source
|
|
747
|
+
*/
|
|
748
|
+
function notWithItemsOrElements(prop, target, source) {
|
|
749
|
+
if (!target.items && !target.elements || !source.target)
|
|
750
|
+
target[prop] = source[prop];
|
|
751
|
+
}
|
|
752
|
+
|
|
703
753
|
/**
|
|
704
754
|
* Some properties must not be copied over if the type of this member
|
|
705
755
|
* is a reference to another element.
|
package/lib/utils/file.js
CHANGED
|
@@ -61,12 +61,12 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
function realpath(path, cb) {
|
|
64
|
-
return fs.realpath(path, cb);
|
|
64
|
+
return fs.realpath.native(path, cb);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function realpathSync(path, cb) {
|
|
68
68
|
try {
|
|
69
|
-
cb(null, fs.realpathSync(path));
|
|
69
|
+
cb(null, fs.realpathSync.native(path));
|
|
70
70
|
}
|
|
71
71
|
catch (err) {
|
|
72
72
|
cb(err, null);
|
|
@@ -87,7 +87,7 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
87
87
|
}
|
|
88
88
|
let body = fileCache[filename];
|
|
89
89
|
if (body && typeof body === 'object' && body.realname) {
|
|
90
|
-
filename = body.realname; // use fs.realpath name
|
|
90
|
+
filename = body.realname; // use fs.realpath.native name
|
|
91
91
|
body = fileCache[filename];
|
|
92
92
|
}
|
|
93
93
|
if (body !== undefined && body !== true) { // true: we just know it is there
|
|
@@ -28,13 +28,18 @@ const extensions = [ '.cds', '.csn', '.json' ];
|
|
|
28
28
|
* - Why a global? The Umbrella could pass it as an option.
|
|
29
29
|
*
|
|
30
30
|
* @param {string} modulePath
|
|
31
|
+
* @param {CSN.Options} options
|
|
31
32
|
* @returns {string}
|
|
32
33
|
*/
|
|
33
|
-
function adaptCdsModule(modulePath) {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
function adaptCdsModule(modulePath, options = {}) {
|
|
35
|
+
if (modulePath.startsWith( '@sap/cds/' )) {
|
|
36
|
+
if (options.cdsHome)
|
|
37
|
+
return options.cdsHome + modulePath.slice(8);
|
|
36
38
|
// eslint-disable-next-line
|
|
37
|
-
|
|
39
|
+
if (global['cds'] && global['cds'].home)
|
|
40
|
+
// eslint-disable-next-line
|
|
41
|
+
return global['cds'].home + modulePath.slice(8);
|
|
42
|
+
}
|
|
38
43
|
return modulePath;
|
|
39
44
|
}
|
|
40
45
|
|
|
@@ -42,6 +47,7 @@ function adaptCdsModule(modulePath) {
|
|
|
42
47
|
* @param {object} dep
|
|
43
48
|
* @param {object} fileCache
|
|
44
49
|
* @param {CSN.Options} options
|
|
50
|
+
* @param {object} messageFunctions
|
|
45
51
|
*/
|
|
46
52
|
function resolveModule( dep, fileCache, options, messageFunctions ) {
|
|
47
53
|
const _fs = cdsFs(fileCache, options.traceFs);
|
|
@@ -58,7 +64,7 @@ function resolveModule( dep, fileCache, options, messageFunctions ) {
|
|
|
58
64
|
realpath: _fs.realpath,
|
|
59
65
|
};
|
|
60
66
|
return new Promise( (fulfill, reject) => {
|
|
61
|
-
const lookupPath = adaptCdsModule(dep.module);
|
|
67
|
+
const lookupPath = adaptCdsModule(dep.module, options);
|
|
62
68
|
resolveCDS( lookupPath, opts, (err, res) => {
|
|
63
69
|
// console.log('RESOLVE', dep, res, err)
|
|
64
70
|
if (err) {
|
|
@@ -105,6 +111,7 @@ function resolveModule( dep, fileCache, options, messageFunctions ) {
|
|
|
105
111
|
* @param {object} dep
|
|
106
112
|
* @param {object} fileCache
|
|
107
113
|
* @param {CSN.Options} options
|
|
114
|
+
* @param {object} messageFunctions
|
|
108
115
|
*/
|
|
109
116
|
function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
|
|
110
117
|
const _fs = cdsFs(fileCache, options.traceFs);
|
|
@@ -118,7 +125,7 @@ function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
|
|
|
118
125
|
|
|
119
126
|
let result = null;
|
|
120
127
|
let error = null;
|
|
121
|
-
const lookupPath = adaptCdsModule(dep.module);
|
|
128
|
+
const lookupPath = adaptCdsModule(dep.module, options);
|
|
122
129
|
|
|
123
130
|
resolveCDS( lookupPath, opts, (err, res) => {
|
|
124
131
|
if (err)
|
package/lib/utils/timetrace.js
CHANGED
|
@@ -14,47 +14,43 @@ class StopWatch {
|
|
|
14
14
|
*/
|
|
15
15
|
constructor(id) {
|
|
16
16
|
this.id = id;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// eslint-disable-next-line no-multi-assign
|
|
20
|
-
this.startTime = this.lapTime = process.hrtime();
|
|
17
|
+
this.startTime = process.hrtime.bigint();
|
|
18
|
+
this.lapTime = this.startTime;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
* Start watch.
|
|
23
|
+
*/
|
|
26
24
|
start() {
|
|
27
|
-
|
|
28
|
-
this.
|
|
25
|
+
this.startTime = process.hrtime.bigint();
|
|
26
|
+
this.lapTime = this.startTime;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
/**
|
|
32
|
-
* Stop and return delta T
|
|
30
|
+
* Stop and return delta T in nanoseconds,
|
|
33
31
|
* but do not set start time
|
|
34
32
|
*/
|
|
35
33
|
stop() {
|
|
36
|
-
|
|
34
|
+
const endTime = process.hrtime.bigint();
|
|
35
|
+
return endTime - this.startTime;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
/**
|
|
40
|
-
* return lap time
|
|
41
|
-
*/
|
|
42
38
|
lap() {
|
|
43
|
-
const
|
|
44
|
-
|
|
39
|
+
const endTime = process.hrtime.bigint();
|
|
40
|
+
const dt = endTime - this.startTime;
|
|
41
|
+
this.lapTime = process.hrtime.bigint();
|
|
45
42
|
return dt;
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
// stop as sec.ns float
|
|
49
45
|
stopInFloatSecs() {
|
|
50
46
|
const dt = this.stop();
|
|
51
|
-
return dt
|
|
47
|
+
return dt / BigInt(1000000000);
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
// lap as sec.ns float
|
|
55
51
|
lapInFloatSecs() {
|
|
56
52
|
const dt = this.lap();
|
|
57
|
-
return dt
|
|
53
|
+
return dt / BigInt(1000000000);
|
|
58
54
|
}
|
|
59
55
|
}
|
|
60
56
|
|
|
@@ -63,7 +59,7 @@ class StopWatch {
|
|
|
63
59
|
*
|
|
64
60
|
* Results are logged to stderr
|
|
65
61
|
*
|
|
66
|
-
* To enable time tracing, set
|
|
62
|
+
* To enable time tracing, set CDSC_TIMETRACING to true in the environment
|
|
67
63
|
*
|
|
68
64
|
* @class TimeTracer
|
|
69
65
|
*/
|
|
@@ -107,10 +103,13 @@ class TimeTracer {
|
|
|
107
103
|
stop() {
|
|
108
104
|
try {
|
|
109
105
|
const current = this.traceStack.pop();
|
|
110
|
-
const
|
|
106
|
+
const dt = current.stop();
|
|
111
107
|
const base = `${ ' '.repeat(this.traceStack.length * 2) }${ current.id } took:`;
|
|
108
|
+
const sec = (dt / BigInt(1000000000)).toString();
|
|
109
|
+
// first, get remaining ns, then convert to ms.
|
|
110
|
+
const msec = ((dt % BigInt(1000000000)) / BigInt(1000000)).toString();
|
|
112
111
|
// eslint-disable-next-line no-console
|
|
113
|
-
console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`,
|
|
112
|
+
console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, sec, msec );
|
|
114
113
|
}
|
|
115
114
|
catch (e) {
|
|
116
115
|
// eslint-disable-next-line no-console
|