@sap/cds-compiler 2.12.0 → 2.13.6
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 +110 -15
- package/bin/cdsc.js +13 -13
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +28 -63
- package/lib/api/options.js +3 -3
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +25 -4
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +158 -123
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +4 -7
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +14 -3
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +32 -13
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +111 -46
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +64 -37
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +5 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +9 -8
- package/lib/edm/edm.js +11 -12
- package/lib/edm/edmPreprocessor.js +137 -73
- package/lib/edm/edmUtils.js +116 -22
- package/lib/gen/Dictionary.json +10 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5282 -4265
- package/lib/json/from-csn.js +12 -1
- package/lib/json/to-csn.js +126 -66
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +76 -3
- package/lib/language/language.g4 +297 -130
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +468 -59
- package/lib/main.js +35 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +225 -156
- package/lib/model/csnUtils.js +192 -223
- package/lib/model/enrichCsn.js +70 -29
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +5 -4
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +73 -288
- package/lib/render/toHdbcds.js +25 -23
- package/lib/render/toSql.js +98 -41
- package/lib/render/utils/common.js +5 -10
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +103 -305
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +55 -52
- package/lib/transform/db/expansion.js +46 -24
- package/lib/transform/db/flattening.js +553 -102
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +59 -6
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +67 -183
- package/lib/transform/forOdataNew.js +17 -171
- package/lib/transform/localized.js +34 -19
- package/lib/transform/odata/generateForeignKeyElements.js +1 -1
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +36 -22
- package/lib/transform/translateAssocsToJoins.js +2 -19
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/universalCsnEnricher.js +0 -237
package/lib/model/csnUtils.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { csnRefs } = require('../model/csnRefs');
|
|
3
|
+
const { csnRefs, implicitAs } = require('../model/csnRefs');
|
|
4
4
|
const { applyTransformations, applyTransformationsOnNonDictionary } = require('../transform/db/applyTransformations');
|
|
5
5
|
const { isBuiltinType } = require('../compiler/builtins.js')
|
|
6
6
|
const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
|
|
7
|
+
const { ModelError } = require("../base/error");
|
|
7
8
|
const version = require('../../package.json').version;
|
|
8
9
|
|
|
9
10
|
// Low-level utility functions to work with compact CSN.
|
|
@@ -36,15 +37,15 @@ const version = require('../../package.json').version;
|
|
|
36
37
|
* Get utility functions for a given CSN.
|
|
37
38
|
* @param {CSN.Model} model (Compact) CSN model
|
|
38
39
|
*/
|
|
39
|
-
function getUtils(model) {
|
|
40
|
-
const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
|
|
40
|
+
function getUtils(model, universalReady) {
|
|
41
|
+
const { artifactRef, inspectRef, effectiveType, getOrigin, targetAspect, getColumn, getElement, initDefinition } = csnRefs(model, universalReady);
|
|
41
42
|
|
|
42
43
|
return {
|
|
43
44
|
getCsnDef,
|
|
44
45
|
isStructured,
|
|
45
46
|
getFinalType,
|
|
46
47
|
getFinalTypeDef,
|
|
47
|
-
|
|
48
|
+
isManagedAssociation,
|
|
48
49
|
isAssocOrComposition,
|
|
49
50
|
isAssociation,
|
|
50
51
|
isComposition,
|
|
@@ -62,6 +63,10 @@ function getUtils(model) {
|
|
|
62
63
|
get$combined,
|
|
63
64
|
getOrigin,
|
|
64
65
|
getQueryPrimarySource,
|
|
66
|
+
targetAspect,
|
|
67
|
+
getColumn,
|
|
68
|
+
getElement,
|
|
69
|
+
initDefinition
|
|
65
70
|
};
|
|
66
71
|
|
|
67
72
|
/**
|
|
@@ -71,125 +76,125 @@ function getUtils(model) {
|
|
|
71
76
|
* @returns {object}
|
|
72
77
|
*/
|
|
73
78
|
function get$combined(query) {
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
return getSources(query);
|
|
80
|
+
}
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Get the union of all elements from the from clause
|
|
84
|
+
* - descend into unions, following the lead query
|
|
85
|
+
* - merge all queries in case of joins
|
|
86
|
+
* - follow subqueries
|
|
87
|
+
*
|
|
88
|
+
* @param {CSN.Query} query Query to check
|
|
89
|
+
* @param {boolean} [isSubquery]
|
|
90
|
+
* @returns {object} Map of sources
|
|
91
|
+
*/
|
|
92
|
+
function getSources(query, isSubquery=false) {
|
|
93
|
+
// Remark CW: better just a while along query.SET.args[0]
|
|
94
|
+
if (query.SET) {
|
|
95
|
+
if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
|
|
96
|
+
return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
return getSources(query.SET.args[0], isSubquery);
|
|
99
|
+
}
|
|
100
|
+
else if (query.SELECT) {
|
|
101
|
+
if (query.SELECT.from.args) {
|
|
102
|
+
return walkArgs(query.SELECT.from.args);
|
|
93
103
|
}
|
|
94
|
-
else if (query.SELECT) {
|
|
95
|
-
|
|
96
|
-
return walkArgs(query.SELECT.from.args);
|
|
97
|
-
}
|
|
98
|
-
else if (query.SELECT.from.ref) {
|
|
99
|
-
let art = artifactRef(query.SELECT.from);
|
|
104
|
+
else if (query.SELECT.from.ref) {
|
|
105
|
+
let art = artifactRef(query.SELECT.from);
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
if(art.target)
|
|
108
|
+
art = artifactRef(art.target);
|
|
103
109
|
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
if(isSubquery && !query.SELECT.elements)
|
|
111
|
+
throw new ModelError('Expected subquery to have .elements');
|
|
106
112
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
112
|
-
return getSources(query.SELECT.from, true);
|
|
113
|
-
}
|
|
113
|
+
return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
|
|
114
|
+
query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
|
|
115
|
+
query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
|
|
114
116
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let elements = Object.create(null);
|
|
118
|
-
for (const arg of args) {
|
|
119
|
-
if (arg.args) {
|
|
120
|
-
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
121
|
-
}
|
|
122
|
-
else if (arg.ref) {
|
|
123
|
-
const art = artifactRef(arg);
|
|
124
|
-
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
125
|
-
}
|
|
126
|
-
else if (arg.SELECT || arg.SET) {
|
|
127
|
-
elements = mergeElementMaps(elements, getSources(arg));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return elements;
|
|
117
|
+
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
118
|
+
return getSources(query.SELECT.from, true);
|
|
132
119
|
}
|
|
120
|
+
}
|
|
133
121
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Merge two maps of elements together
|
|
138
|
-
*
|
|
139
|
-
* @param {object} mapA Map a - will be returned
|
|
140
|
-
* @param {object} mapB Map b - will not be returned
|
|
141
|
-
* @returns {object} mapA
|
|
142
|
-
*/
|
|
143
|
-
function mergeElementMaps(mapA, mapB) {
|
|
144
|
-
for (const elementName in mapB) {
|
|
145
|
-
if (!mapA[elementName])
|
|
146
|
-
mapA[elementName] = [];
|
|
147
|
-
|
|
148
|
-
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
149
|
-
}
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
150
124
|
|
|
151
|
-
|
|
125
|
+
function walkArgs(args) {
|
|
126
|
+
let elements = Object.create(null);
|
|
127
|
+
for (const arg of args) {
|
|
128
|
+
if (arg.args) {
|
|
129
|
+
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
130
|
+
}
|
|
131
|
+
else if (arg.ref) {
|
|
132
|
+
const art = artifactRef(arg);
|
|
133
|
+
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
134
|
+
}
|
|
135
|
+
else if (arg.SELECT || arg.SET) {
|
|
136
|
+
elements = mergeElementMaps(elements, getSources(arg));
|
|
152
137
|
}
|
|
138
|
+
}
|
|
153
139
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
*
|
|
157
|
-
* @param {any} existingMap map to merge into - will be returned
|
|
158
|
-
* @param {object} elements elements to merge into the map
|
|
159
|
-
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
160
|
-
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
161
|
-
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
162
|
-
* @returns {object} existingMap
|
|
163
|
-
*/
|
|
164
|
-
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
165
|
-
for (const elementName in elements) {
|
|
166
|
-
const element = elements[elementName];
|
|
167
|
-
if (!existingMap[elementName])
|
|
168
|
-
existingMap[elementName] = [];
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
existingMap[elementName].push({
|
|
172
|
-
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
140
|
+
return elements;
|
|
141
|
+
}
|
|
175
142
|
|
|
176
|
-
|
|
177
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Merge two maps of elements together
|
|
145
|
+
*
|
|
146
|
+
* @param {object} mapA Map a - will be returned
|
|
147
|
+
* @param {object} mapB Map b - will not be returned
|
|
148
|
+
* @returns {object} mapA
|
|
149
|
+
*/
|
|
150
|
+
function mergeElementMaps(mapA, mapB) {
|
|
151
|
+
for (const elementName in mapB) {
|
|
152
|
+
if (!mapA[elementName])
|
|
153
|
+
mapA[elementName] = [];
|
|
154
|
+
|
|
155
|
+
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
178
156
|
}
|
|
179
157
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* @param {string|object} name Absolute name of the artifact
|
|
183
|
-
*/
|
|
184
|
-
function getBaseName(name) {
|
|
185
|
-
if (!name)
|
|
186
|
-
return name;
|
|
158
|
+
return mapA;
|
|
159
|
+
}
|
|
187
160
|
|
|
188
|
-
|
|
189
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Merge elements into an existing map
|
|
163
|
+
*
|
|
164
|
+
* @param {any} existingMap map to merge into - will be returned
|
|
165
|
+
* @param {object} elements elements to merge into the map
|
|
166
|
+
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
167
|
+
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
168
|
+
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
169
|
+
* @returns {object} existingMap
|
|
170
|
+
*/
|
|
171
|
+
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
172
|
+
for (const elementName in elements) {
|
|
173
|
+
const element = elements[elementName];
|
|
174
|
+
if (!existingMap[elementName])
|
|
175
|
+
existingMap[elementName] = [];
|
|
190
176
|
|
|
191
|
-
|
|
177
|
+
|
|
178
|
+
existingMap[elementName].push({
|
|
179
|
+
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
180
|
+
});
|
|
192
181
|
}
|
|
182
|
+
|
|
183
|
+
return existingMap;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Return the name part of the artifact name - no namespace etc.
|
|
188
|
+
* @param {string|object} name Absolute name of the artifact
|
|
189
|
+
*/
|
|
190
|
+
function getBaseName(name) {
|
|
191
|
+
if (!name)
|
|
192
|
+
return name;
|
|
193
|
+
|
|
194
|
+
if (name.id)
|
|
195
|
+
return name.id.substring( name.id.lastIndexOf('.')+1 );
|
|
196
|
+
|
|
197
|
+
return name.substring( name.lastIndexOf('.')+1 )
|
|
193
198
|
}
|
|
194
199
|
|
|
195
200
|
/**
|
|
@@ -216,11 +221,11 @@ function getUtils(model) {
|
|
|
216
221
|
|
|
217
222
|
/**
|
|
218
223
|
* Create an object to track visited objects identified by a unique string.
|
|
219
|
-
* @param {string} [
|
|
224
|
+
* @param {string} [initialId] Initial entry (optional)
|
|
220
225
|
*/
|
|
221
|
-
function createVisited(
|
|
226
|
+
function createVisited(initialId) {
|
|
222
227
|
let visited = Object.create(null);
|
|
223
|
-
check(
|
|
228
|
+
check(initialId);
|
|
224
229
|
return { check };
|
|
225
230
|
|
|
226
231
|
/**
|
|
@@ -231,7 +236,7 @@ function getUtils(model) {
|
|
|
231
236
|
function check(id) {
|
|
232
237
|
if (!id) return;
|
|
233
238
|
if (visited[id]) {
|
|
234
|
-
throw new
|
|
239
|
+
throw new ModelError('Circular dependency');
|
|
235
240
|
}
|
|
236
241
|
visited[id] = true;
|
|
237
242
|
}
|
|
@@ -245,7 +250,7 @@ function getUtils(model) {
|
|
|
245
250
|
if (model.definitions[defName])
|
|
246
251
|
return model.definitions[defName]
|
|
247
252
|
else
|
|
248
|
-
throw new
|
|
253
|
+
throw new ModelError(`Nonexistent definition in the model: '${defName}'`);
|
|
249
254
|
}
|
|
250
255
|
|
|
251
256
|
/**
|
|
@@ -298,7 +303,7 @@ function getUtils(model) {
|
|
|
298
303
|
|
|
299
304
|
// Return true if 'node' is a managed association element
|
|
300
305
|
// TODO: what about elements having a type, which (finally) is an assoc?
|
|
301
|
-
function
|
|
306
|
+
function isManagedAssociation(node) {
|
|
302
307
|
return node.target !== undefined && node.on === undefined && node.keys;
|
|
303
308
|
}
|
|
304
309
|
|
|
@@ -536,7 +541,7 @@ function getUtils(model) {
|
|
|
536
541
|
if (cycleCheck) {
|
|
537
542
|
let visited = path.length? type + ':' + path.join('.') : type;
|
|
538
543
|
if (cycleCheck[visited])
|
|
539
|
-
throw new
|
|
544
|
+
throw new ModelError('Circular type chain on type ' + type);
|
|
540
545
|
else
|
|
541
546
|
cycleCheck[visited] = true;
|
|
542
547
|
}
|
|
@@ -649,7 +654,7 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
649
654
|
}
|
|
650
655
|
|
|
651
656
|
path = [...path]; // Copy
|
|
652
|
-
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', '
|
|
657
|
+
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
|
|
653
658
|
propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
|
|
654
659
|
}
|
|
655
660
|
|
|
@@ -706,45 +711,6 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
|
|
|
706
711
|
}
|
|
707
712
|
}
|
|
708
713
|
|
|
709
|
-
/**
|
|
710
|
-
* For each property named 'ref' in 'node' (recursively), call callback(ref, node, path)
|
|
711
|
-
*
|
|
712
|
-
* @param {object} node
|
|
713
|
-
* @param {refCallback|refCallback[]} callback
|
|
714
|
-
* @param {CSN.Path} path
|
|
715
|
-
*/
|
|
716
|
-
function forEachRef(node, callback, path = []) {
|
|
717
|
-
if (node === null || typeof node !== 'object') {
|
|
718
|
-
// Primitive node
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
if(node._ignore){
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if(Array.isArray(node)){
|
|
727
|
-
for (let i = 0; i < node.length; i++) {
|
|
728
|
-
// Descend recursively
|
|
729
|
-
forEachRef(node[i], callback, path.concat([i]));
|
|
730
|
-
}
|
|
731
|
-
} else {
|
|
732
|
-
for (let name in node) {
|
|
733
|
-
if (!Object.hasOwnProperty.call( node, name ))
|
|
734
|
-
continue;
|
|
735
|
-
// If ref found within a non-dictionary, call callback
|
|
736
|
-
if (name === 'ref' && Object.getPrototypeOf(node)) {
|
|
737
|
-
if(Array.isArray(callback))
|
|
738
|
-
callback.forEach(cb => cb( node.ref, node, path ));
|
|
739
|
-
else
|
|
740
|
-
callback( node.ref, node, path );
|
|
741
|
-
}
|
|
742
|
-
// Descend recursively
|
|
743
|
-
forEachRef(node[name], callback, path.concat([name]));
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
|
|
748
714
|
// Like Object.assign() but copies also non enumerable properties
|
|
749
715
|
function assignAll(target, ...sources) {
|
|
750
716
|
sources.forEach(source => {
|
|
@@ -830,39 +796,6 @@ function forAllQueries(query, callback, path = []){
|
|
|
830
796
|
}
|
|
831
797
|
}
|
|
832
798
|
|
|
833
|
-
function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
834
|
-
if(artifact.elements) {
|
|
835
|
-
cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
if(includeActions && artifact.actions) {
|
|
839
|
-
Object.entries(artifact.actions).forEach( ([actionName, action]) => {
|
|
840
|
-
const path = ['definitions', artifactName, 'actions', actionName];
|
|
841
|
-
if(action.params) {
|
|
842
|
-
Object.entries(action.params).forEach( ([paramName, param]) => {
|
|
843
|
-
if(param.elements)
|
|
844
|
-
cb(param, param.elements, path.concat(['params', paramName, 'elements']));
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
if(action.returns && action.returns.elements)
|
|
848
|
-
cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
if(artifact.query) {
|
|
853
|
-
forAllQueries(artifact.query, (q, p) => {
|
|
854
|
-
const s = q.SELECT;
|
|
855
|
-
if(s) {
|
|
856
|
-
if(s.elements) {
|
|
857
|
-
cb(s, s.elements, [...p, 'elements']);
|
|
858
|
-
} else if(s.$elements) { // huh?, is just refloc output
|
|
859
|
-
cb(s, s.$elements, [...p, '$elements']);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
}, ['definitions', artifactName, 'query'])
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
|
|
866
799
|
/**
|
|
867
800
|
* Compare a given annotation value with an expectation value and return
|
|
868
801
|
*
|
|
@@ -925,48 +858,48 @@ function isEdmPropertyRendered(elementCsn, options) {
|
|
|
925
858
|
|
|
926
859
|
/**
|
|
927
860
|
* Return the resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
928
|
-
*
|
|
861
|
+
* mode.
|
|
929
862
|
*
|
|
930
|
-
* - For the 'hdbcds' naming
|
|
863
|
+
* - For the 'hdbcds' naming mode, this means converting '.' to '::' on
|
|
931
864
|
* the border between namespace and top-level artifact and correctly replacing some '.' with '_'.
|
|
932
|
-
* - For the 'plain' naming
|
|
933
|
-
* - For the 'quoted' naming
|
|
865
|
+
* - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
|
|
866
|
+
* - For the 'quoted' naming mode, this means correctly replacing some '.' with '_'.
|
|
934
867
|
*
|
|
935
868
|
* If the old function signature is used - with a namespace as the third argument - the result might be wrong,
|
|
936
869
|
* since the '.' -> '_' conversion for quoted/hdbcds is missing.
|
|
937
870
|
*
|
|
938
|
-
* @param {string} artifactName The name of the artifact
|
|
939
|
-
* @param {('plain'|'quoted'|'hdbcds')}
|
|
871
|
+
* @param {string} artifactName The fully qualified name of the artifact
|
|
872
|
+
* @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
|
|
940
873
|
* @param {CSN.Model|string|undefined} csn
|
|
941
|
-
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
874
|
+
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
|
|
942
875
|
*/
|
|
943
|
-
function getArtifactDatabaseNameOf(artifactName,
|
|
876
|
+
function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
|
|
944
877
|
if(csn && typeof csn === 'object' && csn.definitions)
|
|
945
|
-
if (
|
|
946
|
-
return getResultingName(csn,
|
|
878
|
+
if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
|
|
879
|
+
return getResultingName(csn, sqlMapping, artifactName);
|
|
947
880
|
}
|
|
948
|
-
else if (
|
|
881
|
+
else if (sqlMapping === 'plain') {
|
|
949
882
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
950
883
|
} else {
|
|
951
|
-
throw new Error('Unknown naming
|
|
884
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
952
885
|
}
|
|
953
886
|
else {
|
|
954
887
|
console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
|
|
955
|
-
if (
|
|
888
|
+
if (sqlMapping === 'hdbcds') {
|
|
956
889
|
if (csn) {
|
|
957
890
|
const namespace = String(csn);
|
|
958
891
|
return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
|
|
959
892
|
}
|
|
960
893
|
return artifactName;
|
|
961
894
|
}
|
|
962
|
-
else if (
|
|
895
|
+
else if (sqlMapping === 'plain') {
|
|
963
896
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
964
897
|
}
|
|
965
|
-
else if (
|
|
898
|
+
else if (sqlMapping === 'quoted') {
|
|
966
899
|
return artifactName;
|
|
967
900
|
}
|
|
968
901
|
else {
|
|
969
|
-
throw new Error('Unknown naming
|
|
902
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
970
903
|
}
|
|
971
904
|
}
|
|
972
905
|
}
|
|
@@ -1052,29 +985,29 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1052
985
|
|
|
1053
986
|
|
|
1054
987
|
/**
|
|
1055
|
-
* Return the resulting database element name for 'elemName', depending on the current
|
|
1056
|
-
*
|
|
1057
|
-
* - For the 'hdbcds' naming
|
|
1058
|
-
* - For the 'plain' naming
|
|
1059
|
-
* - For the 'quoted' naming
|
|
1060
|
-
* No other naming
|
|
988
|
+
* Return the resulting database element name for 'elemName', depending on the current
|
|
989
|
+
* naming mode.
|
|
990
|
+
* - For the 'hdbcds' naming mode, this is just 'elemName'.
|
|
991
|
+
* - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
|
|
992
|
+
* - For the 'quoted' naming mode, it means converting all '.' to '_'.
|
|
993
|
+
* No other naming modes are accepted!
|
|
1061
994
|
*
|
|
1062
|
-
* @param {string} elemName
|
|
1063
|
-
* @param {('plain'|'quoted'|'hdbcds')}
|
|
1064
|
-
* @returns {string} The resulting database element name for 'elemName', depending on the current naming
|
|
995
|
+
* @param {string} elemName The name of the element
|
|
996
|
+
* @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
|
|
997
|
+
* @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
|
|
1065
998
|
*/
|
|
1066
|
-
function getElementDatabaseNameOf(elemName,
|
|
1067
|
-
if (
|
|
999
|
+
function getElementDatabaseNameOf(elemName, sqlMapping) {
|
|
1000
|
+
if (sqlMapping === 'hdbcds') {
|
|
1068
1001
|
return elemName;
|
|
1069
1002
|
}
|
|
1070
|
-
else if (
|
|
1003
|
+
else if (sqlMapping === 'plain') {
|
|
1071
1004
|
return elemName.replace(/\./g, '_').toUpperCase();
|
|
1072
1005
|
}
|
|
1073
|
-
else if (
|
|
1006
|
+
else if (sqlMapping === 'quoted') {
|
|
1074
1007
|
return elemName.replace(/\./g, '_');
|
|
1075
1008
|
}
|
|
1076
1009
|
else {
|
|
1077
|
-
throw new Error('Unknown naming
|
|
1010
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
1078
1011
|
}
|
|
1079
1012
|
}
|
|
1080
1013
|
|
|
@@ -1152,7 +1085,7 @@ function setDependencies( csn ) {
|
|
|
1152
1085
|
* @returns {boolean}
|
|
1153
1086
|
*/
|
|
1154
1087
|
function isPersistedOnDatabase(art) {
|
|
1155
|
-
return !(
|
|
1088
|
+
return !('entity' === art.kind && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
|
|
1156
1089
|
}
|
|
1157
1090
|
|
|
1158
1091
|
/**
|
|
@@ -1220,10 +1153,10 @@ function mergeOptions(...optionsObjects) {
|
|
|
1220
1153
|
}
|
|
1221
1154
|
// Check against improper overwriting
|
|
1222
1155
|
if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
|
|
1223
|
-
throw new
|
|
1156
|
+
throw new ModelError(`Cannot overwrite structured option "${name}" with array or scalar value`);
|
|
1224
1157
|
}
|
|
1225
1158
|
if ((isScalar(left) && typeof left !== 'boolean' || Array.isArray(left)) && isObject(right) && !Array.isArray(right)) {
|
|
1226
|
-
throw new
|
|
1159
|
+
throw new ModelError(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
|
|
1227
1160
|
}
|
|
1228
1161
|
|
|
1229
1162
|
// Copy or overwrite properties from right to left
|
|
@@ -1378,7 +1311,7 @@ function forEachPath(node, callback) {
|
|
|
1378
1311
|
* @returns {boolean}
|
|
1379
1312
|
*/
|
|
1380
1313
|
function hasValidSkipOrExists(artifact) {
|
|
1381
|
-
return
|
|
1314
|
+
return artifact.kind === 'entity' &&
|
|
1382
1315
|
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
|
|
1383
1316
|
|
|
1384
1317
|
}
|
|
@@ -1447,7 +1380,7 @@ function getServiceNames(csn) {
|
|
|
1447
1380
|
}
|
|
1448
1381
|
|
|
1449
1382
|
/**
|
|
1450
|
-
* Check
|
|
1383
|
+
* Check whether the artifact is @cds.persistence.skip
|
|
1451
1384
|
*
|
|
1452
1385
|
* @param {CSN.Artifact} artifact
|
|
1453
1386
|
* @returns {Boolean}
|
|
@@ -1501,6 +1434,42 @@ function getVariableReplacement(ref, options) {
|
|
|
1501
1434
|
}
|
|
1502
1435
|
}
|
|
1503
1436
|
|
|
1437
|
+
/**
|
|
1438
|
+
*
|
|
1439
|
+
* @param {object} obj
|
|
1440
|
+
* @param {*} other
|
|
1441
|
+
* @param {boolean} noExtendedProps
|
|
1442
|
+
* @returns {boolean} returns equality
|
|
1443
|
+
*
|
|
1444
|
+
* noExtendedProps remove '$', '_' and '@' properties from
|
|
1445
|
+
* the comparision. This eliminates false negatives such as
|
|
1446
|
+
* mismatching $locations or @odata.foreignKey4.
|
|
1447
|
+
*/
|
|
1448
|
+
function isDeepEqual(obj, other, noExtendedProps) {
|
|
1449
|
+
let objectKeys = Object.keys(obj);
|
|
1450
|
+
let otherKeys = Object.keys(other);
|
|
1451
|
+
|
|
1452
|
+
if(noExtendedProps) {
|
|
1453
|
+
objectKeys = objectKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1454
|
+
otherKeys = otherKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1455
|
+
}
|
|
1456
|
+
if (objectKeys.length !== otherKeys.length)
|
|
1457
|
+
return false;
|
|
1458
|
+
|
|
1459
|
+
for (let key of objectKeys) {
|
|
1460
|
+
const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
|
|
1461
|
+
&& (other[key] !== null && typeof other[key] === 'object');
|
|
1462
|
+
|
|
1463
|
+
if (areValuesObjects) {
|
|
1464
|
+
if (!isDeepEqual(obj[key], other[key], noExtendedProps))
|
|
1465
|
+
return false;
|
|
1466
|
+
} else if (obj[key] !== other[key]) {
|
|
1467
|
+
return false;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return true;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1504
1473
|
module.exports = {
|
|
1505
1474
|
getUtils,
|
|
1506
1475
|
cloneCsn,
|
|
@@ -1511,9 +1480,7 @@ module.exports = {
|
|
|
1511
1480
|
forEachDefinition,
|
|
1512
1481
|
forEachMember,
|
|
1513
1482
|
forEachMemberRecursively,
|
|
1514
|
-
forEachRef,
|
|
1515
1483
|
forAllQueries,
|
|
1516
|
-
forAllElements,
|
|
1517
1484
|
hasAnnotationValue,
|
|
1518
1485
|
isEdmPropertyRendered,
|
|
1519
1486
|
getArtifactDatabaseNameOf,
|
|
@@ -1541,5 +1508,7 @@ module.exports = {
|
|
|
1541
1508
|
getServiceNames,
|
|
1542
1509
|
isSkipped,
|
|
1543
1510
|
walkCsnPath,
|
|
1544
|
-
getVariableReplacement
|
|
1511
|
+
getVariableReplacement,
|
|
1512
|
+
implicitAs,
|
|
1513
|
+
isDeepEqual,
|
|
1545
1514
|
};
|