@sap/cds-compiler 4.0.2 → 4.1.2
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 +100 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_BETA.md +11 -0
- package/lib/api/main.js +31 -11
- package/lib/api/validate.js +1 -1
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +84 -38
- package/lib/base/messages.js +11 -10
- package/lib/base/model.js +6 -2
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/onConditions.js +17 -12
- package/lib/checks/queryNoDbArtifacts.js +132 -72
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +1 -1
- package/lib/compiler/assert-consistency.js +44 -16
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +7 -8
- package/lib/compiler/checks.js +274 -197
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +3 -3
- package/lib/compiler/define.js +63 -50
- package/lib/compiler/extend.js +38 -20
- package/lib/compiler/finalize-parse-cdl.js +2 -1
- package/lib/compiler/generate.js +0 -8
- package/lib/compiler/index.js +9 -7
- package/lib/compiler/kick-start.js +2 -0
- package/lib/compiler/populate.js +139 -110
- package/lib/compiler/propagator.js +4 -3
- package/lib/compiler/resolve.js +157 -126
- package/lib/compiler/shared.js +706 -404
- package/lib/compiler/tweak-assocs.js +21 -10
- package/lib/compiler/utils.js +228 -36
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/edm.js +4 -1
- package/lib/edm/edmPreprocessor.js +5 -4
- package/lib/edm/edmUtils.js +2 -4
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3987 -3963
- package/lib/json/from-csn.js +43 -47
- package/lib/json/to-csn.js +11 -11
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +59 -59
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +5 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +20 -16
- package/lib/model/revealInternalProperties.js +29 -21
- package/lib/modelCompare/compare.js +112 -39
- package/lib/modelCompare/utils/filter.js +54 -24
- package/lib/optionProcessor.js +6 -6
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +34 -20
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +77 -26
- package/lib/render/utils/common.js +3 -3
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +61 -20
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +8 -8
- package/lib/transform/db/expansion.js +17 -21
- package/lib/transform/db/flattening.js +23 -23
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +6 -6
- package/lib/transform/forRelationalDB.js +69 -75
- package/lib/transform/localized.js +6 -5
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +4 -101
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/package.json +1 -1
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -5,7 +5,7 @@ const {
|
|
|
5
5
|
getResultingName, forEachMemberRecursively,
|
|
6
6
|
} = require('../../model/csnUtils');
|
|
7
7
|
const { setProp, isDeprecatedEnabled } = require('../../base/model');
|
|
8
|
-
const { getTransformers } = require('../
|
|
8
|
+
const { getTransformers } = require('../transformUtils');
|
|
9
9
|
const { ModelError } = require('../../base/error');
|
|
10
10
|
const draftAnnotation = '@odata.draft.enabled';
|
|
11
11
|
const booleanBuiltin = 'cds.Boolean';
|
|
@@ -124,16 +124,18 @@ function generateDrafts( csn, options, pathDelimiter, messageFunctions ) {
|
|
|
124
124
|
error(null, path, 'Expecting element to have a type when used in a draft-enabled artifact');
|
|
125
125
|
if (elt.key && elt.key === true && !elt.virtual)
|
|
126
126
|
keys.push(elt);
|
|
127
|
-
}, [ 'definitions', artifactName ],
|
|
127
|
+
}, [ 'definitions', artifactName ], false, { elementsOnly: true });
|
|
128
128
|
|
|
129
129
|
// In contrast to EDM, the DB entity may have more than one technical keys but should have ideally exactly one key of type cds.UUID
|
|
130
|
-
if (keys.length !== 1)
|
|
131
|
-
warning(null, [ 'definitions', artifactName ],
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
130
|
+
if (keys.length !== 1) {
|
|
131
|
+
warning(null, [ 'definitions', artifactName ], { count: keys.length },
|
|
132
|
+
'Entity annotated with “@odata.draft.enabled” should have exactly one key element, but found $(COUNT)');
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const uuidCount = keys.reduce((acc, k) => ((k.type === 'cds.String' && k.$renamed === 'cds.UUID' && k.length === 36) ? acc + 1 : acc), 0);
|
|
136
|
+
if (uuidCount === 0)
|
|
137
|
+
warning(null, [ 'definitions', artifactName ], 'Entity annotated with “@odata.draft.enabled” should have one key element of type “cds.UUID”');
|
|
138
|
+
}
|
|
137
139
|
|
|
138
140
|
// Ignore boolean return value. We know that we're inside a service or else we wouldn't have reached this code.
|
|
139
141
|
const matchingService = getMatchingService(artifactName) || '';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { forEachDefinition, getServiceNames } = require('../../model/csnUtils');
|
|
4
4
|
const { forEach } = require('../../utils/objectUtils');
|
|
5
5
|
const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');
|
|
6
|
-
const { getTransformers } = require('../
|
|
6
|
+
const { getTransformers } = require('../transformUtils');
|
|
7
7
|
const { ModelError } = require('../../base/error');
|
|
8
8
|
const { makeMessageFunction } = require('../../base/messages');
|
|
9
9
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { makeMessageFunction } = require('../base/messages');
|
|
4
4
|
const { isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
|
|
5
|
-
const transformUtils = require('./
|
|
5
|
+
const transformUtils = require('./transformUtils');
|
|
6
6
|
const { cloneCsnNonDict,
|
|
7
7
|
forEachDefinition,
|
|
8
8
|
forEachMemberRecursively,
|
|
@@ -392,11 +392,11 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
392
392
|
if (node['@mandatory']&& node['@Common.FieldControl'] === undefined) {
|
|
393
393
|
setAnnotation(node, '@Common.FieldControl', { '#': 'Mandatory' });
|
|
394
394
|
}
|
|
395
|
-
if (node['@assert.range'] != null
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
395
|
+
if (node['@assert.range'] != null &&
|
|
396
|
+
(Array.isArray(node['@assert.range']) &&
|
|
397
|
+
node['@assert.range'].length === 2)) {
|
|
398
|
+
setAnnotation(node, '@Validation.Minimum', node['@assert.range'][0]);
|
|
399
|
+
setAnnotation(node, '@Validation.Maximum', node['@assert.range'][1]);
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
}
|
|
@@ -7,7 +7,7 @@ const { cloneCsnNonDict,
|
|
|
7
7
|
isAspect, walkCsnPath, isPersistedOnDatabase,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
9
|
const { makeMessageFunction } = require('../base/messages');
|
|
10
|
-
const transformUtils = require('./
|
|
10
|
+
const transformUtils = require('./transformUtils');
|
|
11
11
|
const { translateAssocsToJoinsCSN } = require('./translateAssocsToJoins');
|
|
12
12
|
const { csnRefs, pathId, traverseQuery } = require('../model/csnRefs');
|
|
13
13
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
@@ -26,7 +26,7 @@ const expansion = require('./db/expansion');
|
|
|
26
26
|
const assertUnique = require('./db/assertUnique');
|
|
27
27
|
const generateDrafts = require('./draft/db');
|
|
28
28
|
const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
|
|
29
|
-
const { getViewTransformer } = require('./db/views');
|
|
29
|
+
const { getViewTransformer, ensureColumnNames } = require('./db/views');
|
|
30
30
|
const cdsPersistence = require('./db/cdsPersistence');
|
|
31
31
|
const temporal = require('./db/temporal');
|
|
32
32
|
const associations = require('./db/associations')
|
|
@@ -98,17 +98,17 @@ function forEachDefinition(csn, cb) {
|
|
|
98
98
|
* - (250) Remove name space definitions again (only in forRelationalDB). Maybe we can omit inserting namespace definitions
|
|
99
99
|
* completely (TODO)
|
|
100
100
|
*
|
|
101
|
-
* @param {CSN.Model}
|
|
101
|
+
* @param {CSN.Model} csn
|
|
102
102
|
* @param {CSN.Options} options
|
|
103
103
|
* @param {string} moduleName The calling compiler module name, e.g. `to.hdi` or `to.hdbcds`.
|
|
104
104
|
*/
|
|
105
|
-
function transformForRelationalDBWithCsn(
|
|
105
|
+
function transformForRelationalDBWithCsn(csn, options, moduleName) {
|
|
106
106
|
// copy the model as we don't want to change the input model
|
|
107
107
|
timetrace.start('HANA transformation');
|
|
108
108
|
|
|
109
109
|
timetrace.start('Clone CSN');
|
|
110
110
|
/** @type {CSN.Model} */
|
|
111
|
-
|
|
111
|
+
csn = cloneCsnNonDict(csn, options);
|
|
112
112
|
timetrace.stop('Clone CSN');
|
|
113
113
|
|
|
114
114
|
checkCSNVersion(csn, options);
|
|
@@ -117,20 +117,13 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
117
117
|
// There is also an explicit default length via options.defaultStringLength
|
|
118
118
|
const implicitDefaultLengths = getDefaultTypeLengths(options.sqlDialect);
|
|
119
119
|
|
|
120
|
+
/** @type {object} */
|
|
120
121
|
let csnUtils;
|
|
122
|
+
/** @type {object} */
|
|
123
|
+
let messageFunctions;
|
|
121
124
|
let message, error, warning, info; // message functions
|
|
122
125
|
/** @type {() => void} */
|
|
123
126
|
let throwWithAnyError;
|
|
124
|
-
// csnUtils
|
|
125
|
-
let artifactRef,
|
|
126
|
-
inspectRef,
|
|
127
|
-
effectiveType,
|
|
128
|
-
initDefinition,
|
|
129
|
-
dropDefinitionCache,
|
|
130
|
-
get$combined,
|
|
131
|
-
getCsnDef,
|
|
132
|
-
isAssocOrComposition,
|
|
133
|
-
addStringAnnotationTo;
|
|
134
127
|
// transformUtils
|
|
135
128
|
let addDefaultTypeFacets,
|
|
136
129
|
expandStructsInExpression,
|
|
@@ -148,6 +141,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
148
141
|
bindCsnReference();
|
|
149
142
|
}
|
|
150
143
|
|
|
144
|
+
ensureColumnNames(csn, options, csnUtils);
|
|
145
|
+
|
|
151
146
|
const dialect = options.sqlDialect;
|
|
152
147
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
153
148
|
if (!doA2J)
|
|
@@ -159,14 +154,14 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
159
154
|
timetrace.start('Validate');
|
|
160
155
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
161
156
|
const cleanup = validate.forRelationalDB(csn, {
|
|
162
|
-
|
|
157
|
+
...messageFunctions, csnUtils, ...csnUtils, csn, options, isAspect
|
|
163
158
|
});
|
|
164
159
|
timetrace.stop('Validate');
|
|
165
160
|
|
|
166
|
-
rewriteCalculatedElementsInViews(csn, options, pathDelimiter,
|
|
161
|
+
rewriteCalculatedElementsInViews(csn, options, csnUtils, pathDelimiter, messageFunctions);
|
|
167
162
|
|
|
168
163
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
169
|
-
handleExists(csn, options, error, inspectRef, initDefinition, dropDefinitionCache);
|
|
164
|
+
handleExists(csn, options, error, csnUtils.inspectRef, csnUtils.initDefinition, csnUtils.dropDefinitionCache);
|
|
170
165
|
|
|
171
166
|
// Check if structured elements and managed associations are compared in an expression
|
|
172
167
|
// and expand these structured elements. This tuple expansion allows all other
|
|
@@ -179,19 +174,17 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
179
174
|
|
|
180
175
|
throwWithAnyError();
|
|
181
176
|
|
|
182
|
-
const transformCsn = transformUtils.transformModel;
|
|
183
|
-
|
|
184
177
|
forEachDefinition(csn, [
|
|
185
178
|
// (001) Add a temporal where condition to views where applicable before assoc2join
|
|
186
179
|
// assoc2join eventually rewrites the table aliases
|
|
187
|
-
temporal.getViewDecorator(csn,
|
|
180
|
+
temporal.getViewDecorator(csn, messageFunctions, csnUtils),
|
|
188
181
|
// check unique constraints - further processing is done in rewriteUniqueConstraints
|
|
189
182
|
assertUnique.prepare(csn, options, error, info)
|
|
190
183
|
]);
|
|
191
184
|
|
|
192
185
|
if(doA2J) {
|
|
193
186
|
// Expand a structured thing in: keys, columns, order by, group by
|
|
194
|
-
expansion.expandStructureReferences(csn, options, pathDelimiter,
|
|
187
|
+
expansion.expandStructureReferences(csn, options, pathDelimiter, messageFunctions, csnUtils);
|
|
195
188
|
bindCsnReference();
|
|
196
189
|
}
|
|
197
190
|
|
|
@@ -217,7 +210,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
217
210
|
flattening.flattenElements(csn, options, pathDelimiter, error);
|
|
218
211
|
} else {
|
|
219
212
|
// For to.hdbcds with naming mode hdbcds we also need to resolve the types
|
|
220
|
-
flattening.resolveTypeReferences(csn, options,
|
|
213
|
+
flattening.resolveTypeReferences(csn, options, new WeakMap(), pathDelimiter);
|
|
221
214
|
}
|
|
222
215
|
|
|
223
216
|
// (010) If requested, translate associations to joins
|
|
@@ -227,7 +220,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
227
220
|
bindCsnReference();
|
|
228
221
|
|
|
229
222
|
const redoProjections = [];
|
|
230
|
-
// Use the "raw" forEachDefinition here to ensure that the
|
|
223
|
+
// Use the "raw" forEachDefinition here to ensure that the $ignore takes effect
|
|
231
224
|
_forEachDefinition(csn, (artifact) => {
|
|
232
225
|
if(artifact.kind === 'entity' && artifact.projection) {
|
|
233
226
|
artifact.query = { SELECT: artifact.projection };
|
|
@@ -242,8 +235,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
242
235
|
}
|
|
243
236
|
})
|
|
244
237
|
} else if(artifact.kind === 'annotation' || artifact.kind === 'action' || artifact.kind === 'function' || artifact.kind === 'event'){
|
|
245
|
-
//
|
|
246
|
-
artifact
|
|
238
|
+
// $ignore actions etc. - this loop seemed handy for this, as we can hook into an existing if
|
|
239
|
+
artifact.$ignore = true;
|
|
247
240
|
}
|
|
248
241
|
});
|
|
249
242
|
|
|
@@ -251,20 +244,30 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
251
244
|
|
|
252
245
|
timetrace.start('Transform CSN')
|
|
253
246
|
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
247
|
+
// Rename primitive types, make UUID a String; replace `items` by cds.LargeString
|
|
248
|
+
//
|
|
249
|
+
// First, gather all nodes that are arrayed: Don't replace inline, or getFinalTypeInfo()
|
|
250
|
+
// may not return `.items` for types that were already processed.
|
|
251
|
+
{
|
|
252
|
+
const removeItems = new Set();
|
|
253
|
+
applyTransformations(csn, {
|
|
254
|
+
type: (node) => {
|
|
255
|
+
if (node.items || node.type && csnUtils.getFinalTypeInfo(node.type)?.items)
|
|
256
|
+
removeItems.add(node);
|
|
257
|
+
renamePrimitiveTypesAndUuid(node.type, node, 'type');
|
|
258
|
+
addDefaultTypeFacets(node, implicitDefaultLengths);
|
|
259
|
+
},
|
|
260
|
+
items: (node) => removeItems.add(node),
|
|
261
|
+
});
|
|
260
262
|
// no support for array-of - turn into CLOB/Text
|
|
261
263
|
// must be done after A2J or compiler checks could change
|
|
262
264
|
// (e.g. annotation def checks for arrayed types)
|
|
263
|
-
|
|
265
|
+
for (const node of removeItems) {
|
|
264
266
|
node.type = 'cds.LargeString';
|
|
265
267
|
delete node.items;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
+
}
|
|
269
|
+
removeItems.clear();
|
|
270
|
+
}
|
|
268
271
|
|
|
269
272
|
forEachDefinition(csn, [
|
|
270
273
|
// (040) Ignore entities and views that are abstract or implemented
|
|
@@ -273,7 +276,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
273
276
|
cdsPersistence.getAnnoProcessor(),
|
|
274
277
|
// (050) Check @cds.valid.from/to only on entity
|
|
275
278
|
// Views are checked in (001), unbalanced valid.from/to's or mismatching origins
|
|
276
|
-
temporal.getAnnotationHandler(csn, options, pathDelimiter,
|
|
279
|
+
temporal.getAnnotationHandler(csn, options, pathDelimiter, messageFunctions)
|
|
277
280
|
]);
|
|
278
281
|
|
|
279
282
|
// eliminate the doA2J in the functions 'handleManagedAssociationFKs' and 'createForeignKeyElements'
|
|
@@ -285,7 +288,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
285
288
|
|
|
286
289
|
// (045) Strip all query-ish properties from views and projections annotated with '@cds.persistence.table',
|
|
287
290
|
// and make them entities
|
|
288
|
-
forEachDefinition(csn, cdsPersistence.getPersistenceTableProcessor(csn, options,
|
|
291
|
+
forEachDefinition(csn, cdsPersistence.getPersistenceTableProcessor(csn, options, messageFunctions));
|
|
289
292
|
|
|
290
293
|
// Allow using managed associations as steps in on-conditions to access their fks
|
|
291
294
|
// To be done after handleAssociations, since then the foreign keys of the managed assocs
|
|
@@ -341,19 +344,19 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
341
344
|
createReferentialConstraints(csn, options);
|
|
342
345
|
|
|
343
346
|
// no constraints for drafts
|
|
344
|
-
generateDrafts(csn, options, pathDelimiter,
|
|
347
|
+
generateDrafts(csn, options, pathDelimiter, messageFunctions);
|
|
345
348
|
|
|
346
349
|
// Set the final constraint paths and produce hana tc indexes if required
|
|
347
350
|
// See function comment for extensive information.
|
|
348
351
|
assertUnique.rewrite(csn, options, pathDelimiter);
|
|
349
352
|
|
|
350
353
|
// Associations that point to things marked with @cds.persistence.skip are removed
|
|
351
|
-
forEachDefinition(csn, cdsPersistence.getAssocToSkippedIgnorer(csn, options,
|
|
354
|
+
forEachDefinition(csn, cdsPersistence.getAssocToSkippedIgnorer(csn, options, messageFunctions, csnUtils));
|
|
352
355
|
|
|
353
356
|
// Apply view-specific transformations
|
|
354
357
|
// (160) Projections now finally become views
|
|
355
358
|
// Replace managed association in group/order by with foreign keys
|
|
356
|
-
const transformEntityOrViewPass2 = getViewTransformer(csn, options,
|
|
359
|
+
const transformEntityOrViewPass2 = getViewTransformer(csn, options, messageFunctions, transformCommon);
|
|
357
360
|
forEachDefinition(csn, transformViews);
|
|
358
361
|
|
|
359
362
|
// Recursively apply transformCommon and attach @cds.persistence.name
|
|
@@ -384,14 +387,14 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
384
387
|
|
|
385
388
|
const killers = {
|
|
386
389
|
// Used to ignore actions etc from processing and remove associations/elements
|
|
387
|
-
'
|
|
390
|
+
'$ignore': function (parent, a, b, path){
|
|
388
391
|
if(path.length > 2) {
|
|
389
392
|
const tail = path[path.length-1];
|
|
390
393
|
const parentPath = path.slice(0, -1)
|
|
391
394
|
const parentParent = walkCsnPath(csn, parentPath);
|
|
392
395
|
delete parentParent[tail];
|
|
393
396
|
} else {
|
|
394
|
-
delete parent
|
|
397
|
+
delete parent.$ignore;
|
|
395
398
|
}
|
|
396
399
|
},
|
|
397
400
|
// Still used in flattenStructuredElements - in db/flattening.js
|
|
@@ -441,7 +444,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
441
444
|
/* ----------------------------------- Functions start here -----------------------------------------------*/
|
|
442
445
|
|
|
443
446
|
function bindCsnReference(){
|
|
444
|
-
|
|
447
|
+
messageFunctions = makeMessageFunction(csn, options, moduleName);
|
|
448
|
+
({ error, warning, info, message, throwWithAnyError } = messageFunctions);
|
|
445
449
|
|
|
446
450
|
({ flattenStructuredElement,
|
|
447
451
|
flattenStructStepsInRef,
|
|
@@ -451,22 +455,12 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
451
455
|
expandStructsInExpression,
|
|
452
456
|
csnUtils
|
|
453
457
|
} = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
454
|
-
|
|
455
|
-
({ artifactRef,
|
|
456
|
-
inspectRef,
|
|
457
|
-
effectiveType,
|
|
458
|
-
initDefinition,
|
|
459
|
-
dropDefinitionCache,
|
|
460
|
-
get$combined,
|
|
461
|
-
getCsnDef,
|
|
462
|
-
isAssocOrComposition,
|
|
463
|
-
addStringAnnotationTo,
|
|
464
|
-
} = csnUtils);
|
|
465
458
|
}
|
|
466
459
|
|
|
467
460
|
function bindCsnReferenceOnly(){
|
|
468
461
|
// invalidate caches for CSN ref API
|
|
469
|
-
|
|
462
|
+
const csnRefApi = csnRefs(csn);
|
|
463
|
+
Object.assign(csnUtils, csnRefApi);
|
|
470
464
|
}
|
|
471
465
|
|
|
472
466
|
function handleMixinOnConditions(artifact, artifactName) {
|
|
@@ -513,7 +507,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
513
507
|
// not explicitly in column list, check query sources
|
|
514
508
|
// get$combined also includes elements which are part of "excluding {}"
|
|
515
509
|
// this shouldn't be an issue here, as such references get rejected
|
|
516
|
-
const elementsOfQuerySources = get$combined(query);
|
|
510
|
+
const elementsOfQuerySources = csnUtils.get$combined(query);
|
|
517
511
|
forEach(elementsOfQuerySources, (id, element) => {
|
|
518
512
|
// if the ref points to an element which is not explicitly exposed in the column list,
|
|
519
513
|
// but through the '*' operator -> replace the $projection / $self with the correct source entity
|
|
@@ -538,7 +532,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
538
532
|
* @param {string} artifactName
|
|
539
533
|
*/
|
|
540
534
|
function transformViews(artifact, artifactName) {
|
|
541
|
-
if (!artifact
|
|
535
|
+
if (!artifact.$ignore) {
|
|
542
536
|
// Do things specific for entities and views (pass 2)
|
|
543
537
|
if ((artifact.kind === 'entity') && artifact.query) {
|
|
544
538
|
|
|
@@ -557,7 +551,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
557
551
|
|
|
558
552
|
const process = (parent, prop, query, path) => {
|
|
559
553
|
transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop))
|
|
560
|
-
replaceAssociationsInGroupByOrderBy(parent, options, inspectRef, error, path.concat(prop));
|
|
554
|
+
replaceAssociationsInGroupByOrderBy(parent, options, csnUtils.inspectRef, error, path.concat(prop));
|
|
561
555
|
return query;
|
|
562
556
|
}
|
|
563
557
|
applyTransformationsOnNonDictionary(csn.definitions, artifactName, {
|
|
@@ -572,9 +566,9 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
572
566
|
* @param {string} artifactName
|
|
573
567
|
*/
|
|
574
568
|
function recursivelyApplyCommon(artifact, artifactName) {
|
|
575
|
-
if (!artifact
|
|
569
|
+
if (!artifact.$ignore) {
|
|
576
570
|
if (artifact.kind !== 'service' && artifact.kind !== 'context')
|
|
577
|
-
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
571
|
+
csnUtils.addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
578
572
|
|
|
579
573
|
forEachMemberRecursively(artifact, (member, memberName, property, path) => {
|
|
580
574
|
if (property === 'returns')
|
|
@@ -584,7 +578,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
584
578
|
// Virtual elements in entities and types are not annotated, as they have no DB representation.
|
|
585
579
|
// In views they are, as we generate a null expression for them (null as <colname>)
|
|
586
580
|
if ((!member.virtual || artifact.query))
|
|
587
|
-
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
|
|
581
|
+
csnUtils.addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
|
|
588
582
|
}, [ 'definitions', artifactName ]);
|
|
589
583
|
}
|
|
590
584
|
}
|
|
@@ -594,7 +588,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
594
588
|
* @param {string} artifactName
|
|
595
589
|
*/
|
|
596
590
|
function removeKeyPropInType(artifact, artifactName) {
|
|
597
|
-
if (!artifact
|
|
591
|
+
if (!artifact.$ignore && artifact.kind === 'type') {
|
|
598
592
|
forEachMemberRecursively(artifact, (member) => {
|
|
599
593
|
if (member.key)
|
|
600
594
|
delete member.key;
|
|
@@ -618,7 +612,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
618
612
|
function doit(dict, subPath) {
|
|
619
613
|
for (const elemName in dict) {
|
|
620
614
|
const elem = dict[elemName];
|
|
621
|
-
if (elem.on && isAssocOrComposition(elem))
|
|
615
|
+
if (elem.on && csnUtils.isAssocOrComposition(elem))
|
|
622
616
|
processBacklinkAssoc(elem, elemName, artifact, artifactName, subPath.concat([ elemName, 'on' ]));
|
|
623
617
|
}
|
|
624
618
|
}
|
|
@@ -641,7 +635,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
641
635
|
// For HANA: Report an error on
|
|
642
636
|
// - view with parameters that has an element of type association/composition
|
|
643
637
|
// - association that points to entity with parameters
|
|
644
|
-
if (options.sqlDialect === 'hana' && member.target && isAssocOrComposition(member) && !isBetaEnabled(options, 'assocsWithParams')) {
|
|
638
|
+
if (options.sqlDialect === 'hana' && member.target && csnUtils.isAssocOrComposition(member) && !isBetaEnabled(options, 'assocsWithParams')) {
|
|
645
639
|
if (artifact.params) {
|
|
646
640
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
647
641
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
@@ -676,7 +670,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
676
670
|
* @param {string} artifactName
|
|
677
671
|
*/
|
|
678
672
|
function handleChecksForWithParameters(artifact, artifactName) {
|
|
679
|
-
if (!artifact
|
|
673
|
+
if (!artifact.$ignore && artifact.params && (artifact.kind === 'entity')) {
|
|
680
674
|
if (!artifact.query) { // table entity with params
|
|
681
675
|
// Allow with plain
|
|
682
676
|
error(null, [ 'definitions', artifactName ], { '#': options.toSql ? 'sql' : 'std' }, {
|
|
@@ -714,7 +708,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
714
708
|
// restore all (non-enumerable) properties that wouldn't survive reaugmentation/compactification into the new compact model
|
|
715
709
|
forEachDefinition(csn, (art, artName) => {
|
|
716
710
|
if(art['$tableConstraints']) {
|
|
717
|
-
|
|
711
|
+
newCsn.definitions[artName].$tableConstraints = art['$tableConstraints'];
|
|
718
712
|
}
|
|
719
713
|
if (art.technicalConfig)
|
|
720
714
|
newCsn.definitions[artName].technicalConfig = art.technicalConfig;
|
|
@@ -790,7 +784,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
790
784
|
if (elem.default && elem.default['#']) {
|
|
791
785
|
let Enum = elem.enum;
|
|
792
786
|
if (!Enum && !isBuiltinType(elem.type)) {
|
|
793
|
-
const typeDef = getCsnDef(elem.type);
|
|
787
|
+
const typeDef = csnUtils.getCsnDef(elem.type);
|
|
794
788
|
Enum = typeDef && typeDef.enum;
|
|
795
789
|
}
|
|
796
790
|
if (!Enum) {
|
|
@@ -852,7 +846,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
852
846
|
// Check if one side is $self and the other an association
|
|
853
847
|
// (if so, replace all three tokens with the condition generated from the other side, in parentheses)
|
|
854
848
|
if (isDollarSelfOrProjectionOperand(xprArgs[i]) && isAssociationOperand(xprArgs[i + 2], path.concat([ i + 2 ]))) {
|
|
855
|
-
const assoc = inspectRef(path.concat([ i + 2 ])).art;
|
|
849
|
+
const assoc = csnUtils.inspectRef(path.concat([ i + 2 ])).art;
|
|
856
850
|
if (multipleExprs)
|
|
857
851
|
result.push('(');
|
|
858
852
|
const backlinkName = xprArgs[i + 2].ref[xprArgs[i + 2].ref.length - 1];
|
|
@@ -867,7 +861,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
867
861
|
attachBacklinkInformation(backlinkName);
|
|
868
862
|
}
|
|
869
863
|
else if (isDollarSelfOrProjectionOperand(xprArgs[i + 2]) && isAssociationOperand(xprArgs[i], path.concat([ i ]))) {
|
|
870
|
-
const assoc = inspectRef(path.concat([ i ])).art;
|
|
864
|
+
const assoc = csnUtils.inspectRef(path.concat([ i ])).art;
|
|
871
865
|
if (multipleExprs)
|
|
872
866
|
result.push('(');
|
|
873
867
|
const backlinkName = xprArgs[i].ref[xprArgs[i].ref.length - 1];
|
|
@@ -943,7 +937,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
943
937
|
if(assoc.keys.length)
|
|
944
938
|
return transformDollarSelfComparisonWithManagedAssoc(assocOp, assoc, assocName, elemName);
|
|
945
939
|
else {
|
|
946
|
-
elem
|
|
940
|
+
elem.$ignore = true;
|
|
947
941
|
return [];
|
|
948
942
|
}
|
|
949
943
|
}
|
|
@@ -1034,10 +1028,10 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1034
1028
|
function checkTypeParameters(artifact, artifactName) {
|
|
1035
1029
|
forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
|
|
1036
1030
|
// Check type parameters (length, precision, scale ...)
|
|
1037
|
-
if (!member
|
|
1031
|
+
if (!member.$ignore && member.type)
|
|
1038
1032
|
_check(member, memberName, csn, path);
|
|
1039
1033
|
|
|
1040
|
-
if (!member
|
|
1034
|
+
if (!member.$ignore && member.items && member.items.type)
|
|
1041
1035
|
_check(member.items, memberName, csn, path.concat([ 'items' ]));
|
|
1042
1036
|
}, [ 'definitions', artifactName ]);
|
|
1043
1037
|
|
|
@@ -1079,7 +1073,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1079
1073
|
// (which can currently only be 'positiveInteger') and (optional) the value is in a given range
|
|
1080
1074
|
function checkTypeParamValue(node, paramName, range = null, path = null) {
|
|
1081
1075
|
const paramValue = node[paramName];
|
|
1082
|
-
if (paramValue
|
|
1076
|
+
if (paramValue == null) {
|
|
1083
1077
|
if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type)) {
|
|
1084
1078
|
return true;
|
|
1085
1079
|
} else {
|
|
@@ -1134,7 +1128,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1134
1128
|
if (typeof val === 'object' && val.ref) {
|
|
1135
1129
|
// Replace a reference by references to it's elements, if it is structured
|
|
1136
1130
|
const path = [ 'definitions', artName, 'technicalConfig', dialect, 'indexes', name, idx ];
|
|
1137
|
-
const { art } = inspectRef(path);
|
|
1131
|
+
const { art } = csnUtils.inspectRef(path);
|
|
1138
1132
|
if (!art) {
|
|
1139
1133
|
// A reference that has no artifact (e.g. the reference to the index name itself). Just copy it over
|
|
1140
1134
|
flattenedIndex.push(val);
|
|
@@ -1176,8 +1170,8 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
|
|
|
1176
1170
|
}
|
|
1177
1171
|
}
|
|
1178
1172
|
}
|
|
1179
|
-
}
|
|
1180
1173
|
|
|
1174
|
+
}
|
|
1181
1175
|
|
|
1182
1176
|
module.exports = {
|
|
1183
1177
|
transformForRelationalDBWithCsn,
|
|
@@ -330,8 +330,8 @@ function _addLocalizationViews(csn, options, useJoins, config) {
|
|
|
330
330
|
let keyCount = 0;
|
|
331
331
|
let textElements = [];
|
|
332
332
|
|
|
333
|
-
forEachGeneric(art, 'elements', (elem, elemName
|
|
334
|
-
if (elem
|
|
333
|
+
forEachGeneric(art, 'elements', (elem, elemName , _prop, path) => {
|
|
334
|
+
if (elem.$ignore) // from SAP HANA backend
|
|
335
335
|
return;
|
|
336
336
|
|
|
337
337
|
if (elem.key || elem.$key)
|
|
@@ -340,9 +340,10 @@ function _addLocalizationViews(csn, options, useJoins, config) {
|
|
|
340
340
|
if (elem.key || elem.$key || elem.localized)
|
|
341
341
|
textElements.push( elemName );
|
|
342
342
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
343
|
+
if ((elem.key|| elem.$key) && elem.localized) {
|
|
344
|
+
messageFunctions.warning('def-ignoring-localized', path, { keyword: 'localized' },
|
|
345
|
+
'Keyword $(KEYWORD) is ignored for primary keys');
|
|
346
|
+
}
|
|
346
347
|
}, artPath);
|
|
347
348
|
|
|
348
349
|
if (textElements.length <= keyCount || keyCount <= 0)
|
|
@@ -94,7 +94,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
94
94
|
function expandToFinalBaseType(node, defName) {
|
|
95
95
|
if (!node) return;
|
|
96
96
|
// TODO: Clarify how should events be handled?
|
|
97
|
-
// They are not treated by the
|
|
97
|
+
// They are not treated by the transformUtils::toFinalBaseType function
|
|
98
98
|
// in the same manner as named types, because the elements of structured events are not
|
|
99
99
|
// propagated as it is with types.
|
|
100
100
|
// It is ok to skip the expansion to the final base type for now as events are not rendered in
|
|
@@ -130,7 +130,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
130
130
|
*/
|
|
131
131
|
if (isBuiltinType(finalBaseType.type)) {
|
|
132
132
|
/*
|
|
133
|
-
use
|
|
133
|
+
use transformUtils::toFinalBaseType for the moment,
|
|
134
134
|
as it is collects along the chain of types
|
|
135
135
|
attributes that need to be propagated
|
|
136
136
|
enum, length, scale, etc.
|
|
@@ -161,7 +161,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
161
161
|
*/
|
|
162
162
|
if (isBuiltinType(finalBaseType.type)) {
|
|
163
163
|
/*
|
|
164
|
-
use
|
|
164
|
+
use transformUtils::toFinalBaseType for the moment,
|
|
165
165
|
as it is collects along the chain of types
|
|
166
166
|
attributes that need to be propagated
|
|
167
167
|
enum, length, scale, etc.
|