@sap/cds-compiler 6.1.0 → 6.3.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 +78 -0
- package/bin/cdsc.js +17 -6
- package/bin/cdsse.js +1 -1
- package/bin/cdsv2m.js +1 -1
- package/lib/api/main.js +29 -7
- package/lib/api/options.js +1 -1
- package/lib/base/builtins.js +9 -0
- package/lib/base/keywords.js +1 -1
- package/lib/base/message-registry.js +41 -10
- package/lib/base/messages.js +13 -6
- package/lib/base/model.js +1 -1
- package/lib/base/optionProcessorHelper.js +7 -2
- package/lib/checks/assocOutsideService.js +17 -30
- package/lib/checks/checkForTypes.js +0 -18
- package/lib/checks/checkPathsInStoredCalcElement.js +2 -1
- package/lib/checks/featureFlags.js +4 -1
- package/lib/checks/onConditions.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +16 -15
- package/lib/checks/types.js +1 -1
- package/lib/checks/utils.js +30 -6
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +3 -1
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +85 -39
- package/lib/compiler/define.js +24 -5
- package/lib/compiler/extend.js +1 -1
- package/lib/compiler/finalize-parse-cdl.js +9 -1
- package/lib/compiler/generate.js +4 -4
- package/lib/compiler/index.js +88 -6
- package/lib/compiler/lsp-api.js +2 -0
- package/lib/compiler/populate.js +8 -8
- package/lib/compiler/propagator.js +1 -1
- package/lib/compiler/resolve.js +22 -21
- package/lib/compiler/shared.js +6 -6
- package/lib/compiler/tweak-assocs.js +53 -31
- package/lib/compiler/utils.js +9 -16
- package/lib/compiler/xpr-rewrite.js +2 -2
- package/lib/gen/BaseParser.js +35 -29
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1424 -1430
- package/lib/gen/Dictionary.json +1 -2
- package/lib/gen/cdlKeywords.json +26 -0
- package/lib/inspect/inspectPropagation.js +1 -1
- package/lib/json/from-csn.js +2 -2
- package/lib/json/to-csn.js +1 -1
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/model/cloneCsn.js +1 -0
- package/lib/model/csnRefs.js +9 -4
- package/lib/model/csnUtils.js +67 -2
- package/lib/optionProcessor.js +9 -9
- package/lib/parsers/AstBuildingParser.js +28 -26
- package/lib/parsers/identifiers.js +2 -30
- package/lib/render/toCdl.js +73 -13
- package/lib/render/toSql.js +127 -108
- package/lib/render/utils/common.js +4 -2
- package/lib/render/utils/sql.js +67 -0
- package/lib/transform/addTenantFields.js +4 -4
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/associations.js +37 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
- package/lib/transform/db/assocsToQueries/utils.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +37 -36
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/processSqlServices.js +20 -2
- package/lib/transform/draft/db.js +20 -20
- package/lib/transform/draft/odata.js +38 -40
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/flattening.js +40 -47
- package/lib/transform/effective/main.js +6 -4
- package/lib/transform/forOdata.js +201 -92
- package/lib/transform/forRelationalDB.js +151 -142
- package/lib/transform/localized.js +116 -109
- package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
- package/lib/transform/odata/createForeignKeys.js +73 -70
- package/lib/transform/odata/flattening.js +216 -200
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
- package/lib/transform/odata/toFinalBaseType.js +40 -39
- package/lib/transform/odata/typesExposure.js +151 -133
- package/lib/transform/odata/utils.js +7 -6
- package/lib/transform/parseExpr.js +165 -162
- package/lib/transform/transformUtils.js +184 -551
- package/lib/transform/translateAssocsToJoins.js +511 -596
- package/lib/transform/tupleExpansion.js +495 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +2 -2
- package/lib/base/cleanSymbols.js +0 -17
- package/lib/checks/nonexpandableStructured.js +0 -39
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
forEachDefinition, forEachMemberRecursively,
|
|
4
5
|
getServiceNames, applyAnnotationsFromExtensions,
|
|
5
|
-
transformAnnotationExpression
|
|
6
|
+
transformAnnotationExpression,
|
|
7
|
+
} = require('../../model/csnUtils');
|
|
6
8
|
const { forEach } = require('../../utils/objectUtils');
|
|
7
9
|
const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');
|
|
8
10
|
const { getTransformers } = require('../transformUtils');
|
|
9
11
|
const { makeMessageFunction } = require('../../base/messages');
|
|
10
|
-
const { isBetaEnabled } = require('../../base/model');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* - Generate artificial draft fields if requested
|
|
@@ -59,7 +60,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
59
60
|
const filterDict = Object.create(null);
|
|
60
61
|
|
|
61
62
|
// validate the 'DRAFT.DraftAdministrativeData_DraftMessage' type if already present in the model
|
|
62
|
-
if (
|
|
63
|
+
if (options.draftMessages) {
|
|
63
64
|
const draftAdminDataMessagesType = csn.definitions['DRAFT.DraftAdministrativeData_DraftMessage'];
|
|
64
65
|
if (draftAdminDataMessagesType && !isValidDraftAdminDataMessagesType(draftAdminDataMessagesType)) {
|
|
65
66
|
error(null, [ 'definitions', 'DRAFT.DraftAdministrativeData_DraftMessage' ], { name: 'DRAFT.DraftAdministrativeData_DraftMessage' },
|
|
@@ -98,7 +99,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
98
99
|
artifact.actions && artifact.actions.draftPrepare)
|
|
99
100
|
return;
|
|
100
101
|
|
|
101
|
-
if(!visitedArtifacts[artifactName])
|
|
102
|
+
if (!visitedArtifacts[artifactName])
|
|
102
103
|
visitedArtifacts[artifactName] = artifact;
|
|
103
104
|
|
|
104
105
|
const draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');
|
|
@@ -118,7 +119,7 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
118
119
|
|
|
119
120
|
// Generate the DraftAdministrativeData projection into the service, unless there is already one
|
|
120
121
|
// @ts-ignore
|
|
121
|
-
const draftAdminDataProjectionName = `${getServiceOfArtifact(artifactName, services)}.DraftAdministrativeData`;
|
|
122
|
+
const draftAdminDataProjectionName = `${ getServiceOfArtifact(artifactName, services) }.DraftAdministrativeData`;
|
|
122
123
|
let draftAdminDataProjection = csn.definitions[draftAdminDataProjectionName];
|
|
123
124
|
if (!draftAdminDataProjection) {
|
|
124
125
|
// @ts-ignore
|
|
@@ -180,19 +181,15 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
180
181
|
// ... on SiblingEntity.IsActiveEntity != IsActiveEntity ...
|
|
181
182
|
siblingEntity.SiblingEntity.on = createAssociationPathComparison('SiblingEntity', 'IsActiveEntity', '!=', 'IsActiveEntity');
|
|
182
183
|
|
|
183
|
-
if (
|
|
184
|
+
if (options.draftMessages) {
|
|
184
185
|
const draftMessages = { DraftMessages: { '@Core.Computed': true, virtual: true, items: { type: 'DRAFT.DraftAdministrativeData_DraftMessage' } } };
|
|
185
186
|
addElement(draftMessages, artifact, artifactName);
|
|
186
187
|
|
|
187
188
|
if (!artifact['@Common.SideEffects#alwaysFetchMessages'] && artifact['@Common.SideEffects#alwaysFetchMessages'] !== null) {
|
|
188
|
-
setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.SourceEntities', ['']);
|
|
189
|
-
setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.TargetProperties', ['DraftMessages'] );
|
|
189
|
+
setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.SourceEntities', [ '' ]);
|
|
190
|
+
setAnnotation(artifact, '@Common.SideEffects#alwaysFetchMessages.TargetProperties', [ 'DraftMessages' ] );
|
|
190
191
|
}
|
|
191
|
-
setAnnotation(artifact, '@Common.Messages', { '=': 'DraftMessages', ref: ['DraftMessages'] });
|
|
192
|
-
setAnnotationAddressViaNavigationPath(artifactName, services);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (options.addAnnotationAddressViaNavigationPath) {
|
|
192
|
+
setAnnotation(artifact, '@Common.Messages', { '=': 'DraftMessages', ref: [ 'DraftMessages' ] });
|
|
196
193
|
setAnnotationAddressViaNavigationPath(artifactName, services);
|
|
197
194
|
}
|
|
198
195
|
|
|
@@ -249,50 +246,51 @@ function generateDrafts( csn, options, services, messageFunctions ) {
|
|
|
249
246
|
}
|
|
250
247
|
}
|
|
251
248
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
249
|
+
/*
|
|
250
|
+
* After draft decoration, all visited artifacts are supposed to have the draft state elements
|
|
251
|
+
* Is/HasActiveEntity, HasDraftEntity. Now, $draft.<postfix> (with postfix defined as magic variable
|
|
252
|
+
* in the core compiler builtins) needs to be translated into $self.<postfix>.
|
|
253
|
+
*
|
|
254
|
+
* It has to be processed after the late 'applyAnnotationsFromExtensions' which could also merge in
|
|
255
|
+
* some $draft path expressions.
|
|
259
256
|
*/
|
|
260
257
|
function rewriteDollarDraft() {
|
|
261
|
-
|
|
262
258
|
function $draft2$self(member) {
|
|
263
|
-
Object.keys(member).forEach(pn => {
|
|
264
|
-
if(pn[0] === '@') {
|
|
259
|
+
Object.keys(member).forEach((pn) => {
|
|
260
|
+
if (pn[0] === '@') {
|
|
265
261
|
transformAnnotationExpression(member, pn, {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
262
|
+
ref: (_parent, _prop, xpr, _path, _p, _ppn, ctx) => {
|
|
263
|
+
if (xpr[0] === '$draft') {
|
|
264
|
+
xpr[0] = '$self';
|
|
265
|
+
if (ctx?.annoExpr?.['='])
|
|
266
|
+
ctx.annoExpr['='] = true;
|
|
272
267
|
}
|
|
273
268
|
},
|
|
274
|
-
);
|
|
269
|
+
});
|
|
275
270
|
}
|
|
276
271
|
});
|
|
277
272
|
}
|
|
278
273
|
|
|
279
274
|
// entity parameters are not substituted as the EDM param entity is not draft enabled
|
|
280
|
-
Object.entries(visitedArtifacts).forEach(([artName, art]) => {
|
|
275
|
+
Object.entries(visitedArtifacts).forEach(([ artName, art ]) => {
|
|
281
276
|
$draft2$self(art);
|
|
282
|
-
forEachMemberRecursively(
|
|
277
|
+
forEachMemberRecursively(
|
|
278
|
+
art, $draft2$self,
|
|
283
279
|
[ 'definitions', artName ],
|
|
284
280
|
true, { elementsOnly: true }
|
|
285
281
|
);
|
|
286
|
-
if(art.actions) {
|
|
287
|
-
Object.entries(art.actions).forEach(([actionName, action]) => {
|
|
282
|
+
if (art.actions) {
|
|
283
|
+
Object.entries(art.actions).forEach(([ actionName, action ]) => {
|
|
288
284
|
$draft2$self(action);
|
|
289
|
-
forEachMemberRecursively(
|
|
290
|
-
|
|
291
|
-
|
|
285
|
+
forEachMemberRecursively(
|
|
286
|
+
action, $draft2$self,
|
|
287
|
+
[ 'definitions', artName, 'actions', actionName ]
|
|
288
|
+
);
|
|
289
|
+
if (action.returns)
|
|
292
290
|
$draft2$self(action.returns);
|
|
293
|
-
})
|
|
291
|
+
});
|
|
294
292
|
}
|
|
295
|
-
})
|
|
293
|
+
});
|
|
296
294
|
}
|
|
297
295
|
|
|
298
296
|
// Set the @Common.AddressViaNavigationPath annotation to the service of
|
|
@@ -43,7 +43,7 @@ function turnAssociationsIntoUnmanaged( csn, options, csnUtils, messageFunctions
|
|
|
43
43
|
orderBy: expandManagedToFksInArray(true),
|
|
44
44
|
}, [], { allowArtifact: artifact => artifact.query !== undefined || artifact.projection !== undefined });
|
|
45
45
|
|
|
46
|
-
forEachDefinition(csn, associations.getFKAccessFinalizer(csn, csnUtils, '_', true));
|
|
46
|
+
forEachDefinition(csn, associations.getFKAccessFinalizer(csn, options, csnUtils, '_', true));
|
|
47
47
|
applyTransformations(csn, {
|
|
48
48
|
elements: (_parent, prop, elements, path) => {
|
|
49
49
|
forEachMember(_parent, (element, elementName, _prop, elPath) => {
|
|
@@ -55,54 +55,47 @@ function flattenRefs(csn, options, csnUtils, messageFunctions) {
|
|
|
55
55
|
const refFlattener = getStructStepsFlattener(csn, options, messageFunctions, resolved, '_', adaptRefs);
|
|
56
56
|
|
|
57
57
|
forEachDefinition(csn, (def, defName) => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}, [ 'definitions', defName, 'actions', an ]);
|
|
97
|
-
}
|
|
58
|
+
applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, { processAnnotations: true, skipDict: { actions: 1 } }, [ 'definitions' ]);
|
|
59
|
+
|
|
60
|
+
adaptRefs.forEach(fn => fn());
|
|
61
|
+
adaptRefs.length = 0;
|
|
62
|
+
|
|
63
|
+
// explicit binding parameter of bound action
|
|
64
|
+
if (def.actions) {
|
|
65
|
+
const special$self = !csn?.definitions?.$self && '$self';
|
|
66
|
+
Object.entries(def.actions).forEach(([ an, a ]) => {
|
|
67
|
+
if (a.params) {
|
|
68
|
+
const params = Object.entries(a.params);
|
|
69
|
+
const firstParam = params[0][1];
|
|
70
|
+
const type = firstParam?.items?.type || firstParam?.type;
|
|
71
|
+
if (type === special$self) {
|
|
72
|
+
const bindingParamName = params[0][0];
|
|
73
|
+
const markBindingParam = {
|
|
74
|
+
ref: (parent, prop, xpr) => {
|
|
75
|
+
if ((xpr[0].id || xpr[0]) === bindingParamName)
|
|
76
|
+
setProp(parent, '$bparam', true);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
Object.keys(a)
|
|
81
|
+
.filter(pn => pn.startsWith('@') && a[pn])
|
|
82
|
+
.forEach((pn) => {
|
|
83
|
+
transformAnnotationExpression(a, pn, [ markBindingParam, refFlattener ], [ 'definitions', defName, 'actions', an ]);
|
|
84
|
+
adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
|
|
85
|
+
adaptRefs.length = 0;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
forEachMemberRecursively(a, (member, memberName, prop, path) => {
|
|
90
|
+
Object.keys(member).filter(pn => pn.startsWith('@') && member[pn]).forEach((pn) => {
|
|
91
|
+
transformAnnotationExpression(member, pn, [ markBindingParam, refFlattener ], path);
|
|
92
|
+
adaptRefs.forEach(fn => fn(true, 1, parent => parent.$bparam));
|
|
93
|
+
adaptRefs.length = 0;
|
|
94
|
+
});
|
|
95
|
+
}, [ 'definitions', defName, 'actions', an ]);
|
|
98
96
|
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
applyTransformationsOnNonDictionary(csn.definitions, defName, refFlattener, { processAnnotations: false }, [ 'definitions' ]);
|
|
104
|
-
adaptRefs.forEach(fn => fn());
|
|
105
|
-
adaptRefs.length = 0;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
106
99
|
}
|
|
107
100
|
});
|
|
108
101
|
|
|
@@ -39,10 +39,12 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
39
39
|
messageFunctions.setModel(csn);
|
|
40
40
|
|
|
41
41
|
const transformerUtils = transformUtils.getTransformers(csn, options, messageFunctions, '_');
|
|
42
|
-
const { expandStructsInExpression } = transformerUtils;
|
|
43
42
|
const redoProjections = queries.projectionToSELECTAndAddColumns(csn);
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
// re-use from transformers; otherwise we have two caches and tuple expansion
|
|
45
|
+
// would use a different one than handleExists, leading to unnecessary re-initialization
|
|
46
|
+
let { csnUtils } = transformerUtils;
|
|
47
|
+
csnUtils.initAllDefinitions();
|
|
46
48
|
|
|
47
49
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
48
50
|
const cleanup = validate.forRelationalDB(csn, {
|
|
@@ -53,13 +55,13 @@ function effectiveCsn( model, options, messageFunctions ) {
|
|
|
53
55
|
rewriteCalculatedElementsInViews(csn, options, csnUtils, '_', messageFunctions);
|
|
54
56
|
|
|
55
57
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
56
|
-
handleExists(csn, options, messageFunctions
|
|
58
|
+
handleExists(csn, options, messageFunctions, csnUtils);
|
|
57
59
|
|
|
58
60
|
// Check if structured elements and managed associations are compared in an expression
|
|
59
61
|
// and expand these structured elements. This tuple expansion allows all other
|
|
60
62
|
// subsequent procession steps to see plain paths in expressions.
|
|
61
63
|
// If errors are detected, throwWithAnyError() will return from further processing
|
|
62
|
-
expandStructsInExpression(
|
|
64
|
+
transformerUtils.expandStructsInExpression({ drillRef: true });
|
|
63
65
|
|
|
64
66
|
messageFunctions.throwWithAnyError();
|
|
65
67
|
|