@sap/cds-compiler 2.13.8 → 3.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 +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
const {
|
|
4
4
|
getUtils, walkCsnPath,
|
|
5
5
|
applyTransformations, applyTransformationsOnNonDictionary,
|
|
6
|
-
isBuiltinType,
|
|
6
|
+
isBuiltinType, cloneCsnNonDict,
|
|
7
7
|
copyAnnotations, implicitAs, isDeepEqual,
|
|
8
8
|
} = require('../../model/csnUtils');
|
|
9
9
|
const transformUtils = require('../transformUtilsNew');
|
|
10
10
|
const { csnRefs } = require('../../model/csnRefs');
|
|
11
11
|
const { setProp } = require('../../base/model');
|
|
12
|
+
const { forEach } = require('../../utils/objectUtils');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Strip off leading $self from refs where applicable
|
|
@@ -46,6 +47,7 @@ function removeLeadingSelf(csn) {
|
|
|
46
47
|
* @param {object} iterateOptions
|
|
47
48
|
*/
|
|
48
49
|
function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOptions = {}) {
|
|
50
|
+
const typeCache = Object.create(null); // TODO: Argument as well?
|
|
49
51
|
/**
|
|
50
52
|
* Remove .localized from the element and any sub-elements
|
|
51
53
|
*
|
|
@@ -66,26 +68,29 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
66
68
|
stack.push(...Object.values(current.elements));
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
const { toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
70
|
-
const { getServiceName,
|
|
71
|
+
const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
72
|
+
const { getServiceName, getFinalBaseTypeWithProps } = csnUtils;
|
|
71
73
|
|
|
72
74
|
// We don't want to iterate over actions
|
|
73
75
|
if (iterateOptions.skipDict && !iterateOptions.skipDict.actions)
|
|
74
76
|
iterateOptions.skipDict.actions = true;
|
|
75
77
|
else
|
|
76
78
|
iterateOptions.skipDict = { actions: true };
|
|
79
|
+
|
|
80
|
+
const ignoreOdataKinds = { aspect: 1, event: 1, type: 1 };
|
|
81
|
+
const replaceWithDummyKinds = { action: 1, function: 1, event: 1 };
|
|
77
82
|
applyTransformations(csn, {
|
|
78
|
-
cast: (parent) => {
|
|
83
|
+
cast: (parent, prop, cast, path) => {
|
|
79
84
|
// Resolve cast already - we otherwise lose .localized
|
|
80
|
-
if (
|
|
81
|
-
toFinalBaseType(parent.cast, resolved, true);
|
|
85
|
+
if (cast.type && !isBuiltinType(cast.type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(cast.type, path) && !isODataItems(cast.type)))
|
|
86
|
+
toFinalBaseType(parent.cast, resolved, true, typeCache);
|
|
82
87
|
},
|
|
83
88
|
// @ts-ignore
|
|
84
|
-
type: (parent, prop, type,
|
|
85
|
-
if (options.toOdata && parent.kind &&
|
|
89
|
+
type: (parent, prop, type, path) => {
|
|
90
|
+
if (options.toOdata && parent.kind && parent.kind in ignoreOdataKinds)
|
|
86
91
|
return;
|
|
87
|
-
if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type) && !isODataItems(type))) {
|
|
88
|
-
toFinalBaseType(parent, resolved);
|
|
92
|
+
if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
|
|
93
|
+
toFinalBaseType(parent, resolved, true, typeCache);
|
|
89
94
|
// structured types might not have the child-types replaced.
|
|
90
95
|
// Drill down to ensure this.
|
|
91
96
|
if (parent.elements) {
|
|
@@ -94,7 +99,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
94
99
|
const elements = stack.pop();
|
|
95
100
|
for (const e of Object.values(elements)) {
|
|
96
101
|
if (e.type && !isBuiltinType(e.type))
|
|
97
|
-
toFinalBaseType(e, resolved);
|
|
102
|
+
toFinalBaseType(e, resolved, true, typeCache);
|
|
98
103
|
|
|
99
104
|
if (e.elements)
|
|
100
105
|
stack.push(e.elements);
|
|
@@ -110,40 +115,6 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
110
115
|
parent.type = 'cds.LargeString';
|
|
111
116
|
delete parent.items;
|
|
112
117
|
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* OData V4 only:
|
|
116
|
-
* Do not replace a type ref if:
|
|
117
|
-
* The type definition is terminating on a scalar type (that can also be a derived type chain)
|
|
118
|
-
* AND the typeName (that is the start of that (derived) type chain is defined within the same
|
|
119
|
-
* service as the artifact from which the type reference has to be resolved.
|
|
120
|
-
*
|
|
121
|
-
* @param {string} typeName
|
|
122
|
-
* @returns {boolean}
|
|
123
|
-
*/
|
|
124
|
-
function isODataV4BuiltinFromService(typeName) {
|
|
125
|
-
if (!options.toOdata || (options.toOdata && options.toOdata.version === 'v2'))
|
|
126
|
-
return false;
|
|
127
|
-
|
|
128
|
-
const typeServiceName = getServiceName(typeName);
|
|
129
|
-
const finalBaseType = getFinalBaseType(typeName);
|
|
130
|
-
// we need the service of the current definition
|
|
131
|
-
const currDefServiceName = getServiceName(csnPath[1]);
|
|
132
|
-
|
|
133
|
-
return typeServiceName === currDefServiceName && isBuiltinType(finalBaseType);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* OData stops replacing types @ 'items', if the type ref is a user defined type
|
|
138
|
-
* AND that type has items, don't do toFinalBaseType
|
|
139
|
-
*
|
|
140
|
-
* @param {string} typeName
|
|
141
|
-
* @returns {boolean}
|
|
142
|
-
*/
|
|
143
|
-
function isODataItems(typeName) {
|
|
144
|
-
const typeDef = csn.definitions[typeName];
|
|
145
|
-
return !!(options.toOdata && typeDef && typeDef.items);
|
|
146
|
-
}
|
|
147
118
|
},
|
|
148
119
|
// HANA/SQLite do not support array-of - turn into CLOB/Text
|
|
149
120
|
items: (parent) => {
|
|
@@ -159,8 +130,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
159
130
|
|
|
160
131
|
// Do not do for OData
|
|
161
132
|
// TODO:factor out somewhere else
|
|
162
|
-
if (!options.toOdata &&
|
|
163
|
-
([ 'action', 'function', 'event' ].includes(artifact.kind))) {
|
|
133
|
+
if (!options.toOdata && artifact.kind in replaceWithDummyKinds) {
|
|
164
134
|
const dummy = { kind: artifact.kind };
|
|
165
135
|
if (artifact.$location)
|
|
166
136
|
setProp(dummy, '$location', artifact.$location);
|
|
@@ -169,6 +139,42 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
169
139
|
}
|
|
170
140
|
// TODO: skipDict options as default function arguments not via Object.assign
|
|
171
141
|
} ], iterateOptions);
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* OData V4 only:
|
|
146
|
+
* Do not replace a type ref if:
|
|
147
|
+
* The type definition is terminating on a scalar type (that can also be a derived type chain)
|
|
148
|
+
* AND the typeName (that is the start of that (derived) type chain is defined within the same
|
|
149
|
+
* service as the artifact from which the type reference has to be resolved.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} typeName
|
|
152
|
+
* @param {CSN.Path} path
|
|
153
|
+
* @returns {boolean}
|
|
154
|
+
*/
|
|
155
|
+
function isODataV4BuiltinFromService(typeName, path) {
|
|
156
|
+
if (!options.toOdata || (options.odataVersion === 'v2') || typeof typeName !== 'string')
|
|
157
|
+
return false;
|
|
158
|
+
|
|
159
|
+
const typeServiceName = getServiceName(typeName);
|
|
160
|
+
const finalBaseType = getFinalBaseTypeWithProps(typeName)?.type;
|
|
161
|
+
// we need the service of the current definition
|
|
162
|
+
const currDefServiceName = getServiceName(path[1]);
|
|
163
|
+
|
|
164
|
+
return typeServiceName === currDefServiceName && isBuiltinType(finalBaseType);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* OData stops replacing types @ 'items', if the type ref is a user defined type
|
|
169
|
+
* AND that type has items, don't do toFinalBaseType
|
|
170
|
+
*
|
|
171
|
+
* @param {string} typeName
|
|
172
|
+
* @returns {boolean}
|
|
173
|
+
*/
|
|
174
|
+
function isODataItems(typeName) {
|
|
175
|
+
const typeDef = csn.definitions[typeName];
|
|
176
|
+
return !!(options.toOdata && typeDef && typeDef.items);
|
|
177
|
+
}
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
/**
|
|
@@ -258,12 +264,11 @@ function flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter, iter
|
|
|
258
264
|
* @param {CSN.Options} options
|
|
259
265
|
* @param {string} pathDelimiter
|
|
260
266
|
* @param {Function} error
|
|
261
|
-
* @param {
|
|
267
|
+
* @param {object} iterateOptions
|
|
262
268
|
*/
|
|
263
269
|
function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}) {
|
|
264
|
-
const {
|
|
265
|
-
const {
|
|
266
|
-
const { effectiveType } = csnRefs(csn);
|
|
270
|
+
const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
271
|
+
const { isAssocOrComposition, effectiveType } = csnUtils;
|
|
267
272
|
const transformers = {
|
|
268
273
|
elements: flatten,
|
|
269
274
|
};
|
|
@@ -284,7 +289,7 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
|
|
|
284
289
|
function flatten(parent, prop, dict, path) {
|
|
285
290
|
if (!parent[prop].$orderedElements)
|
|
286
291
|
setProp(parent[prop], '$orderedElements', []);
|
|
287
|
-
|
|
292
|
+
forEach(dict, (elementName, element) => {
|
|
288
293
|
if (element.elements) {
|
|
289
294
|
// Ignore the structured element, replace it by its flattened form
|
|
290
295
|
// TODO: use $ignore - _ is for links
|
|
@@ -294,11 +299,13 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
|
|
|
294
299
|
const flatElems = flattenStructuredElement(element, elementName, [], path.concat([ 'elements', elementName ]));
|
|
295
300
|
|
|
296
301
|
for (const flatElemName in flatElems) {
|
|
297
|
-
if (parent[prop][flatElemName])
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
error(
|
|
302
|
+
if (parent[prop][flatElemName]) {
|
|
303
|
+
// TODO: combine message ID with generated FK duplicate
|
|
304
|
+
// do the duplicate check in the construct callback, requires to mark generated flat elements,
|
|
305
|
+
// check: Error location should be the existing element like @odata.foreignKey4
|
|
306
|
+
error('name-duplicate-element', path.concat([ 'elements', elementName ]),
|
|
307
|
+
{ '#': 'flatten-element-exist', name: flatElemName });
|
|
308
|
+
}
|
|
302
309
|
|
|
303
310
|
const flatElement = flatElems[flatElemName];
|
|
304
311
|
|
|
@@ -315,16 +322,16 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
|
|
|
315
322
|
const firstRef = onPart.ref[0];
|
|
316
323
|
|
|
317
324
|
/*
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
325
|
+
when element is defined in the current name resolution scope, like
|
|
326
|
+
entity E {
|
|
327
|
+
key x: Integer;
|
|
328
|
+
s : {
|
|
329
|
+
y : Integer;
|
|
330
|
+
a3 : association to E on a3.x = y;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
We need to replace y with s_y and a3 with s_a3 - we must take care to not escape our local scope
|
|
334
|
+
*/
|
|
328
335
|
const prefix = flatElement._flatElementNameWithDots.split('.').slice(0, -1).join(pathDelimiter);
|
|
329
336
|
const possibleFlatName = prefix + pathDelimiter + firstRef;
|
|
330
337
|
|
|
@@ -418,11 +425,11 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
|
|
|
418
425
|
}
|
|
419
426
|
});
|
|
420
427
|
},
|
|
421
|
-
}, [], {
|
|
428
|
+
}, [], Object.assign({
|
|
422
429
|
skipIgnore: false,
|
|
423
430
|
allowArtifact: artifact => (artifact.kind === 'entity' || artifact.kind === 'type'),
|
|
424
431
|
skipDict: { actions: true },
|
|
425
|
-
});
|
|
432
|
+
}, iterateOptions));
|
|
426
433
|
}
|
|
427
434
|
createForeignKeyElements();
|
|
428
435
|
|
|
@@ -469,7 +476,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
|
|
|
469
476
|
const flat = flattenStructuredElement(art, ref[ref.length - 1], [], pathToKey);
|
|
470
477
|
Object.keys(flat).forEach((flatElemName) => {
|
|
471
478
|
const key = assoc.keys[i];
|
|
472
|
-
const clone =
|
|
479
|
+
const clone = cloneCsnNonDict(assoc.keys[i], options);
|
|
473
480
|
if (clone.as) {
|
|
474
481
|
const lastRef = clone.ref[clone.ref.length - 1];
|
|
475
482
|
// Cut off the last ref part from the beginning of the flat name
|
|
@@ -533,7 +540,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
|
|
|
533
540
|
* @returns {object} The clone of base
|
|
534
541
|
*/
|
|
535
542
|
function cloneAndExtendRef(key, base, ref) {
|
|
536
|
-
const clone =
|
|
543
|
+
const clone = cloneCsnNonDict(base, options);
|
|
537
544
|
if (key.ref) {
|
|
538
545
|
// We build a ref that contains the aliased fk - that element will be created later on, so this ref is not resolvable yet
|
|
539
546
|
// Therefore we keep it as $ref - ref is the non-aliased, resolvable "clone"
|
|
@@ -604,8 +611,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
|
|
|
604
611
|
((options.toOdata && isDeepEqual(element, parent[prop][fk[0]], true)) ||
|
|
605
612
|
!options.toOdata)) {
|
|
606
613
|
// error location is the colliding element
|
|
607
|
-
error(
|
|
608
|
-
'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element');
|
|
614
|
+
error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-exists', name: fk[0], art: elementName });
|
|
609
615
|
}
|
|
610
616
|
// attach a proper $path
|
|
611
617
|
setProp(element, '$path', eltPath);
|
|
@@ -614,10 +620,8 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
|
|
|
614
620
|
|
|
615
621
|
// check for duplicate foreign keys
|
|
616
622
|
Object.entries(refCount).forEach(([ name, occ ]) => {
|
|
617
|
-
if (occ > 1)
|
|
618
|
-
error(
|
|
619
|
-
'Duplicate definition of foreign key element $(NAME)');
|
|
620
|
-
}
|
|
623
|
+
if (occ > 1)
|
|
624
|
+
error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-gen', name, art: elementName });
|
|
621
625
|
});
|
|
622
626
|
if (element.keys) {
|
|
623
627
|
element.keys.forEach((key, i) => {
|
|
@@ -682,17 +686,21 @@ function createForeignKeysInternal(path, element, prefix, csn, options, pathDeli
|
|
|
682
686
|
return fks;
|
|
683
687
|
|
|
684
688
|
let finalElement = element;
|
|
689
|
+
let finalTypeName; // TODO: Find a way to not rely on $path?
|
|
685
690
|
// TODO: effectiveType's return value is 'path' for the next inspectRef
|
|
686
691
|
if (element.type && !isBuiltinType(element.type)) {
|
|
687
692
|
const tmpElt = effectiveType(element);
|
|
688
693
|
// effective type resolves to structs and enums only but not scalars
|
|
689
694
|
if (Object.keys(tmpElt).length) {
|
|
690
695
|
finalElement = tmpElt;
|
|
696
|
+
finalTypeName = finalElement.$path[1];
|
|
691
697
|
}
|
|
692
698
|
else {
|
|
693
699
|
// unwind a derived type chain to a scalar type
|
|
694
|
-
while (finalElement.type && !isBuiltinType(finalElement.type))
|
|
700
|
+
while (finalElement.type && !isBuiltinType(finalElement.type)) {
|
|
701
|
+
finalTypeName = finalElement.type;
|
|
695
702
|
finalElement = csn.definitions[finalElement.type];
|
|
703
|
+
}
|
|
696
704
|
}
|
|
697
705
|
}
|
|
698
706
|
|
|
@@ -709,7 +717,7 @@ function createForeignKeysInternal(path, element, prefix, csn, options, pathDeli
|
|
|
709
717
|
}
|
|
710
718
|
// TODO: has managed assoc keys?
|
|
711
719
|
finalElement.keys.forEach((key, keyIndex) => {
|
|
712
|
-
const continuePath =
|
|
720
|
+
const continuePath = getContinuePath([ 'keys', keyIndex ]);
|
|
713
721
|
const alias = key.as || implicitAs(key.ref);
|
|
714
722
|
const result = inspectRef(continuePath);
|
|
715
723
|
fks = fks.concat(createForeignKeysInternal(result, result.art, alias, csn, options, pathDelimiter, lvl + 1));
|
|
@@ -735,12 +743,33 @@ function createForeignKeysInternal(path, element, prefix, csn, options, pathDeli
|
|
|
735
743
|
Object.entries(finalElement.elements).forEach(([ elemName, elem ]) => {
|
|
736
744
|
// Skip already produced foreign keys
|
|
737
745
|
if (!elem['@odata.foreignKey4']) {
|
|
738
|
-
const continuePath =
|
|
746
|
+
const continuePath = getContinuePath([ 'elements', elemName ]);
|
|
739
747
|
fks = fks.concat(createForeignKeysInternal(continuePath, elem, elemName, csn, options, pathDelimiter, lvl + 1));
|
|
740
748
|
}
|
|
741
749
|
});
|
|
742
750
|
}
|
|
743
751
|
|
|
752
|
+
/**
|
|
753
|
+
* Get the path to continue resolving references
|
|
754
|
+
*
|
|
755
|
+
* If we are currently inside of a type, we need to start our path fresh from that given type.
|
|
756
|
+
* Otherwise, we would try to resolve .elements on a thing that does not exist.
|
|
757
|
+
*
|
|
758
|
+
* We also respect if we have a previous inspectRef result as our base.
|
|
759
|
+
*
|
|
760
|
+
* @param {Array} additions
|
|
761
|
+
* @returns {CSN.Path}
|
|
762
|
+
*/
|
|
763
|
+
function getContinuePath(additions) {
|
|
764
|
+
if (csn.definitions[finalElement.type])
|
|
765
|
+
return [ 'definitions', finalElement.type, ...additions ];
|
|
766
|
+
else if (finalTypeName)
|
|
767
|
+
return [ 'definitions', finalTypeName, ...additions ];
|
|
768
|
+
else if (isInspectRefResult)
|
|
769
|
+
return [ path, ...additions ];
|
|
770
|
+
return [ ...path, ...additions ];
|
|
771
|
+
}
|
|
772
|
+
|
|
744
773
|
fks.forEach((fk) => {
|
|
745
774
|
// prepend current prefix
|
|
746
775
|
fk[0] = `${prefix}${pathDelimiter}${fk[0]}`;
|
|
@@ -110,8 +110,8 @@ function handleExists(csn, options, error) {
|
|
|
110
110
|
sources[join.as] = join.as;
|
|
111
111
|
}
|
|
112
112
|
else if (join.args) {
|
|
113
|
-
const
|
|
114
|
-
sources = Object.assign(sources,
|
|
113
|
+
const subSources = getJoinSources(join.args);
|
|
114
|
+
sources = Object.assign(sources, subSources);
|
|
115
115
|
}
|
|
116
116
|
else if (join.ref) {
|
|
117
117
|
sources[join.ref[join.ref.length - 1]] = join.ref[join.ref.length - 1];
|
|
@@ -196,7 +196,7 @@ function handleExists(csn, options, error) {
|
|
|
196
196
|
const stack = [ [ null, startAssoc, startRest, startIndex ] ];
|
|
197
197
|
const { links } = inspectRef(path);
|
|
198
198
|
while (stack.length > 0) {
|
|
199
|
-
// previous: to nest "up" if the previous assoc did not
|
|
199
|
+
// previous: to nest "up" if the previous assoc did not originally have a filter
|
|
200
200
|
// assoc: the assoc path step
|
|
201
201
|
// rest: path steps after assoc
|
|
202
202
|
// index: index of after-assoc in the overall ref-array - so we know where to start looking for the next assoc
|
|
@@ -411,14 +411,14 @@ function handleExists(csn, options, error) {
|
|
|
411
411
|
* Translate an `EXISTS <managed assoc>` into a part of a WHERE condition.
|
|
412
412
|
*
|
|
413
413
|
* For each of the foreign keys, do:
|
|
414
|
-
* + build the target side by prefixing `target`
|
|
414
|
+
* + build the target side by prefixing `target` in front of the ref
|
|
415
415
|
* + build the source side by prefixing `base` (if not already part of `current`)
|
|
416
|
-
* and the assoc name itself (current)
|
|
416
|
+
* and the assoc name itself (current) in front of the ref
|
|
417
417
|
* + Compare source and target with `=`
|
|
418
418
|
*
|
|
419
419
|
* If there is more than one foreign key, join with `and`.
|
|
420
420
|
*
|
|
421
|
-
* The new tokens are
|
|
421
|
+
* The new tokens are immediately added to the WHERE of the subselect
|
|
422
422
|
*
|
|
423
423
|
* @param {CSN.Element} root
|
|
424
424
|
* @param {string} target
|
|
@@ -752,7 +752,7 @@ function handleExists(csn, options, error) {
|
|
|
752
752
|
*
|
|
753
753
|
* @param {string} target
|
|
754
754
|
* @param {TokenStream} where
|
|
755
|
-
* @returns {TokenStream} The input-where with the refs
|
|
755
|
+
* @returns {TokenStream} The input-where with the refs transformed to absolute ones
|
|
756
756
|
*/
|
|
757
757
|
function remapExistingWhere(target, where) {
|
|
758
758
|
return where.map((part) => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
getUtils,
|
|
4
|
+
getUtils, cloneCsnNonDict, applyTransformationsOnNonDictionary,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
|
-
const { implicitAs
|
|
6
|
+
const { implicitAs } = require('../../model/csnRefs');
|
|
7
7
|
const { isBetaEnabled } = require('../../base/model');
|
|
8
8
|
const { ModelError } = require('../../base/error');
|
|
9
9
|
|
|
@@ -69,9 +69,9 @@ function usesMixinAssociation(query, association, associationName) {
|
|
|
69
69
|
function getViewTransformer(csn, options, messageFunctions, transformCommon) {
|
|
70
70
|
const {
|
|
71
71
|
get$combined, isAssocOrComposition,
|
|
72
|
+
inspectRef, queryOrMain, // csnRefs
|
|
72
73
|
} = getUtils(csn);
|
|
73
|
-
const
|
|
74
|
-
const pathDelimiter = (options.forHana.names === 'hdbcds') ? '.' : '_';
|
|
74
|
+
const pathDelimiter = options.forHana && (options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
75
75
|
const { error, info } = messageFunctions;
|
|
76
76
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
77
77
|
|
|
@@ -209,7 +209,7 @@ function getViewTransformer(csn, options, messageFunctions, transformCommon) {
|
|
|
209
209
|
const assocCol = columnMap[elemName];
|
|
210
210
|
if (assocCol && assocCol.ref) {
|
|
211
211
|
elem.keys.forEach((foreignKey) => {
|
|
212
|
-
const ref =
|
|
212
|
+
const ref = cloneCsnNonDict(assocCol.ref, options);
|
|
213
213
|
ref[ref.length - 1] = [ getLastRefStepString(ref) ].concat(foreignKey.as).join(pathDelimiter);
|
|
214
214
|
const result = {
|
|
215
215
|
ref,
|
|
@@ -277,7 +277,7 @@ function getViewTransformer(csn, options, messageFunctions, transformCommon) {
|
|
|
277
277
|
|
|
278
278
|
// Copy the association element to the MIXIN clause under its alias name
|
|
279
279
|
// Needs to be a deep copy, as we transform the on-condition
|
|
280
|
-
const mixinElem =
|
|
280
|
+
const mixinElem = cloneCsnNonDict(elem, options);
|
|
281
281
|
// Perform common transformations on the newly generated MIXIN element (won't be reached otherwise)
|
|
282
282
|
transformCommon(mixinElem, mixinElemName);
|
|
283
283
|
|
|
@@ -457,6 +457,9 @@ function checkIsNotMixinByItself(query, columnMap, elementName) {
|
|
|
457
457
|
if (query && query.SELECT && query.SELECT.mixin) {
|
|
458
458
|
const col = columnMap[elementName];
|
|
459
459
|
|
|
460
|
+
if (!col.ref) // No ref -> new association, but not a mixin.
|
|
461
|
+
return true;
|
|
462
|
+
|
|
460
463
|
// Use getLastRefStepString - with hdbcds.hdbcds and malicious CSN input we might have .id
|
|
461
464
|
const realName = getLastRefStepString(col.ref);
|
|
462
465
|
// If the element is not part of the mixin => True
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
hasAnnotationValue,
|
|
4
|
+
hasAnnotationValue, getServiceNames, forEachDefinition,
|
|
5
5
|
getResultingName, forEachMemberRecursively,
|
|
6
6
|
} = require('../../model/csnUtils');
|
|
7
7
|
const { setProp, isDeprecatedEnabled } = require('../../base/model');
|
|
@@ -19,15 +19,15 @@ const booleanBuiltin = 'cds.Boolean';
|
|
|
19
19
|
* @param {object} messageFunctions
|
|
20
20
|
*/
|
|
21
21
|
function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
22
|
-
const draftSuffix = isDeprecatedEnabled(options, '
|
|
22
|
+
const draftSuffix = isDeprecatedEnabled(options, '_generatedEntityNameWithUnderscore') ? '_drafts' : '.drafts';
|
|
23
23
|
// All services of the model - needed for drafts
|
|
24
24
|
const allServices = getServiceNames(csn);
|
|
25
25
|
const draftRoots = new WeakMap();
|
|
26
26
|
const {
|
|
27
27
|
createForeignKeyElement, createAndAddDraftAdminDataProjection, createScalarElement, createAssociationElement,
|
|
28
|
-
addElement, copyAndAddElement, createAssociationPathComparison,
|
|
28
|
+
addElement, copyAndAddElement, createAssociationPathComparison, csnUtils,
|
|
29
29
|
} = getTransformers(csn, options, pathDelimiter);
|
|
30
|
-
const { getCsnDef, isComposition } =
|
|
30
|
+
const { getCsnDef, isComposition } = csnUtils;
|
|
31
31
|
const { error, warning } = messageFunctions;
|
|
32
32
|
|
|
33
33
|
forEachDefinition(csn, generateDraft);
|
|
@@ -155,7 +155,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
155
155
|
'Generated entity $(NAME) conflicts with existing artifact');
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const persistenceName = getResultingName(csn, options.
|
|
158
|
+
const persistenceName = getResultingName(csn, options.sqlMapping, draftsArtifactName);
|
|
159
159
|
// Duplicate the artifact as a draft shadow entity
|
|
160
160
|
if (csn.definitions[persistenceName]) {
|
|
161
161
|
const definingDraftRoot = draftRoots.get(csn.definitions[persistenceName]);
|
|
@@ -187,7 +187,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
187
187
|
for (const elemName in artifact.elements) {
|
|
188
188
|
const origElem = artifact.elements[elemName];
|
|
189
189
|
let elem;
|
|
190
|
-
if ((isDeprecatedEnabled(options, '
|
|
190
|
+
if ((isDeprecatedEnabled(options, '_renderVirtualElements') && origElem.virtual) || !origElem.virtual)
|
|
191
191
|
elem = copyAndAddElement(origElem, draftsArtifact, draftsArtifactName, elemName)[elemName];
|
|
192
192
|
if (elem) {
|
|
193
193
|
// Remove "virtual" - cap/issues 4956
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { forEachDefinition,
|
|
3
|
+
const { forEachDefinition, getServiceNames } = require('../../model/csnUtils');
|
|
4
4
|
const { forEach } = require('../../utils/objectUtils');
|
|
5
5
|
const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');
|
|
6
6
|
const { getTransformers } = require('../transformUtilsNew');
|
|
@@ -30,15 +30,14 @@ function generateDrafts(csn, options, services) {
|
|
|
30
30
|
createAssociationElement, createAssociationPathComparison,
|
|
31
31
|
addElement, createAction, assignAction,
|
|
32
32
|
resetAnnotation,
|
|
33
|
+
csnUtils,
|
|
33
34
|
} = getTransformers(csn, options);
|
|
34
|
-
|
|
35
35
|
const {
|
|
36
36
|
getFinalType,
|
|
37
37
|
getServiceName,
|
|
38
38
|
hasAnnotationValue,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} = getUtils(csn);
|
|
39
|
+
getFinalBaseTypeWithProps,
|
|
40
|
+
} = csnUtils;
|
|
42
41
|
|
|
43
42
|
const { error, info } = makeMessageFunction(csn, options, 'for.odata');
|
|
44
43
|
|
|
@@ -197,8 +196,8 @@ function generateDrafts(csn, options, services) {
|
|
|
197
196
|
stack.push(elem);
|
|
198
197
|
}
|
|
199
198
|
else if (elem.type) { // types - possibly structured
|
|
200
|
-
const typeDef =
|
|
201
|
-
if (typeDef
|
|
199
|
+
const typeDef = getFinalBaseTypeWithProps(elem.type);
|
|
200
|
+
if (typeDef?.elements)
|
|
202
201
|
stack.push(typeDef);
|
|
203
202
|
}
|
|
204
203
|
});
|