@sap/cds-compiler 4.4.4 → 4.6.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 +88 -0
- package/bin/cdsc.js +18 -11
- package/bin/cdsv2m.js +7 -5
- package/doc/CHANGELOG_BETA.md +22 -0
- package/lib/api/main.js +306 -144
- package/lib/api/options.js +18 -6
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +45 -10
- package/lib/base/messages.js +33 -16
- package/lib/base/model.js +4 -0
- package/lib/base/optionProcessorHelper.js +45 -176
- package/lib/checks/annotationsOData.js +49 -0
- package/lib/checks/elements.js +32 -34
- package/lib/checks/enricher.js +39 -3
- package/lib/checks/validator.js +8 -7
- package/lib/compiler/assert-consistency.js +40 -17
- package/lib/compiler/builtins.js +30 -53
- package/lib/compiler/checks.js +46 -14
- package/lib/compiler/cycle-detector.js +1 -4
- package/lib/compiler/define.js +35 -10
- package/lib/compiler/extend.js +21 -7
- package/lib/compiler/generate.js +3 -0
- package/lib/compiler/populate.js +5 -1
- package/lib/compiler/propagator.js +46 -9
- package/lib/compiler/resolve.js +94 -35
- package/lib/compiler/shared.js +60 -33
- package/lib/compiler/tweak-assocs.js +188 -92
- package/lib/compiler/utils.js +11 -1
- package/lib/edm/annotations/edmJson.js +41 -66
- package/lib/edm/annotations/genericTranslation.js +27 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -3
- package/lib/edm/csn2edm.js +28 -11
- package/lib/edm/edmInboundChecks.js +58 -15
- package/lib/edm/edmPreprocessor.js +12 -16
- package/lib/edm/edmUtils.js +5 -2
- package/lib/gen/Dictionary.json +10 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +15 -2
- package/lib/gen/language.tokens +1 -0
- package/lib/gen/languageParser.js +6557 -5618
- package/lib/json/from-csn.js +4 -5
- package/lib/json/to-csn.js +29 -4
- package/lib/language/antlrParser.js +19 -1
- package/lib/language/errorStrategy.js +28 -7
- package/lib/language/genericAntlrParser.js +118 -24
- package/lib/language/textUtils.js +16 -0
- package/lib/main.d.ts +28 -3
- package/lib/main.js +3 -0
- package/lib/model/csnRefs.js +4 -1
- package/lib/model/csnUtils.js +20 -14
- package/lib/model/revealInternalProperties.js +5 -2
- package/lib/optionProcessor.js +23 -22
- package/lib/render/manageConstraints.js +13 -29
- package/lib/render/toCdl.js +47 -26
- package/lib/render/toHdbcds.js +63 -42
- package/lib/render/toRename.js +6 -10
- package/lib/render/toSql.js +71 -117
- package/lib/render/utils/common.js +41 -6
- package/lib/transform/.eslintrc.json +9 -1
- package/lib/transform/addTenantFields.js +228 -0
- package/lib/transform/db/applyTransformations.js +57 -4
- package/lib/transform/db/assertUnique.js +4 -4
- package/lib/transform/db/backlinks.js +13 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +24 -3
- package/lib/transform/db/flattening.js +70 -71
- package/lib/transform/db/killAnnotations.js +37 -0
- package/lib/transform/db/rewriteCalculatedElements.js +46 -6
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/draft/db.js +2 -16
- package/lib/transform/draft/odata.js +3 -3
- package/lib/transform/effective/associations.js +3 -5
- package/lib/transform/effective/main.js +6 -9
- package/lib/transform/forOdata.js +26 -55
- package/lib/transform/forRelationalDB.js +38 -18
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/transform/transformUtils.js +47 -34
- package/lib/transform/translateAssocsToJoins.js +45 -11
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +7 -6
|
@@ -9,15 +9,15 @@ const transformUtils = require('../transformUtils');
|
|
|
9
9
|
const { csnRefs } = require('../../model/csnRefs');
|
|
10
10
|
const { setProp, isBetaEnabled } = require('../../base/model');
|
|
11
11
|
const { forEach } = require('../../utils/objectUtils');
|
|
12
|
-
const { cardinality2str } = require('../../model/csnUtils');
|
|
13
|
-
|
|
12
|
+
const { cardinality2str, isAnnotationExpression } = require('../../model/csnUtils');
|
|
13
|
+
const { transformExpression } = require('./applyTransformations');
|
|
14
14
|
/**
|
|
15
15
|
* Strip off leading $self from refs where applicable
|
|
16
16
|
*
|
|
17
17
|
* @param {CSN.Model} csn
|
|
18
18
|
*/
|
|
19
19
|
function removeLeadingSelf( csn ) {
|
|
20
|
-
const magicVars = [ '$now', '$self', '$projection', '$user', '$session', '$at' ];
|
|
20
|
+
const magicVars = [ '$now', '$self', '$projection', '$user', '$tenant', '$session', '$at' ];
|
|
21
21
|
applyTransformations(csn, {
|
|
22
22
|
elements: (parent, prop, elements) => {
|
|
23
23
|
for (const [ elementName, element ] of Object.entries(elements)) {
|
|
@@ -42,11 +42,12 @@ function removeLeadingSelf( csn ) {
|
|
|
42
42
|
*
|
|
43
43
|
* @param {CSN.Model} csn
|
|
44
44
|
* @param {CSN.Options} options
|
|
45
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
45
46
|
* @param {WeakMap} resolved Cache for resolved refs
|
|
46
47
|
* @param {string} pathDelimiter
|
|
47
48
|
* @param {object} iterateOptions
|
|
48
49
|
*/
|
|
49
|
-
function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOptions = {} ) {
|
|
50
|
+
function resolveTypeReferences( csn, options, messageFunctions, resolved, pathDelimiter, iterateOptions = {} ) {
|
|
50
51
|
/**
|
|
51
52
|
* Remove .localized from the element and any sub-elements
|
|
52
53
|
*
|
|
@@ -67,7 +68,7 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
|
|
|
67
68
|
stack.push(...Object.values(current.elements));
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
|
-
const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
71
|
+
const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
|
|
71
72
|
const { getServiceName, getFinalTypeInfo } = csnUtils;
|
|
72
73
|
|
|
73
74
|
// We don't want to iterate over actions
|
|
@@ -95,18 +96,17 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
|
|
|
95
96
|
|
|
96
97
|
// structured types might not have the child-types replaced.
|
|
97
98
|
// Drill down to ensure this.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
99
|
+
const nextElements = node.elements || node.items?.elements;
|
|
100
|
+
const stack = nextElements ? [ nextElements ] : [];
|
|
101
|
+
while (stack.length > 0) {
|
|
102
|
+
const elements = stack.pop();
|
|
103
|
+
for (const e of Object.values(elements)) {
|
|
104
|
+
toFinalBaseType(e, resolved, true);
|
|
105
|
+
if (!options.toOdata && e.items) // items could have unresolved types
|
|
106
|
+
toFinalBaseType(e.items, resolved, true);
|
|
107
|
+
const next = e.elements || e.items?.elements;
|
|
108
|
+
if (next)
|
|
109
|
+
stack.push(next);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -164,27 +164,28 @@ function resolveTypeReferences( csn, options, resolved, pathDelimiter, iterateOp
|
|
|
164
164
|
*/
|
|
165
165
|
function isODataItems( typeName ) {
|
|
166
166
|
const typeDef = csn.definitions[typeName];
|
|
167
|
-
return !!(options.toOdata && typeDef && typeDef
|
|
167
|
+
return !!(options.toOdata && typeDef && typeDef?.items);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
/**
|
|
172
172
|
* @param {CSN.Model} csn
|
|
173
173
|
* @param {CSN.Options} options
|
|
174
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
174
175
|
* @param {WeakMap} resolved Cache for resolved refs
|
|
175
176
|
* @param {string} pathDelimiter
|
|
176
177
|
* @param {object} iterateOptions
|
|
177
178
|
*/
|
|
178
|
-
function flattenAllStructStepsInRefs( csn, options, resolved, pathDelimiter, iterateOptions = {} ) {
|
|
179
|
+
function flattenAllStructStepsInRefs( csn, options, messageFunctions, resolved, pathDelimiter, iterateOptions = {} ) {
|
|
179
180
|
const { inspectRef, effectiveType } = csnRefs(csn);
|
|
180
|
-
const { flattenStructStepsInRef } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
181
|
+
const { flattenStructStepsInRef } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
|
|
181
182
|
const adaptRefs = [];
|
|
182
183
|
|
|
183
184
|
/**
|
|
184
185
|
* For each step of the links, check if there is a type reference.
|
|
185
186
|
* If there is, resolve it and store the result in a WeakMap.
|
|
186
187
|
*
|
|
187
|
-
* @param {Array} [links
|
|
188
|
+
* @param {Array} [links]
|
|
188
189
|
* @todo seems too hacky
|
|
189
190
|
* @returns {WeakMap} A WeakMap where a link is the key and the type is the value
|
|
190
191
|
*/
|
|
@@ -258,12 +259,13 @@ function flattenAllStructStepsInRefs( csn, options, resolved, pathDelimiter, ite
|
|
|
258
259
|
/**
|
|
259
260
|
* @param {CSN.Model} csn
|
|
260
261
|
* @param {CSN.Options} options
|
|
262
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
261
263
|
* @param {string} pathDelimiter
|
|
262
|
-
* @param {Function} error
|
|
263
264
|
* @param {object} iterateOptions
|
|
264
265
|
*/
|
|
265
|
-
function flattenElements( csn, options,
|
|
266
|
-
const {
|
|
266
|
+
function flattenElements( csn, options, messageFunctions, pathDelimiter, iterateOptions = {} ) {
|
|
267
|
+
const { error } = messageFunctions;
|
|
268
|
+
const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
|
|
267
269
|
const { isAssocOrComposition, effectiveType } = csnUtils;
|
|
268
270
|
const transformers = {
|
|
269
271
|
elements: flatten,
|
|
@@ -309,33 +311,28 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
309
311
|
if (flatElement.notNull !== false && !branch.some(s => !s.notNull))
|
|
310
312
|
flatElement.notNull = true;
|
|
311
313
|
|
|
312
|
-
|
|
313
314
|
if (flatElement.type && isAssocOrComposition(flatElement) && flatElement.on) {
|
|
314
315
|
// unmanaged relations can't be primary key
|
|
315
316
|
delete flatElement.key;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (onPart.ref) {
|
|
319
|
-
const firstRef = onPart.ref[0];
|
|
320
|
-
|
|
321
|
-
/*
|
|
322
|
-
when element is defined in the current name resolution scope, like
|
|
323
|
-
entity E {
|
|
324
|
-
key x: Integer;
|
|
325
|
-
s : {
|
|
326
|
-
y : Integer;
|
|
327
|
-
a3 : association to E on a3.x = y;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
|
|
331
|
-
*/
|
|
317
|
+
transformExpression(flatElement, 'on', {
|
|
318
|
+
ref: (_parent, _prop, xpr) => {
|
|
332
319
|
const prefix = flatElement._flatElementNameWithDots.split('.').slice(0, -1).join(pathDelimiter);
|
|
333
|
-
const possibleFlatName = prefix + pathDelimiter +
|
|
334
|
-
|
|
320
|
+
const possibleFlatName = prefix + pathDelimiter + xpr[0];
|
|
321
|
+
/*
|
|
322
|
+
when element is defined in the current name resolution scope, like
|
|
323
|
+
entity E {
|
|
324
|
+
key x: Integer;
|
|
325
|
+
s : {
|
|
326
|
+
y : Integer;
|
|
327
|
+
a3 : association to E on a3.x = y;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
|
|
331
|
+
*/
|
|
335
332
|
if (flatElems[possibleFlatName])
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
}
|
|
333
|
+
xpr[0] = possibleFlatName;
|
|
334
|
+
},
|
|
335
|
+
});
|
|
339
336
|
}
|
|
340
337
|
parent[prop].$orderedElements.push([ flatElemName, flatElement ]);
|
|
341
338
|
// Still add them - otherwise we might not detect collisions between generated elements.
|
|
@@ -434,16 +431,16 @@ function linkForeignKeyAnnotationExtensionsToAssociation( csn, options ) {
|
|
|
434
431
|
/**
|
|
435
432
|
* @param {CSN.Model} csn
|
|
436
433
|
* @param {CSN.Options} options
|
|
437
|
-
* @param {
|
|
438
|
-
* @param {Function} warning
|
|
434
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
439
435
|
* @param {string} pathDelimiter
|
|
440
436
|
* @param {boolean} flattenKeyRefs
|
|
441
437
|
* @param {object} csnUtils
|
|
442
438
|
* @param {object} iterateOptions
|
|
443
439
|
*/
|
|
444
|
-
function handleManagedAssociationsAndCreateForeignKeys( csn, options,
|
|
440
|
+
function handleManagedAssociationsAndCreateForeignKeys( csn, options, messageFunctions, pathDelimiter, flattenKeyRefs, csnUtils, iterateOptions = {} ) {
|
|
441
|
+
const { error, warning } = messageFunctions;
|
|
445
442
|
const { isManagedAssociation, inspectRef, isStructured } = csnUtils;
|
|
446
|
-
const { flattenStructStepsInRef, flattenStructuredElement } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
443
|
+
const { flattenStructStepsInRef, flattenStructuredElement } = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter);
|
|
447
444
|
if (flattenKeyRefs) {
|
|
448
445
|
applyTransformations(csn, {
|
|
449
446
|
elements: (parent, prop, elements, path) => {
|
|
@@ -814,6 +811,29 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
|
|
|
814
811
|
return [ [ prefix, newFk ] ];
|
|
815
812
|
}
|
|
816
813
|
|
|
814
|
+
fks.forEach((fk) => {
|
|
815
|
+
// prepend current prefix
|
|
816
|
+
fk[0] = `${prefix}${pathDelimiter}${fk[0]}`;
|
|
817
|
+
// if this is the entry association, decorate the final foreign keys with the association props
|
|
818
|
+
if (lvl === 0) {
|
|
819
|
+
if (options.transformation !== 'effective')
|
|
820
|
+
fk[1]['@odata.foreignKey4'] = prefix;
|
|
821
|
+
if (options.transformation === 'odata' || options.transformation === 'effective') {
|
|
822
|
+
const validAnnoNames = Object.keys(element).filter(pn => pn[0] === '@' && !isAnnotationExpression(element[pn]));
|
|
823
|
+
copyAnnotations(element, fk[1], true, {}, validAnnoNames);
|
|
824
|
+
}
|
|
825
|
+
// propagate not null to final foreign key
|
|
826
|
+
for (const prop of [ 'notNull', 'key' ]) {
|
|
827
|
+
if (element[prop] !== undefined)
|
|
828
|
+
fk[1][prop] = element[prop];
|
|
829
|
+
}
|
|
830
|
+
if (element.$location)
|
|
831
|
+
setProp(fk[1], '$location', element.$location);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
return fks;
|
|
835
|
+
|
|
836
|
+
|
|
817
837
|
/**
|
|
818
838
|
* Get the path to continue resolving references
|
|
819
839
|
*
|
|
@@ -834,27 +854,6 @@ function createForeignKeys( csnUtils, path, element, prefix, csn, options, pathD
|
|
|
834
854
|
return [ path, ...additions ];
|
|
835
855
|
return [ ...path, ...additions ];
|
|
836
856
|
}
|
|
837
|
-
|
|
838
|
-
fks.forEach((fk) => {
|
|
839
|
-
// prepend current prefix
|
|
840
|
-
fk[0] = `${prefix}${pathDelimiter}${fk[0]}`;
|
|
841
|
-
// if this is the entry association, decorate the final foreign keys with the association props
|
|
842
|
-
if (lvl === 0) {
|
|
843
|
-
if (options.transformation !== 'effective')
|
|
844
|
-
fk[1]['@odata.foreignKey4'] = prefix;
|
|
845
|
-
if (options.transformation === 'odata' || options.transformation === 'effective')
|
|
846
|
-
copyAnnotations(element, fk[1], true);
|
|
847
|
-
|
|
848
|
-
// propagate not null to final foreign key
|
|
849
|
-
for (const prop of [ 'notNull', 'key' ]) {
|
|
850
|
-
if (element[prop] !== undefined)
|
|
851
|
-
fk[1][prop] = element[prop];
|
|
852
|
-
}
|
|
853
|
-
if (element.$location)
|
|
854
|
-
setProp(fk[1], '$location', element.$location);
|
|
855
|
-
}
|
|
856
|
-
});
|
|
857
|
-
return fks;
|
|
858
857
|
}
|
|
859
858
|
|
|
860
859
|
module.exports = {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const requiredAnnos = {
|
|
4
|
+
'@cds.persistence.skip': true,
|
|
5
|
+
'@cds.persistence.exists': true,
|
|
6
|
+
'@cds.persistence.table': true,
|
|
7
|
+
'@cds.persistence.journal': true, // Build checks on it
|
|
8
|
+
'@sql.append': true,
|
|
9
|
+
'@sql.prepend': true,
|
|
10
|
+
'@sql.replace': true, // We do a check on this, no real function
|
|
11
|
+
'@assert.unique': true, // We do a check on this, no real function
|
|
12
|
+
'@assert.integrity': true,
|
|
13
|
+
'@cds.valid.from': true,
|
|
14
|
+
'@cds.valid.to': true,
|
|
15
|
+
'@cds.valid.key': true,
|
|
16
|
+
'@odata.draft.enabled': true,
|
|
17
|
+
'@fiori.draft.enabled': true,
|
|
18
|
+
'@cds.persistence.calcview': true,
|
|
19
|
+
'@cds.persistence.udf': true,
|
|
20
|
+
'@cds.autoexpose': true,
|
|
21
|
+
'@cds.autoexposed': true,
|
|
22
|
+
'@cds.redirection.target': true,
|
|
23
|
+
'@Core.Computed': true,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @param {object} carrier
|
|
29
|
+
* @param {string} annoKey
|
|
30
|
+
*/
|
|
31
|
+
function killNonrequiredAnno( carrier, annoKey ) {
|
|
32
|
+
if (!requiredAnnos[annoKey] && !annoKey.startsWith('@assert.unique.'))
|
|
33
|
+
delete carrier[annoKey];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
module.exports = { killNonrequiredAnno };
|
|
@@ -149,8 +149,11 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
149
149
|
art, env, links, scope,
|
|
150
150
|
} = getRefInfo(parent, p);
|
|
151
151
|
|
|
152
|
+
// calc element publishes association, treat as regular
|
|
153
|
+
// unmanaged association
|
|
154
|
+
const calcElementIsAssoc = art?.value && art.target;
|
|
152
155
|
// TODO: Calculated elements on-write
|
|
153
|
-
if (art?.value && !art.value.stored) {
|
|
156
|
+
if (art?.value && !art.value.stored && !calcElementIsAssoc) {
|
|
154
157
|
const alias = parent.as || implicitAs(parent.ref);
|
|
155
158
|
// TODO: What about other scopes? expand/inline?
|
|
156
159
|
const value = (scope !== 'ref-target') ? absolutifyPaths(env, art, ref, links, name).value : keepAssocStepsInRef(ref, links, art).value;
|
|
@@ -446,11 +449,15 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
446
449
|
unfoldingMap[name] = [ true, [ ...columns ] ];
|
|
447
450
|
}
|
|
448
451
|
}
|
|
449
|
-
else
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
452
|
+
else {
|
|
453
|
+
if (usesCalcOnRead(branches))
|
|
454
|
+
containsCalcOnRead = true;
|
|
455
|
+
if (!columnMap[name] && hasStar) { // Via * - just append
|
|
456
|
+
unfoldingMap[name] = [ true, [ { ref: [ name ] } ] ];
|
|
457
|
+
}
|
|
458
|
+
else { // just a random column - keep
|
|
459
|
+
unfoldingMap[name] = [ false, [ columnMap[name] ] ];
|
|
460
|
+
}
|
|
454
461
|
}
|
|
455
462
|
}
|
|
456
463
|
}
|
|
@@ -491,6 +498,39 @@ function rewriteCalculatedElementsInViews( csn, options, csnUtils, pathDelimiter
|
|
|
491
498
|
return false;
|
|
492
499
|
}
|
|
493
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Returns true if the branch/column uses a calc-on-read,
|
|
503
|
+
* for example in a filter.
|
|
504
|
+
*
|
|
505
|
+
* TODO: Enable calculated elements next to nested projections
|
|
506
|
+
*
|
|
507
|
+
* @param {object} branches
|
|
508
|
+
* @returns {boolean}
|
|
509
|
+
*/
|
|
510
|
+
function usesCalcOnRead( branches ) {
|
|
511
|
+
let returnValue = false;
|
|
512
|
+
for (const branchName in branches) {
|
|
513
|
+
const column = branches[branchName]?.steps[0]?._column;
|
|
514
|
+
if (column) {
|
|
515
|
+
applyTransformationsOnNonDictionary({ column }, 'column', {
|
|
516
|
+
// eslint-disable-next-line no-loop-func
|
|
517
|
+
ref: (parent) => {
|
|
518
|
+
if (hasOnReadValue(parent))
|
|
519
|
+
returnValue = true;
|
|
520
|
+
},
|
|
521
|
+
}, {
|
|
522
|
+
drillRef: true,
|
|
523
|
+
// skip subqueries and nested projections
|
|
524
|
+
// calculated elements and nested projections
|
|
525
|
+
// only conflict on same level
|
|
526
|
+
skipStandard: [ 'SELECT', 'expand', 'inline' ],
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return returnValue;
|
|
532
|
+
}
|
|
533
|
+
|
|
494
534
|
/**
|
|
495
535
|
* A leaf can reference a column which in turn references a real element - that might have a .value.
|
|
496
536
|
* Find such cases.
|
|
@@ -171,7 +171,7 @@ function getAnnotationHandler( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
171
171
|
const { error } = messageFunctions;
|
|
172
172
|
const {
|
|
173
173
|
extractValidFromToKeyElement, checkAssignment, checkMultipleAssignments, recurseElements,
|
|
174
|
-
} = getTransformers(csn, options, pathDelimiter);
|
|
174
|
+
} = getTransformers(csn, options, messageFunctions, pathDelimiter);
|
|
175
175
|
|
|
176
176
|
return handleTemporalAnnotations;
|
|
177
177
|
/**
|
|
@@ -26,7 +26,7 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
26
26
|
const {
|
|
27
27
|
createForeignKeyElement, createAndAddDraftAdminDataProjection, createScalarElement, createAssociationElement,
|
|
28
28
|
addElement, copyAndAddElement, createAssociationPathComparison, csnUtils,
|
|
29
|
-
} = getTransformers(csn, options, pathDelimiter);
|
|
29
|
+
} = getTransformers(csn, options, messageFunctions, pathDelimiter);
|
|
30
30
|
const { getCsnDef, isComposition } = csnUtils;
|
|
31
31
|
const { error, warning } = messageFunctions;
|
|
32
32
|
const generatedArtifacts = Object.create(null);
|
|
@@ -122,26 +122,12 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
122
122
|
|
|
123
123
|
generatedArtifacts[draftsArtifactName] = true;
|
|
124
124
|
|
|
125
|
-
//
|
|
126
|
-
const keys = [];
|
|
125
|
+
// TODO: Do we really need this? Is this possibly done by a validator earlier?
|
|
127
126
|
forEachMemberRecursively(artifact, (elt, name, prop, path) => {
|
|
128
127
|
if (!elt.elements && !elt.type && !elt.virtual) // only check leafs
|
|
129
128
|
error(null, path, 'Expecting element to have a type when used in a draft-enabled artifact');
|
|
130
|
-
if (elt.key && elt.key === true && !elt.virtual)
|
|
131
|
-
keys.push(elt);
|
|
132
129
|
}, [ 'definitions', artifactName ], false, { elementsOnly: true });
|
|
133
130
|
|
|
134
|
-
// In contrast to EDM, the DB entity may have more than one technical keys but should have ideally exactly one key of type cds.UUID
|
|
135
|
-
if (keys.length !== 1) {
|
|
136
|
-
warning(null, [ 'definitions', artifactName ], { count: keys.length },
|
|
137
|
-
'Entity annotated with “@odata.draft.enabled” should have exactly one key element, but found $(COUNT)');
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
const uuidCount = keys.reduce((acc, k) => ((k.type === 'cds.UUID' || k.type === 'cds.String' && k.$renamed === 'cds.UUID' && k.length === 36) ? acc + 1 : acc), 0);
|
|
141
|
-
if (uuidCount === 0)
|
|
142
|
-
warning(null, [ 'definitions', artifactName ], 'Entity annotated with “@odata.draft.enabled” should have one key element of type “cds.UUID”');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
131
|
// Ignore boolean return value. We know that we're inside a service or else we wouldn't have reached this code.
|
|
146
132
|
const matchingService = getMatchingService(artifactName) || '';
|
|
147
133
|
// Generate the DraftAdministrativeData projection into the service, unless there is already one
|
|
@@ -27,21 +27,21 @@ const { makeMessageFunction } = require('../../base/messages');
|
|
|
27
27
|
* @todo check if needed at all: Remove '$projection' from paths in the element's ON-condition
|
|
28
28
|
*/
|
|
29
29
|
function generateDrafts( csn, options, services ) {
|
|
30
|
+
const messageFunctions = makeMessageFunction(csn, options, 'for.odata');
|
|
31
|
+
const { error, info } = messageFunctions;
|
|
30
32
|
const {
|
|
31
33
|
createAndAddDraftAdminDataProjection, createScalarElement,
|
|
32
34
|
createAssociationElement, createAssociationPathComparison,
|
|
33
35
|
addElement, createAction, assignAction,
|
|
34
36
|
resetAnnotation,
|
|
35
37
|
csnUtils,
|
|
36
|
-
} = getTransformers(csn, options);
|
|
38
|
+
} = getTransformers(csn, options, messageFunctions);
|
|
37
39
|
const {
|
|
38
40
|
getServiceName,
|
|
39
41
|
hasAnnotationValue,
|
|
40
42
|
getFinalTypeInfo,
|
|
41
43
|
} = csnUtils;
|
|
42
44
|
|
|
43
|
-
const { error, info } = makeMessageFunction(csn, options, 'for.odata');
|
|
44
|
-
|
|
45
45
|
if (!services)
|
|
46
46
|
services = getServiceNames(csn);
|
|
47
47
|
|
|
@@ -17,14 +17,12 @@ const backlinks = require('../db/backlinks');
|
|
|
17
17
|
* @param {CSN.Model} csn Input CSN - will not be transformed
|
|
18
18
|
* @param {CSN.Options} options
|
|
19
19
|
* @param {object} csnUtils
|
|
20
|
-
* @param {object} messageFunctions
|
|
21
|
-
* @param {Function} messageFunctions.error
|
|
22
|
-
* @param {Function} messageFunctions.warning
|
|
20
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
23
21
|
* @todo Remove .keys afterwards
|
|
24
22
|
* @todo Add created foreign keys into .columns in case of a query?
|
|
25
23
|
* @returns {CSN.Model}
|
|
26
24
|
*/
|
|
27
|
-
function turnAssociationsIntoUnmanaged( csn, options, csnUtils,
|
|
25
|
+
function turnAssociationsIntoUnmanaged( csn, options, csnUtils, messageFunctions ) {
|
|
28
26
|
// TODO: Do we really need this?
|
|
29
27
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
30
28
|
setProp(artifact, '$path', [ 'definitions', artifactName ]);
|
|
@@ -33,7 +31,7 @@ function turnAssociationsIntoUnmanaged( csn, options, csnUtils, { error, warning
|
|
|
33
31
|
}, [ 'definitions', artifactName ]);
|
|
34
32
|
});
|
|
35
33
|
// Flatten out the fks and create the corresponding elements
|
|
36
|
-
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options,
|
|
34
|
+
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_', true, csnUtils, { allowArtifact: () => true, skipDict: {} });
|
|
37
35
|
|
|
38
36
|
// Add the foreign keys also to the columns if the association itself was explicitly selected
|
|
39
37
|
// TODO: Extend the expansion to also expand managed to their foreign
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const { isBetaEnabled } = require('../../base/model');
|
|
4
4
|
const { CompilerAssertion } = require('../../base/error');
|
|
5
|
-
const { makeMessageFunction } = require('../../base/messages');
|
|
6
5
|
const { cloneCsnNonDict, getUtils, isAspect } = require('../../model/csnUtils');
|
|
7
6
|
const transformUtils = require('../transformUtils');
|
|
8
7
|
const flattening = require('../db/flattening');
|
|
@@ -26,21 +25,21 @@ const { rewriteCalculatedElementsInViews, processCalculatedElementsInEntities }
|
|
|
26
25
|
* @private
|
|
27
26
|
* @param {CSN.Model} model Input CSN - will not be transformed
|
|
28
27
|
* @param {CSN.Options} options
|
|
28
|
+
* @param {object} messageFunctions
|
|
29
29
|
* @returns {CSN.Model}
|
|
30
30
|
*/
|
|
31
|
-
function effectiveCsn( model, options ) {
|
|
31
|
+
function effectiveCsn( model, options, messageFunctions ) {
|
|
32
32
|
if (!isBetaEnabled(options, 'effectiveCsn'))
|
|
33
33
|
throw new CompilerAssertion('effective CSN is only supported with beta flag `effectiveCsn`!');
|
|
34
34
|
|
|
35
35
|
const csn = cloneCsnNonDict(model, options);
|
|
36
|
-
|
|
37
36
|
delete csn.vocabularies; // must not be set for effective CSN
|
|
37
|
+
messageFunctions.setModel(csn);
|
|
38
38
|
|
|
39
|
-
const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, '_');
|
|
39
|
+
const { expandStructsInExpression } = transformUtils.getTransformers(csn, options, messageFunctions, '_');
|
|
40
40
|
queries.projectionToSELECTAndAddColumns(csn);
|
|
41
41
|
|
|
42
42
|
let csnUtils = getUtils(csn, 'init-all');
|
|
43
|
-
const messageFunctions = makeMessageFunction(csn, options, 'for.effective');
|
|
44
43
|
|
|
45
44
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
46
45
|
const cleanup = validate.forRelationalDB(csn, {
|
|
@@ -65,13 +64,11 @@ function effectiveCsn( model, options ) {
|
|
|
65
64
|
|
|
66
65
|
const resolveTypesInActionsAfterFlattening = types.resolve(csn, csnUtils);
|
|
67
66
|
|
|
68
|
-
csnUtils = getUtils(csn, 'init-all');
|
|
69
|
-
|
|
70
67
|
// Remove properties attached by validator - they do not "grow" as the model grows.
|
|
71
68
|
cleanup();
|
|
72
69
|
|
|
73
|
-
flattening.flattenAllStructStepsInRefs(csn, options, new WeakMap(), '_');
|
|
74
|
-
flattening.flattenElements(csn, options, '_'
|
|
70
|
+
flattening.flattenAllStructStepsInRefs(csn, options, messageFunctions, new WeakMap(), '_');
|
|
71
|
+
flattening.flattenElements(csn, options, messageFunctions, '_');
|
|
75
72
|
|
|
76
73
|
resolveTypesInActionsAfterFlattening();
|
|
77
74
|
|