@sap/cds-compiler 5.5.2 → 5.7.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 +43 -1
- package/bin/cdsse.js +4 -0
- package/bin/cdsv2m.js +2 -1
- package/doc/Versioning.md +4 -4
- package/lib/api/options.js +1 -0
- package/lib/base/builtins.js +2 -2
- package/lib/base/dictionaries.js +1 -2
- package/lib/base/keywords.js +3 -1
- package/lib/base/lazyload.js +1 -1
- package/lib/base/message-registry.js +170 -143
- package/lib/base/messages.js +69 -59
- package/lib/base/model.js +3 -3
- package/lib/base/node-helpers.js +17 -16
- package/lib/base/optionProcessorHelper.js +13 -14
- package/lib/base/shuffle.js +4 -1
- package/lib/checks/structuredAnnoExpressions.js +1 -1
- package/lib/compiler/assert-consistency.js +1 -1
- package/lib/compiler/builtins.js +4 -1
- package/lib/compiler/extend.js +20 -5
- package/lib/compiler/resolve.js +45 -9
- package/lib/compiler/shared.js +1 -0
- package/lib/edm/annotations/edmJson.js +3 -3
- package/lib/edm/annotations/genericTranslation.js +5 -1
- package/lib/edm/annotations/vocabularyDefinitions.js +8 -2
- package/lib/edm/edmUtils.js +2 -1
- package/lib/gen/BaseParser.js +142 -103
- package/lib/gen/CdlParser.js +2240 -2201
- package/lib/gen/Dictionary.json +185 -6
- package/lib/json/from-csn.js +2 -0
- package/lib/json/to-csn.js +13 -4
- package/lib/language/docCommentParser.js +11 -5
- package/lib/language/errorStrategy.js +3 -3
- package/lib/language/genericAntlrParser.js +2 -0
- package/lib/model/csnUtils.js +6 -1
- package/lib/optionProcessor.js +5 -1
- package/lib/parsers/AstBuildingParser.js +200 -86
- package/lib/parsers/CdlGrammar.g4 +142 -86
- package/lib/parsers/Lexer.js +5 -3
- package/lib/parsers/index.js +1 -1
- package/lib/render/toCdl.js +6 -5
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +5 -3
- package/lib/render/utils/common.js +19 -6
- package/lib/render/utils/standardDatabaseFunctions.js +576 -0
- package/lib/transform/addTenantFields.js +2 -1
- package/lib/transform/db/expansion.js +3 -0
- package/lib/transform/db/flattening.js +18 -77
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/rewriteCalculatedElements.js +14 -19
- package/lib/transform/db/temporal.js +2 -1
- package/lib/transform/forOdata.js +1 -1
- package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
- package/lib/transform/odata/createForeignKeys.js +25 -9
- package/lib/transform/odata/flattening.js +11 -1
- package/lib/transform/transformUtils.js +20 -85
- package/package.json +2 -1
- package/bin/cds_update_annotations.js +0 -180
package/lib/compiler/resolve.js
CHANGED
|
@@ -82,7 +82,7 @@ const expWithFilter = [ 'from', 'expand', 'inline' ];
|
|
|
82
82
|
// exception in case of an error, but push the corresponding error object to
|
|
83
83
|
// that property (should be a vector).
|
|
84
84
|
function resolve( model ) {
|
|
85
|
-
|
|
85
|
+
const { options } = model;
|
|
86
86
|
// Get shared functionality and the message function:
|
|
87
87
|
const {
|
|
88
88
|
info, warning, error, message,
|
|
@@ -90,6 +90,7 @@ function resolve( model ) {
|
|
|
90
90
|
const {
|
|
91
91
|
resolvePath,
|
|
92
92
|
resolveDefinitionName,
|
|
93
|
+
attachAndEmitValidNames,
|
|
93
94
|
traverseExpr,
|
|
94
95
|
effectiveType,
|
|
95
96
|
getOrigin,
|
|
@@ -103,7 +104,7 @@ function resolve( model ) {
|
|
|
103
104
|
} );
|
|
104
105
|
|
|
105
106
|
const ignoreSpecifiedElements
|
|
106
|
-
= isDeprecatedEnabled(
|
|
107
|
+
= isDeprecatedEnabled( options, 'ignoreSpecifiedQueryElements' );
|
|
107
108
|
|
|
108
109
|
return doResolve();
|
|
109
110
|
|
|
@@ -127,7 +128,7 @@ function resolve( model ) {
|
|
|
127
128
|
// Phase 4: resolve all artifacts:
|
|
128
129
|
forEachDefinition( model, resolveRefs );
|
|
129
130
|
forEachGeneric( model, 'vocabularies', resolveRefs );
|
|
130
|
-
if (
|
|
131
|
+
if (options.lspMode) {
|
|
131
132
|
for (const name in model.sources)
|
|
132
133
|
resolveDefinitionName( model.sources[name].namespace );
|
|
133
134
|
}
|
|
@@ -405,7 +406,7 @@ function resolve( model ) {
|
|
|
405
406
|
const allowedInMain = [ 'entity', 'aspect', 'event' ].includes( adHocOrMainKind( art ) );
|
|
406
407
|
const isTopLevelElement = parent && (parent.kind !== 'element' || parent.targetAspect);
|
|
407
408
|
|
|
408
|
-
if (
|
|
409
|
+
if (options.lspMode && art.name && !art._main)
|
|
409
410
|
resolveDefinitionName( art );
|
|
410
411
|
|
|
411
412
|
// Check KEY (TODO: make this an extra function)
|
|
@@ -551,9 +552,11 @@ function resolve( model ) {
|
|
|
551
552
|
addForeignKeyNavigations( art );
|
|
552
553
|
}
|
|
553
554
|
|
|
554
|
-
|
|
555
|
+
resolveExprWithEnum( art.default, 'default', art );
|
|
556
|
+
|
|
555
557
|
// TODO: distinguish not by $syntax (it is semantics), but whether in query
|
|
556
|
-
|
|
558
|
+
const valueCtx = (art.$syntax === 'calc') ? 'calc' : 'column';
|
|
559
|
+
resolveExprWithEnum( art.value, valueCtx, art );
|
|
557
560
|
if (art.type?.$inferred === 'cast')
|
|
558
561
|
inferTypePropertiesFromCast( art );
|
|
559
562
|
if (art.value) {
|
|
@@ -612,7 +615,7 @@ function resolve( model ) {
|
|
|
612
615
|
// If specified type is `null`, type could not be resolved.
|
|
613
616
|
else if (!compToAssoc && sType && sType !== iType &&
|
|
614
617
|
// Special case for $recompilation: allow one level of type indirection. See #12113.
|
|
615
|
-
(!
|
|
618
|
+
(!options.$recompile || sType !== iType.type?._artifact)) {
|
|
616
619
|
const typeName = !iTypeArt && 'typeExtra' || // no inferred type prop
|
|
617
620
|
iType?.name && sType?.name && 'typeName' || // both types are named
|
|
618
621
|
'type'; // unknown type names
|
|
@@ -1391,8 +1394,13 @@ function resolve( model ) {
|
|
|
1391
1394
|
|
|
1392
1395
|
function resolveExprInAnnotations( art ) {
|
|
1393
1396
|
for (const anno in art) {
|
|
1394
|
-
if (anno.charAt(0) === '@')
|
|
1397
|
+
if (anno.charAt(0) === '@') {
|
|
1398
|
+
const { name } = art[anno];
|
|
1399
|
+
const annoDef = model.vocabularies?.[name.id];
|
|
1400
|
+
if (annoDef)
|
|
1401
|
+
setLink( name, '_artifact', annoDef );
|
|
1395
1402
|
resolveAnnoExpr( art[anno], art );
|
|
1403
|
+
}
|
|
1396
1404
|
}
|
|
1397
1405
|
}
|
|
1398
1406
|
|
|
@@ -1400,7 +1408,7 @@ function resolve( model ) {
|
|
|
1400
1408
|
if (expr.$tokenTexts) {
|
|
1401
1409
|
if (!anno.kind)
|
|
1402
1410
|
initAnnotationForExpression( anno, art );
|
|
1403
|
-
|
|
1411
|
+
resolveExprWithEnum( expr, 'annotation', anno );
|
|
1404
1412
|
}
|
|
1405
1413
|
else if (expr.literal === 'array') {
|
|
1406
1414
|
expr.val.forEach( val => resolveAnnoExpr( val, art, anno ) );
|
|
@@ -1428,6 +1436,34 @@ function resolve( model ) {
|
|
|
1428
1436
|
// Might be useful for future recursive types.
|
|
1429
1437
|
}
|
|
1430
1438
|
|
|
1439
|
+
function resolveExprWithEnum( expr, exprCtx, art ) {
|
|
1440
|
+
traverseExpr( expr, exprCtx, art, resolveExprItem );
|
|
1441
|
+
const sym = expr?.sym;
|
|
1442
|
+
const assignment = art;
|
|
1443
|
+
// Check enum as top-level annotation value:
|
|
1444
|
+
if (art?.kind === '$annotation')
|
|
1445
|
+
art = art === expr && art.name._artifact;
|
|
1446
|
+
if (!art)
|
|
1447
|
+
return;
|
|
1448
|
+
const symbols = effectiveType( art )?.enum;
|
|
1449
|
+
if (!sym || !symbols)
|
|
1450
|
+
return;
|
|
1451
|
+
// TODO: or warning if enum symbol but non-enum type?
|
|
1452
|
+
if (symbols[sym.id]) {
|
|
1453
|
+
setLink( sym, '_artifact', symbols[sym.id] );
|
|
1454
|
+
}
|
|
1455
|
+
else {
|
|
1456
|
+
let type = art._effectiveType;
|
|
1457
|
+
// inferred enums can't be extended (yet): show underlying enum
|
|
1458
|
+
while (type.enum[$inferred])
|
|
1459
|
+
type = getOrigin( type );
|
|
1460
|
+
const err = message( 'ref-undefined-enum', [ sym.location, assignment ],
|
|
1461
|
+
{ id: sym.id, type } );
|
|
1462
|
+
if (options.newParser)
|
|
1463
|
+
attachAndEmitValidNames( err, symbols );
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1431
1467
|
function resolveExpr( expr, exprCtx, user ) {
|
|
1432
1468
|
traverseExpr( expr, exprCtx, user, resolveExprItem );
|
|
1433
1469
|
}
|
package/lib/compiler/shared.js
CHANGED
|
@@ -1172,6 +1172,7 @@ function fns( model ) {
|
|
|
1172
1172
|
// TODO: if it becomes non-configurable, we can omit this warning
|
|
1173
1173
|
let id = pathName( path );
|
|
1174
1174
|
let head = path[0]._artifact || { _parent: art };
|
|
1175
|
+
// eslint-disable-next-line sonarjs/no-nested-assignment
|
|
1175
1176
|
while ((head = head?._parent) && head.kind === 'builtin')
|
|
1176
1177
|
id = `${ head.name.id }.${ id }`;
|
|
1177
1178
|
const msgId = (art.$uncheckedElements) ? 'ref-unknown-var' : 'ref-undefined-var';
|
|
@@ -538,8 +538,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
538
538
|
}
|
|
539
539
|
else {
|
|
540
540
|
const facetVal = facetArg.args[0].val;
|
|
541
|
-
const
|
|
542
|
-
if (
|
|
541
|
+
const isNan = Number.isNaN(Number.parseInt(facetVal, 10));
|
|
542
|
+
if (isNan && options.isV4() && facetName === 'Scale' && facetVal !== 'variable') {
|
|
543
543
|
error('odata-anno-xpr-args', location, {
|
|
544
544
|
anno,
|
|
545
545
|
op: `${facetFuncName}(…)`,
|
|
@@ -548,7 +548,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
|
|
|
548
548
|
'#': 'wrongval_meta_list',
|
|
549
549
|
});
|
|
550
550
|
}
|
|
551
|
-
else if (
|
|
551
|
+
else if (isNan && facetName !== 'Scale') {
|
|
552
552
|
error('odata-anno-xpr-args', location, {
|
|
553
553
|
anno, op: `${facetFuncName}(…)`, meta: 'number', '#': 'wrongval_meta',
|
|
554
554
|
});
|
|
@@ -64,7 +64,7 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
64
64
|
// Crawl over the csn and trigger the annotation translation for all kinds
|
|
65
65
|
// of annotated things.
|
|
66
66
|
// Note: only works for single service
|
|
67
|
-
// Note: we assume that all objects
|
|
67
|
+
// Note: we assume that all objects lie flat in the service, i.e. objName always
|
|
68
68
|
// looks like <service name, can contain dots>.<id>
|
|
69
69
|
forEachDefinition(reqDefs, (def, defName) => {
|
|
70
70
|
if (defName === serviceName || defName.startsWith(`${serviceName}.`)) {
|
|
@@ -1345,6 +1345,10 @@ function csn2annotationEdm( reqDefs, reqDefsUtils, csnVocabularies, serviceName,
|
|
|
1345
1345
|
// if DataFieldAbstract is expected and no explicit type is provided, automatically choose DataField
|
|
1346
1346
|
if (dTypeName === 'UI.DataFieldAbstract')
|
|
1347
1347
|
actualTypeName = 'UI.DataField';
|
|
1348
|
+
// if SemanticObjectMappingAbstract is expected and no explicit type
|
|
1349
|
+
// is provided, automatically choose SemanticObjectMappingType
|
|
1350
|
+
else if (dTypeName === 'Common.SemanticObjectMappingAbstract')
|
|
1351
|
+
actualTypeName = 'Common.SemanticObjectMappingType';
|
|
1348
1352
|
|
|
1349
1353
|
else
|
|
1350
1354
|
actualTypeName = dTypeName;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
|
-
OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/
|
|
4
|
+
OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/main/vocabularies
|
|
5
5
|
Aggregation (published)
|
|
6
6
|
Authorization (published)
|
|
7
7
|
Capabilities (published)
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
Temporal (published)
|
|
13
13
|
Validation (published)
|
|
14
14
|
|
|
15
|
-
SAP: https://github.com/SAP/odata-vocabularies/tree/
|
|
15
|
+
SAP: https://github.com/SAP/odata-vocabularies/tree/main/vocabularies
|
|
16
16
|
Analytics (published)
|
|
17
17
|
CodeList (published)
|
|
18
18
|
Common (published)
|
|
19
19
|
Communication (published)
|
|
20
20
|
DataIntegration (published)
|
|
21
|
+
EntityRelationship (experimental)
|
|
21
22
|
Graph (published, experimental)
|
|
22
23
|
Hierarchy (published, experimental)
|
|
23
24
|
HTML5 (published, experimental)
|
|
@@ -75,6 +76,11 @@ const vocabularyDefinitions = {
|
|
|
75
76
|
inc: { Alias: 'DataIntegration', Namespace: 'com.sap.vocabularies.DataIntegration.v1' },
|
|
76
77
|
int: { filename: 'DataIntegration.xml' },
|
|
77
78
|
},
|
|
79
|
+
EntityRelationship: {
|
|
80
|
+
ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/EntityRelationship.xml' },
|
|
81
|
+
inc: { Alias: 'EntityRelationship', Namespace: 'com.sap.vocabularies.EntityRelationship.v1' },
|
|
82
|
+
int: { filename: 'EntityRelationship.xml' },
|
|
83
|
+
},
|
|
78
84
|
Graph: {
|
|
79
85
|
ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Graph.xml' },
|
|
80
86
|
inc: { Alias: 'Graph', Namespace: 'com.sap.vocabularies.Graph.v1' },
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -866,8 +866,9 @@ function createSchemaRef( serviceRoots, targetSchemaName ) {
|
|
|
866
866
|
function path4( def, _path = def['@path'] ) {
|
|
867
867
|
if (_path)
|
|
868
868
|
return _path.replace(/^\//, '');
|
|
869
|
+
const last = def.name.split('.').at(-1); // > my.very.CatalogService --> CatalogService
|
|
869
870
|
return ( // generate one from the service's name
|
|
870
|
-
|
|
871
|
+
last
|
|
871
872
|
.replace(/Service$/, '') // > CatalogService --> Catalog
|
|
872
873
|
.replace(/([a-z0-9])([A-Z])/g, (_, c, C) => `${c}-${C.toLowerCase()}`) // > ODataFooBarX9 --> odata-foo-bar-x9
|
|
873
874
|
.replace(/_/g, '-') // > foo_bar_baz --> foo-bar-baz
|