@sap/cds-compiler 3.6.2 → 3.7.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 +49 -0
- package/README.md +3 -0
- package/bin/cdsc.js +9 -5
- package/doc/CHANGELOG_BETA.md +20 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +2 -1
- package/lib/base/dictionaries.js +10 -0
- package/lib/base/message-registry.js +56 -12
- package/lib/base/messages.js +39 -20
- package/lib/base/model.js +1 -0
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +9 -5
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +8 -5
- package/lib/checks/types.js +6 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +20 -23
- package/lib/compiler/base.js +1 -2
- package/lib/compiler/builtins.js +2 -2
- package/lib/compiler/checks.js +237 -242
- package/lib/compiler/define.js +63 -75
- package/lib/compiler/extend.js +325 -22
- package/lib/compiler/finalize-parse-cdl.js +1 -55
- package/lib/compiler/kick-start.js +6 -7
- package/lib/compiler/populate.js +284 -288
- package/lib/compiler/propagator.js +15 -13
- package/lib/compiler/resolve.js +136 -306
- package/lib/compiler/shared.js +42 -44
- package/lib/compiler/tweak-assocs.js +29 -27
- package/lib/compiler/utils.js +29 -3
- package/lib/edm/annotations/genericTranslation.js +7 -13
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +0 -4
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +1 -5
- package/lib/gen/Dictionary.json +34 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2429 -2401
- package/lib/inspect/inspectPropagation.js +2 -0
- package/lib/json/from-csn.js +87 -41
- package/lib/json/to-csn.js +47 -16
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +109 -28
- package/lib/language/language.g4 +20 -4
- package/lib/model/csnRefs.js +1 -1
- package/lib/model/csnUtils.js +1 -0
- package/lib/model/revealInternalProperties.js +1 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +20 -7
- package/lib/render/toHdbcds.js +2 -8
- package/lib/render/toSql.js +4 -3
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +559 -0
- package/lib/transform/db/transformExists.js +4 -0
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forRelationalDB.js +38 -28
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +14 -8
- package/lib/transform/transformUtilsNew.js +6 -5
- package/lib/transform/translateAssocsToJoins.js +49 -33
- package/package.json +1 -1
|
@@ -83,13 +83,16 @@ function validateOnCondition( member, memberName, property, path ) {
|
|
|
83
83
|
if (_links[j].art.target && !((_links[j].art === member) || ref[j] === '$self' || ref[j] === '$projection' || (validDollarSelf && j === _links.length - 1))) {
|
|
84
84
|
if (_links[j].art.on) {
|
|
85
85
|
// It's an unmanaged association - traversal is always forbidden
|
|
86
|
-
this.error(
|
|
86
|
+
this.error('ref-unexpected-navigation', csnPath, { '#': 'unmanaged', id, elemref });
|
|
87
87
|
}
|
|
88
88
|
else {
|
|
89
89
|
// It's a managed association - access of the foreign keys is allowed
|
|
90
90
|
const nextRef = ref[j + 1].id || ref[j + 1];
|
|
91
|
-
if (!_links[j].art.keys.some(r => r.ref[0] === nextRef))
|
|
92
|
-
this.error(
|
|
91
|
+
if (!_links[j].art.keys.some(r => r.ref[0] === nextRef)) {
|
|
92
|
+
this.error('ref-unexpected-navigation', csnPath, {
|
|
93
|
+
'#': 'std', id, elemref, name: nextRef,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
93
96
|
}
|
|
94
97
|
}
|
|
95
98
|
|
|
@@ -113,8 +116,8 @@ function validateOnCondition( member, memberName, property, path ) {
|
|
|
113
116
|
// 2) Path ends on an association (managed or unmanaged) and the other operand is a '$self'
|
|
114
117
|
|
|
115
118
|
// If this path ends structured or on an association, perform the check:
|
|
116
|
-
if ((type.target) &&
|
|
117
|
-
!( /* 1) */ (type.target && type.keys) && validStructuredElement ||
|
|
119
|
+
if ((type.target || type.elements) &&
|
|
120
|
+
!( /* 1) */ (type.target && type.keys || type.elements) && validStructuredElement ||
|
|
118
121
|
/* 2) */ (type.target && validDollarSelf)) &&
|
|
119
122
|
!type.virtual) {
|
|
120
123
|
// Do nothing - handled by lib/checks/nonexpandableStructured.js
|
package/lib/checks/types.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { hasAnnotationValue } = require('../model/csnUtils');
|
|
4
|
+
const { isBetaEnabled } = require('../base/model');
|
|
4
5
|
|
|
5
6
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
6
7
|
|
|
@@ -51,7 +52,11 @@ function checkElementTypeDefinitionHasType( member, memberName, prop, path ) {
|
|
|
51
52
|
// Computed elements, e.g. "1+1 as foo" in a view don't have a valid type and
|
|
52
53
|
// are skipped here. References to such columns are checked further below.
|
|
53
54
|
const parent = this.csn.definitions[path[1]];
|
|
54
|
-
|
|
55
|
+
|
|
56
|
+
// should only happen with csn input, not in cdl
|
|
57
|
+
// calculated elements may not have a .type (requires beta flag)
|
|
58
|
+
if ((!member.value || !isBetaEnabled(this.options, 'calculatedElements')) &&
|
|
59
|
+
!parent.projection && !parent.query && !hasArtifactTypeInformation(member)) {
|
|
55
60
|
errorAboutMissingType(this.error, path, memberName, true);
|
|
56
61
|
return;
|
|
57
62
|
}
|
package/lib/checks/validator.js
CHANGED
|
@@ -12,7 +12,7 @@ const { validateSelectItems } = require('./selectItems');
|
|
|
12
12
|
const { rejectParamDefaultsInHanaCds, warnAboutDefaultOnAssociationForHanaCds } = require('./defaultValues');
|
|
13
13
|
const validateCdsPersistenceAnnotation = require('./cdsPersistence');
|
|
14
14
|
const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
|
|
15
|
-
const
|
|
15
|
+
const validateHasPersistedElements = require('./hasPersistedElements');
|
|
16
16
|
const checkForHanaTypes = require('./checkForTypes');
|
|
17
17
|
const checkForParams = require('./parameters');
|
|
18
18
|
// forOdata
|
|
@@ -30,7 +30,8 @@ const {
|
|
|
30
30
|
checkTypeIsScalar, checkDecimalScale,
|
|
31
31
|
} = require('./types');
|
|
32
32
|
const {
|
|
33
|
-
checkPrimaryKey, checkVirtualElement, checkManagedAssoc,
|
|
33
|
+
checkPrimaryKey, checkVirtualElement, checkManagedAssoc,
|
|
34
|
+
checkRecursiveTypeUsage, rejectAnnotationsOnCalcElement,
|
|
34
35
|
} = require('./elements');
|
|
35
36
|
const checkForInvalidTarget = require('./invalidTarget');
|
|
36
37
|
const { validateAssociationsInItems } = require('./arrayOfs');
|
|
@@ -54,6 +55,8 @@ const forRelationalDBMemberValidators
|
|
|
54
55
|
warnAboutDefaultOnAssociationForHanaCds,
|
|
55
56
|
// sql.prepend/append
|
|
56
57
|
checkSqlAnnotationOnElement,
|
|
58
|
+
// no temporal annotations on calc elements
|
|
59
|
+
rejectAnnotationsOnCalcElement,
|
|
57
60
|
];
|
|
58
61
|
|
|
59
62
|
const forRelationalDBArtifactValidators
|
|
@@ -61,7 +64,7 @@ const forRelationalDBArtifactValidators
|
|
|
61
64
|
// @cds.persistence has no impact on odata
|
|
62
65
|
validateCdsPersistenceAnnotation,
|
|
63
66
|
// virtual items are not persisted on the db
|
|
64
|
-
|
|
67
|
+
validateHasPersistedElements,
|
|
65
68
|
// sql.prepend/append
|
|
66
69
|
checkSqlAnnotationOnArtifact,
|
|
67
70
|
];
|
|
@@ -254,6 +257,7 @@ function forOdata( csn, that ) {
|
|
|
254
257
|
}
|
|
255
258
|
}
|
|
256
259
|
),
|
|
260
|
+
// eslint-disable-next-line sonarjs/no-empty-collection
|
|
257
261
|
forOdataQueryValidators.concat(commonQueryValidators),
|
|
258
262
|
{
|
|
259
263
|
skipArtifact: this.isExternalServiceMember,
|
|
@@ -72,7 +72,7 @@ const { locationString, hasErrors } = require('../base/messages');
|
|
|
72
72
|
// Properties that can appear where a type can have type arguments.
|
|
73
73
|
const typeProperties = [
|
|
74
74
|
'type', '$typeArgs', 'length', 'precision', 'scale', 'srid',
|
|
75
|
-
'_effectiveType',
|
|
75
|
+
'_effectiveType', '$effectiveSeqNo',
|
|
76
76
|
];
|
|
77
77
|
|
|
78
78
|
class InternalConsistencyError extends Error {
|
|
@@ -191,8 +191,8 @@ function assertConsistency( model, stage ) {
|
|
|
191
191
|
test: isDictionary( definition ),
|
|
192
192
|
requires: [ 'kind', 'name' ],
|
|
193
193
|
optional: [
|
|
194
|
-
'elements', '$autoElement', '$uncheckedElements',
|
|
195
|
-
'$requireElementAccess', '_effectiveType', '_deps',
|
|
194
|
+
'elements', '$autoElement', '$uncheckedElements', '_origin',
|
|
195
|
+
'$requireElementAccess', '_effectiveType', '$effectiveSeqNo', '_deps',
|
|
196
196
|
],
|
|
197
197
|
schema: {
|
|
198
198
|
kind: { test: isString, enum: [ 'builtin' ] },
|
|
@@ -250,18 +250,17 @@ function assertConsistency( model, stage ) {
|
|
|
250
250
|
schema: { args: { inherits: 'query', test: isArray( query ) } },
|
|
251
251
|
requires: [ 'op', 'location', 'args' ],
|
|
252
252
|
optional: [
|
|
253
|
-
'quantifier', 'orderBy', 'limit', '
|
|
254
|
-
'
|
|
253
|
+
'quantifier', 'orderBy', 'limit', 'name', '$parens', 'kind',
|
|
254
|
+
'_parent', '_main', '_leadingQuery', '_effectiveType', '$effectiveSeqNo', // in FROM
|
|
255
255
|
],
|
|
256
256
|
},
|
|
257
257
|
select: { // sub query
|
|
258
258
|
requires: [ 'op', 'location', 'from' ],
|
|
259
259
|
optional: [
|
|
260
260
|
'name', '$parens', 'quantifier', 'mixin', 'excludingDict', 'columns', 'elements', '_deps',
|
|
261
|
-
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit',
|
|
262
|
-
'_projections', '
|
|
261
|
+
'where', 'groupBy', 'having', 'orderBy', '$orderBy', 'limit', '_origin', '_block',
|
|
262
|
+
'_projections', '_parent', '_main', '_effectiveType', '$effectiveSeqNo', '$expand',
|
|
263
263
|
'$tableAliases', 'kind', '_$next', '_combined', '$inlines', '_status',
|
|
264
|
-
'_extension', // for unapplied extensions
|
|
265
264
|
],
|
|
266
265
|
},
|
|
267
266
|
none: { optional: () => true }, // parse error
|
|
@@ -285,7 +284,7 @@ function assertConsistency( model, stage ) {
|
|
|
285
284
|
'elements', '_origin', '_joinParent', '$joinArgsIndex', '$syntax',
|
|
286
285
|
'$parens', '_status', // TODO: only in from
|
|
287
286
|
'scope', '_artifact', '$inferred', 'kind',
|
|
288
|
-
'_effectiveType', // TODO:check this
|
|
287
|
+
'_effectiveType', '$effectiveSeqNo', // TODO:check this
|
|
289
288
|
'$duplicates', // In JOIN if both sides are the same.
|
|
290
289
|
],
|
|
291
290
|
},
|
|
@@ -293,8 +292,8 @@ function assertConsistency( model, stage ) {
|
|
|
293
292
|
requires: [ 'query', 'location' ],
|
|
294
293
|
optional: [
|
|
295
294
|
'$parens',
|
|
296
|
-
'kind', 'name', '_block', '_parent', '_main',
|
|
297
|
-
'_effectiveType', '
|
|
295
|
+
'kind', 'name', '_block', '_parent', '_main', 'elements',
|
|
296
|
+
'_effectiveType', '$effectiveSeqNo', '_origin', '_joinParent', '$joinArgsIndex',
|
|
298
297
|
'$duplicates', // duplicate query in FROM clause
|
|
299
298
|
],
|
|
300
299
|
},
|
|
@@ -340,7 +339,7 @@ function assertConsistency( model, stage ) {
|
|
|
340
339
|
optional: [
|
|
341
340
|
'path', 'elements', '_outer', '_parent', '_main', '_block', 'kind',
|
|
342
341
|
'scope', '_artifact', '$inferred', '$expand', '$inCycle', '$tableAliases', '_$next',
|
|
343
|
-
'_effectiveType',
|
|
342
|
+
'_origin', '_effectiveType', '$effectiveSeqNo',
|
|
344
343
|
],
|
|
345
344
|
},
|
|
346
345
|
target: {
|
|
@@ -373,7 +372,7 @@ function assertConsistency( model, stage ) {
|
|
|
373
372
|
// required to be set by Core Compiler even with parse errors
|
|
374
373
|
test: isString,
|
|
375
374
|
enum: [
|
|
376
|
-
'context', 'service', 'entity', 'type', 'aspect', '
|
|
375
|
+
'context', 'service', 'entity', 'type', 'aspect', 'annotation',
|
|
377
376
|
'element', 'enum', 'action', 'function', 'param', 'key', 'event',
|
|
378
377
|
'annotate', 'extend', '$column',
|
|
379
378
|
'select', '$join', 'mixin',
|
|
@@ -393,10 +392,8 @@ function assertConsistency( model, stage ) {
|
|
|
393
392
|
optional: [
|
|
394
393
|
'location', '$inferred', 'sort', 'nulls',
|
|
395
394
|
'param', 'scope', // for dynamic parameter '?'
|
|
396
|
-
//
|
|
397
|
-
|
|
398
|
-
// not inside value, no `enum` inside `cast`!
|
|
399
|
-
'elements', 'items', 'enum', '$expand', 'target',
|
|
395
|
+
// A2J wrongly propagates the following into a CAST of the CSN passed to compileX:
|
|
396
|
+
'elements', 'items', 'enum',
|
|
400
397
|
],
|
|
401
398
|
|
|
402
399
|
kind: true,
|
|
@@ -515,7 +512,7 @@ function assertConsistency( model, stage ) {
|
|
|
515
512
|
optional: [
|
|
516
513
|
'enum',
|
|
517
514
|
'elements', 'cardinality', 'target', 'on', 'foreignKeys', 'items',
|
|
518
|
-
'_outer', '_effectiveType', 'notNull', '_parent',
|
|
515
|
+
'_outer', '_effectiveType', '$effectiveSeqNo', 'notNull', '_parent',
|
|
519
516
|
'_origin', '_block', '$inferred', '$expand', '$inCycle', '_deps',
|
|
520
517
|
'$syntax',
|
|
521
518
|
'_status', '_redirected',
|
|
@@ -550,6 +547,7 @@ function assertConsistency( model, stage ) {
|
|
|
550
547
|
_artifact: { test: TODO },
|
|
551
548
|
_navigation: { test: TODO },
|
|
552
549
|
_effectiveType: { kind: true, test: TODO },
|
|
550
|
+
$effectiveSeqNo: { kind: true, test: isNumber },
|
|
553
551
|
_joinParent: { test: TODO },
|
|
554
552
|
$joinArgsIndex: { test: isNumber },
|
|
555
553
|
_outer: { test: TODO }, // for returns/items
|
|
@@ -565,20 +563,19 @@ function assertConsistency( model, stage ) {
|
|
|
565
563
|
'$tableAliases', '$inlines',
|
|
566
564
|
],
|
|
567
565
|
optional: [
|
|
568
|
-
'_effectiveType', '$parens',
|
|
566
|
+
'_effectiveType', '$effectiveSeqNo', '$parens',
|
|
569
567
|
'_deps', '$expand',
|
|
570
568
|
// query specific
|
|
571
569
|
'where', 'columns', 'mixin', 'quantifier', 'offset',
|
|
572
570
|
'orderBy', '$orderBy', 'groupBy', 'excludingDict', 'having',
|
|
573
|
-
'limit', '_status',
|
|
574
|
-
'_extension', // for unapplied extensions
|
|
571
|
+
'limit', '_status', '_origin', '_effectiveType', '$effectiveSeqNo',
|
|
575
572
|
],
|
|
576
573
|
},
|
|
577
574
|
_leadingQuery: { kind: true, test: TODO },
|
|
578
575
|
$replacement: { kind: true, test: TODO }, // for smart * in queries
|
|
579
|
-
_origin: { kind:
|
|
576
|
+
_origin: { kind: true, test: TODO },
|
|
580
577
|
_pathHead: { kind: [ 'element', undefined ], test: TODO }, // column or * (wildcard)
|
|
581
|
-
_from: { kind: true, test: TODO }, //
|
|
578
|
+
_from: { kind: true, test: TODO }, // TODO: not necessary anymore ?
|
|
582
579
|
// array of $tableAlias (or includes) for explicit and implicit redirection:
|
|
583
580
|
_redirected: { kind: true, test: TODO },
|
|
584
581
|
// ...array of table aliases for targets from orig to new
|
package/lib/compiler/base.js
CHANGED
|
@@ -43,14 +43,13 @@ const kindProperties = {
|
|
|
43
43
|
source: { artifacts: true }, // TODO -> $source
|
|
44
44
|
using: {},
|
|
45
45
|
extend: {
|
|
46
|
-
isExtension: true,
|
|
47
46
|
noDep: 'special',
|
|
48
47
|
elements: true, /* only for parse-cdl */
|
|
49
48
|
actions: true, /* only for parse-cdl */
|
|
50
49
|
enum: true, /* only for parse-cdl */
|
|
51
50
|
},
|
|
52
51
|
annotate: {
|
|
53
|
-
|
|
52
|
+
noDep: 'special', elements: true, enum: true, actions: true, params: true,
|
|
54
53
|
},
|
|
55
54
|
builtin: {}, // = CURRENT_DATE, TODO: improve
|
|
56
55
|
$parameters: {}, // $parameters in query entities
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -447,11 +447,11 @@ function initBuiltins( model ) {
|
|
|
447
447
|
const absolute = prefix + name;
|
|
448
448
|
// TODO: reconsider whether to set a type to itself - looks wrong
|
|
449
449
|
const art = {
|
|
450
|
-
kind: 'type', builtin: true, name: { absolute },
|
|
450
|
+
kind: 'type', builtin: true, name: { absolute },
|
|
451
451
|
};
|
|
452
|
-
setProp( art.type, '_artifact', art );
|
|
453
452
|
if (parent)
|
|
454
453
|
parent._subArtifacts[name] = art;
|
|
454
|
+
setProp( art, '_origin', '' );
|
|
455
455
|
setProp( art, '_effectiveType', art );
|
|
456
456
|
setProp( art, '_deps', [] );
|
|
457
457
|
Object.assign( art, builtins[name] );
|