@sap/cds-compiler 4.0.2 → 4.1.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 +100 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_BETA.md +11 -0
- package/lib/api/main.js +31 -11
- package/lib/api/validate.js +1 -1
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +84 -38
- package/lib/base/messages.js +11 -10
- package/lib/base/model.js +6 -2
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/onConditions.js +17 -12
- package/lib/checks/queryNoDbArtifacts.js +132 -72
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +1 -1
- package/lib/compiler/assert-consistency.js +44 -16
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +7 -8
- package/lib/compiler/checks.js +274 -197
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +3 -3
- package/lib/compiler/define.js +63 -50
- package/lib/compiler/extend.js +38 -20
- package/lib/compiler/finalize-parse-cdl.js +2 -1
- package/lib/compiler/generate.js +0 -8
- package/lib/compiler/index.js +9 -7
- package/lib/compiler/kick-start.js +2 -0
- package/lib/compiler/populate.js +139 -110
- package/lib/compiler/propagator.js +4 -3
- package/lib/compiler/resolve.js +157 -126
- package/lib/compiler/shared.js +706 -404
- package/lib/compiler/tweak-assocs.js +21 -10
- package/lib/compiler/utils.js +228 -36
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/edm.js +4 -1
- package/lib/edm/edmPreprocessor.js +5 -4
- package/lib/edm/edmUtils.js +2 -4
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3987 -3963
- package/lib/json/from-csn.js +43 -47
- package/lib/json/to-csn.js +11 -11
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +59 -59
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +20 -16
- package/lib/model/revealInternalProperties.js +29 -21
- package/lib/modelCompare/compare.js +112 -39
- package/lib/modelCompare/utils/filter.js +54 -24
- package/lib/optionProcessor.js +6 -6
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +34 -20
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +77 -26
- package/lib/render/utils/common.js +3 -3
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +61 -20
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +8 -8
- package/lib/transform/db/expansion.js +17 -21
- package/lib/transform/db/flattening.js +23 -23
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +6 -6
- package/lib/transform/forRelationalDB.js +69 -75
- package/lib/transform/localized.js +6 -5
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/package.json +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -420,17 +420,10 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
420
420
|
if (typeof type === 'string' && isBuiltinType(type))
|
|
421
421
|
return;
|
|
422
422
|
|
|
423
|
-
|
|
424
|
-
if (resolved.has(type)) {
|
|
425
|
-
typeRef = resolved.get(type)?.art
|
|
426
|
-
// The cached entry may not be resolved, yet.
|
|
427
|
-
if (typeRef.type && !isBuiltinType(typeRef.type))
|
|
428
|
-
typeRef = getFinalTypeInfo(typeRef.type);
|
|
429
|
-
} else {
|
|
430
|
-
typeRef = getFinalTypeInfo(type);
|
|
431
|
-
}
|
|
423
|
+
const typeRef = getFinalTypeInfo(type, (t) => resolved.get(t)?.art || csnUtils.artifactRef(t));
|
|
432
424
|
if(!typeRef)
|
|
433
425
|
return;
|
|
426
|
+
|
|
434
427
|
if (typeRef.elements || typeRef.items) {
|
|
435
428
|
// Copy elements/items and we're finished. No need to look up actual base type,
|
|
436
429
|
// since it must also be structured and must contain at least as many elements,
|
|
@@ -909,7 +902,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
909
902
|
if (annotation === undefined) {
|
|
910
903
|
throw new CompilerAssertion('Annotation ' + fromName + ' not found in ' + JSON.stringify(node));
|
|
911
904
|
}
|
|
912
|
-
if(node[toName]
|
|
905
|
+
if(node[toName] == null) {
|
|
913
906
|
delete node[fromName];
|
|
914
907
|
node[toName] = annotation;
|
|
915
908
|
}
|
|
@@ -931,9 +924,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
931
924
|
if (value === undefined) {
|
|
932
925
|
throw new CompilerAssertion('Annotation value must not be undefined');
|
|
933
926
|
}
|
|
934
|
-
|
|
935
|
-
if(node[name] === undefined || node[name] === null)
|
|
936
|
-
node[name] = value;
|
|
927
|
+
node[name] ??= value;
|
|
937
928
|
}
|
|
938
929
|
|
|
939
930
|
/**
|
|
@@ -1352,93 +1343,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1352
1343
|
|
|
1353
1344
|
}
|
|
1354
1345
|
|
|
1355
|
-
/**
|
|
1356
|
-
* Modify the given CSN/artifact in-place, applying the given customTransformations.
|
|
1357
|
-
* Dictionaries are correctly handled - a "type" transformer will not be called on an entity called "type".
|
|
1358
|
-
*
|
|
1359
|
-
* A custom transformation function has the following signature:
|
|
1360
|
-
* (any, object, string, CSN.Path) => undefined
|
|
1361
|
-
*
|
|
1362
|
-
* Given that we have a custom transformation for "type" and stumble upon a thing like below:
|
|
1363
|
-
*
|
|
1364
|
-
* {
|
|
1365
|
-
* type: "cds.String",
|
|
1366
|
-
* anotherProp: 1
|
|
1367
|
-
* }
|
|
1368
|
-
*
|
|
1369
|
-
* The input for the function would be:
|
|
1370
|
-
*
|
|
1371
|
-
* ("cds.String", { type: <>, anotherProp: <>}, "type", [xy, "type"])
|
|
1372
|
-
*
|
|
1373
|
-
* @param {CSN.Model} csn
|
|
1374
|
-
* @param {object} customTransformations Dictionary of functions to apply - if the property matches a key in this dict, it will be called
|
|
1375
|
-
* @param {boolean} [transformNonEnumerableElements=false] Transform non-enumerable elements to work with cds linked...
|
|
1376
|
-
* @returns {CSN.Model|CSN.Artifact} Return the CSN/artifact
|
|
1377
|
-
*/
|
|
1378
|
-
function transformModel(csn, customTransformations, transformNonEnumerableElements=false){
|
|
1379
|
-
const transformers = {
|
|
1380
|
-
elements: dictionary,
|
|
1381
|
-
definitions: dictionary,
|
|
1382
|
-
actions: dictionary,
|
|
1383
|
-
params: dictionary,
|
|
1384
|
-
enum: dictionary,
|
|
1385
|
-
mixin: dictionary,
|
|
1386
|
-
args: dictionary
|
|
1387
|
-
};
|
|
1388
|
-
|
|
1389
|
-
const csnPath = [];
|
|
1390
|
-
if (csn.definitions)
|
|
1391
|
-
dictionary( csn, 'definitions', csn.definitions );
|
|
1392
|
-
else {
|
|
1393
|
-
// fake it till you make it
|
|
1394
|
-
const obj = { definitions: Object.create(null)};
|
|
1395
|
-
obj.definitions.thing = csn;
|
|
1396
|
-
dictionary(obj, 'definitions', obj.definitions);
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
return csn;
|
|
1400
|
-
|
|
1401
|
-
function standard( parent, prop, node ) {
|
|
1402
|
-
// checking for .kind and .type is safe because annotations with such properties, are already flattened out
|
|
1403
|
-
const isAnnotation = () => (typeof prop === 'string' && prop.startsWith('@') && !node.kind && !node.type);
|
|
1404
|
-
if (!node || node._ignore || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ) || isAnnotation())
|
|
1405
|
-
return;
|
|
1406
|
-
|
|
1407
|
-
csnPath.push( prop );
|
|
1408
|
-
|
|
1409
|
-
if (Array.isArray(node)) {
|
|
1410
|
-
node.forEach( (n, i) => standard( node, i, n ) );
|
|
1411
|
-
}
|
|
1412
|
-
else {
|
|
1413
|
-
const iterateOver = Object.getOwnPropertyNames( node );
|
|
1414
|
-
// cds-linked resolves types and add's them to elements as non-enum - need to be processed
|
|
1415
|
-
if(transformNonEnumerableElements && node.elements && !Object.prototype.propertyIsEnumerable.call(node, 'elements')){
|
|
1416
|
-
iterateOver.push('elements');
|
|
1417
|
-
}
|
|
1418
|
-
for (const name of iterateOver) {
|
|
1419
|
-
if(customTransformations[name])
|
|
1420
|
-
customTransformations[name](node[name], node, name, csnPath.concat(name))
|
|
1421
|
-
|
|
1422
|
-
const trans = transformers[name] || standard;
|
|
1423
|
-
trans( node, name, node[name] );
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
csnPath.pop();
|
|
1427
|
-
}
|
|
1428
|
-
function dictionary( node, prop, dict ) {
|
|
1429
|
-
csnPath.push( prop );
|
|
1430
|
-
|
|
1431
|
-
if (Array.isArray(dict)) {
|
|
1432
|
-
dict.forEach( (n, i) => standard(dict, i, n))
|
|
1433
|
-
} else {
|
|
1434
|
-
for (const name of Object.getOwnPropertyNames( dict ))
|
|
1435
|
-
standard( dict, name, dict[name] );
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
csnPath.pop();
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
1346
|
/**
|
|
1443
1347
|
* Mandatory input transformation for all backends:
|
|
1444
1348
|
* Replace
|
|
@@ -1465,7 +1369,6 @@ function rewriteBuiltinTypeRef(csn) {
|
|
|
1465
1369
|
module.exports = {
|
|
1466
1370
|
// This function retrieves the actual exports
|
|
1467
1371
|
getTransformers,
|
|
1468
|
-
transformModel,
|
|
1469
1372
|
RelationalOperators,
|
|
1470
1373
|
rewriteBuiltinTypeRef,
|
|
1471
1374
|
};
|
|
@@ -1124,9 +1124,14 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1124
1124
|
// get paths of managed assocs (unmanaged assocs are not allowed in FK paths)
|
|
1125
1125
|
if(element.foreignKeys)
|
|
1126
1126
|
{
|
|
1127
|
-
for(
|
|
1127
|
+
for(const fkn in element.foreignKeys)
|
|
1128
1128
|
{
|
|
1129
|
-
|
|
1129
|
+
const fk = element.foreignKeys[fkn];
|
|
1130
|
+
// ignore an unmanaged association
|
|
1131
|
+
if(fk.targetElement._artifact.target &&
|
|
1132
|
+
fk.targetElement._artifact.on &&
|
|
1133
|
+
!fk.targetElement._artifact.foreignKeys)
|
|
1134
|
+
continue;
|
|
1130
1135
|
// once a fk is to be followed, treat all sub-paths as srcSide, this will add fk.name.id only
|
|
1131
1136
|
if(srcSide)
|
|
1132
1137
|
paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fk.name.id, fk.targetElement.path.map(ps => ps.id).join(pathDelimiter)));
|
|
@@ -1146,9 +1151,9 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1146
1151
|
// get paths of plain structured elements
|
|
1147
1152
|
else if(element.elements)
|
|
1148
1153
|
{
|
|
1149
|
-
for(
|
|
1154
|
+
for(const n in element.elements)
|
|
1150
1155
|
{
|
|
1151
|
-
|
|
1156
|
+
const elt = element.elements[n];
|
|
1152
1157
|
paths = paths.concat(flattenElement(elt, true, elt.name.id, elt.name.id));
|
|
1153
1158
|
}
|
|
1154
1159
|
}
|
|
@@ -1384,7 +1389,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1384
1389
|
|
|
1385
1390
|
All prefix trees are put underneath the $tableAlias structure with attribute $qat or $fqat.
|
|
1386
1391
|
Each path step appears exactly once for a given filter condition in the prefix tree and
|
|
1387
|
-
has a link to
|
|
1392
|
+
has a link to its definition (origin). The default filter is an empty string ''.
|
|
1388
1393
|
|
|
1389
1394
|
A special note on paths in filter conditions. Filter paths are treated like postfix
|
|
1390
1395
|
paths to an association path step, meaning, they are inserted into the assoc's $qat or $fqat
|
|
@@ -1409,32 +1414,13 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1409
1414
|
|
|
1410
1415
|
let [head, ...tail] = path;
|
|
1411
1416
|
|
|
1412
|
-
if(['$projection', '$self'].includes(head.id) && tail.length && head._navigation.kind === '$self') {
|
|
1413
|
-
// make sure not to truncate tail
|
|
1414
|
-
if(tail.length > 1)
|
|
1415
|
-
[head, ...tail] = tail;
|
|
1416
|
-
else
|
|
1417
|
-
head = tail[0];
|
|
1418
|
-
/*
|
|
1419
|
-
if the head is a path (it better be;) then use it as
|
|
1420
|
-
anchor for _navigation and just merge the tail into that QAT
|
|
1421
|
-
example:
|
|
1422
|
-
entity E { key id: Integer; toE: association to E; toF: association to F;}
|
|
1423
|
-
entity F { key id: Integer; toE: association to E; }
|
|
1424
|
-
view V as select from E { toE, $projection.toE.toF.id };
|
|
1425
|
-
*/
|
|
1426
|
-
let value = env.lead.elements[head.id].value;
|
|
1427
|
-
if(value.path) {
|
|
1428
|
-
head = value.path[0];
|
|
1429
|
-
}
|
|
1430
|
-
else // value is another expression, don't consider it
|
|
1431
|
-
return;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
1417
|
// qatParent is the node where the starting qat is attached to
|
|
1436
1418
|
let qatParent = undefined;
|
|
1437
1419
|
|
|
1420
|
+
// Note: If head is $self, we would need to resolve it if the path follows associations.
|
|
1421
|
+
// However, that is already rejected by SQL backend checks. For example $self paths
|
|
1422
|
+
// can't be "$self.assoc.foo.bar".
|
|
1423
|
+
|
|
1438
1424
|
// FROM and filter paths do not have a _navigation, but for filter paths
|
|
1439
1425
|
// the corresponding path step (to where the filter was attached to) is in env.pathStep
|
|
1440
1426
|
if(!head._navigation)
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# def-missing-type
|
|
2
2
|
|
|
3
3
|
A type artifact doesn’t have proper type information.
|
|
4
4
|
|
|
5
5
|
The message's severity is `Info` but may be raised to `Error` in the SQL,
|
|
6
6
|
SAP HANA, and OData backends. These backends require types to have type
|
|
7
|
-
information. Otherwise they aren’t able to render elements that use this
|
|
7
|
+
information. Otherwise, they aren’t able to render elements that use this
|
|
8
8
|
type (for example, to SQL columns).
|
|
9
9
|
|
|
10
|
-
This message affects CSN input and shouldn’t appear if CDL input is used.
|
|
11
|
-
|
|
12
10
|
## Example
|
|
13
11
|
|
|
14
12
|
Erroneous code example:
|
|
@@ -23,7 +21,7 @@ Erroneous code example:
|
|
|
23
21
|
}
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
`MainType` is of kind "type" but has not further type
|
|
24
|
+
`MainType` is of kind "type" but has not further type-information.
|
|
27
25
|
|
|
28
26
|
## How to Fix
|
|
29
27
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"$comment": "This is an auto-generated file! Do not edit this file directly!",
|
|
3
3
|
"messages": [
|
|
4
4
|
"anno-duplicate-unrelated-layer",
|
|
5
|
-
"check-proper-type",
|
|
6
5
|
"check-proper-type-of",
|
|
7
6
|
"def-duplicate-autoexposed",
|
|
7
|
+
"def-missing-type",
|
|
8
8
|
"extend-repeated-intralayer",
|
|
9
9
|
"extend-unrelated-layer",
|
|
10
10
|
"redirected-to-ambiguous",
|