@sap/cds-compiler 2.11.4 → 2.13.8
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 +159 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +22 -23
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +30 -63
- package/lib/api/options.js +5 -5
- 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 +52 -2
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +10 -6
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +46 -12
- 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 +33 -14
- 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 +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +76 -38
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +204 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +143 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/genericAntlrParser.js +144 -54
- package/lib/language/language.g4 +424 -203
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +472 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +321 -204
- package/lib/model/csnUtils.js +224 -263
- package/lib/model/enrichCsn.js +97 -40
- 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 +7 -6
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +201 -115
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +149 -75
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- 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/braceExpression.js +4 -2
- 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 +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +552 -105
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +94 -28
- 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} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +94 -801
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- 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 +47 -33
- package/lib/transform/translateAssocsToJoins.js +10 -27
- 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/file.js +2 -1
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2340
- package/lib/compiler/resolver.js +0 -2988
- package/lib/transform/universalCsnEnricher.js +0 -67
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,
|
|
@@ -61,6 +62,11 @@ function getUtils(model) {
|
|
|
61
62
|
effectiveType,
|
|
62
63
|
get$combined,
|
|
63
64
|
getOrigin,
|
|
65
|
+
getQueryPrimarySource,
|
|
66
|
+
targetAspect,
|
|
67
|
+
getColumn,
|
|
68
|
+
getElement,
|
|
69
|
+
initDefinition
|
|
64
70
|
};
|
|
65
71
|
|
|
66
72
|
/**
|
|
@@ -70,135 +76,156 @@ function getUtils(model) {
|
|
|
70
76
|
* @returns {object}
|
|
71
77
|
*/
|
|
72
78
|
function get$combined(query) {
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
return getSources(query);
|
|
80
|
+
}
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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);
|
|
90
97
|
|
|
91
|
-
|
|
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);
|
|
92
103
|
}
|
|
93
|
-
else if (query.SELECT) {
|
|
94
|
-
|
|
95
|
-
return walkArgs(query.SELECT.from.args);
|
|
96
|
-
}
|
|
97
|
-
else if (query.SELECT.from.ref) {
|
|
98
|
-
let art = artifactRef(query.SELECT.from);
|
|
104
|
+
else if (query.SELECT.from.ref) {
|
|
105
|
+
let art = artifactRef(query.SELECT.from);
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
if(art.target)
|
|
108
|
+
art = artifactRef(art.target);
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
if(isSubquery && !query.SELECT.elements)
|
|
111
|
+
throw new ModelError('Expected subquery to have .elements');
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
111
|
-
return getSources(query.SELECT.from, true);
|
|
112
|
-
}
|
|
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 );
|
|
113
116
|
}
|
|
117
|
+
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
118
|
+
return getSources(query.SELECT.from, true);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
114
121
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for (const arg of args) {
|
|
118
|
-
if (arg.args) {
|
|
119
|
-
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
120
|
-
}
|
|
121
|
-
else if (arg.ref) {
|
|
122
|
-
const art = artifactRef(arg);
|
|
123
|
-
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
124
|
-
}
|
|
125
|
-
else if (arg.SELECT || arg.SET) {
|
|
126
|
-
elements = mergeElementMaps(elements, getSources(arg));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
129
124
|
|
|
130
|
-
|
|
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));
|
|
131
137
|
}
|
|
138
|
+
}
|
|
132
139
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Merge two maps of elements together
|
|
137
|
-
*
|
|
138
|
-
* @param {object} mapA Map a - will be returned
|
|
139
|
-
* @param {object} mapB Map b - will not be returned
|
|
140
|
-
* @returns {object} mapA
|
|
141
|
-
*/
|
|
142
|
-
function mergeElementMaps(mapA, mapB) {
|
|
143
|
-
for (const elementName in mapB) {
|
|
144
|
-
if (!mapA[elementName])
|
|
145
|
-
mapA[elementName] = [];
|
|
146
|
-
|
|
147
|
-
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
148
|
-
}
|
|
140
|
+
return elements;
|
|
141
|
+
}
|
|
149
142
|
|
|
150
|
-
|
|
151
|
-
|
|
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] = [];
|
|
152
154
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
*
|
|
156
|
-
* @param {any} existingMap map to merge into - will be returned
|
|
157
|
-
* @param {object} elements elements to merge into the map
|
|
158
|
-
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
159
|
-
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
160
|
-
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
161
|
-
* @returns {object} existingMap
|
|
162
|
-
*/
|
|
163
|
-
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
164
|
-
for (const elementName in elements) {
|
|
165
|
-
const element = elements[elementName];
|
|
166
|
-
if (!existingMap[elementName])
|
|
167
|
-
existingMap[elementName] = [];
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
existingMap[elementName].push({
|
|
171
|
-
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
155
|
+
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
156
|
+
}
|
|
174
157
|
|
|
175
|
-
|
|
176
|
-
|
|
158
|
+
return mapA;
|
|
159
|
+
}
|
|
160
|
+
|
|
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] = [];
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
existingMap[elementName].push({
|
|
179
|
+
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
180
|
+
});
|
|
177
181
|
}
|
|
178
182
|
|
|
179
|
-
|
|
183
|
+
return existingMap;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
180
187
|
* Return the name part of the artifact name - no namespace etc.
|
|
181
188
|
* @param {string|object} name Absolute name of the artifact
|
|
182
189
|
*/
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
function getBaseName(name) {
|
|
191
|
+
if (!name)
|
|
192
|
+
return name;
|
|
193
|
+
|
|
194
|
+
if (name.id)
|
|
195
|
+
return name.id.substring( name.id.lastIndexOf('.')+1 );
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
return name.substring( name.lastIndexOf('.')+1 )
|
|
198
|
+
}
|
|
189
199
|
|
|
190
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Return the left-most, primary source of the given query.
|
|
202
|
+
* @param {*} query Definition's query object
|
|
203
|
+
*/
|
|
204
|
+
function getQueryPrimarySource(query) {
|
|
205
|
+
if (!query)
|
|
206
|
+
return undefined;
|
|
207
|
+
else if (query.SELECT) {
|
|
208
|
+
return getQueryPrimarySource(query.SELECT);
|
|
209
|
+
} else if (query.SET) {
|
|
210
|
+
return getQueryPrimarySource(query.SET);
|
|
211
|
+
} else if (query.from) {
|
|
212
|
+
return getQueryPrimarySource(query.from);
|
|
213
|
+
} else if (query.ref) {
|
|
214
|
+
return query;
|
|
215
|
+
} else if (query.args) {
|
|
216
|
+
return getQueryPrimarySource(query.args[0]);
|
|
191
217
|
}
|
|
218
|
+
return undefined;
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
|
|
195
222
|
/**
|
|
196
223
|
* Create an object to track visited objects identified by a unique string.
|
|
197
|
-
* @param {string} [
|
|
224
|
+
* @param {string} [initialId] Initial entry (optional)
|
|
198
225
|
*/
|
|
199
|
-
function createVisited(
|
|
226
|
+
function createVisited(initialId) {
|
|
200
227
|
let visited = Object.create(null);
|
|
201
|
-
check(
|
|
228
|
+
check(initialId);
|
|
202
229
|
return { check };
|
|
203
230
|
|
|
204
231
|
/**
|
|
@@ -209,7 +236,7 @@ function getUtils(model) {
|
|
|
209
236
|
function check(id) {
|
|
210
237
|
if (!id) return;
|
|
211
238
|
if (visited[id]) {
|
|
212
|
-
throw new
|
|
239
|
+
throw new ModelError('Circular dependency');
|
|
213
240
|
}
|
|
214
241
|
visited[id] = true;
|
|
215
242
|
}
|
|
@@ -223,14 +250,14 @@ function getUtils(model) {
|
|
|
223
250
|
if (model.definitions[defName])
|
|
224
251
|
return model.definitions[defName]
|
|
225
252
|
else
|
|
226
|
-
throw new
|
|
253
|
+
throw new ModelError(`Nonexistent definition in the model: '${defName}'`);
|
|
227
254
|
}
|
|
228
255
|
|
|
229
256
|
/**
|
|
230
257
|
* Returns true if an artifact is a structured type
|
|
231
258
|
* or a typedef of a structured type.
|
|
232
259
|
*
|
|
233
|
-
* @param {
|
|
260
|
+
* @param {object} obj
|
|
234
261
|
*/
|
|
235
262
|
function isStructured(obj) {
|
|
236
263
|
return obj.elements ||
|
|
@@ -276,7 +303,7 @@ function getUtils(model) {
|
|
|
276
303
|
|
|
277
304
|
// Return true if 'node' is a managed association element
|
|
278
305
|
// TODO: what about elements having a type, which (finally) is an assoc?
|
|
279
|
-
function
|
|
306
|
+
function isManagedAssociation(node) {
|
|
280
307
|
return node.target !== undefined && node.on === undefined && node.keys;
|
|
281
308
|
}
|
|
282
309
|
|
|
@@ -397,7 +424,7 @@ function getUtils(model) {
|
|
|
397
424
|
function getServiceName(artifactName) {
|
|
398
425
|
for(;;) {
|
|
399
426
|
let idx = artifactName.lastIndexOf('.');
|
|
400
|
-
if (idx
|
|
427
|
+
if (idx === -1) return null;
|
|
401
428
|
artifactName = artifactName.substring(0, idx);
|
|
402
429
|
let artifact = model.definitions[artifactName];
|
|
403
430
|
if (artifact && artifact.kind === 'service') {
|
|
@@ -514,7 +541,7 @@ function getUtils(model) {
|
|
|
514
541
|
if (cycleCheck) {
|
|
515
542
|
let visited = path.length? type + ':' + path.join('.') : type;
|
|
516
543
|
if (cycleCheck[visited])
|
|
517
|
-
throw new
|
|
544
|
+
throw new ModelError('Circular type chain on type ' + type);
|
|
518
545
|
else
|
|
519
546
|
cycleCheck[visited] = true;
|
|
520
547
|
}
|
|
@@ -627,7 +654,7 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
627
654
|
}
|
|
628
655
|
|
|
629
656
|
path = [...path]; // Copy
|
|
630
|
-
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', '
|
|
657
|
+
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
|
|
631
658
|
propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
|
|
632
659
|
}
|
|
633
660
|
|
|
@@ -684,45 +711,6 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
|
|
|
684
711
|
}
|
|
685
712
|
}
|
|
686
713
|
|
|
687
|
-
/**
|
|
688
|
-
* For each property named 'ref' in 'node' (recursively), call callback(ref, node, path)
|
|
689
|
-
*
|
|
690
|
-
* @param {object} node
|
|
691
|
-
* @param {refCallback|refCallback[]} callback
|
|
692
|
-
* @param {CSN.Path} path
|
|
693
|
-
*/
|
|
694
|
-
function forEachRef(node, callback, path = []) {
|
|
695
|
-
if (node === null || typeof node !== 'object') {
|
|
696
|
-
// Primitive node
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if(node._ignore){
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
if(Array.isArray(node)){
|
|
705
|
-
for (let i = 0; i < node.length; i++) {
|
|
706
|
-
// Descend recursively
|
|
707
|
-
forEachRef(node[i], callback, path.concat([i]));
|
|
708
|
-
}
|
|
709
|
-
} else {
|
|
710
|
-
for (let name in node) {
|
|
711
|
-
if (!Object.hasOwnProperty.call( node, name ))
|
|
712
|
-
continue;
|
|
713
|
-
// If ref found within a non-dictionary, call callback
|
|
714
|
-
if (name === 'ref' && Object.getPrototypeOf(node)) {
|
|
715
|
-
if(Array.isArray(callback))
|
|
716
|
-
callback.forEach(cb => cb( node.ref, node, path ));
|
|
717
|
-
else
|
|
718
|
-
callback( node.ref, node, path );
|
|
719
|
-
}
|
|
720
|
-
// Descend recursively
|
|
721
|
-
forEachRef(node[name], callback, path.concat([name]));
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
714
|
// Like Object.assign() but copies also non enumerable properties
|
|
727
715
|
function assignAll(target, ...sources) {
|
|
728
716
|
sources.forEach(source => {
|
|
@@ -808,39 +796,6 @@ function forAllQueries(query, callback, path = []){
|
|
|
808
796
|
}
|
|
809
797
|
}
|
|
810
798
|
|
|
811
|
-
function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
812
|
-
if(artifact.elements) {
|
|
813
|
-
cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
if(includeActions && artifact.actions) {
|
|
817
|
-
Object.entries(artifact.actions).forEach( ([actionName, action]) => {
|
|
818
|
-
const path = ['definitions', artifactName, 'actions', actionName];
|
|
819
|
-
if(action.params) {
|
|
820
|
-
Object.entries(action.params).forEach( ([paramName, param]) => {
|
|
821
|
-
if(param.elements)
|
|
822
|
-
cb(param, param.elements, path.concat(['params', paramName, 'elements']));
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
if(action.returns && action.returns.elements)
|
|
826
|
-
cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
if(artifact.query) {
|
|
831
|
-
forAllQueries(artifact.query, (q, p) => {
|
|
832
|
-
const s = q.SELECT;
|
|
833
|
-
if(s) {
|
|
834
|
-
if(s.elements) {
|
|
835
|
-
cb(s, s.elements, [...p, 'elements']);
|
|
836
|
-
} else if(s.$elements) { // huh?, is just refloc output
|
|
837
|
-
cb(s, s.$elements, [...p, '$elements']);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}, ['definitions', artifactName, 'query'])
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
799
|
/**
|
|
845
800
|
* Compare a given annotation value with an expectation value and return
|
|
846
801
|
*
|
|
@@ -859,11 +814,14 @@ function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
|
859
814
|
* @param {CSN.Artifact} artifact
|
|
860
815
|
* @param {string} annotationName Name of the annotation (including the at-sign)
|
|
861
816
|
* @param {any} expected
|
|
817
|
+
* @param {boolean} caseInsensitive
|
|
862
818
|
* @returns {boolean}
|
|
863
819
|
*/
|
|
864
|
-
function hasAnnotationValue(artifact, annotationName, expected = true) {
|
|
820
|
+
function hasAnnotationValue(artifact, annotationName, expected = true, caseInsensitive = false) {
|
|
865
821
|
if(expected === false)
|
|
866
822
|
return artifact[annotationName] === expected || artifact[annotationName] === null;
|
|
823
|
+
else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
|
|
824
|
+
return artifact[annotationName].toLowerCase() === expected.toLowerCase();
|
|
867
825
|
else
|
|
868
826
|
return artifact[annotationName] === expected;
|
|
869
827
|
}
|
|
@@ -900,48 +858,48 @@ function isEdmPropertyRendered(elementCsn, options) {
|
|
|
900
858
|
|
|
901
859
|
/**
|
|
902
860
|
* Return the resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
903
|
-
*
|
|
861
|
+
* mode.
|
|
904
862
|
*
|
|
905
|
-
* - For the 'hdbcds' naming
|
|
863
|
+
* - For the 'hdbcds' naming mode, this means converting '.' to '::' on
|
|
906
864
|
* the border between namespace and top-level artifact and correctly replacing some '.' with '_'.
|
|
907
|
-
* - For the 'plain' naming
|
|
908
|
-
* - 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 '_'.
|
|
909
867
|
*
|
|
910
868
|
* If the old function signature is used - with a namespace as the third argument - the result might be wrong,
|
|
911
869
|
* since the '.' -> '_' conversion for quoted/hdbcds is missing.
|
|
912
870
|
*
|
|
913
|
-
* @param {string} artifactName The name of the artifact
|
|
914
|
-
* @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
|
|
915
873
|
* @param {CSN.Model|string|undefined} csn
|
|
916
|
-
* @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.
|
|
917
875
|
*/
|
|
918
|
-
function getArtifactDatabaseNameOf(artifactName,
|
|
876
|
+
function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
|
|
919
877
|
if(csn && typeof csn === 'object' && csn.definitions)
|
|
920
|
-
if (
|
|
921
|
-
return getResultingName(csn,
|
|
878
|
+
if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
|
|
879
|
+
return getResultingName(csn, sqlMapping, artifactName);
|
|
922
880
|
}
|
|
923
|
-
else if (
|
|
881
|
+
else if (sqlMapping === 'plain') {
|
|
924
882
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
925
883
|
} else {
|
|
926
|
-
throw new Error('Unknown naming
|
|
884
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
927
885
|
}
|
|
928
886
|
else {
|
|
929
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.`);
|
|
930
|
-
if (
|
|
888
|
+
if (sqlMapping === 'hdbcds') {
|
|
931
889
|
if (csn) {
|
|
932
890
|
const namespace = String(csn);
|
|
933
891
|
return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
|
|
934
892
|
}
|
|
935
893
|
return artifactName;
|
|
936
894
|
}
|
|
937
|
-
else if (
|
|
895
|
+
else if (sqlMapping === 'plain') {
|
|
938
896
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
939
897
|
}
|
|
940
|
-
else if (
|
|
898
|
+
else if (sqlMapping === 'quoted') {
|
|
941
899
|
return artifactName;
|
|
942
900
|
}
|
|
943
901
|
else {
|
|
944
|
-
throw new Error('Unknown naming
|
|
902
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
945
903
|
}
|
|
946
904
|
}
|
|
947
905
|
}
|
|
@@ -1027,29 +985,29 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1027
985
|
|
|
1028
986
|
|
|
1029
987
|
/**
|
|
1030
|
-
* Return the resulting database element name for 'elemName', depending on the current
|
|
1031
|
-
*
|
|
1032
|
-
* - For the 'hdbcds' naming
|
|
1033
|
-
* - For the 'plain' naming
|
|
1034
|
-
* - For the 'quoted' naming
|
|
1035
|
-
* 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!
|
|
1036
994
|
*
|
|
1037
|
-
* @param {string} elemName
|
|
1038
|
-
* @param {('plain'|'quoted'|'hdbcds')}
|
|
1039
|
-
* @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.
|
|
1040
998
|
*/
|
|
1041
|
-
function getElementDatabaseNameOf(elemName,
|
|
1042
|
-
if (
|
|
999
|
+
function getElementDatabaseNameOf(elemName, sqlMapping) {
|
|
1000
|
+
if (sqlMapping === 'hdbcds') {
|
|
1043
1001
|
return elemName;
|
|
1044
1002
|
}
|
|
1045
|
-
else if (
|
|
1003
|
+
else if (sqlMapping === 'plain') {
|
|
1046
1004
|
return elemName.replace(/\./g, '_').toUpperCase();
|
|
1047
1005
|
}
|
|
1048
|
-
else if (
|
|
1006
|
+
else if (sqlMapping === 'quoted') {
|
|
1049
1007
|
return elemName.replace(/\./g, '_');
|
|
1050
1008
|
}
|
|
1051
1009
|
else {
|
|
1052
|
-
throw new Error('Unknown naming
|
|
1010
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
1053
1011
|
}
|
|
1054
1012
|
}
|
|
1055
1013
|
|
|
@@ -1127,7 +1085,7 @@ function setDependencies( csn ) {
|
|
|
1127
1085
|
* @returns {boolean}
|
|
1128
1086
|
*/
|
|
1129
1087
|
function isPersistedOnDatabase(art) {
|
|
1130
|
-
return !(
|
|
1088
|
+
return !('entity' === art.kind && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
|
|
1131
1089
|
}
|
|
1132
1090
|
|
|
1133
1091
|
/**
|
|
@@ -1195,10 +1153,10 @@ function mergeOptions(...optionsObjects) {
|
|
|
1195
1153
|
}
|
|
1196
1154
|
// Check against improper overwriting
|
|
1197
1155
|
if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
|
|
1198
|
-
throw new
|
|
1156
|
+
throw new ModelError(`Cannot overwrite structured option "${name}" with array or scalar value`);
|
|
1199
1157
|
}
|
|
1200
1158
|
if ((isScalar(left) && typeof left !== 'boolean' || Array.isArray(left)) && isObject(right) && !Array.isArray(right)) {
|
|
1201
|
-
throw new
|
|
1159
|
+
throw new ModelError(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
|
|
1202
1160
|
}
|
|
1203
1161
|
|
|
1204
1162
|
// Copy or overwrite properties from right to left
|
|
@@ -1232,38 +1190,6 @@ function mergeOptions(...optionsObjects) {
|
|
|
1232
1190
|
}
|
|
1233
1191
|
}
|
|
1234
1192
|
|
|
1235
|
-
// Return the name of the top-level artifact surrounding the artifact 'name'
|
|
1236
|
-
// in 'model'.
|
|
1237
|
-
// We define "top-level artifact" to be an artifact that has either no parent or only
|
|
1238
|
-
// ancestors of kind 'namespace'. Note that it is possible for a non-top-level artifact
|
|
1239
|
-
// to have a namespace as parent and e.g. a context as grandparent (weird but true).
|
|
1240
|
-
// Will return the artifact 'name' if it is a top-level artifact itself, and 'undefined'
|
|
1241
|
-
// if there is no artifact surrounding 'name' in the model
|
|
1242
|
-
// TODO: to be checked by author: still intended behaviour with 'cds' prefix?
|
|
1243
|
-
// TODO: Can this be replaced by getRootArtifactName? Or maybe not rely on namespace-hacking...
|
|
1244
|
-
// FIXME: This only works with namespace-hacking, i.e. adding them as artifacts...
|
|
1245
|
-
function getTopLevelArtifactNameOf(name, model) {
|
|
1246
|
-
let dotIdx = name.indexOf('.');
|
|
1247
|
-
if (dotIdx == -1) {
|
|
1248
|
-
// No '.' in the name, i.e. no parent - this is a top-level artifact (if it exists)
|
|
1249
|
-
return model.definitions[name] ? name : undefined;
|
|
1250
|
-
}
|
|
1251
|
-
// If the first name part is not in the model, there is nothing to find
|
|
1252
|
-
if (!model.definitions[name.substring(0, dotIdx)]) {
|
|
1253
|
-
return undefined;
|
|
1254
|
-
}
|
|
1255
|
-
// Skip forward through '.'s until finding a non-namespace
|
|
1256
|
-
while (dotIdx != -1 && (!model.definitions[name.substring(0, dotIdx)] || model.definitions[name.substring(0, dotIdx)].kind === 'namespace')) {
|
|
1257
|
-
dotIdx = name.indexOf('.', dotIdx + 1);
|
|
1258
|
-
}
|
|
1259
|
-
if (dotIdx == -1) {
|
|
1260
|
-
// This is a top-level artifact
|
|
1261
|
-
return name;
|
|
1262
|
-
}
|
|
1263
|
-
// The skipped part of 'name' is the top-level artifact name
|
|
1264
|
-
return name.substring(0, dotIdx);
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
1193
|
/**
|
|
1268
1194
|
* If the artifact with the name given is part of a context (or multiple), return the top-most context.
|
|
1269
1195
|
* Else, return the artifact itself. Namespaces are not of concern here.
|
|
@@ -1385,7 +1311,7 @@ function forEachPath(node, callback) {
|
|
|
1385
1311
|
* @returns {boolean}
|
|
1386
1312
|
*/
|
|
1387
1313
|
function hasValidSkipOrExists(artifact) {
|
|
1388
|
-
return
|
|
1314
|
+
return artifact.kind === 'entity' &&
|
|
1389
1315
|
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
|
|
1390
1316
|
|
|
1391
1317
|
}
|
|
@@ -1454,7 +1380,7 @@ function getServiceNames(csn) {
|
|
|
1454
1380
|
}
|
|
1455
1381
|
|
|
1456
1382
|
/**
|
|
1457
|
-
* Check
|
|
1383
|
+
* Check whether the artifact is @cds.persistence.skip
|
|
1458
1384
|
*
|
|
1459
1385
|
* @param {CSN.Artifact} artifact
|
|
1460
1386
|
* @returns {Boolean}
|
|
@@ -1473,8 +1399,8 @@ function isSkipped(artifact) {
|
|
|
1473
1399
|
function walkCsnPath(csn, path) {
|
|
1474
1400
|
/** @type {object} */
|
|
1475
1401
|
let obj = csn;
|
|
1476
|
-
for(
|
|
1477
|
-
obj = obj[
|
|
1402
|
+
for(const segment of path){
|
|
1403
|
+
obj = obj[segment];
|
|
1478
1404
|
}
|
|
1479
1405
|
|
|
1480
1406
|
return obj;
|
|
@@ -1483,16 +1409,16 @@ function walkCsnPath(csn, path) {
|
|
|
1483
1409
|
/**
|
|
1484
1410
|
* If provided, get the replacement string for the given magic variable ref.
|
|
1485
1411
|
* No validation is done that the ref is actually magic!
|
|
1486
|
-
*
|
|
1487
|
-
* @param {array} ref
|
|
1488
|
-
* @param {CSN.Options} options
|
|
1489
|
-
* @returns {string|null}
|
|
1412
|
+
*
|
|
1413
|
+
* @param {array} ref
|
|
1414
|
+
* @param {CSN.Options} options
|
|
1415
|
+
* @returns {string|null}
|
|
1490
1416
|
*/
|
|
1491
1417
|
function getVariableReplacement(ref, options) {
|
|
1492
1418
|
if(options && options.variableReplacements) {
|
|
1493
1419
|
let replacement = options.variableReplacements;
|
|
1494
|
-
for(
|
|
1495
|
-
replacement = replacement[
|
|
1420
|
+
for(const segment of ref) {
|
|
1421
|
+
replacement = replacement[segment];
|
|
1496
1422
|
if(replacement === undefined)
|
|
1497
1423
|
return null;
|
|
1498
1424
|
}
|
|
@@ -1508,6 +1434,42 @@ function getVariableReplacement(ref, options) {
|
|
|
1508
1434
|
}
|
|
1509
1435
|
}
|
|
1510
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
|
+
|
|
1511
1473
|
module.exports = {
|
|
1512
1474
|
getUtils,
|
|
1513
1475
|
cloneCsn,
|
|
@@ -1518,9 +1480,7 @@ module.exports = {
|
|
|
1518
1480
|
forEachDefinition,
|
|
1519
1481
|
forEachMember,
|
|
1520
1482
|
forEachMemberRecursively,
|
|
1521
|
-
forEachRef,
|
|
1522
1483
|
forAllQueries,
|
|
1523
|
-
forAllElements,
|
|
1524
1484
|
hasAnnotationValue,
|
|
1525
1485
|
isEdmPropertyRendered,
|
|
1526
1486
|
getArtifactDatabaseNameOf,
|
|
@@ -1534,7 +1494,6 @@ module.exports = {
|
|
|
1534
1494
|
generatedByCompilerVersion,
|
|
1535
1495
|
getNormalizedQuery,
|
|
1536
1496
|
mergeOptions,
|
|
1537
|
-
getTopLevelArtifactNameOf,
|
|
1538
1497
|
getRootArtifactName,
|
|
1539
1498
|
getLastPartOfRef,
|
|
1540
1499
|
getParentNamesOf,
|
|
@@ -1549,5 +1508,7 @@ module.exports = {
|
|
|
1549
1508
|
getServiceNames,
|
|
1550
1509
|
isSkipped,
|
|
1551
1510
|
walkCsnPath,
|
|
1552
|
-
getVariableReplacement
|
|
1511
|
+
getVariableReplacement,
|
|
1512
|
+
implicitAs,
|
|
1513
|
+
isDeepEqual,
|
|
1553
1514
|
};
|