@sap/cds-compiler 2.5.2 → 2.11.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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { copyAnnotations } = require('../../model/csnUtils');
|
|
4
|
-
const { setProp } = require('../../base/model');
|
|
5
4
|
const { cloneCsn, forEachDefinition } = require('../../model/csnUtils');
|
|
6
5
|
const { attachPathOnPartialCSN } = require('./attachPath');
|
|
7
6
|
|
|
@@ -50,13 +49,12 @@ const { isNotNull, setNotNull, setUpNotNull } = function () {
|
|
|
50
49
|
* During the OData transformations in flat-mode, all structured elements will be flattened.
|
|
51
50
|
* This module performs the complete flattening.
|
|
52
51
|
* It also provides information to the reference flattener: elements produced for specific path in the CSN structure.
|
|
53
|
-
* Each generated element gets hidden attributes:
|
|
54
|
-
* - _flatElementNameWithDots - names in the element path concatenated with dot
|
|
55
52
|
* @param {CSN.Model} csn CSN-object to flatten
|
|
56
53
|
* @param {*} csnUtils instances of utility functions
|
|
57
54
|
* @param {*} options
|
|
58
55
|
* @param {*} referenceFlattener
|
|
59
56
|
* @param {Function} error
|
|
57
|
+
* @param {Function} isExternalServiceMember returns true for an artifact that is part of an external service
|
|
60
58
|
*/
|
|
61
59
|
function flattenCSN(csn, csnUtils, options, referenceFlattener, error, isExternalServiceMember) {
|
|
62
60
|
forEachDefinition(csn, (def, defName, propertyName, path) =>
|
|
@@ -76,17 +74,22 @@ function flattenDefinition(definition, definitionPath, csnUtils, options, refere
|
|
|
76
74
|
if (definition.kind !== 'entity' && definition.kind !== 'view')
|
|
77
75
|
return;
|
|
78
76
|
|
|
79
|
-
let { newFlatElements } = flattenStructure(definition, definitionPath, csnUtils, options, error, referenceFlattener);
|
|
77
|
+
let { newFlatElements } = flattenStructure(definition.elements, definitionPath, csnUtils, options, error, referenceFlattener);
|
|
80
78
|
|
|
81
79
|
attachPathOnPartialCSN(newFlatElements, definitionPath.concat('elements'));
|
|
82
|
-
|
|
83
80
|
definition.elements = newFlatElements;
|
|
81
|
+
|
|
82
|
+
if (definition.params) {
|
|
83
|
+
let { newFlatElements } = flattenStructure(definition.params, definitionPath, csnUtils, options, error, referenceFlattener);
|
|
84
|
+
attachPathOnPartialCSN(newFlatElements, definitionPath.concat('params'));
|
|
85
|
+
definition.params = newFlatElements;
|
|
86
|
+
}
|
|
84
87
|
} // flattenDefinition
|
|
85
88
|
|
|
86
89
|
/**
|
|
87
90
|
* Flattens structured element by calling element flattener for each structured child.
|
|
88
91
|
* Returns a dictionary containing all the new elements for the given structure.
|
|
89
|
-
* @param {*}
|
|
92
|
+
* @param {*} dictionary to flatten
|
|
90
93
|
* @param {CSN.Path} path the path of the structure in the CSN tree
|
|
91
94
|
* @param {*} csnUtils
|
|
92
95
|
* @param {Function} error Error message function
|
|
@@ -95,14 +98,14 @@ function flattenDefinition(definition, definitionPath, csnUtils, options, refere
|
|
|
95
98
|
* @param {*} [newFlatElements]
|
|
96
99
|
* @param {boolean} [isTopLevelElement] states if this is a top level element
|
|
97
100
|
*/
|
|
98
|
-
function flattenStructure(
|
|
101
|
+
function flattenStructure(dictionary, path, csnUtils, options, error, referenceFlattener = undefined, elementPathInStructure = [],
|
|
99
102
|
newFlatElements = Object.create(null), isTopLevelElement = true, isParentNotNull = false) {
|
|
100
103
|
|
|
101
|
-
if (!isTopLevelElement) addPropsForPropagationFromElement(
|
|
104
|
+
if (!isTopLevelElement) addPropsForPropagationFromElement(dictionary);
|
|
102
105
|
|
|
103
106
|
let generatedNewFlatElementsNames = []; // holds the names of all new child elements of the structure
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
dictionary && Object.entries(dictionary).forEach(([elementName, element]) => {
|
|
106
109
|
let currPath = path.concat('elements', elementName);
|
|
107
110
|
|
|
108
111
|
if (isTopLevelElement) {
|
|
@@ -120,8 +123,8 @@ function flattenStructure(struct, path, csnUtils, options, error, referenceFlatt
|
|
|
120
123
|
addPropsForPropagationFromElement(element);
|
|
121
124
|
|
|
122
125
|
// if the child element is structured itself -> needs to be flattened
|
|
123
|
-
const
|
|
124
|
-
let result = flattenStructure(
|
|
126
|
+
const elements = element.elements || csnUtils.getFinalBaseType(element.type).elements;
|
|
127
|
+
let result = flattenStructure(elements, currPath, csnUtils, options, error, referenceFlattener, elementPathInStructure.concat(elementName), newFlatElements, false, isNotNull());
|
|
125
128
|
generatedNewFlatElementsNames.push(...result.generatedNewFlatElementsNames); // accomulate names of produced elements
|
|
126
129
|
|
|
127
130
|
} else { // when we do not need to flat, this is scalar or empty (cds-compiler#4337) -> needs to be registered in referenceFlattener
|
|
@@ -129,7 +132,6 @@ function flattenStructure(struct, path, csnUtils, options, error, referenceFlatt
|
|
|
129
132
|
let elementNameWithDots = elementPathInStructure.concat(elementName).join('.');
|
|
130
133
|
addNewElementToResult(element, newElementName, elementNameWithDots, currPath);
|
|
131
134
|
}
|
|
132
|
-
|
|
133
135
|
});
|
|
134
136
|
|
|
135
137
|
if (referenceFlattener) {
|
|
@@ -137,30 +139,29 @@ function flattenStructure(struct, path, csnUtils, options, error, referenceFlatt
|
|
|
137
139
|
}
|
|
138
140
|
return { newFlatElements, generatedNewFlatElementsNames };
|
|
139
141
|
|
|
140
|
-
|
|
141
142
|
// adds newly created element into the final dictionary of elements
|
|
142
143
|
function addNewElementToResult(element, elementName, elementNameWithDots, path) {
|
|
143
144
|
if (newFlatElements[elementName]) {
|
|
144
145
|
error(null, path, `Generated element ${elementName} conflicts with other generated element`);
|
|
145
146
|
} else {
|
|
146
|
-
let
|
|
147
|
+
let newPath = path.slice(0, 2).concat('elements', elementName);
|
|
148
|
+
let newElement = createNewElement(element, elementNameWithDots, newPath);
|
|
147
149
|
newFlatElements[elementName] = newElement;
|
|
148
150
|
generatedNewFlatElementsNames.push(elementName);
|
|
149
151
|
|
|
150
152
|
if (referenceFlattener) {
|
|
151
|
-
let newPath = path.slice(0, 2).concat('elements', elementName);
|
|
152
153
|
referenceFlattener.registerElementTransition(path, newPath);
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
} // addNewElementToResult
|
|
156
157
|
|
|
157
158
|
// creates new element by copying the properties of the originating element
|
|
158
|
-
function createNewElement(element, elementNameWithDots) {
|
|
159
|
+
function createNewElement(element, elementNameWithDots, path) {
|
|
159
160
|
let newElement = cloneCsn(element, options);
|
|
160
161
|
if (!isTopLevelElement) propagatePropsToElement(newElement);
|
|
161
162
|
if (isNotNull() === undefined) delete newElement.notNull;
|
|
162
|
-
if (!isTopLevelElement) {
|
|
163
|
-
|
|
163
|
+
if (!isTopLevelElement && referenceFlattener) {
|
|
164
|
+
referenceFlattener.setElementNameWithDots(path, elementNameWithDots);
|
|
164
165
|
}
|
|
165
166
|
return newElement;
|
|
166
167
|
} // createNewElement
|
|
@@ -25,7 +25,6 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
25
25
|
// collect in this variable all the newly exposed types
|
|
26
26
|
const exposedStructTypes = [];
|
|
27
27
|
const schemas = Object.create(null);
|
|
28
|
-
|
|
29
28
|
// walk through the definitions of the given CSN and expose types where needed
|
|
30
29
|
forEachDefinition(csn, (def, defName, propertyName, path) => {
|
|
31
30
|
// we do expose types only for definition from inside services
|
|
@@ -33,11 +32,11 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
33
32
|
if (serviceName) {
|
|
34
33
|
if (['type', 'entity', 'view'].includes(def.kind)) {
|
|
35
34
|
forEachMember(def, (element, elementName, propertyName, path) => {
|
|
36
|
-
if (
|
|
35
|
+
if (['elements', 'params'].includes(propertyName)) {
|
|
37
36
|
const artificialtName = `${isMultiSchema ?
|
|
38
37
|
defNameWithoutServiceOrContextName(defName, serviceName)
|
|
39
38
|
: defNameWithoutServiceOrContextName(defName, serviceName).replace(/\./g, '_')}_${elementName}`;
|
|
40
|
-
exposeTypeOf(element, elementName, defName, serviceName, artificialtName, path);
|
|
39
|
+
exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, artificialtName, path);
|
|
41
40
|
}
|
|
42
41
|
}, path);
|
|
43
42
|
}
|
|
@@ -64,11 +63,11 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
64
63
|
* @param {string} artificialName
|
|
65
64
|
* @param {CSN.Path} path
|
|
66
65
|
*/
|
|
67
|
-
function exposeTypeOf(node, memberName, defName, service, artificialName, path) {
|
|
66
|
+
function exposeTypeOf(node, isKey, memberName, defName, service, artificialName, path) {
|
|
68
67
|
if (isArrayed(node))
|
|
69
|
-
exposeArrayOfTypeOf(node, memberName, defName, service, artificialName, path);
|
|
68
|
+
exposeArrayOfTypeOf(node, isKey, memberName, defName, service, artificialName, path);
|
|
70
69
|
else if (csnUtils.isStructured(node))
|
|
71
|
-
exposeStructTypeOf(node, memberName, defName, service, artificialName, path);
|
|
70
|
+
exposeStructTypeOf(node, isKey, memberName, defName, service, artificialName, path);
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
/**
|
|
@@ -90,12 +89,12 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
90
89
|
function exposeTypesOfAction(action, actionName, defName, service, path) {
|
|
91
90
|
if (action.returns) {
|
|
92
91
|
const artificialName = `return_${actionName.replace(/\./g, '_')}`;
|
|
93
|
-
exposeTypeOf(action.returns, actionName, defName, service, artificialName, path.concat(['returns']));
|
|
92
|
+
exposeTypeOf(action.returns, false, actionName, defName, service, artificialName, path.concat(['returns']));
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
action.params && Object.entries(action.params).forEach(([paramName, param]) => {
|
|
97
96
|
const artificialName = `param_${actionName.replace(/\./g, '_')}_${paramName}`;
|
|
98
|
-
exposeTypeOf(param, actionName, defName, service, artificialName, path.concat(['params', paramName]));
|
|
97
|
+
exposeTypeOf(param, false, actionName, defName, service, artificialName, path.concat(['params', paramName]));
|
|
99
98
|
});
|
|
100
99
|
}
|
|
101
100
|
|
|
@@ -108,9 +107,11 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
108
107
|
* @param {String} service
|
|
109
108
|
* @param {String} artificialName
|
|
110
109
|
*/
|
|
111
|
-
function exposeStructTypeOf(node, memberName, defName, service, artificialName, path, parentName) {
|
|
110
|
+
function exposeStructTypeOf(node, isKey, memberName, defName, service, artificialName, path, parentName) {
|
|
111
|
+
if (node.items) exposeStructTypeOf(node.items, isKey, memberName, defName, service, artificialName, path);
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
// start conservative, assume we're in a named type
|
|
114
|
+
let isAnonymous = false;
|
|
114
115
|
|
|
115
116
|
if (isExposableStructure(node)) {
|
|
116
117
|
let typeDef = node.type ? csnUtils.getCsnDef(node.type) : /* structure|anonymous type */ node;
|
|
@@ -124,6 +125,11 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
124
125
|
// and the new target. Consequently, we now have both type and elements properties in this case, and the elements should be taken as a priority
|
|
125
126
|
// as the correct target is there and no longer in the type definition
|
|
126
127
|
let newTypeElements = (node.type && node.elements) ? node.elements : typeDef.elements;
|
|
128
|
+
// if node and typeDef are identical, we're anonymous
|
|
129
|
+
isAnonymous = node === typeDef;
|
|
130
|
+
// if we've left the anonymous world, we're no longer in a key def
|
|
131
|
+
if (!isAnonymous)
|
|
132
|
+
isKey = false;
|
|
127
133
|
|
|
128
134
|
let newType = exposeStructType(newTypeFullName, newTypeElements, memberName, path);
|
|
129
135
|
if (!newType) {
|
|
@@ -138,6 +144,7 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
138
144
|
newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
|
|
139
145
|
if (node.elements && node.elements[elemName].$location) setProp(newElem, '$location', node.elements[elemName].$location);
|
|
140
146
|
exposeStructTypeOf(newElem,
|
|
147
|
+
isKey,
|
|
141
148
|
memberName,
|
|
142
149
|
typeDef.kind === 'type' ? node.type : defName,
|
|
143
150
|
service,
|
|
@@ -251,6 +258,9 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
251
258
|
error(null, path, `"${elemName}": Element name conflicts with existing element`);
|
|
252
259
|
}
|
|
253
260
|
let cloned = cloneCsn(element, options);
|
|
261
|
+
// if this was an anonymous sub element of a key, mark it as not nullable
|
|
262
|
+
if(isAnonymous && isKey && !cloned.key && cloned.notNull === undefined)
|
|
263
|
+
cloned.notNull = true;
|
|
254
264
|
type.elements[elemName] = cloned;
|
|
255
265
|
});
|
|
256
266
|
|
|
@@ -265,12 +275,12 @@ function typesExposure(csn, whatsMyServiceName, options, csnUtils, message) {
|
|
|
265
275
|
|
|
266
276
|
// If a member is of type "array of <named type|anonymous type>", we expose the arrayed type,
|
|
267
277
|
// like we expose structures in structured mode
|
|
268
|
-
function exposeArrayOfTypeOf(node, memberName, defName, service, artificialName, path) {
|
|
278
|
+
function exposeArrayOfTypeOf(node, isKey, memberName, defName, service, artificialName, path) {
|
|
269
279
|
// if anonymously defined in place -> we always expose the type
|
|
270
280
|
// this would be definition like 'elem: array of { ... }'
|
|
271
281
|
// and we use the artificial name for the new type name
|
|
272
282
|
if (node.items && !node.type) {
|
|
273
|
-
exposeStructTypeOf(node.items, memberName, defName, service, artificialName, path.concat('items'));
|
|
283
|
+
exposeStructTypeOf(node.items, isKey, memberName, defName, service, artificialName, path.concat('items'));
|
|
274
284
|
}
|
|
275
285
|
// we can have both of the 'type' and 'items' in the cases:
|
|
276
286
|
// 1. 'elem: Foo' and 'type Foo: array of Baz' and 'type Baz: { ... }'
|
|
@@ -8,9 +8,8 @@ const { hasErrors, makeMessageFunction } = require('../base/messages');
|
|
|
8
8
|
const { setProp } = require('../base/model');
|
|
9
9
|
const { csnRefs } = require('../model/csnRefs');
|
|
10
10
|
|
|
11
|
-
const { copyAnnotations } = require('../model/csnUtils');
|
|
12
|
-
const { cloneCsn,
|
|
13
|
-
getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
11
|
+
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
+
const { cloneCsn, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
14
13
|
|
|
15
14
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
16
15
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
@@ -62,6 +61,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
62
61
|
recurseElements,
|
|
63
62
|
renameAnnotation,
|
|
64
63
|
setAnnotation,
|
|
64
|
+
resetAnnotation,
|
|
65
65
|
expandStructsInExpression,
|
|
66
66
|
};
|
|
67
67
|
|
|
@@ -80,6 +80,14 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
80
80
|
else if(defStrLen5k)
|
|
81
81
|
element.length = 5000;
|
|
82
82
|
}
|
|
83
|
+
if (element.type === 'cds.Binary' && element.length === undefined) {
|
|
84
|
+
if(options.defaultBinaryLength) {
|
|
85
|
+
element.length = options.defaultBinaryLength;
|
|
86
|
+
setProp(element, '$default', true);
|
|
87
|
+
}
|
|
88
|
+
else if(defStrLen5k)
|
|
89
|
+
element.length = 5000;
|
|
90
|
+
}
|
|
83
91
|
/*
|
|
84
92
|
if (element.type === 'cds.Decimal' && element.precision === undefined && options.precision) {
|
|
85
93
|
element.precision = options.precision;
|
|
@@ -287,56 +295,64 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
287
295
|
return result;
|
|
288
296
|
}
|
|
289
297
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Return a copy of 'ref' where all path steps resulting from struct traversal are
|
|
300
|
+
* fused together into one step, using '_' (so that the path fits again for flattened
|
|
301
|
+
* structs), e.g.
|
|
302
|
+
* [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in
|
|
303
|
+
* [ (Entity), (struct1_struct2_assoc), (elem) ]
|
|
304
|
+
*
|
|
305
|
+
* @param {string[]} ref
|
|
306
|
+
* @param {CSN.Path} path CSN path to the ref
|
|
307
|
+
* @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
|
|
308
|
+
* @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
|
|
309
|
+
* @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
|
|
310
|
+
* @returns {string[]}
|
|
311
|
+
*/
|
|
312
|
+
function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap()) {
|
|
297
313
|
// Refs of length 1 cannot contain steps - no need to check
|
|
298
314
|
if (ref.length < 2) {
|
|
299
315
|
return ref;
|
|
300
316
|
}
|
|
301
317
|
|
|
302
|
-
|
|
303
|
-
return flatten(ref, path);
|
|
304
|
-
} catch (e) {
|
|
305
|
-
if (e.message && e.message === "Scope 'ref-target' but no entity was provided.") {
|
|
306
|
-
// TODO: should not happen anymore
|
|
307
|
-
return flatten(ref, path);
|
|
308
|
-
} else {
|
|
309
|
-
throw e;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
318
|
+
return flatten(ref, path);
|
|
312
319
|
|
|
313
320
|
function flatten(ref, path) {
|
|
314
321
|
let result = [];
|
|
315
322
|
//let stack = []; // IDs of path steps not yet processed or part of a struct traversal
|
|
316
|
-
|
|
323
|
+
if(!links && !scope) { // calculate JIT if not supplied
|
|
324
|
+
const res = inspectRef(path);
|
|
325
|
+
links = res.links;
|
|
326
|
+
scope = res.scope;
|
|
327
|
+
}
|
|
317
328
|
if (scope === '$magic')
|
|
318
329
|
return ref;
|
|
319
330
|
let flattenStep = false;
|
|
320
331
|
links.forEach((value, idx) => {
|
|
321
|
-
if (flattenStep)
|
|
322
|
-
result[result.length - 1] += pathDelimiter + ref[idx];
|
|
332
|
+
if (flattenStep) {
|
|
333
|
+
result[result.length - 1] += pathDelimiter + (ref[idx].id ? ref[idx].id : ref[idx]);
|
|
334
|
+
// if we had a filter or args, we had an assoc so this step is done
|
|
335
|
+
// we then keep along the filter/args by updating the id of the current ref
|
|
336
|
+
if(ref[idx].id) {
|
|
337
|
+
ref[idx].id = result[result.length-1];
|
|
338
|
+
result[result.length-1] = ref[idx];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
323
341
|
else {
|
|
324
342
|
result.push(ref[idx]);
|
|
325
343
|
}
|
|
326
|
-
|
|
344
|
+
|
|
345
|
+
flattenStep = value.art && !value.art.kind && !value.art.SELECT && !value.art.from && (value.art.elements || effectiveType(value.art).elements || (resolvedLinkTypes.get(value)||{}).elements);
|
|
327
346
|
});
|
|
328
|
-
|
|
329
|
-
// If the path starts with '$self', this is now redundant (because of flattening) and can be omitted,
|
|
330
|
-
// making life easier for consumers
|
|
331
|
-
if (result[0] === '$self' && result.length > 1 && !magicVars.includes(result[1])) {
|
|
332
|
-
result = result.slice(1);
|
|
333
|
-
}
|
|
347
|
+
|
|
334
348
|
return result;
|
|
335
349
|
}
|
|
336
350
|
}
|
|
337
351
|
|
|
338
352
|
/**
|
|
339
353
|
* Copy properties of the referenced type, but don't resolve to the final base type.
|
|
354
|
+
*
|
|
355
|
+
* Do not copy the length if it was just set via the default-value.
|
|
340
356
|
*
|
|
341
357
|
* @param {any} node Node to copy to
|
|
342
358
|
* @returns {void}
|
|
@@ -366,24 +382,31 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
366
382
|
Object.assign(node, { srid: typeDef.srid });
|
|
367
383
|
}
|
|
368
384
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Replace the type of 'node' with its final base type (in contrast to the compiler,
|
|
387
|
+
* also unravel derived enum types, i.e. take the final base type of the enum's base type.
|
|
388
|
+
* Similar with associations and compositions (we probably need a _baseType link)
|
|
389
|
+
*
|
|
390
|
+
* @param {CSN.Artifact} node
|
|
391
|
+
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
392
|
+
* @param {boolean} [keepLocalized=false] Wether to clone .localized from a type def
|
|
393
|
+
* @returns {void}
|
|
394
|
+
*/
|
|
395
|
+
function toFinalBaseType(node, resolved, keepLocalized=false) {
|
|
373
396
|
// Nothing to do if no type (or if array/struct type)
|
|
374
397
|
if (!node || !node.type) return;
|
|
375
398
|
// In case of a ref -> Follow the ref
|
|
376
399
|
if (node.type && node.type.ref) {
|
|
377
|
-
|
|
400
|
+
const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
|
|
378
401
|
if(finalBaseType === null)
|
|
379
402
|
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
|
|
380
|
-
if
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
node.elements = cloneCsn(finalBaseType.elements, options); // copy elements
|
|
403
|
+
if(finalBaseType.elements) {
|
|
404
|
+
// This changes the order - to be discussed!
|
|
405
|
+
node.elements = cloneCsn(finalBaseType, options).elements; // copy elements
|
|
384
406
|
delete node.type; // delete the type reference as edm processing does not expect it
|
|
385
|
-
} else if
|
|
386
|
-
|
|
407
|
+
} else if(finalBaseType.items) {
|
|
408
|
+
// This changes the order - to be discussed!
|
|
409
|
+
node.items = cloneCsn(finalBaseType, options).items; // copy items
|
|
387
410
|
delete node.type;
|
|
388
411
|
} else {
|
|
389
412
|
node.type=finalBaseType;
|
|
@@ -396,10 +419,31 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
396
419
|
// The type might already be a full fledged type def (array of)
|
|
397
420
|
let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
|
|
398
421
|
// Nothing to do if type is an array or a struct type
|
|
399
|
-
if (typeDef.items || typeDef.elements)
|
|
422
|
+
if (typeDef.items || typeDef.elements) {
|
|
423
|
+
if(!(options.transformation === 'hdbcds' || options.toSql))
|
|
424
|
+
return;
|
|
425
|
+
|
|
426
|
+
// cloneCsn only works correctly if we start "from the top"
|
|
427
|
+
const clone = cloneCsn({definitions: {'TypeDef': typeDef }}, options);
|
|
428
|
+
// With hdbcds-hdbcds, don't resolve structured types - but propagrate ".items", to turn into LargeString later on.
|
|
429
|
+
if(typeDef.items) {
|
|
430
|
+
delete node.type;
|
|
431
|
+
Object.assign(node, {items: clone.definitions.TypeDef.items});
|
|
432
|
+
}
|
|
433
|
+
if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
434
|
+
if(!typeDef.items)
|
|
435
|
+
delete node.type;
|
|
436
|
+
Object.assign(node, {elements: clone.definitions.TypeDef.elements});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
400
442
|
// if the declared element is an enum, these values are with priority
|
|
401
|
-
if (!node.enum && typeDef.enum)
|
|
402
|
-
|
|
443
|
+
if (!node.enum && typeDef.enum) {
|
|
444
|
+
const clone = cloneCsn({definitions: {'TypeDef': typeDef }}, options).definitions.TypeDef.enum;
|
|
445
|
+
Object.assign(node, { enum: clone });
|
|
446
|
+
}
|
|
403
447
|
if (node.length === undefined && typeDef.length !== undefined)
|
|
404
448
|
Object.assign(node, { length: typeDef.length });
|
|
405
449
|
if (node.precision === undefined && typeDef.precision !== undefined)
|
|
@@ -408,6 +452,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
408
452
|
Object.assign(node, { scale: typeDef.scale });
|
|
409
453
|
if (node.srid === undefined && typeDef.srid !== undefined)
|
|
410
454
|
Object.assign(node, { srid: typeDef.srid });
|
|
455
|
+
if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
|
|
456
|
+
Object.assign(node, { localized: typeDef.localized });
|
|
411
457
|
node.type = typeDef.type;
|
|
412
458
|
toFinalBaseType(node);
|
|
413
459
|
}
|
|
@@ -888,6 +934,34 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
888
934
|
node[name] = value;
|
|
889
935
|
}
|
|
890
936
|
|
|
937
|
+
/**
|
|
938
|
+
* Assigns unconditionally annotation to a node, which means it overwrites already existing annotation assignment.
|
|
939
|
+
* Overwritting is when the assignment differs from undefined and null, also when differs from the already set value.
|
|
940
|
+
* Setting new assignment results false as return value and overwriting - true.
|
|
941
|
+
*
|
|
942
|
+
* @param {object} node Assignee
|
|
943
|
+
* @param {string} name Annotation name
|
|
944
|
+
* @param {any} value Annotation value
|
|
945
|
+
* @param {function} info function that reports info messages
|
|
946
|
+
* @param {CSN.Path} path location of the warning
|
|
947
|
+
* @returns {boolean} wasOverwritten true when the annotation was overwritten
|
|
948
|
+
*/
|
|
949
|
+
function resetAnnotation(node, name, value, info, path) {
|
|
950
|
+
if (!name.startsWith('@')) {
|
|
951
|
+
throw Error('Annotation name should start with "@": ' + name);
|
|
952
|
+
}
|
|
953
|
+
if (value === undefined) {
|
|
954
|
+
throw Error('Annotation value must not be undefined');
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
const wasOverwritten = node[name] !== undefined && node[name] !== null && node[name] !== value;
|
|
958
|
+
const oldValue = node[name];
|
|
959
|
+
node[name] = value;
|
|
960
|
+
if(wasOverwritten)
|
|
961
|
+
info(null, path, { anno: name, prop: value, otherprop: oldValue },
|
|
962
|
+
`Value $(OTHERPROP) of annotation $(ANNO) is overwritten with new value $(PROP)`);
|
|
963
|
+
return wasOverwritten;
|
|
964
|
+
}
|
|
891
965
|
|
|
892
966
|
/*
|
|
893
967
|
Resolve the type of an artifact
|
|
@@ -969,7 +1043,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
969
1043
|
function flattenPath(path, fullRef=false, followMgdAssoc=false) {
|
|
970
1044
|
let art = path._art;
|
|
971
1045
|
if(art) {
|
|
972
|
-
if(art &&
|
|
1046
|
+
if(art && !((art.items && art.items.elements) || art.elements)) {
|
|
973
1047
|
if(followMgdAssoc && art.target && art.keys) {
|
|
974
1048
|
let rc = [];
|
|
975
1049
|
for(const k of art.keys) {
|
|
@@ -982,9 +1056,9 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
982
1056
|
}
|
|
983
1057
|
return rc;
|
|
984
1058
|
}
|
|
985
|
-
if(art.type.ref)
|
|
1059
|
+
if(art.type && art.type.ref)
|
|
986
1060
|
art = resolvePath(art.type);
|
|
987
|
-
else if(!isBuiltinType(art.type))
|
|
1061
|
+
else if(art.type && !isBuiltinType(art.type))
|
|
988
1062
|
art = model.definitions[art.type];
|
|
989
1063
|
}
|
|
990
1064
|
const elements = art.items && art.items.elements || art.elements;
|
|
@@ -1006,41 +1080,33 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1006
1080
|
return [path];
|
|
1007
1081
|
}
|
|
1008
1082
|
|
|
1009
|
-
|
|
1083
|
+
/**
|
|
1010
1084
|
* Expand structured expression arguments to flat reference paths.
|
|
1011
1085
|
* Structured elements are real sub element lists and managed associations.
|
|
1012
1086
|
* All unmanaged association definitions are rewritten if applicable (elements/mixins).
|
|
1013
|
-
* Also, HAVING and WHERE clauses are rewritten.
|
|
1087
|
+
* Also, HAVING and WHERE clauses are rewritten. We also check for infix filters and
|
|
1088
|
+
* .xpr in columns.
|
|
1014
1089
|
*
|
|
1015
|
-
*
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
},
|
|
1036
|
-
path);
|
|
1037
|
-
if(query.SELECT.having)
|
|
1038
|
-
query.SELECT.having = expand(query.SELECT.having, queryPath.concat(['SELECT', 'having']))
|
|
1039
|
-
if(query.SELECT.where)
|
|
1040
|
-
query.SELECT.where = expand(query.SELECT.where, queryPath.concat(['SELECT', 'where']))
|
|
1041
|
-
}
|
|
1042
|
-
}, path.concat([ 'query' ]));
|
|
1043
|
-
}
|
|
1090
|
+
* @todo Check if can be skipped for abstract entity and or cds.persistence.skip ?
|
|
1091
|
+
* @param {CSN.Model} csn
|
|
1092
|
+
* @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts
|
|
1093
|
+
*/
|
|
1094
|
+
function expandStructsInExpression(csn, options = {}) {
|
|
1095
|
+
applyTransformations(csn, {
|
|
1096
|
+
'on': (parent, name, on, path) => {
|
|
1097
|
+
parent.on = expand(parent.on, path);
|
|
1098
|
+
},
|
|
1099
|
+
'having': (parent, name, having, path) => {
|
|
1100
|
+
parent.having = expand(parent.having, path);
|
|
1101
|
+
},
|
|
1102
|
+
'where': (parent, name, where, path) => {
|
|
1103
|
+
parent.where = expand(parent.where, path);
|
|
1104
|
+
},
|
|
1105
|
+
'xpr': (parent, name, xpr, path) => {
|
|
1106
|
+
parent.xpr = expand(parent.xpr, path);
|
|
1107
|
+
}
|
|
1108
|
+
}, undefined, undefined, options);
|
|
1109
|
+
|
|
1044
1110
|
/*
|
|
1045
1111
|
flatten structured leaf types and return array of paths
|
|
1046
1112
|
Flattening stops on all non-structured types.
|