@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
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
4
3
|
const { isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
5
4
|
const transformUtils = require('./transformUtils');
|
|
6
5
|
const { cloneCsnNonDict,
|
|
@@ -24,6 +23,7 @@ const associations = require('./db/associations')
|
|
|
24
23
|
const expansion = require('./db/expansion');
|
|
25
24
|
const generateDrafts = require('./draft/odata');
|
|
26
25
|
|
|
26
|
+
const { addTenantFields } = require('./addTenantFields');
|
|
27
27
|
const { addLocalizationViews } = require('./localized');
|
|
28
28
|
|
|
29
29
|
// Transformation for ODATA. Expects a CSN 'inputModel', processes it for ODATA.
|
|
@@ -68,23 +68,22 @@ const { addLocalizationViews } = require('./localized');
|
|
|
68
68
|
// (Linter check candidate)
|
|
69
69
|
module.exports = { transform4odataWithCsn };
|
|
70
70
|
|
|
71
|
-
function transform4odataWithCsn(inputModel, options) {
|
|
71
|
+
function transform4odataWithCsn(inputModel, options, messageFunctions) {
|
|
72
72
|
timetrace.start('OData transformation');
|
|
73
73
|
|
|
74
74
|
// copy the model as we don't want to change the input model
|
|
75
75
|
let csn = cloneCsnNonDict(inputModel, options);
|
|
76
|
+
messageFunctions.setModel(csn);
|
|
76
77
|
|
|
77
|
-
const { message, error, warning, info, throwWithAnyError } =
|
|
78
|
+
const { message, error, warning, info, throwWithAnyError } = messageFunctions;
|
|
78
79
|
throwWithAnyError();
|
|
79
80
|
|
|
80
81
|
// the new transformer works only with new CSN
|
|
81
82
|
checkCSNVersion(csn, options);
|
|
82
83
|
|
|
83
|
-
const transformers = transformUtils.getTransformers(csn, options, '_');
|
|
84
|
+
const transformers = transformUtils.getTransformers(csn, options, messageFunctions, '_');
|
|
84
85
|
const {
|
|
85
|
-
addDefaultTypeFacets,
|
|
86
|
-
extractValidFromToKeyElement,
|
|
87
|
-
checkAssignment, checkMultipleAssignments,
|
|
86
|
+
addDefaultTypeFacets, checkMultipleAssignments,
|
|
88
87
|
recurseElements, setAnnotation, renameAnnotation,
|
|
89
88
|
expandStructsInExpression,
|
|
90
89
|
csnUtils,
|
|
@@ -119,6 +118,9 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
119
118
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
120
119
|
enrichUniversalCsn(csn, options);
|
|
121
120
|
|
|
121
|
+
if (options.tenantAsColumn)
|
|
122
|
+
addTenantFields(csn, options);
|
|
123
|
+
|
|
122
124
|
const keepLocalizedViews = isDeprecatedEnabled(options, '_createLocalizedViews');
|
|
123
125
|
|
|
124
126
|
function acceptLocalizedView(_name, parent) {
|
|
@@ -132,17 +134,17 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
132
134
|
transformUtils.rewriteBuiltinTypeRef(csn);
|
|
133
135
|
|
|
134
136
|
const cleanup = validate.forOdata(csn, {
|
|
135
|
-
message, error, warning, info, inspectRef, effectiveType, getFinalTypeInfo, artifactRef,
|
|
137
|
+
message, error, warning, info, inspectRef, effectiveType, getFinalTypeInfo, artifactRef,
|
|
138
|
+
options, csnUtils, services, isAspect, isExternalServiceMember, recurseElements,
|
|
139
|
+
checkMultipleAssignments, csn,
|
|
136
140
|
});
|
|
137
141
|
|
|
138
142
|
|
|
139
143
|
// Throw exception in case of errors
|
|
140
144
|
throwWithAnyError();
|
|
141
145
|
|
|
142
|
-
//
|
|
143
|
-
// TODO: Move in the validator
|
|
146
|
+
// TODO: Refactor out the following logic
|
|
144
147
|
forEachDefinition(csn, [
|
|
145
|
-
checkTemporalAnnotationsAssignment,
|
|
146
148
|
(def) => {
|
|
147
149
|
// Convert a projection into a query for internal processing will be re-converted
|
|
148
150
|
// at the end of the OData processing
|
|
@@ -175,21 +177,20 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
175
177
|
// If errors are detected, throwWithAnyError() will return from further processing
|
|
176
178
|
expandStructsInExpression(csn, { skipArtifact: isExternalServiceMember, drillRef: true });
|
|
177
179
|
|
|
178
|
-
|
|
179
180
|
if (!structuredOData) {
|
|
180
181
|
expansion.expandStructureReferences(csn, options, '_', { error, info, throwWithAnyError }, csnUtils, { skipArtifact: isExternalServiceMember });
|
|
181
182
|
const resolved = new WeakMap();
|
|
182
183
|
// No refs with struct-steps exist anymore
|
|
183
184
|
|
|
184
|
-
flattening.flattenAllStructStepsInRefs(csn, options, resolved, '_', { skipArtifact: isExternalServiceMember });
|
|
185
|
+
flattening.flattenAllStructStepsInRefs(csn, options, messageFunctions, resolved, '_', { skipArtifact: isExternalServiceMember });
|
|
185
186
|
// No type references exist anymore
|
|
186
187
|
// Needs to happen exactly between flattenAllStructStepsInRefs and flattenElements to keep model resolvable.
|
|
187
188
|
// OData doesn't resolve type chains after the first 'items'
|
|
188
|
-
flattening.resolveTypeReferences(csn, options, resolved, '_',
|
|
189
|
+
flattening.resolveTypeReferences(csn, options, messageFunctions, resolved, '_',
|
|
189
190
|
{ skip: [ 'action', 'aspect', 'event', 'function', 'type'],
|
|
190
191
|
skipArtifact: isExternalServiceMember, skipStandard: { items: true } });
|
|
191
192
|
// No structured elements exists anymore
|
|
192
|
-
flattening.flattenElements(csn, options, '_',
|
|
193
|
+
flattening.flattenElements(csn, options, messageFunctions, '_',
|
|
193
194
|
{ skip: ['action', 'aspect', 'event', 'function', 'type'],
|
|
194
195
|
skipArtifact: isExternalServiceMember,
|
|
195
196
|
skipStandard: { items: true }, // don't drill further into .items
|
|
@@ -198,7 +199,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
198
199
|
|
|
199
200
|
// TODO: add the generated foreign keys to the columns when we are in a view
|
|
200
201
|
// see db/views.js::addForeignKeysToColumns
|
|
201
|
-
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options,
|
|
202
|
+
flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, '_', !structuredOData, csnUtils,{ skipArtifact: isExternalServiceMember });
|
|
202
203
|
|
|
203
204
|
// Allow using managed associations as steps in on-conditions to access their fks
|
|
204
205
|
// To be done after handleManagedAssociationsAndCreateForeignKeys,
|
|
@@ -272,7 +273,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
272
273
|
|
|
273
274
|
// Convert a query back into a projection for CSN compliance as
|
|
274
275
|
// the very last conversion step of the OData transformation
|
|
275
|
-
if (def.kind === 'entity' && def.query && def.
|
|
276
|
+
if (def.kind === 'entity' && def.query && def.projection) {
|
|
276
277
|
delete def.query;
|
|
277
278
|
}
|
|
278
279
|
}, { skipArtifact: isExternalServiceMember })
|
|
@@ -288,29 +289,6 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
288
289
|
timetrace.stop('OData transformation');
|
|
289
290
|
return csn;
|
|
290
291
|
|
|
291
|
-
// TODO: Move this to checks?
|
|
292
|
-
// @ts-ignore
|
|
293
|
-
function checkTemporalAnnotationsAssignment(artifact, artifactName, propertyName, path) {
|
|
294
|
-
// Gather all element names with @cds.valid.from/to/key
|
|
295
|
-
let validFrom = [], validTo = [], validKey = [];
|
|
296
|
-
recurseElements(artifact, ['definitions', artifactName], (member, path) => {
|
|
297
|
-
let [f, t, k] = extractValidFromToKeyElement(member, path);
|
|
298
|
-
validFrom.push(...f);
|
|
299
|
-
validTo.push(...t);
|
|
300
|
-
validKey.push(...k);
|
|
301
|
-
});
|
|
302
|
-
// Check that @cds.valid.from/to/key is only in valid places
|
|
303
|
-
validFrom.forEach(obj => checkAssignment('@cds.valid.from', obj.element, obj.path, artifact));
|
|
304
|
-
validTo.forEach(obj => checkAssignment('@cds.valid.to', obj.element, obj.path, artifact));
|
|
305
|
-
validKey.forEach(obj => checkAssignment('@cds.valid.key', obj.element, obj.path, artifact));
|
|
306
|
-
checkMultipleAssignments(validFrom, '@cds.valid.from', artifact, artifactName);
|
|
307
|
-
checkMultipleAssignments(validTo, '@cds.valid.to', artifact, artifactName);
|
|
308
|
-
checkMultipleAssignments(validKey, '@cds.valid.key', artifact, artifactName);
|
|
309
|
-
if (validKey.length && !(validFrom.length && validTo.length)) {
|
|
310
|
-
error(null, path, 'Annotation “@cds.valid.key” was used but “@cds.valid.from” and “@cds.valid.to” are missing');
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
292
|
// Mark elements that are annotated with @odata.on.insert/update with the annotation @Core.Computed.
|
|
315
293
|
function annotateCoreComputed(node) {
|
|
316
294
|
// If @Core.Computed is explicitly set, don't overwrite it!
|
|
@@ -347,7 +325,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
347
325
|
return;
|
|
348
326
|
// Rename according to map above
|
|
349
327
|
const renamePrefix = (name in renameMappings)
|
|
350
|
-
? name
|
|
328
|
+
? name
|
|
351
329
|
: renameShortCuts.find(p => name.startsWith(p + '.'));
|
|
352
330
|
if(renamePrefix) {
|
|
353
331
|
const mapping = renameMappings[renamePrefix];
|
|
@@ -442,25 +420,18 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
442
420
|
}
|
|
443
421
|
}
|
|
444
422
|
|
|
445
|
-
// CDXCORE-481
|
|
446
423
|
// (4.5) If the member is an association whose target has @cds.odata.valuelist annotate it
|
|
447
424
|
// with @Common.ValueList.viaAssociation.
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
// This must be done before foreign keys are calculated and the annotations are propagated
|
|
452
|
-
// to them. This will make sure that association and all its foreign keys are annotated with
|
|
453
|
-
// Common.ValueList in the final EDM.
|
|
454
|
-
// Do this only if the association is navigable and the enclosing artifact is
|
|
455
|
-
// a service member (don't pollute the CSN with unnecessary annotations).
|
|
456
|
-
// TODO: test???
|
|
425
|
+
// Do this only if the association is navigable(@odata.navigable) and the enclosing artifact is
|
|
426
|
+
// a service member (don't pollute the CSN with unnecessary annotations, that is ensured by the caller
|
|
427
|
+
// of this function).
|
|
457
428
|
function addCommonValueListviaAssociation(member, memberName) {
|
|
458
|
-
|
|
429
|
+
const vlAnno = '@Common.ValueList.viaAssociation';
|
|
459
430
|
if (isAssociation(member)) {
|
|
460
|
-
|
|
461
|
-
|
|
431
|
+
const navigable = member['@odata.navigable'] !== false; // navigable disabled only if explicitly set to false
|
|
432
|
+
const targetDef = getCsnDef(member.target);
|
|
462
433
|
if (navigable && targetDef['@cds.odata.valuelist'] && !member[vlAnno]) {
|
|
463
|
-
member
|
|
434
|
+
setAnnotation(member, vlAnno, { '=': memberName });
|
|
464
435
|
}
|
|
465
436
|
}
|
|
466
437
|
}
|
|
@@ -6,13 +6,13 @@ const { cloneCsnNonDict,
|
|
|
6
6
|
getArtifactDatabaseNameOf, getElementDatabaseNameOf, isBuiltinType, applyTransformations,
|
|
7
7
|
isAspect, walkCsnPath, isPersistedOnDatabase
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
10
9
|
const transformUtils = require('./transformUtils');
|
|
11
10
|
const { translateAssocsToJoinsCSN } = require('./translateAssocsToJoins');
|
|
12
11
|
const { csnRefs, pathId, traverseQuery, columnAlias} = require('../model/csnRefs');
|
|
13
12
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
14
13
|
const validate = require('../checks/validator');
|
|
15
14
|
const { rejectManagedAssociationsAndStructuresForHdbcdsNames } = require('../checks/selectItems');
|
|
15
|
+
const { addTenantFields } = require('../transform/addTenantFields');
|
|
16
16
|
const { addLocalizationViewsWithJoins, addLocalizationViews } = require('../transform/localized');
|
|
17
17
|
const { timetrace } = require('../utils/timetrace');
|
|
18
18
|
const { createReferentialConstraints, assertConstraintIdentifierUniqueness } = require('./db/constraints');
|
|
@@ -100,9 +100,9 @@ function forEachDefinition(csn, cb) {
|
|
|
100
100
|
*
|
|
101
101
|
* @param {CSN.Model} csn
|
|
102
102
|
* @param {CSN.Options} options
|
|
103
|
-
* @param {
|
|
103
|
+
* @param {object} messageFunctions Message functions such as `error()`, `info()`, …
|
|
104
104
|
*/
|
|
105
|
-
function transformForRelationalDBWithCsn(csn, options,
|
|
105
|
+
function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
106
106
|
// copy the model as we don't want to change the input model
|
|
107
107
|
timetrace.start('HANA transformation');
|
|
108
108
|
|
|
@@ -111,6 +111,9 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
111
111
|
csn = cloneCsnNonDict(csn, options);
|
|
112
112
|
timetrace.stop('Clone CSN');
|
|
113
113
|
|
|
114
|
+
if (options.tenantAsColumn)
|
|
115
|
+
addTenantFields(csn, options);
|
|
116
|
+
|
|
114
117
|
checkCSNVersion(csn, options);
|
|
115
118
|
|
|
116
119
|
const pathDelimiter = (options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
@@ -120,12 +123,11 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
120
123
|
/** @type {object} */
|
|
121
124
|
let csnUtils;
|
|
122
125
|
/** @type {object} */
|
|
123
|
-
let
|
|
124
|
-
let message, error, warning, info; // message functions
|
|
126
|
+
let message, error, warning; // message functions
|
|
125
127
|
/** @type {() => void} */
|
|
126
128
|
let throwWithAnyError;
|
|
127
129
|
// transformUtils
|
|
128
|
-
let addDefaultTypeFacets,
|
|
130
|
+
let addDefaultTypeFacets,
|
|
129
131
|
expandStructsInExpression,
|
|
130
132
|
flattenStructuredElement,
|
|
131
133
|
flattenStructStepsInRef;
|
|
@@ -180,12 +182,13 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
180
182
|
// assoc2join eventually rewrites the table aliases
|
|
181
183
|
temporal.getViewDecorator(csn, messageFunctions, csnUtils),
|
|
182
184
|
// check unique constraints - further processing is done in rewriteUniqueConstraints
|
|
183
|
-
assertUnique.prepare(csn, options,
|
|
185
|
+
assertUnique.prepare(csn, options, messageFunctions)
|
|
184
186
|
]);
|
|
185
187
|
|
|
186
188
|
if(doA2J) {
|
|
187
189
|
// Expand a structured thing in: keys, columns, order by, group by
|
|
188
|
-
|
|
190
|
+
// In addition, kill all non-sql-backend relevant annotations
|
|
191
|
+
expansion.expandStructureReferences(csn, options, pathDelimiter, messageFunctions, csnUtils, { processAnnotations: true });
|
|
189
192
|
bindCsnReference();
|
|
190
193
|
}
|
|
191
194
|
|
|
@@ -203,15 +206,15 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
203
206
|
if(doA2J) {
|
|
204
207
|
const resolved = new WeakMap();
|
|
205
208
|
// No refs with struct-steps exist anymore
|
|
206
|
-
flattening.flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter);
|
|
209
|
+
flattening.flattenAllStructStepsInRefs(csn, options, messageFunctions, resolved, pathDelimiter);
|
|
207
210
|
// No type references exist anymore
|
|
208
211
|
// Needs to happen exactly between flattenAllStructStepsInRefs and flattenElements to keep model resolvable.
|
|
209
|
-
flattening.resolveTypeReferences(csn, options, resolved, pathDelimiter);
|
|
212
|
+
flattening.resolveTypeReferences(csn, options, messageFunctions, resolved, pathDelimiter);
|
|
210
213
|
// No structured elements exists anymore
|
|
211
|
-
flattening.flattenElements(csn, options,
|
|
214
|
+
flattening.flattenElements(csn, options, messageFunctions, pathDelimiter);
|
|
212
215
|
} else {
|
|
213
216
|
// For to.hdbcds with naming mode hdbcds we also need to resolve the types
|
|
214
|
-
flattening.resolveTypeReferences(csn, options, new WeakMap(), pathDelimiter);
|
|
217
|
+
flattening.resolveTypeReferences(csn, options, messageFunctions, new WeakMap(), pathDelimiter);
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
// With flattening errors, it makes little sense to continue.
|
|
@@ -284,7 +287,7 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
284
287
|
]);
|
|
285
288
|
|
|
286
289
|
// eliminate the doA2J in the functions 'handleManagedAssociationFKs' and 'createForeignKeyElements'
|
|
287
|
-
doA2J && flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options,
|
|
290
|
+
doA2J && flattening.handleManagedAssociationsAndCreateForeignKeys(csn, options, messageFunctions, pathDelimiter, true, csnUtils, { skipDict: { actions: true }, allowArtifact: artifact => (artifact.kind === 'entity') });
|
|
288
291
|
|
|
289
292
|
doA2J && forEachDefinition(csn, flattenIndexes);
|
|
290
293
|
// Managed associations get an on-condition - in views and entities
|
|
@@ -357,6 +360,9 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
357
360
|
// Associations that point to things marked with @cds.persistence.skip are removed
|
|
358
361
|
forEachDefinition(csn, cdsPersistence.getAssocToSkippedIgnorer(csn, options, messageFunctions, csnUtils));
|
|
359
362
|
|
|
363
|
+
// some errors can't be handled in the subsequent processing steps for e.g. HDBCDS
|
|
364
|
+
messageFunctions.throwWithError();
|
|
365
|
+
|
|
360
366
|
// Apply view-specific transformations
|
|
361
367
|
// (160) Projections now finally become views
|
|
362
368
|
// Replace managed association in group/order by with foreign keys
|
|
@@ -448,15 +454,15 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
448
454
|
/* ----------------------------------- Functions start here -----------------------------------------------*/
|
|
449
455
|
|
|
450
456
|
function bindCsnReference(){
|
|
451
|
-
messageFunctions
|
|
452
|
-
({ error, warning,
|
|
457
|
+
messageFunctions.setModel(csn);
|
|
458
|
+
({ error, warning, message, throwWithAnyError } = messageFunctions);
|
|
453
459
|
|
|
454
460
|
({ flattenStructuredElement,
|
|
455
461
|
flattenStructStepsInRef,
|
|
456
462
|
addDefaultTypeFacets,
|
|
457
463
|
expandStructsInExpression,
|
|
458
464
|
csnUtils
|
|
459
|
-
} = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
465
|
+
} = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter));
|
|
460
466
|
}
|
|
461
467
|
|
|
462
468
|
function bindCsnReferenceOnly(){
|
|
@@ -613,10 +619,10 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
613
619
|
// (100 a) Ignore the property 'masked' itself (but not its effect on projections)
|
|
614
620
|
if (member.masked)
|
|
615
621
|
delete member.masked;
|
|
616
|
-
//
|
|
622
|
+
// Report an error on
|
|
617
623
|
// - view with parameters that has an element of type association/composition
|
|
618
624
|
// - association that points to entity with parameters
|
|
619
|
-
if (
|
|
625
|
+
if (member.target && csnUtils.isAssocOrComposition(member) && !isBetaEnabled(options, 'assocsWithParams')) {
|
|
620
626
|
if (artifact.params) {
|
|
621
627
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
622
628
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
@@ -861,6 +867,20 @@ function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
|
861
867
|
checkTypeParamValue(node, 'srid', { max: Number.MAX_SAFE_INTEGER }, path);
|
|
862
868
|
break;
|
|
863
869
|
}
|
|
870
|
+
case 'cds.Vector': {
|
|
871
|
+
if (options.sqlDialect !== 'hana') {
|
|
872
|
+
error('ref-unsupported-type', path, {
|
|
873
|
+
'#': 'hana', type: node.type, value: 'hana',
|
|
874
|
+
othervalue: options.sqlDialect
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
else if (options.transformation === 'hdbcds') {
|
|
878
|
+
error('ref-unsupported-type', path, {
|
|
879
|
+
'#': 'hdbcds', type: node.type, value: options.sqlDialect
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
864
884
|
}
|
|
865
885
|
}
|
|
866
886
|
|
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
} = require('../../model/csnUtils');
|
|
8
8
|
const { isArtifactInSomeService, isArtifactInService } = require('./utils');
|
|
9
9
|
|
|
10
|
-
function expandToFinalBaseType(csn, transformers, csnUtils, services, options
|
|
10
|
+
function expandToFinalBaseType(csn, transformers, csnUtils, services, options) {
|
|
11
11
|
const isV4 = options.odataVersion === 'v4';
|
|
12
12
|
const special$self = !csn?.definitions?.$self && '$self';
|
|
13
13
|
forEachDefinition(csn, (def, defName) => {
|
|
@@ -63,7 +63,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
63
63
|
if (def.kind === 'type' && def.items && isArtifactInSomeService(defName, services)) {
|
|
64
64
|
expandFirstLevelOfArrayed(def);
|
|
65
65
|
}
|
|
66
|
-
}
|
|
66
|
+
});
|
|
67
67
|
|
|
68
68
|
if(isBetaEnabled(options, 'odataTerms')) {
|
|
69
69
|
forEachGeneric(csn, 'vocabularies', (def, defName) => {
|
|
@@ -75,7 +75,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
75
75
|
|
|
76
76
|
expandToFinalBaseType(def, defName);
|
|
77
77
|
expandToFinalBaseType(def.items, defName);
|
|
78
|
-
}, []
|
|
78
|
+
}, []);
|
|
79
79
|
}
|
|
80
80
|
// In case we have in the model something like:
|
|
81
81
|
// type Foo: array of Bar; type Bar: { qux: Integer };
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* @module typesExposure
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const { setProp } = require('../../base/model');
|
|
9
|
+
const { setProp, isBetaEnabled } = require('../../base/model');
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
|
-
const {
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const { getNamespace, copyAnnotations, cloneCsnNonDict,
|
|
12
|
+
isBuiltinType, isAnnotationExpression,
|
|
13
|
+
forEachDefinition, forEachMember, forEachGeneric } = require('../../model/csnUtils');
|
|
14
14
|
const { CompilerAssertion } = require('../../base/error');
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -219,7 +219,16 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
});
|
|
222
|
-
|
|
222
|
+
|
|
223
|
+
// expression annos and their sub annotations are not propagated to type
|
|
224
|
+
let [ xprANames, nxprANames ] = Object.keys(typeDef).reduce((acc, pn) => {
|
|
225
|
+
if (pn[0] === '@')
|
|
226
|
+
acc[isAnnotationExpression(typeDef[pn]) ? 0 : 1].push(pn);
|
|
227
|
+
return acc;
|
|
228
|
+
}, [ [], [] ]);
|
|
229
|
+
nxprANames = nxprANames.filter(an => !xprANames.some(ean => an.startsWith(`${ean}.`)));
|
|
230
|
+
copyAnnotations(typeDef, newType, false, {}, nxprANames);
|
|
231
|
+
|
|
223
232
|
// if the origin type had items, add items to exposed type
|
|
224
233
|
if(typeDef.kind === 'type') {
|
|
225
234
|
if(typeDef.items) {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// different backends.
|
|
5
5
|
// The sibling of model/transform/TransformUtil.js which works with compacted new CSN.
|
|
6
6
|
|
|
7
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
8
7
|
const { setProp } = require('../base/model');
|
|
9
8
|
|
|
10
9
|
const { copyAnnotations, applyTransformations, isDollarSelfOrProjectionOperand } = require('../model/csnUtils');
|
|
@@ -20,8 +19,8 @@ const RelationalOperators = ['=', '!=', '<>', 'is' /*, 'like'*/,...RestrictedOpe
|
|
|
20
19
|
// 'model' is compacted new style CSN
|
|
21
20
|
// TODO: Error and warnings handling with compacted CSN? - currently just throw new ModelError for everything
|
|
22
21
|
// TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
|
|
23
|
-
function getTransformers(model, options, pathDelimiter = '_') {
|
|
24
|
-
const { message, error, warning, info } =
|
|
22
|
+
function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
|
|
23
|
+
const { message, error, warning, info } = msgFunctions;
|
|
25
24
|
const csnUtils = getUtils(model);
|
|
26
25
|
const {
|
|
27
26
|
getCsnDef,
|
|
@@ -321,48 +320,62 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
321
320
|
* @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
|
|
322
321
|
* @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
|
|
323
322
|
* @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
|
|
323
|
+
* @param {bool} [refParentIsItems] relative ref has an items root (suspend flattening by caller)
|
|
324
324
|
* @returns {string[]}
|
|
325
325
|
*/
|
|
326
|
-
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap()) {
|
|
326
|
+
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), refParentIsItems=false) {
|
|
327
327
|
// Refs of length 1 cannot contain steps - no need to check
|
|
328
328
|
if (ref.length < 2 || (scope === '$self' && ref.length === 2)) {
|
|
329
329
|
return ref;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
|
|
332
|
+
const result = scope === '$self' ? [ref[0]] : [];
|
|
333
|
+
//let stack = []; // IDs of path steps not yet processed or part of a struct traversal
|
|
334
|
+
if(!links && !scope) { // calculate JIT if not supplied
|
|
335
|
+
const res = inspectRef(path);
|
|
336
|
+
links = res.links;
|
|
337
|
+
scope = res.scope;
|
|
338
|
+
}
|
|
339
|
+
if (scope === '$magic')
|
|
340
|
+
return ref;
|
|
333
341
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
ref[i].id = result[result.length-1];
|
|
355
|
-
result[result.length-1] = ref[i];
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
else {
|
|
359
|
-
result.push(ref[i]);
|
|
342
|
+
// Don't process a leading $self - it will a .art with .elements!
|
|
343
|
+
let i = scope === '$self' ? 1 : 0;
|
|
344
|
+
|
|
345
|
+
// read property from resolved path link
|
|
346
|
+
const art = (propName) =>
|
|
347
|
+
(links[i].art?.[propName] ||
|
|
348
|
+
effectiveType(links[i].art)[propName] ||
|
|
349
|
+
(resolvedLinkTypes.get(links[i])||{})[propName]);
|
|
350
|
+
|
|
351
|
+
let flattenStep = false;
|
|
352
|
+
let nextIsItems = !!art('items') || (refParentIsItems && i === 0);
|
|
353
|
+
for(; i < links.length; i++) {
|
|
354
|
+
|
|
355
|
+
if (flattenStep && !nextIsItems) {
|
|
356
|
+
result[result.length - 1] += pathDelimiter + (ref[i].id ? ref[i].id : ref[i]);
|
|
357
|
+
// if we had a filter or args, we had an assoc so this step is done
|
|
358
|
+
// we then keep along the filter/args by updating the id of the current ref
|
|
359
|
+
if(ref[i].id) {
|
|
360
|
+
ref[i].id = result[result.length-1];
|
|
361
|
+
result[result.length-1] = ref[i];
|
|
360
362
|
}
|
|
361
|
-
|
|
363
|
+
// suspend flattening if the next path step has some 'items'
|
|
364
|
+
nextIsItems = !!art('items');
|
|
362
365
|
}
|
|
363
|
-
|
|
364
|
-
|
|
366
|
+
else {
|
|
367
|
+
result.push(ref[i]);
|
|
368
|
+
}
|
|
369
|
+
// revoke items suspension for next assoc step
|
|
370
|
+
if(nextIsItems && art('target'))
|
|
371
|
+
nextIsItems = false;
|
|
372
|
+
|
|
373
|
+
flattenStep = !links[i].art?.kind &&
|
|
374
|
+
!links[i].art?.SELECT &&
|
|
375
|
+
!links[i].art?.from &&
|
|
376
|
+
art('elements');
|
|
365
377
|
}
|
|
378
|
+
return result;
|
|
366
379
|
}
|
|
367
380
|
|
|
368
381
|
/**
|