@sap/cds-compiler 2.4.4 → 2.10.2
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 +241 -1
- package/bin/.eslintrc.json +17 -0
- package/bin/cds_update_identifiers.js +8 -7
- package/bin/cdsc.js +180 -132
- package/bin/cdshi.js +18 -11
- package/bin/cdsse.js +38 -32
- package/bin/cdsv2m.js +8 -7
- package/doc/CHANGELOG_BETA.md +36 -1
- package/lib/api/main.js +81 -100
- package/lib/api/options.js +17 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/location.js +2 -2
- package/lib/base/message-registry.js +66 -4
- package/lib/base/messages.js +84 -27
- package/lib/base/model.js +2 -61
- package/lib/checks/arrayOfs.js +0 -1
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/enricher.js +8 -2
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +27 -9
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +66 -13
- package/lib/compiler/assert-consistency.js +24 -12
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +101 -39
- package/lib/compiler/index.js +88 -59
- package/lib/compiler/resolver.js +455 -209
- package/lib/compiler/shared.js +57 -33
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +128 -99
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -127
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +74 -28
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +18 -4
- package/lib/gen/language.tokens +124 -118
- package/lib/gen/languageLexer.interp +13 -1
- package/lib/gen/languageLexer.js +870 -839
- package/lib/gen/languageLexer.tokens +116 -111
- package/lib/gen/languageParser.js +5894 -5614
- package/lib/json/from-csn.js +152 -67
- package/lib/json/to-csn.js +334 -135
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +24 -14
- package/lib/language/language.g4 +188 -128
- package/lib/main.d.ts +435 -0
- package/lib/main.js +31 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +463 -187
- package/lib/model/csnUtils.js +280 -136
- package/lib/model/enrichCsn.js +75 -4
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +70 -25
- package/lib/optionProcessor.js +13 -10
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +123 -40
- package/lib/render/toHdbcds.js +156 -65
- package/lib/render/toSql.js +87 -11
- package/lib/render/utils/common.js +55 -9
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/{sql → db}/.eslintrc.json +0 -0
- package/lib/transform/{sql → db}/assertUnique.js +7 -8
- package/lib/transform/{sql → db}/constraints.js +35 -20
- package/lib/transform/db/draft.js +353 -0
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
- package/lib/transform/{sql → db}/helpers.js +0 -0
- package/lib/transform/{sql → db}/transformExists.js +256 -60
- package/lib/transform/forHanaNew.js +216 -765
- package/lib/transform/forOdataNew.js +60 -56
- package/lib/transform/localized.js +48 -26
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
- package/lib/transform/odata/generateForeignKeyElements.js +13 -12
- package/lib/transform/odata/referenceFlattener.js +60 -36
- package/lib/transform/odata/sortByAssociationDependency.js +4 -4
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +21 -22
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +27 -17
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +141 -77
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/timetrace.js +6 -1
- package/package.json +2 -1
- package/lib/base/deepCopy.js +0 -66
- package/lib/json/walker.js +0 -26
- package/lib/utils/string.js +0 -17
|
@@ -8,9 +8,8 @@ const { hasErrors, makeMessageFunction } = require('../base/messages');
|
|
|
8
8
|
const { setProp } = require('../base/model');
|
|
9
9
|
const { csnRefs } = require('../model/csnRefs');
|
|
10
10
|
|
|
11
|
-
const { copyAnnotations } = require('../model/csnUtils');
|
|
12
|
-
const { cloneCsn,
|
|
13
|
-
getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
11
|
+
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
+
const { cloneCsn, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
14
13
|
|
|
15
14
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
16
15
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
@@ -22,7 +21,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
22
21
|
const {
|
|
23
22
|
getCsnDef,
|
|
24
23
|
getFinalBaseType,
|
|
25
|
-
|
|
24
|
+
hasAnnotationValue,
|
|
26
25
|
inspectRef,
|
|
27
26
|
isStructured,
|
|
28
27
|
} = getUtils(model);
|
|
@@ -62,7 +61,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
62
61
|
recurseElements,
|
|
63
62
|
renameAnnotation,
|
|
64
63
|
setAnnotation,
|
|
65
|
-
|
|
64
|
+
resetAnnotation,
|
|
65
|
+
expandStructsInExpression,
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
// Try to apply length, precision, scale from options if no type facet is set on the primitive types 'cds.String' or 'cds.Decimal'.
|
|
@@ -268,7 +268,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
268
268
|
let flatElem = cloneCsn(childElem, options);
|
|
269
269
|
// Don't take over notNull from leaf elements
|
|
270
270
|
delete flatElem.notNull;
|
|
271
|
-
setProp(flatElem, '$viaTransform', true); // FIXME: This name is not ideal but used elsewhere, too)
|
|
272
271
|
setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
|
|
273
272
|
addGeneratedFlattenedElement(flatElem, flatElemName);
|
|
274
273
|
}
|
|
@@ -288,49 +287,56 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
288
287
|
return result;
|
|
289
288
|
}
|
|
290
289
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
290
|
+
/**
|
|
291
|
+
* Return a copy of 'ref' where all path steps resulting from struct traversal are
|
|
292
|
+
* fused together into one step, using '_' (so that the path fits again for flattened
|
|
293
|
+
* structs), e.g.
|
|
294
|
+
* [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in
|
|
295
|
+
* [ (Entity), (struct1_struct2_assoc), (elem) ]
|
|
296
|
+
*
|
|
297
|
+
* @param {string[]} ref
|
|
298
|
+
* @param {CSN.Path} path CSN path to the ref
|
|
299
|
+
* @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
|
|
300
|
+
* @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
|
|
301
|
+
* @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
|
|
302
|
+
* @returns {string[]}
|
|
303
|
+
*/
|
|
304
|
+
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap()) {
|
|
298
305
|
// Refs of length 1 cannot contain steps - no need to check
|
|
299
306
|
if (ref.length < 2) {
|
|
300
307
|
return ref;
|
|
301
308
|
}
|
|
302
309
|
|
|
303
|
-
|
|
304
|
-
return flatten(ref, path);
|
|
305
|
-
} catch (e) {
|
|
306
|
-
if (e.message && e.message === "Scope 'ref-where' but no entity was provided.") {
|
|
307
|
-
return flatten(ref, path);
|
|
308
|
-
} else {
|
|
309
|
-
throw e;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
310
|
+
return flatten(ref, path);
|
|
312
311
|
|
|
313
312
|
function flatten(ref, path) {
|
|
314
313
|
let result = [];
|
|
315
314
|
//let stack = []; // IDs of path steps not yet processed or part of a struct traversal
|
|
316
|
-
|
|
315
|
+
if(!links && !scope) { // calculate JIT if not supplied
|
|
316
|
+
const res = inspectRef(path);
|
|
317
|
+
links = res.links;
|
|
318
|
+
scope = res.scope;
|
|
319
|
+
}
|
|
317
320
|
if (scope === '$magic')
|
|
318
321
|
return ref;
|
|
319
322
|
let flattenStep = false;
|
|
320
323
|
links.forEach((value, idx) => {
|
|
321
|
-
if (flattenStep)
|
|
322
|
-
result[result.length - 1] += pathDelimiter + ref[idx];
|
|
324
|
+
if (flattenStep) {
|
|
325
|
+
result[result.length - 1] += pathDelimiter + (ref[idx].id ? ref[idx].id : ref[idx]);
|
|
326
|
+
// if we had a filter or args, we had an assoc so this step is done
|
|
327
|
+
// we then keep along the filter/args by updating the id of the current ref
|
|
328
|
+
if(ref[idx].id) {
|
|
329
|
+
ref[idx].id = result[result.length-1];
|
|
330
|
+
result[result.length-1] = ref[idx];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
323
333
|
else {
|
|
324
334
|
result.push(ref[idx]);
|
|
325
335
|
}
|
|
326
|
-
|
|
336
|
+
|
|
337
|
+
flattenStep = value.art && !value.art.kind && !value.art.SELECT && !value.art.from && (value.art.elements || effectiveType(value.art).elements || (resolvedLinkTypes.get(value)||{}).elements);
|
|
327
338
|
});
|
|
328
|
-
|
|
329
|
-
// If the path starts with '$self', this is now redundant (because of flattening) and can be omitted,
|
|
330
|
-
// making life easier for consumers
|
|
331
|
-
if (result[0] === '$self' && result.length > 1 && !magicVars.includes(result[1])) {
|
|
332
|
-
result = result.slice(1);
|
|
333
|
-
}
|
|
339
|
+
|
|
334
340
|
return result;
|
|
335
341
|
}
|
|
336
342
|
}
|
|
@@ -366,22 +372,31 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
366
372
|
Object.assign(node, { srid: typeDef.srid });
|
|
367
373
|
}
|
|
368
374
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
375
|
+
/**
|
|
376
|
+
* Replace the type of 'node' with its final base type (in contrast to the compiler,
|
|
377
|
+
* also unravel derived enum types, i.e. take the final base type of the enum's base type.
|
|
378
|
+
* Similar with associations and compositions (we probably need a _baseType link)
|
|
379
|
+
*
|
|
380
|
+
* @param {CSN.Artifact} node
|
|
381
|
+
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
382
|
+
* @param {boolean} [keepLocalized=false] Wether to clone .localized from a type def
|
|
383
|
+
* @returns {void}
|
|
384
|
+
*/
|
|
385
|
+
function toFinalBaseType(node, resolved, keepLocalized=false) {
|
|
373
386
|
// Nothing to do if no type (or if array/struct type)
|
|
374
387
|
if (!node || !node.type) return;
|
|
375
388
|
// In case of a ref -> Follow the ref
|
|
376
389
|
if (node.type && node.type.ref) {
|
|
377
|
-
|
|
390
|
+
const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
|
|
378
391
|
if(finalBaseType === null)
|
|
379
392
|
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
|
|
380
|
-
if
|
|
381
|
-
|
|
393
|
+
if(finalBaseType.elements) {
|
|
394
|
+
// This changes the order - to be discussed!
|
|
395
|
+
node.elements = cloneCsn(finalBaseType, options).elements; // copy elements
|
|
382
396
|
delete node.type; // delete the type reference as edm processing does not expect it
|
|
383
|
-
} else if
|
|
384
|
-
|
|
397
|
+
} else if(finalBaseType.items) {
|
|
398
|
+
// This changes the order - to be discussed!
|
|
399
|
+
node.items = cloneCsn(finalBaseType, options).items; // copy items
|
|
385
400
|
delete node.type;
|
|
386
401
|
} else {
|
|
387
402
|
node.type=finalBaseType;
|
|
@@ -394,10 +409,31 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
394
409
|
// The type might already be a full fledged type def (array of)
|
|
395
410
|
let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
|
|
396
411
|
// Nothing to do if type is an array or a struct type
|
|
397
|
-
if (typeDef.items || typeDef.elements)
|
|
412
|
+
if (typeDef.items || typeDef.elements) {
|
|
413
|
+
if(!(options.transformation === 'hdbcds' || options.toSql))
|
|
414
|
+
return;
|
|
415
|
+
|
|
416
|
+
// cloneCsn only works correctly if we start "from the top"
|
|
417
|
+
const clone = cloneCsn({definitions: {'TypeDef': typeDef }}, options);
|
|
418
|
+
// With hdbcds-hdbcds, don't resolve structured types - but propagrate ".items", to turn into LargeString later on.
|
|
419
|
+
if(typeDef.items) {
|
|
420
|
+
delete node.type;
|
|
421
|
+
Object.assign(node, {items: clone.definitions.TypeDef.items});
|
|
422
|
+
}
|
|
423
|
+
if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
424
|
+
if(!typeDef.items)
|
|
425
|
+
delete node.type;
|
|
426
|
+
Object.assign(node, {elements: clone.definitions.TypeDef.elements});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
398
432
|
// if the declared element is an enum, these values are with priority
|
|
399
|
-
if (!node.enum && typeDef.enum)
|
|
400
|
-
|
|
433
|
+
if (!node.enum && typeDef.enum) {
|
|
434
|
+
const clone = cloneCsn({definitions: {'TypeDef': typeDef }}, options).definitions.TypeDef.enum;
|
|
435
|
+
Object.assign(node, { enum: clone });
|
|
436
|
+
}
|
|
401
437
|
if (node.length === undefined && typeDef.length !== undefined)
|
|
402
438
|
Object.assign(node, { length: typeDef.length });
|
|
403
439
|
if (node.precision === undefined && typeDef.precision !== undefined)
|
|
@@ -406,6 +442,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
406
442
|
Object.assign(node, { scale: typeDef.scale });
|
|
407
443
|
if (node.srid === undefined && typeDef.srid !== undefined)
|
|
408
444
|
Object.assign(node, { srid: typeDef.srid });
|
|
445
|
+
if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
|
|
446
|
+
Object.assign(node, { localized: typeDef.localized });
|
|
409
447
|
node.type = typeDef.type;
|
|
410
448
|
toFinalBaseType(node);
|
|
411
449
|
}
|
|
@@ -765,13 +803,13 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
765
803
|
*/
|
|
766
804
|
function extractValidFromToKeyElement(element, path) {
|
|
767
805
|
let validFroms = [], validTos = [], validKeys = [];
|
|
768
|
-
if (
|
|
806
|
+
if (hasAnnotationValue(element, '@cds.valid.from')) {
|
|
769
807
|
validFroms.push({ element, path: [...path] });
|
|
770
808
|
}
|
|
771
|
-
if (
|
|
809
|
+
if (hasAnnotationValue(element, '@cds.valid.to')) {
|
|
772
810
|
validTos.push({ element, path: [...path] });
|
|
773
811
|
}
|
|
774
|
-
if (
|
|
812
|
+
if (hasAnnotationValue(element, '@cds.valid.key')) {
|
|
775
813
|
validKeys.push({ element, path: [...path] });
|
|
776
814
|
}
|
|
777
815
|
return [validFroms, validTos, validKeys];
|
|
@@ -856,7 +894,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
856
894
|
if (!toName.startsWith('@')) {
|
|
857
895
|
throw Error('Annotation name should start with "@": ' + toName);
|
|
858
896
|
}
|
|
859
|
-
if (annotation
|
|
897
|
+
if (annotation === undefined) {
|
|
860
898
|
throw Error('Annotation ' + fromName + ' not found in ' + JSON.stringify(node));
|
|
861
899
|
}
|
|
862
900
|
if(node[toName] === undefined || node[toName] === null) {
|
|
@@ -886,6 +924,34 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
886
924
|
node[name] = value;
|
|
887
925
|
}
|
|
888
926
|
|
|
927
|
+
/**
|
|
928
|
+
* Assigns unconditionally annotation to a node, which means it overwrites already existing annotation assignment.
|
|
929
|
+
* Overwritting is when the assignment differs from undefined and null, also when differs from the already set value.
|
|
930
|
+
* Setting new assignment results false as return value and overwriting - true.
|
|
931
|
+
*
|
|
932
|
+
* @param {object} node Assignee
|
|
933
|
+
* @param {string} name Annotation name
|
|
934
|
+
* @param {any} value Annotation value
|
|
935
|
+
* @param {function} info function that reports info messages
|
|
936
|
+
* @param {CSN.Path} path location of the warning
|
|
937
|
+
* @returns {boolean} wasOverwritten true when the annotation was overwritten
|
|
938
|
+
*/
|
|
939
|
+
function resetAnnotation(node, name, value, info, path) {
|
|
940
|
+
if (!name.startsWith('@')) {
|
|
941
|
+
throw Error('Annotation name should start with "@": ' + name);
|
|
942
|
+
}
|
|
943
|
+
if (value === undefined) {
|
|
944
|
+
throw Error('Annotation value must not be undefined');
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
const wasOverwritten = node[name] !== undefined && node[name] !== null && node[name] !== value;
|
|
948
|
+
const oldValue = node[name];
|
|
949
|
+
node[name] = value;
|
|
950
|
+
if(wasOverwritten)
|
|
951
|
+
info(null, path, { anno: name, prop: value, otherprop: oldValue },
|
|
952
|
+
`Value $(OTHERPROP) of annotation $(ANNO) is overwritten with new value $(PROP)`);
|
|
953
|
+
return wasOverwritten;
|
|
954
|
+
}
|
|
889
955
|
|
|
890
956
|
/*
|
|
891
957
|
Resolve the type of an artifact
|
|
@@ -967,7 +1033,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
967
1033
|
function flattenPath(path, fullRef=false, followMgdAssoc=false) {
|
|
968
1034
|
let art = path._art;
|
|
969
1035
|
if(art) {
|
|
970
|
-
if(art &&
|
|
1036
|
+
if(art && !((art.items && art.items.elements) || art.elements)) {
|
|
971
1037
|
if(followMgdAssoc && art.target && art.keys) {
|
|
972
1038
|
let rc = [];
|
|
973
1039
|
for(const k of art.keys) {
|
|
@@ -980,9 +1046,9 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
980
1046
|
}
|
|
981
1047
|
return rc;
|
|
982
1048
|
}
|
|
983
|
-
if(art.type.ref)
|
|
1049
|
+
if(art.type && art.type.ref)
|
|
984
1050
|
art = resolvePath(art.type);
|
|
985
|
-
else if(!isBuiltinType(art.type))
|
|
1051
|
+
else if(art.type && !isBuiltinType(art.type))
|
|
986
1052
|
art = model.definitions[art.type];
|
|
987
1053
|
}
|
|
988
1054
|
const elements = art.items && art.items.elements || art.elements;
|
|
@@ -1004,35 +1070,33 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1004
1070
|
return [path];
|
|
1005
1071
|
}
|
|
1006
1072
|
|
|
1007
|
-
|
|
1008
|
-
* Expand structured
|
|
1073
|
+
/**
|
|
1074
|
+
* Expand structured expression arguments to flat reference paths.
|
|
1009
1075
|
* Structured elements are real sub element lists and managed associations.
|
|
1010
1076
|
* All unmanaged association definitions are rewritten if applicable (elements/mixins).
|
|
1077
|
+
* Also, HAVING and WHERE clauses are rewritten. We also check for infix filters and
|
|
1078
|
+
* .xpr in columns.
|
|
1011
1079
|
*
|
|
1012
|
-
*
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
path);
|
|
1033
|
-
}
|
|
1034
|
-
}, path.concat([ 'query' ]));
|
|
1035
|
-
}
|
|
1080
|
+
* @todo Check if can be skipped for abstract entity and or cds.persistence.skip ?
|
|
1081
|
+
* @param {CSN.Model} csn
|
|
1082
|
+
* @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts
|
|
1083
|
+
*/
|
|
1084
|
+
function expandStructsInExpression(csn, options = {}) {
|
|
1085
|
+
applyTransformations(csn, {
|
|
1086
|
+
'on': (parent, name, on, path) => {
|
|
1087
|
+
parent.on = expand(parent.on, path);
|
|
1088
|
+
},
|
|
1089
|
+
'having': (parent, name, having, path) => {
|
|
1090
|
+
parent.having = expand(parent.having, path);
|
|
1091
|
+
},
|
|
1092
|
+
'where': (parent, name, where, path) => {
|
|
1093
|
+
parent.where = expand(parent.where, path);
|
|
1094
|
+
},
|
|
1095
|
+
'xpr': (parent, name, xpr, path) => {
|
|
1096
|
+
parent.xpr = expand(parent.xpr, path);
|
|
1097
|
+
}
|
|
1098
|
+
}, undefined, undefined, options);
|
|
1099
|
+
|
|
1036
1100
|
/*
|
|
1037
1101
|
flatten structured leaf types and return array of paths
|
|
1038
1102
|
Flattening stops on all non-structured types.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { setProp, forEachGeneric, forEachDefinition, isBetaEnabled } = require('../base/model');
|
|
4
|
-
var {
|
|
4
|
+
var { makeMessageFunction } = require('../base/messages');
|
|
5
|
+
const { recompileX } = require('../compiler/index');
|
|
5
6
|
var { linkToOrigin } = require('../compiler/shared');
|
|
6
7
|
const {compactModel, compactExpr} = require('../json/to-csn');
|
|
7
8
|
const { deduplicateMessages } = require('../base/messages');
|
|
@@ -12,13 +13,10 @@ const internalArtifactKinds = ['builtin'/*, '$parameters'*/, 'param'];
|
|
|
12
13
|
|
|
13
14
|
function translateAssocsToJoinsCSN(csn, options){
|
|
14
15
|
timetrace.start('Recompiling model');
|
|
15
|
-
let { augment } = require('../json/from-csn');
|
|
16
|
-
// Append `.csn` to `.cds` files to indicate recompilation.
|
|
17
|
-
const file = csn.$location && csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
|
|
18
|
-
let xsn = augment(csn, file, options);
|
|
19
|
-
const { compileSourcesX } = require('../compiler');
|
|
20
16
|
// Do not re-complain about localized
|
|
21
|
-
const
|
|
17
|
+
const compileOptions = { ...options, $skipNameCheck: true };
|
|
18
|
+
delete compileOptions.csnFlavor;
|
|
19
|
+
const model = recompileX(csn, compileOptions);
|
|
22
20
|
timetrace.stop();
|
|
23
21
|
timetrace.start('Translating associations to joins');
|
|
24
22
|
translateAssocsToJoins(model, options);
|
|
@@ -46,9 +44,9 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
// If A2J reports error - end! Continuing with a broken CSN makes no sense
|
|
49
|
-
|
|
47
|
+
makeMessageFunction(model, options).throwWithError();
|
|
50
48
|
// FIXME: Move this somewhere more appropriate
|
|
51
|
-
const compact = compactModel(model);
|
|
49
|
+
const compact = compactModel(model, compileOptions);
|
|
52
50
|
return compact;
|
|
53
51
|
}
|
|
54
52
|
|
|
@@ -618,7 +616,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
618
616
|
const id = this.id();
|
|
619
617
|
if(elt) {
|
|
620
618
|
let found = true;
|
|
621
|
-
const epath = elt
|
|
619
|
+
const epath = [elt];
|
|
622
620
|
const epl = epath.length+offset;
|
|
623
621
|
if(epl < path.length) {
|
|
624
622
|
for(let i = 0; i < epl && found; i++) {
|
|
@@ -630,7 +628,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
630
628
|
}
|
|
631
629
|
if(id) {
|
|
632
630
|
let found = true;
|
|
633
|
-
const epath = id
|
|
631
|
+
const epath = [id];
|
|
634
632
|
const epl = epath.length+offset;
|
|
635
633
|
if(epl < path.length) {
|
|
636
634
|
for(let i = 0; i < epl && found; i++) {
|
|
@@ -745,9 +743,14 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
745
743
|
function swapTableAliasesForFwdAssoc(fwdAssoc, srcAlias, tgtAlias) {
|
|
746
744
|
let newSrcAlias = tgtAlias;
|
|
747
745
|
let newTgtAlias = {};
|
|
748
|
-
// first try to identify table alias for complex views or
|
|
749
|
-
|
|
750
|
-
|
|
746
|
+
// first try to identify table alias for complex views or redirected associations
|
|
747
|
+
if(fwdAssoc._redirected && fwdAssoc._redirected.length &&
|
|
748
|
+
// redirected target must have a $QA
|
|
749
|
+
fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA &&
|
|
750
|
+
// $QA's artifact must either be same srcAlias artifact
|
|
751
|
+
(fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA._artifact === srcAlias._artifact ||
|
|
752
|
+
// OR original assoc is a mixin (then just use the $QA)
|
|
753
|
+
assoc.kind === 'mixin')) {
|
|
751
754
|
newTgtAlias.id = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.name.id;
|
|
752
755
|
newTgtAlias._artifact = fwdAssoc._redirected[fwdAssoc._redirected.length-1]._effectiveType;
|
|
753
756
|
newTgtAlias._navigation = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.path[0]._navigation;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { forEachDefinition } = require('../base/model');
|
|
4
|
+
const {
|
|
5
|
+
applyTransformations,
|
|
6
|
+
cloneCsn,
|
|
7
|
+
getUtils,
|
|
8
|
+
isBuiltinType,
|
|
9
|
+
} = require('../model/csnUtils');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Loop through a universal CSN and enrich it with the properties
|
|
13
|
+
* from the source definition - modifies the input model in-place
|
|
14
|
+
*
|
|
15
|
+
* @param {CSN.Model} csn
|
|
16
|
+
* @param {CSN.Options} options
|
|
17
|
+
*/
|
|
18
|
+
module.exports = function(csn, options) {
|
|
19
|
+
let { getOrigin, getFinalType, getFinalTypeDef } = getUtils(csn);
|
|
20
|
+
// User-defined structured types do not have the elements propagated any longer
|
|
21
|
+
// if there is no association among the elements. For that reason,
|
|
22
|
+
// as a first step propagate the elements of these
|
|
23
|
+
forEachDefinition(csn, (def) => {
|
|
24
|
+
if (def.kind === 'type' && def.type && !def.elements) {
|
|
25
|
+
const finalType = getFinalType(def.type);
|
|
26
|
+
if (isBuiltinType(finalType)) return;
|
|
27
|
+
const finalTypeDef = getFinalTypeDef(def.type);
|
|
28
|
+
if (finalTypeDef.elements)
|
|
29
|
+
def.elements = cloneCsn(finalTypeDef.elements, options);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// as a second step, loop through all the $origin properties in the model
|
|
34
|
+
// and propagate the properties from the origin definition
|
|
35
|
+
applyTransformations(csn, {
|
|
36
|
+
'$origin': (node, _$orign, $originValue, _path, parent, propName) => {
|
|
37
|
+
if (!node.kind) { // we do not want to replace whole definitions
|
|
38
|
+
if (Array.isArray($originValue))
|
|
39
|
+
propagatePropsFromOrigin(node, propName, parent);
|
|
40
|
+
else if ($originValue.$origin && Array.isArray($originValue.$origin)) {
|
|
41
|
+
// cover the case of query entity elements where we have own and ihnerited attributes/annotations
|
|
42
|
+
propagatePropsFromOrigin($originValue, propName, parent);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}, undefined, undefined, options);
|
|
48
|
+
|
|
49
|
+
function propagatePropsFromOrigin(member, memberName, construct) {
|
|
50
|
+
// TODO: shall the $origin be kept as part of the element?
|
|
51
|
+
const origin = getOrigin(member);
|
|
52
|
+
if (origin.kind) return;
|
|
53
|
+
if (member.elements && origin.type) {
|
|
54
|
+
delete member.$origin;
|
|
55
|
+
member.type = origin.type;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
let newMember = cloneCsn(origin, options);
|
|
59
|
+
// keep targets and keys of assoc, if it was redirected
|
|
60
|
+
if (origin.type === 'cds.Association') {
|
|
61
|
+
newMember.target = member.target || newMember.target;
|
|
62
|
+
newMember.keys = member.keys || newMember.keys;
|
|
63
|
+
}
|
|
64
|
+
// TODO: check if this works fine for items/returns/actions
|
|
65
|
+
construct[memberName] = newMember;
|
|
66
|
+
}
|
|
67
|
+
}
|
package/lib/utils/file.js
CHANGED
|
@@ -15,16 +15,6 @@ function splitLines(src) {
|
|
|
15
15
|
return src.split(/\r\n?|\n/);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* Change Windows style line endings to Unix style
|
|
20
|
-
*
|
|
21
|
-
* @param {string} src
|
|
22
|
-
* @returns {string}
|
|
23
|
-
*/
|
|
24
|
-
function normalizeLineEndings(src) {
|
|
25
|
-
return (src && process.platform === 'win32') ? src.replace(/\r\n/g, '\n') : src;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
18
|
/**
|
|
29
19
|
* Returns filesystem utils readFile(), isFile(), realpath() for _CDS_ usage.
|
|
30
20
|
* This includes a trace as well as usage of a file cache.
|
|
@@ -181,6 +171,5 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
181
171
|
|
|
182
172
|
module.exports = {
|
|
183
173
|
splitLines,
|
|
184
|
-
normalizeLineEndings,
|
|
185
174
|
cdsFs,
|
|
186
175
|
};
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
10
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
11
10
|
const { cdsFs } = require('./file');
|
|
12
11
|
|
|
13
12
|
const DEFAULT_ENCODING = 'utf-8';
|
|
@@ -44,7 +43,7 @@ function adaptCdsModule(modulePath) {
|
|
|
44
43
|
* @param {object} fileCache
|
|
45
44
|
* @param {CSN.Options} options
|
|
46
45
|
*/
|
|
47
|
-
function resolveModule( dep, fileCache, options ) {
|
|
46
|
+
function resolveModule( dep, fileCache, options, messageFunctions ) {
|
|
48
47
|
const _fs = cdsFs(fileCache, options.traceFs);
|
|
49
48
|
// let opts = { extensions, basedir: dep.basedir, preserveSymlinks: false };
|
|
50
49
|
// `preserveSymlinks` option does not really work -> provide workaround anyway...
|
|
@@ -96,7 +95,7 @@ function resolveModule( dep, fileCache, options ) {
|
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
}).catch( () => {
|
|
99
|
-
_errorFileNotFound(dep, options);
|
|
98
|
+
_errorFileNotFound(dep, options, messageFunctions);
|
|
100
99
|
return false;
|
|
101
100
|
});
|
|
102
101
|
}
|
|
@@ -107,7 +106,7 @@ function resolveModule( dep, fileCache, options ) {
|
|
|
107
106
|
* @param {object} fileCache
|
|
108
107
|
* @param {CSN.Options} options
|
|
109
108
|
*/
|
|
110
|
-
function resolveModuleSync( dep, fileCache, options ) {
|
|
109
|
+
function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
|
|
111
110
|
const _fs = cdsFs(fileCache, options.traceFs);
|
|
112
111
|
const opts = {
|
|
113
112
|
extensions,
|
|
@@ -129,7 +128,7 @@ function resolveModuleSync( dep, fileCache, options ) {
|
|
|
129
128
|
});
|
|
130
129
|
|
|
131
130
|
if (error) {
|
|
132
|
-
_errorFileNotFound(dep, options);
|
|
131
|
+
_errorFileNotFound(dep, options, messageFunctions);
|
|
133
132
|
return false;
|
|
134
133
|
}
|
|
135
134
|
|
|
@@ -148,7 +147,7 @@ function resolveModuleSync( dep, fileCache, options ) {
|
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
if (error) {
|
|
151
|
-
_errorFileNotFound(dep, options);
|
|
150
|
+
_errorFileNotFound(dep, options, messageFunctions);
|
|
152
151
|
return false;
|
|
153
152
|
}
|
|
154
153
|
|
|
@@ -161,8 +160,7 @@ function resolveModuleSync( dep, fileCache, options ) {
|
|
|
161
160
|
return result;
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
function _errorFileNotFound(dep, options) {
|
|
165
|
-
const { error } = makeMessageFunction( null, options, 'compile' );
|
|
163
|
+
function _errorFileNotFound(dep, options, { error }) {
|
|
166
164
|
if (dep.resolved) {
|
|
167
165
|
let resolved = path.relative( dep.basedir, dep.resolved );
|
|
168
166
|
if (options.testMode)
|
package/lib/utils/timetrace.js
CHANGED
|
@@ -95,5 +95,10 @@ class TimeTracer {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
const ignoreTimeTrace = {
|
|
99
|
+
start: () => { /* ignore */ },
|
|
100
|
+
stop: () => { /* ignore */ },
|
|
101
|
+
};
|
|
102
|
+
|
|
98
103
|
const doTimeTrace = process && process.env && process.env.CDSC_TIMETRACING !== undefined;
|
|
99
|
-
module.exports = doTimeTrace ? new TimeTracer() :
|
|
104
|
+
module.exports = doTimeTrace ? new TimeTracer() : ignoreTimeTrace;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.2",
|
|
4
4
|
"description": "CDS (Core Data Services) compiler and backends",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"cdsse": "bin/cdsse.js"
|
|
12
12
|
},
|
|
13
13
|
"main": "lib/main.js",
|
|
14
|
+
"types": "lib/main.d.ts",
|
|
14
15
|
"scripts": {
|
|
15
16
|
"postinstall": "node lib/fix_antlr4-8_warning.js"
|
|
16
17
|
},
|