@sap/cds-compiler 3.9.4 → 4.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 +92 -4
- package/README.md +0 -1
- package/bin/cdsc.js +11 -23
- package/bin/cdsse.js +3 -3
- package/doc/API.md +5 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +17 -1
- package/doc/CHANGELOG_DEPRECATED.md +28 -0
- package/lib/api/.eslintrc.json +1 -1
- package/lib/api/main.js +26 -8
- package/lib/api/options.js +2 -0
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +143 -64
- package/lib/base/messages.js +213 -107
- package/lib/base/model.js +11 -11
- package/lib/checks/.eslintrc.json +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +26 -3
- package/lib/checks/onConditions.js +67 -12
- package/lib/checks/queryNoDbArtifacts.js +106 -105
- package/lib/checks/sql-snippets.js +2 -0
- package/lib/checks/types.js +12 -6
- package/lib/checks/validator.js +2 -2
- package/lib/compiler/assert-consistency.js +10 -8
- package/lib/compiler/builtins.js +8 -2
- package/lib/compiler/checks.js +52 -35
- package/lib/compiler/define.js +31 -26
- package/lib/compiler/extend.js +120 -65
- package/lib/compiler/finalize-parse-cdl.js +12 -43
- package/lib/compiler/generate.js +16 -5
- package/lib/compiler/index.js +8 -5
- package/lib/compiler/kick-start.js +4 -3
- package/lib/compiler/populate.js +96 -95
- package/lib/compiler/propagator.js +7 -8
- package/lib/compiler/resolve.js +377 -103
- package/lib/compiler/shared.js +794 -517
- package/lib/compiler/tweak-assocs.js +8 -6
- package/lib/compiler/utils.js +44 -0
- package/lib/edm/annotations/genericTranslation.js +12 -4
- package/lib/edm/csn2edm.js +34 -32
- package/lib/edm/edm.js +34 -31
- package/lib/edm/edmAnnoPreprocessor.js +0 -23
- package/lib/edm/edmInboundChecks.js +7 -2
- package/lib/edm/edmPreprocessor.js +18 -17
- package/lib/edm/edmUtils.js +8 -4
- package/lib/gen/Dictionary.json +18 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +4 -2
- package/lib/gen/languageParser.js +5006 -4582
- package/lib/json/from-csn.js +157 -112
- package/lib/json/to-csn.js +60 -89
- package/lib/language/antlrParser.js +17 -13
- package/lib/language/docCommentParser.js +11 -1
- package/lib/language/genericAntlrParser.js +13 -10
- package/lib/language/language.g4 +168 -97
- package/lib/main.d.ts +128 -36
- package/lib/main.js +1 -1
- package/lib/model/csnRefs.js +24 -5
- package/lib/model/csnUtils.js +9 -8
- package/lib/model/revealInternalProperties.js +7 -12
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +40 -2
- package/lib/optionProcessor.js +0 -3
- package/lib/render/toCdl.js +247 -214
- package/lib/render/toHdbcds.js +197 -181
- package/lib/render/toSql.js +325 -289
- package/lib/render/utils/common.js +42 -4
- package/lib/render/utils/delta.js +1 -1
- package/lib/render/utils/sql.js +3 -3
- package/lib/transform/braceExpression.js +2 -2
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/associations.js +24 -12
- package/lib/transform/db/expansion.js +17 -18
- package/lib/transform/db/flattening.js +17 -21
- package/lib/transform/db/rewriteCalculatedElements.js +171 -64
- package/lib/transform/db/views.js +3 -4
- package/lib/transform/draft/db.js +21 -12
- package/lib/transform/draft/odata.js +4 -0
- package/lib/transform/forOdataNew.js +11 -10
- package/lib/transform/forRelationalDB.js +12 -7
- package/lib/transform/localized.js +4 -2
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/parseExpr.js +3 -0
- package/lib/transform/transformUtilsNew.js +43 -23
- package/lib/transform/translateAssocsToJoins.js +7 -6
- package/lib/transform/universalCsn/.eslintrc.json +1 -1
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
- package/package.json +2 -2
- package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
- package/share/messages/message-explanations.json +1 -1
|
@@ -610,8 +610,10 @@ function _addLocalizationViews(csn, options, useJoins, config) {
|
|
|
610
610
|
override: true,
|
|
611
611
|
filter: (name) => name.startsWith('localized.'),
|
|
612
612
|
notFound(name, index) {
|
|
613
|
-
if (!ignoreUnknownExtensions)
|
|
614
|
-
messageFunctions.message('anno-undefined-art', [ 'extensions', index ],
|
|
613
|
+
if (!ignoreUnknownExtensions) {
|
|
614
|
+
messageFunctions.message('anno-undefined-art', [ 'extensions', index ],
|
|
615
|
+
{ art: name });
|
|
616
|
+
}
|
|
615
617
|
},
|
|
616
618
|
});
|
|
617
619
|
}
|
|
@@ -103,14 +103,14 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
103
103
|
|
|
104
104
|
if(node.type && !isBuiltinType(node.type)) {
|
|
105
105
|
const finalBaseType = csnUtils.getFinalTypeInfo(node.type);
|
|
106
|
-
if(
|
|
106
|
+
if(finalBaseType == null) {
|
|
107
107
|
/*
|
|
108
|
-
type could not be resolved,
|
|
109
|
-
Today, all type refs must be resolvable,
|
|
110
|
-
|
|
108
|
+
type could not be resolved, delete type property to be equal to a typeless element
|
|
109
|
+
definition. Today, all type refs must be resolvable, input validations
|
|
110
|
+
checkTypeDefinitionHasType, checkElementTypeDefinitionHasType
|
|
111
111
|
guarantee this. In the future this may change.
|
|
112
112
|
*/
|
|
113
|
-
node.type
|
|
113
|
+
delete node.type;
|
|
114
114
|
}
|
|
115
115
|
else {
|
|
116
116
|
if (isExpandable(finalBaseType) || node.kind === 'type') {
|
|
@@ -26,7 +26,7 @@ const { CompilerAssertion } = require('../../base/error');
|
|
|
26
26
|
* and edges e(v_r, v_d) such that all v_r, v_d are elements of S (v_rs, v_ds) and with that
|
|
27
27
|
* all edges are { e(v_rs, v_ds) }.
|
|
28
28
|
*
|
|
29
|
-
* The input CSN may contain edges e(v_rs, v_dns) with v_r element of S_i (v_rs) and v_d not element
|
|
29
|
+
* The input CSN may contain edges e(v_rs, v_dns) with v_r element of S_i (v_rs) and v_d not element
|
|
30
30
|
* of S_i (v_dns).
|
|
31
31
|
*
|
|
32
32
|
* The aim of this algorithm is to produce Tc's for all requested S_i by 'filling' up the missing
|
|
@@ -50,7 +50,7 @@ const { CompilerAssertion } = require('../../base/error');
|
|
|
50
50
|
*
|
|
51
51
|
* If name(v_dns) has no prefix segments, the fallback schema name is prepended instead:
|
|
52
52
|
* name(v_ds) = name(v_s) + '.' + fallbackschema + name(v_dns);
|
|
53
|
-
*
|
|
53
|
+
*
|
|
54
54
|
* @param {CSN.Model} csn
|
|
55
55
|
* @param {function} whatsMyServiceName
|
|
56
56
|
* @param {string[]} requestedServiceNames
|
|
@@ -160,7 +160,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
160
160
|
// we're no longer in a key def => don't set notNull:true on named types
|
|
161
161
|
if(isKey)
|
|
162
162
|
isKey = false;
|
|
163
|
-
// in case this was a named type and if the
|
|
163
|
+
// in case this was a named type and if the openness does not match the type definition
|
|
164
164
|
// expose the type as a new one not changing the original definition.
|
|
165
165
|
if(elements && !!node['@open'] !== !!typeDef['@open'])
|
|
166
166
|
fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
|
|
@@ -282,6 +282,9 @@ function parseExpr(xpr, state = { anno: 0, array: true, nary: false }) {
|
|
|
282
282
|
// if(xpr?.func && funkyfuncs.includes(xpr?.func))
|
|
283
283
|
// return xpr;
|
|
284
284
|
for(let n in xpr) {
|
|
285
|
+
// xpr could be an array with polluted prototype
|
|
286
|
+
if (!Object.hasOwnProperty.call(xpr, n))
|
|
287
|
+
continue;
|
|
285
288
|
const x = xpr[n];
|
|
286
289
|
const isAnno = n[0] === '@' && isSimpleAnnoValue(x);
|
|
287
290
|
if(isAnno)
|
|
@@ -66,28 +66,35 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
66
66
|
expandStructsInExpression,
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Try to apply length, precision, scale from options if no type facet is set on the primitive types 'cds.String' or 'cds.Decimal'.
|
|
71
|
+
* If 'obj' has primitive type 'cds.String' and no length try to apply length from options if available or set to default internalDefaultLengths[type].
|
|
72
|
+
* if 'obj' has primitive type 'cds.Decimal' try to apply precision, scale from options if available.
|
|
73
|
+
*
|
|
74
|
+
* @param {CSN.Element} element
|
|
75
|
+
* @param {null|object} [internalDefaultLengths] Either null (no implicit default) or an object `{ 'cds.String': N, 'cds.Binary': N }`.
|
|
76
|
+
* */
|
|
77
|
+
function addDefaultTypeFacets(element, internalDefaultLengths = null) {
|
|
73
78
|
if (!element || !element.type)
|
|
74
79
|
return;
|
|
75
80
|
|
|
76
81
|
if (element.type === 'cds.String' && element.length === undefined) {
|
|
77
|
-
if(options.defaultStringLength) {
|
|
82
|
+
if (options.defaultStringLength) {
|
|
78
83
|
element.length = options.defaultStringLength;
|
|
79
84
|
setProp(element, '$default', true);
|
|
80
85
|
}
|
|
81
|
-
else if(
|
|
82
|
-
element.length =
|
|
86
|
+
else if (internalDefaultLengths !== null) {
|
|
87
|
+
element.length = internalDefaultLengths[element.type];
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
90
|
if (element.type === 'cds.Binary' && element.length === undefined) {
|
|
85
|
-
if(options.defaultBinaryLength) {
|
|
91
|
+
if (options.defaultBinaryLength) {
|
|
86
92
|
element.length = options.defaultBinaryLength;
|
|
87
93
|
setProp(element, '$default', true);
|
|
88
94
|
}
|
|
89
|
-
else if(
|
|
90
|
-
element.length =
|
|
95
|
+
else if(internalDefaultLengths !== null) {
|
|
96
|
+
element.length = internalDefaultLengths[element.type];
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
99
|
/*
|
|
93
100
|
if (element.type === 'cds.Decimal' && element.precision === undefined && options.precision) {
|
|
@@ -273,7 +280,22 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
273
280
|
// This has historic reasons. We don't copy doc-comments because copying annotations
|
|
274
281
|
// is questionable to begin with. Only selected annotations should have been copied,
|
|
275
282
|
// if at all.
|
|
276
|
-
|
|
283
|
+
// When flattening structured elements for OData don't propagate the odata.Type annotations
|
|
284
|
+
// as these would falsify the flattened elements. Type facets must be aligned with
|
|
285
|
+
// EdmTypeFacetMap defined in edm.js
|
|
286
|
+
const excludes = options.toOdata ?
|
|
287
|
+
{
|
|
288
|
+
'@odata.Type': 1,
|
|
289
|
+
'@odata.Scale': 1,
|
|
290
|
+
'@odata.Precision': 1,
|
|
291
|
+
'@odata.MaxLength': 1,
|
|
292
|
+
'@odata.SRID': 1,
|
|
293
|
+
'@odata.FixedLength': 1,
|
|
294
|
+
'@odata.Collation': 1,
|
|
295
|
+
'@odata.Unicode': 1,
|
|
296
|
+
} : {};
|
|
297
|
+
copyAnnotations(elem, flatElem, false, excludes);
|
|
298
|
+
|
|
277
299
|
// Copy selected type properties
|
|
278
300
|
const props = ['key', 'virtual', 'masked', 'viaAll'];
|
|
279
301
|
// 'localized' is needed for OData
|
|
@@ -413,14 +435,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
413
435
|
// Copy elements/items and we're finished. No need to look up actual base type,
|
|
414
436
|
// since it must also be structured and must contain at least as many elements,
|
|
415
437
|
// if not more (in client style CSN).
|
|
416
|
-
if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds'))
|
|
438
|
+
if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds'))
|
|
417
439
|
nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
if (typeRef.items) {
|
|
440
|
+
else if (typeRef.items)
|
|
421
441
|
nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
|
|
422
|
-
|
|
423
|
-
}
|
|
442
|
+
|
|
424
443
|
return;
|
|
425
444
|
}
|
|
426
445
|
|
|
@@ -450,7 +469,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
450
469
|
art.elements && Object.entries(art.elements).forEach(([elemName, artElem]) => {
|
|
451
470
|
let elem = Object.assign({}, artElem);
|
|
452
471
|
// Transfer xrefs, that are redirected to the projection
|
|
453
|
-
// TODO: shall we remove the
|
|
472
|
+
// TODO: shall we remove the transferred elements from the original?
|
|
454
473
|
// if (artElem._xref) {
|
|
455
474
|
// setProp(elem, '_xref', artElem._xref.filter(xref => xref.user && xref.user._main && xref.user._main._service == service));
|
|
456
475
|
// }
|
|
@@ -470,7 +489,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
470
489
|
// Assemble the projection itself and add it into the model
|
|
471
490
|
let projection = {
|
|
472
491
|
'kind': 'entity',
|
|
473
|
-
projection: query.SELECT, // it is important that
|
|
492
|
+
projection: query.SELECT, // it is important that projection and query refer to the same object!
|
|
474
493
|
elements
|
|
475
494
|
};
|
|
476
495
|
// copy annotations from art to projection
|
|
@@ -749,6 +768,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
749
768
|
if (!isBuiltinType(returnTypeName) && !model.definitions[returnTypeName])
|
|
750
769
|
throw new ModelError('Expecting valid return type name: ' + returnTypeName);
|
|
751
770
|
action.returns = { type: returnTypeName };
|
|
771
|
+
// TODO: What about annotation propagation from return type to `returns`?
|
|
752
772
|
}
|
|
753
773
|
|
|
754
774
|
// Add parameter if provided
|
|
@@ -918,7 +938,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
918
938
|
|
|
919
939
|
/**
|
|
920
940
|
* Assigns unconditionally annotation to a node, which means it overwrites already existing annotation assignment.
|
|
921
|
-
*
|
|
941
|
+
* Overwriting is when the assignment differs from undefined and null, also when differs from the already set value.
|
|
922
942
|
* Setting new assignment results false as return value and overwriting - true.
|
|
923
943
|
*
|
|
924
944
|
* @param {object} node Assignee
|
|
@@ -1114,7 +1134,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1114
1134
|
const lhsArt = lhs._art || lhs.ref && !lhs.$scope && inspectRef(location.concat(i)).art;
|
|
1115
1135
|
const rhsArt = rhs._art || rhs.ref && !rhs.$scope && inspectRef(location.concat(i+2)).art;
|
|
1116
1136
|
const lhsIsVal = (lhs.val !== undefined);
|
|
1117
|
-
// if ever rhs should be
|
|
1137
|
+
// if ever rhs should be allowed to be a value uncomment this
|
|
1118
1138
|
const rhsIsVal = (rhs === 'null' /*|| rhs.val !== undefined*/);
|
|
1119
1139
|
|
|
1120
1140
|
// lhs & rhs must be expandable types (structures or managed associations)
|
|
@@ -1300,8 +1320,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1300
1320
|
}
|
|
1301
1321
|
|
|
1302
1322
|
function getType(art) {
|
|
1303
|
-
const
|
|
1304
|
-
return Object.keys(
|
|
1323
|
+
const effArt = effectiveType(art);
|
|
1324
|
+
return Object.keys(effArt).length ? effArt : art.type;
|
|
1305
1325
|
}
|
|
1306
1326
|
|
|
1307
1327
|
function isExpandable(art) {
|
|
@@ -230,7 +230,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
230
230
|
|
|
231
231
|
/*
|
|
232
232
|
Add an artificial QA for each mixin definition. This QA completes the QAT
|
|
233
|
-
|
|
233
|
+
data-structure that requires a QA at the rootQat before starting the join generation.
|
|
234
234
|
This QA is marked as 'mixin' which indicates that the paths of the ON condition must
|
|
235
235
|
not receive the usual source and target table alias (which is used for generic associations)
|
|
236
236
|
but instead just use the rootQA of the individual ON condition paths. These paths are
|
|
@@ -684,7 +684,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
684
684
|
//env.assocStack.includes(fwdAssoc) => recursion
|
|
685
685
|
if(env.assocStack.length === 2) {
|
|
686
686
|
// reuse (ugly) error message from forHana
|
|
687
|
-
error(null, env.assocStack[0].location,
|
|
687
|
+
error(null, [env.assocStack[0].location,env.assocStack[0]],
|
|
688
688
|
{ name: '$self', id: '$self' },
|
|
689
689
|
'An association that uses $(NAME) in its ON-condition can\'t be compared to $(ID)');
|
|
690
690
|
// don't check these paths again
|
|
@@ -706,6 +706,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
706
706
|
}
|
|
707
707
|
|
|
708
708
|
function cloneOnCondExprTree(expr) {
|
|
709
|
+
// TODO: This function is not covered by an tests, only cloneOnCondExprStream is.
|
|
709
710
|
// keep parentheses intact
|
|
710
711
|
if(Array.isArray(expr))
|
|
711
712
|
return expr.map(cloneOnCondition);
|
|
@@ -1126,7 +1127,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1126
1127
|
for(let fkn in element.foreignKeys)
|
|
1127
1128
|
{
|
|
1128
1129
|
let fk = element.foreignKeys[fkn];
|
|
1129
|
-
// once a fk is to be followed, treat all sub
|
|
1130
|
+
// once a fk is to be followed, treat all sub-paths as srcSide, this will add fk.name.id only
|
|
1130
1131
|
if(srcSide)
|
|
1131
1132
|
paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fk.name.id, fk.targetElement.path.map(ps => ps.id).join(pathDelimiter)));
|
|
1132
1133
|
else
|
|
@@ -1199,7 +1200,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1199
1200
|
/*
|
|
1200
1201
|
Munch path steps and append them to a path string until an
|
|
1201
1202
|
assoc step is found. The assoc path step is also appended
|
|
1202
|
-
to the path string. If no assoc path step has
|
|
1203
|
+
to the path string. If no assoc path step has occurred, all
|
|
1203
1204
|
path steps are added to the path string and tail is empty.
|
|
1204
1205
|
|
|
1205
1206
|
Return assocPathStep, the remaining tail path and the path string
|
|
@@ -1219,7 +1220,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1219
1220
|
|
|
1220
1221
|
/*
|
|
1221
1222
|
Substitute the n first path steps of a given path against a FK alias name.
|
|
1222
|
-
Resolve a foreign key of a
|
|
1223
|
+
Resolve a foreign key of a managed association by following the n first
|
|
1223
1224
|
path steps. Longest path matches:
|
|
1224
1225
|
Example: fk tuple { a.b, a.b.c, a.b.e },
|
|
1225
1226
|
path: a.b.c.d.e.f: FK a.b.c is found, even if FK a.b is one level higher in the prefix tree.
|
|
@@ -1866,7 +1867,7 @@ function walkPath(node, env)
|
|
|
1866
1867
|
/*
|
|
1867
1868
|
NOTE: As long as association path steps are not allowed in filters,
|
|
1868
1869
|
it is not required to walk over filter expressions.
|
|
1869
|
-
Simple filter paths are rewritten
|
|
1870
|
+
Simple filter paths are rewritten in createJoinTree (first filter)
|
|
1870
1871
|
and createJoinQA (subsequent one that belong to the ON condition).
|
|
1871
1872
|
|
|
1872
1873
|
If the filter becomes JOIN relevant, default FILTERS (part of the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"root": true,
|
|
3
3
|
"plugins": ["sonarjs", "jsdoc"],
|
|
4
|
-
"extends": ["../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"
|
|
4
|
+
"extends": ["plugin:jsdoc/recommended", "../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"],
|
|
5
5
|
"rules": {
|
|
6
6
|
"prefer-const": "error",
|
|
7
7
|
"quotes": ["error", "single", "avoid-escape"],
|
|
@@ -27,7 +27,9 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
|
|
|
27
27
|
}
|
|
28
28
|
else if (artifact.kind === 'entity' || artifact.kind === 'aspect') {
|
|
29
29
|
forEachMemberRecursively(artifact, (element) => {
|
|
30
|
-
|
|
30
|
+
// Calculated elements, but simple references are ignored for on-read.
|
|
31
|
+
// casts() are also computed. In CSN, they appear next to a `.ref`.
|
|
32
|
+
if (element.value && (!element.value.ref || element.value.cast || element.value.stored))
|
|
31
33
|
setAnnotationIfNotDefined(element, '@Core.Computed', true);
|
|
32
34
|
}, path);
|
|
33
35
|
}
|
|
@@ -95,7 +97,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
|
|
|
95
97
|
return getAncestor(base.SELECT.elements[name], name, base.SELECT);
|
|
96
98
|
}
|
|
97
99
|
else if (base.ref) {
|
|
98
|
-
let artifact = artifactRef(base);
|
|
100
|
+
let artifact = artifactRef.from(base);
|
|
99
101
|
if (artifact.target)
|
|
100
102
|
artifact = artifactRef(artifact.target);
|
|
101
103
|
return artifact.elements[name];
|
|
@@ -145,7 +147,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
|
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
/**
|
|
148
|
-
*
|
|
150
|
+
* Returns true, if the given columns element needs to be annotated with @Core.Computed.
|
|
149
151
|
*
|
|
150
152
|
* @param {CSN.Column} column
|
|
151
153
|
* @returns {boolean}
|
|
@@ -153,9 +155,9 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
|
|
|
153
155
|
function needsCoreComputed( column ) {
|
|
154
156
|
return column &&
|
|
155
157
|
(
|
|
156
|
-
column.xpr || column.list || column.func || column.val !== undefined || column.param ||
|
|
158
|
+
column.xpr || column.list || column.func || column.val !== undefined || column['#'] !== undefined || column.param ||
|
|
157
159
|
column.SELECT || column.SET ||
|
|
158
|
-
column.ref && [ '$at', '$valid', '$now', '$user', '$session' ].includes(column.ref[0])
|
|
160
|
+
column.ref && [ '$at', '$valid', '$now', '$user', '$session', '$parameters' ].includes(column.ref[0])
|
|
159
161
|
);
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { setProp
|
|
3
|
+
const { setProp } = require('../../base/model');
|
|
4
4
|
const shuffleGen = require('../../base/shuffle');
|
|
5
5
|
const { setAnnotationIfNotDefined, makeClientCompatible } = require('./utils');
|
|
6
6
|
const {
|
|
@@ -23,7 +23,6 @@ const { setCoreComputedOnViewsAndCalculatedElements } = require('./coreComputed'
|
|
|
23
23
|
* @param {CSN.Options} options
|
|
24
24
|
*/
|
|
25
25
|
module.exports = (csn, options) => {
|
|
26
|
-
const propagateToReturns = isBetaEnabled( options, 'v4preview' );
|
|
27
26
|
const csnUtils = getUtils(csn, 'init-all');
|
|
28
27
|
const {
|
|
29
28
|
initDefinition, getOrigin, getQueryPrimarySource, artifactRef, getColumn,
|
|
@@ -116,7 +115,7 @@ module.exports = (csn, options) => {
|
|
|
116
115
|
// The $origin properties need to be removed separately
|
|
117
116
|
// as the values are used in csnRef::getOrigin that is used during
|
|
118
117
|
// the propagation above.
|
|
119
|
-
// Currently testMode-only for comparison against client CSN.
|
|
118
|
+
// Currently, testMode-only for comparison against client CSN.
|
|
120
119
|
if (options.testMode)
|
|
121
120
|
makeClientCompatible(csn);
|
|
122
121
|
|
|
@@ -174,10 +173,7 @@ module.exports = (csn, options) => {
|
|
|
174
173
|
|
|
175
174
|
setTargetAspectIfRequired(parent);
|
|
176
175
|
},
|
|
177
|
-
type: ( parent, prop, type
|
|
178
|
-
// annos are not propagated to `returns` (<=v3) and `items`
|
|
179
|
-
if (parentProp === 'returns' && !propagateToReturns)
|
|
180
|
-
return;
|
|
176
|
+
type: ( parent, prop, type ) => {
|
|
181
177
|
const annotationsForBuiltinType = extensions[type];
|
|
182
178
|
Object.assign( parent, annotationsForBuiltinType );
|
|
183
179
|
},
|
|
@@ -288,13 +284,17 @@ module.exports = (csn, options) => {
|
|
|
288
284
|
},
|
|
289
285
|
params: (parent, prop, params) => {
|
|
290
286
|
forEachValue(params, (param) => {
|
|
287
|
+
const propagateToParams = typeof param.type === 'string' ? csn.definitions[param.type]?.kind !== 'entity' : true;
|
|
291
288
|
propagateMemberPropsFromOrigin(param, {
|
|
292
|
-
items: true, elements: true, enum: true, virtual: true,
|
|
289
|
+
'@': !propagateToParams, items: true, elements: true, enum: true, virtual: true,
|
|
293
290
|
});
|
|
294
291
|
});
|
|
295
292
|
},
|
|
296
293
|
returns: (parent, prop, returns) => {
|
|
297
|
-
|
|
294
|
+
// Only propagate to `returns` (return parameter) if return type is not an entity.
|
|
295
|
+
// If returns.type is an array, it is an element ref. If it's not found, it's likely builtin.
|
|
296
|
+
const propagateToParams = typeof returns.type === 'string' ? csn.definitions[returns.type]?.kind !== 'entity' : true;
|
|
297
|
+
propagateMemberPropsFromOrigin(returns, { '@': !propagateToParams, items: true, elements: true });
|
|
298
298
|
if (returns.target)
|
|
299
299
|
calculateForeignKeys(returns);
|
|
300
300
|
},
|
|
@@ -368,7 +368,7 @@ module.exports = (csn, options) => {
|
|
|
368
368
|
* @todo check if still necessary
|
|
369
369
|
*/
|
|
370
370
|
function skipMemberPropagation( origin ) {
|
|
371
|
-
|
|
371
|
+
// For empty members (`{}`), the origin was set in a previous call to `getOrigin(definition)`.
|
|
372
372
|
return !origin;
|
|
373
373
|
}
|
|
374
374
|
}
|
|
@@ -519,7 +519,7 @@ module.exports = (csn, options) => {
|
|
|
519
519
|
forEachValue(elements, (element) => {
|
|
520
520
|
if (element.target) {
|
|
521
521
|
const column = getColumn(element);
|
|
522
|
-
if (column) {
|
|
522
|
+
if (column?.ref) {
|
|
523
523
|
const mixin = query.SELECT.mixin[implicitAs(column.ref)] || {};
|
|
524
524
|
copyProperties(mixin, element, getMemberPropagationRuleFor);
|
|
525
525
|
}
|
|
@@ -552,7 +552,7 @@ module.exports = (csn, options) => {
|
|
|
552
552
|
if (!target.kind)
|
|
553
553
|
return;
|
|
554
554
|
const primarySourceRef = getQueryPrimarySource(target.query || target.projection);
|
|
555
|
-
const artRef = primarySourceRef ? artifactRef(primarySourceRef) : source;
|
|
555
|
+
const artRef = primarySourceRef ? artifactRef.from(primarySourceRef) : source;
|
|
556
556
|
if (!artRef.target)
|
|
557
557
|
target[prop] = source[prop];
|
|
558
558
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-compiler",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
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)",
|
|
@@ -59,6 +59,6 @@
|
|
|
59
59
|
"LICENSE"
|
|
60
60
|
],
|
|
61
61
|
"engines": {
|
|
62
|
-
"node": ">=
|
|
62
|
+
"node": ">=16"
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# duplicate-autoexposed
|
|
1
|
+
# def-duplicate-autoexposed
|
|
2
2
|
|
|
3
3
|
Two or more entities with the same name can’t be auto-exposed in the same
|
|
4
4
|
service.
|
|
@@ -76,3 +76,7 @@ auto-exposing entities. The reason is that the resulting auto-exposed names
|
|
|
76
76
|
could become _long_ names that don’t seem natural nor intuitive. We chose to
|
|
77
77
|
expose the entity name because that’s what most developers want to do when
|
|
78
78
|
they manually expose entities.
|
|
79
|
+
|
|
80
|
+
## Other Notes
|
|
81
|
+
|
|
82
|
+
This message was called `duplicate-autoexposed` in cds-compiler v3 and earlier.
|