@sap/cds-compiler 6.2.2 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/bin/cdsc.js +11 -4
- package/lib/api/options.js +1 -1
- package/lib/base/message-registry.js +36 -7
- package/lib/base/messages.js +11 -4
- package/lib/base/model.js +0 -1
- package/lib/checks/assocOutsideService.js +17 -30
- package/lib/checks/checkForTypes.js +0 -18
- package/lib/checks/checkPathsInStoredCalcElement.js +2 -1
- package/lib/checks/onConditions.js +2 -2
- package/lib/checks/queryNoDbArtifacts.js +16 -15
- package/lib/checks/types.js +1 -1
- package/lib/checks/utils.js +30 -6
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/checks.js +47 -18
- package/lib/compiler/index.js +88 -6
- package/lib/compiler/resolve.js +7 -7
- package/lib/compiler/tweak-assocs.js +47 -25
- package/lib/gen/BaseParser.js +1 -1
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +381 -378
- package/lib/gen/Dictionary.json +0 -2
- package/lib/model/csnRefs.js +9 -4
- package/lib/model/csnUtils.js +67 -2
- package/lib/optionProcessor.js +2 -3
- package/lib/parsers/AstBuildingParser.js +5 -6
- package/lib/render/toCdl.js +10 -4
- package/lib/render/utils/common.js +4 -2
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/associations.js +37 -1
- package/lib/transform/db/assocsToQueries/transformExists.js +21 -32
- package/lib/transform/db/assocsToQueries/utils.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +37 -36
- package/lib/transform/draft/db.js +20 -20
- package/lib/transform/draft/odata.js +38 -40
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/flattening.js +40 -47
- package/lib/transform/effective/main.js +6 -4
- package/lib/transform/forOdata.js +135 -115
- package/lib/transform/forRelationalDB.js +151 -142
- package/lib/transform/localized.js +116 -109
- package/lib/transform/odata/adaptAnnotationRefs.js +21 -16
- package/lib/transform/odata/createForeignKeys.js +73 -70
- package/lib/transform/odata/flattening.js +216 -200
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +47 -45
- package/lib/transform/odata/toFinalBaseType.js +40 -39
- package/lib/transform/odata/typesExposure.js +151 -133
- package/lib/transform/odata/utils.js +7 -6
- package/lib/transform/parseExpr.js +165 -162
- package/lib/transform/transformUtils.js +184 -551
- package/lib/transform/translateAssocsToJoins.js +510 -571
- package/lib/transform/tupleExpansion.js +495 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +1 -1
- package/lib/base/cleanSymbols.js +0 -17
- package/lib/checks/nonexpandableStructured.js +0 -39
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const {
|
|
5
|
+
forEachMemberRecursively, forAllQueries, applyTransformationsOnNonDictionary,
|
|
6
|
+
getArtifactDatabaseNameOf, getElementDatabaseNameOf, applyTransformations,
|
|
7
|
+
walkCsnPath, isPersistedOnDatabase,
|
|
8
|
+
} = require('../model/csnUtils');
|
|
8
9
|
const transformUtils = require('./transformUtils');
|
|
9
10
|
const { translateAssocsToJoinsCSN } = require('./translateAssocsToJoins');
|
|
10
|
-
const {
|
|
11
|
+
const {
|
|
12
|
+
csnRefs, pathId, traverseQuery, columnAlias,
|
|
13
|
+
} = require('../model/csnRefs');
|
|
11
14
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
12
15
|
const validate = require('../checks/validator');
|
|
13
16
|
const { rejectManagedAssociationsAndStructuresForHdbcdsNames } = require('../checks/selectItems');
|
|
@@ -33,11 +36,11 @@ const backlinks = require('./db/backlinks');
|
|
|
33
36
|
const { getDefaultTypeLengths } = require('../render/utils/common');
|
|
34
37
|
const { featureFlags } = require('./featureFlags');
|
|
35
38
|
const { cloneCsnNonDict, cloneFullCsn } = require('../model/cloneCsn');
|
|
36
|
-
const { processSqlServices, createServiceDummy
|
|
39
|
+
const { processSqlServices, createServiceDummy } = require('./db/processSqlServices');
|
|
37
40
|
|
|
38
41
|
// By default: Do not process non-entities/views
|
|
39
42
|
function forEachDefinition(csn, cb) {
|
|
40
|
-
_forEachDefinition(csn, cb, {skip: ['annotation', 'action', 'function','event']})
|
|
43
|
+
_forEachDefinition(csn, cb, { skip: [ 'annotation', 'action', 'function', 'event' ] });
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
/**
|
|
@@ -129,10 +132,10 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
129
132
|
/** @type {() => void} */
|
|
130
133
|
let throwWithAnyError;
|
|
131
134
|
// transformUtils
|
|
132
|
-
let addDefaultTypeFacets
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
let addDefaultTypeFacets;
|
|
136
|
+
let expandStructsInExpression;
|
|
137
|
+
let flattenStructuredElement;
|
|
138
|
+
let flattenStructStepsInRef;
|
|
136
139
|
|
|
137
140
|
bindCsnReference();
|
|
138
141
|
|
|
@@ -163,12 +166,12 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
163
166
|
// exit if validators found errors
|
|
164
167
|
throwWithAnyError();
|
|
165
168
|
|
|
166
|
-
if(csn.meta?.[featureFlags]?.$calculatedElements)
|
|
169
|
+
if (csn.meta?.[featureFlags]?.$calculatedElements)
|
|
167
170
|
rewriteCalculatedElementsInViews(csn, options, csnUtils, pathDelimiter, messageFunctions);
|
|
168
171
|
|
|
169
172
|
timetrace.start('Where-Exists handling');
|
|
170
173
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
171
|
-
handleExists(csn, options,
|
|
174
|
+
handleExists(csn, options, messageFunctions, csnUtils);
|
|
172
175
|
timetrace.stop('Where-Exists handling');
|
|
173
176
|
|
|
174
177
|
// Check if structured elements and managed associations are compared in an expression
|
|
@@ -179,17 +182,17 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
179
182
|
timetrace.start('Expand Structures (expressions + refs)');
|
|
180
183
|
// If this function is ever undefined, we have a bug in our logic.
|
|
181
184
|
// @ts-ignore
|
|
182
|
-
expandStructsInExpression(
|
|
185
|
+
expandStructsInExpression({ drillRef: true });
|
|
183
186
|
|
|
184
187
|
forEachDefinition(csn, [
|
|
185
188
|
// (001) Add a temporal where condition to views where applicable before assoc2join
|
|
186
189
|
// assoc2join eventually rewrites the table aliases
|
|
187
190
|
temporal.getViewDecorator(csn, messageFunctions, csnUtils, options),
|
|
188
191
|
// check unique constraints - further processing is done in rewriteUniqueConstraints
|
|
189
|
-
assertUnique.prepare(csn, options, messageFunctions)
|
|
192
|
+
assertUnique.prepare(csn, options, messageFunctions),
|
|
190
193
|
]);
|
|
191
194
|
|
|
192
|
-
if(doA2J) {
|
|
195
|
+
if (doA2J) {
|
|
193
196
|
// Expand a structured thing in: keys, columns, order by, group by
|
|
194
197
|
// In addition, kill all non-sql-backend relevant annotations
|
|
195
198
|
expansion.expandStructureReferences(csn, options, pathDelimiter, messageFunctions, csnUtils, { processAnnotations: true });
|
|
@@ -220,7 +223,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
220
223
|
// With that, we could still ensure the processing order (assuming we don't run into problems with scoping).
|
|
221
224
|
// To analyze: Increased memory vs. saved cycles
|
|
222
225
|
// Looked at it with AFC: This is only a small part of the overall processing time, enrich step of validator is just as expensive
|
|
223
|
-
if(doA2J) {
|
|
226
|
+
if (doA2J) {
|
|
224
227
|
const resolved = new WeakMap();
|
|
225
228
|
// No refs with struct-steps exist anymore
|
|
226
229
|
flattening.flattenAllStructStepsInRefs(csn, options, messageFunctions, resolved, pathDelimiter);
|
|
@@ -229,7 +232,8 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
229
232
|
flattening.resolveTypeReferences(csn, options, messageFunctions, resolved, pathDelimiter);
|
|
230
233
|
// No structured elements exists anymore
|
|
231
234
|
flattening.flattenElements(csn, options, messageFunctions, pathDelimiter);
|
|
232
|
-
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
233
237
|
// For to.hdbcds with naming mode hdbcds we also need to resolve the types
|
|
234
238
|
flattening.resolveTypeReferences(csn, options, messageFunctions, new WeakMap(), pathDelimiter);
|
|
235
239
|
}
|
|
@@ -247,19 +251,19 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
247
251
|
const redoProjections = [];
|
|
248
252
|
// Use the "raw" forEachDefinition here to ensure that the $ignore takes effect
|
|
249
253
|
_forEachDefinition(csn, (artifact) => {
|
|
250
|
-
if(artifact.kind === 'entity' && artifact.projection) {
|
|
254
|
+
if (artifact.kind === 'entity' && artifact.projection) {
|
|
251
255
|
artifact.query = { SELECT: artifact.projection };
|
|
252
256
|
delete artifact.projection;
|
|
253
257
|
redoProjections.push(() => {
|
|
254
|
-
if(artifact.query) {
|
|
258
|
+
if (artifact.query) {
|
|
255
259
|
artifact.projection = artifact.query.SELECT;
|
|
256
260
|
delete artifact.query;
|
|
257
|
-
if(artifact.$syntax === 'projection')
|
|
261
|
+
if (artifact.$syntax === 'projection')
|
|
258
262
|
delete artifact.$syntax;
|
|
259
|
-
}
|
|
260
263
|
}
|
|
261
|
-
})
|
|
262
|
-
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
else if (artifact.kind === 'annotation' || artifact.kind === 'action' || artifact.kind === 'function' || artifact.kind === 'event') {
|
|
263
267
|
// $ignore actions etc. - this loop seemed handy for this, as we can hook into an existing if
|
|
264
268
|
artifact.$ignore = true;
|
|
265
269
|
}
|
|
@@ -267,21 +271,20 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
267
271
|
|
|
268
272
|
processCalculatedElementsInEntities(csn, options);
|
|
269
273
|
|
|
270
|
-
timetrace.start('Transform CSN')
|
|
274
|
+
timetrace.start('Transform CSN');
|
|
271
275
|
|
|
272
276
|
// Rename primitive types, make UUID a String; replace `items` by cds.LargeString
|
|
273
277
|
//
|
|
274
278
|
// First, gather all nodes that are arrayed: Don't replace inline, or getFinalTypeInfo()
|
|
275
279
|
// may not return `.items` for types that were already processed.
|
|
276
280
|
// TODO: Do this in resolveTypeReferences?
|
|
277
|
-
{
|
|
278
|
-
|
|
279
|
-
type
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
281
|
+
applyTransformations(csn, {
|
|
282
|
+
type: (node) => {
|
|
283
|
+
renamePrimitiveTypesAndUuid(node.type, node, 'type');
|
|
284
|
+
addDefaultTypeFacets(node, implicitDefaultLengths);
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
285
288
|
|
|
286
289
|
forEachDefinition(csn, [
|
|
287
290
|
// (040) Ignore entities and views that are abstract or implemented
|
|
@@ -290,24 +293,29 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
290
293
|
cdsPersistence.getAnnoProcessor(),
|
|
291
294
|
// (050) Check @cds.valid.from/to only on entity
|
|
292
295
|
// Views are checked in (001), unbalanced valid.from/to's or mismatching origins
|
|
293
|
-
temporal.getAnnotationHandler(csn, options, pathDelimiter, messageFunctions)
|
|
296
|
+
temporal.getAnnotationHandler(csn, options, pathDelimiter, messageFunctions),
|
|
294
297
|
]);
|
|
295
298
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
if (doA2J) {
|
|
300
|
+
// eliminate the doA2J in the functions 'handleManagedAssociationFKs' and 'createForeignKeyElements'
|
|
301
|
+
flattening.handleManagedAssociationsAndCreateForeignKeys(
|
|
302
|
+
csn, options, messageFunctions, pathDelimiter, true, csnUtils,
|
|
303
|
+
{ skipDict: { actions: true }, allowArtifact: artifact => (artifact.kind === 'entity') }
|
|
304
|
+
);
|
|
305
|
+
forEachDefinition(csn, flattenIndexes);
|
|
306
|
+
// Managed associations get an on-condition - in views and entities
|
|
307
|
+
associations.attachOnConditions(csn, csnUtils, pathDelimiter);
|
|
308
|
+
}
|
|
302
309
|
|
|
303
310
|
{
|
|
304
311
|
// (045) Strip all query-ish properties from views and projections annotated with '@cds.persistence.table',
|
|
305
312
|
// and make them entities
|
|
306
|
-
const fns = [cdsPersistence.getPersistenceTableProcessor(csn, options, messageFunctions)];
|
|
313
|
+
const fns = [ cdsPersistence.getPersistenceTableProcessor(csn, options, messageFunctions) ];
|
|
307
314
|
// Allow using managed associations as steps in on-conditions to access their fks
|
|
308
315
|
// To be done after handleAssociations, since then the foreign keys of the managed assocs
|
|
309
316
|
// are part of the elements
|
|
310
|
-
if(doA2J)
|
|
317
|
+
if (doA2J)
|
|
318
|
+
fns.push(associations.getFKAccessFinalizer(csn, options, csnUtils, pathDelimiter));
|
|
311
319
|
|
|
312
320
|
forEachDefinition(csn, fns);
|
|
313
321
|
}
|
|
@@ -327,7 +335,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
327
335
|
if (!doA2J && definition.query && isPersistedOnDatabase(definition)) {
|
|
328
336
|
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
329
337
|
const that = { csnUtils, options, error };
|
|
330
|
-
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path)
|
|
338
|
+
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path);
|
|
331
339
|
}
|
|
332
340
|
},
|
|
333
341
|
// (170) Transform '$self' in backlink associations to appropriate key comparisons
|
|
@@ -335,7 +343,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
335
343
|
// draft shadow entities have crooked '_artifact' links, confusing the backlink processing.
|
|
336
344
|
// But it must also happen after flattenForeignKeys has been called for all artifacts,
|
|
337
345
|
// because otherwise we would produce wrong ON-conditions for the keys involved. Sigh ...
|
|
338
|
-
backlinks.getBacklinkTransformer(csnUtils, messageFunctions, options, pathDelimiter, doA2J)
|
|
346
|
+
backlinks.getBacklinkTransformer(csnUtils, messageFunctions, options, pathDelimiter, doA2J),
|
|
339
347
|
]);
|
|
340
348
|
|
|
341
349
|
/**
|
|
@@ -343,7 +351,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
343
351
|
* For to.hdbcds with naming mode "hdbcds", no foreign keys are calculated,
|
|
344
352
|
* hence we do not generate the referential constraints for them.
|
|
345
353
|
*/
|
|
346
|
-
if(options.sqlDialect !== 'plain' && options.sqlDialect !== 'h2' && doA2J)
|
|
354
|
+
if (options.sqlDialect !== 'plain' && options.sqlDialect !== 'h2' && doA2J)
|
|
347
355
|
createReferentialConstraints(csn, options);
|
|
348
356
|
|
|
349
357
|
// no constraints for drafts
|
|
@@ -360,24 +368,24 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
360
368
|
messageFunctions.throwWithError();
|
|
361
369
|
|
|
362
370
|
// TODO: Might have to do this earlier if we want special rendering for projections?
|
|
363
|
-
const findAndMarkSqlServiceArtifacts = options.sqlDialect === 'hana' && options.src === 'hdi' && (csn.meta?.[featureFlags]?.$sqlService || csn.meta?.[featureFlags]?.$dummyService || csn.meta?.[featureFlags]?.$dataProductService) ? processSqlServices(csn, options): () => {}
|
|
371
|
+
const findAndMarkSqlServiceArtifacts = options.sqlDialect === 'hana' && options.src === 'hdi' && (csn.meta?.[featureFlags]?.$sqlService || csn.meta?.[featureFlags]?.$dummyService || csn.meta?.[featureFlags]?.$dataProductService) ? processSqlServices(csn, options) : () => {};
|
|
364
372
|
|
|
365
373
|
// Apply view-specific transformations
|
|
366
374
|
// (160) Projections now finally become views
|
|
367
375
|
// Replace managed association in group/order by with foreign keys
|
|
368
376
|
const transformEntityOrViewPass2 = getViewTransformer(csn, options, messageFunctions);
|
|
369
|
-
forEachDefinition(csn, [(artifact, artifactName) => {
|
|
377
|
+
forEachDefinition(csn, [ (artifact, artifactName) => {
|
|
370
378
|
findAndMarkSqlServiceArtifacts(artifact, artifactName);
|
|
371
|
-
if(artifact.$dummyService)
|
|
379
|
+
if (artifact.$dummyService)
|
|
372
380
|
createServiceDummy(artifact, artifactName, csn, messageFunctions);
|
|
373
|
-
},
|
|
381
|
+
}, transformViews ]);
|
|
374
382
|
|
|
375
|
-
if(!doA2J) {
|
|
383
|
+
if (!doA2J) {
|
|
376
384
|
forEachDefinition(csn, [
|
|
377
385
|
// (200) Strip 'key' property from type elements
|
|
378
386
|
removeKeyPropInType,
|
|
379
387
|
(artifact, artifactName) => {
|
|
380
|
-
if(artifact.kind === 'type') {
|
|
388
|
+
if (artifact.kind === 'type') {
|
|
381
389
|
forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
|
|
382
390
|
// Check type parameters (length, precision, scale ...)
|
|
383
391
|
if (!member.$ignore) {
|
|
@@ -388,7 +396,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
388
396
|
}
|
|
389
397
|
}, [ 'definitions', artifactName ]);
|
|
390
398
|
}
|
|
391
|
-
}
|
|
399
|
+
},
|
|
392
400
|
]);
|
|
393
401
|
}
|
|
394
402
|
|
|
@@ -409,55 +417,56 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
409
417
|
csnUtils.addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(name, options.sqlMapping, options.sqlDialect), element);
|
|
410
418
|
});
|
|
411
419
|
// Remove leading $self to keep renderer-diffs smaller
|
|
412
|
-
if(doA2J && options.transformation === 'hdbcds')
|
|
420
|
+
if (doA2J && options.transformation === 'hdbcds')
|
|
413
421
|
flattening.removeLeadingSelf(parent, prop, elements);
|
|
414
|
-
}
|
|
415
|
-
}, [(definitions, artifactName, artifact) => {
|
|
422
|
+
},
|
|
423
|
+
}, [ (definitions, artifactName, artifact) => {
|
|
416
424
|
// Attach @cds.persistence.name to artifacts
|
|
417
425
|
if (!artifact.$ignore && artifact.kind !== 'service' && artifact.kind !== 'context')
|
|
418
426
|
csnUtils.addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
419
|
-
}], { allowArtifact: artifact => artifact.kind === 'entity'});
|
|
427
|
+
} ], { allowArtifact: artifact => artifact.kind === 'entity' });
|
|
420
428
|
|
|
421
429
|
throwWithAnyError();
|
|
422
430
|
|
|
423
|
-
function killProp(parent, prop){
|
|
431
|
+
function killProp(parent, prop) {
|
|
424
432
|
delete parent[prop];
|
|
425
433
|
}
|
|
426
434
|
|
|
427
|
-
function killParent(parent, a, b, path){
|
|
428
|
-
if(path.length > 2) {
|
|
429
|
-
const tail = path[path.length-1];
|
|
430
|
-
const parentPath = path.slice(0, -1)
|
|
435
|
+
function killParent(parent, a, b, path) {
|
|
436
|
+
if (path.length > 2) {
|
|
437
|
+
const tail = path[path.length - 1];
|
|
438
|
+
const parentPath = path.slice(0, -1);
|
|
431
439
|
const parentParent = walkCsnPath(csn, parentPath);
|
|
432
440
|
delete parentParent[tail];
|
|
433
|
-
}
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
434
443
|
delete parent.$ignore;
|
|
435
444
|
}
|
|
436
445
|
}
|
|
437
446
|
|
|
438
447
|
const killers = {
|
|
439
448
|
// Used to ignore actions etc from processing and remove associations/elements
|
|
440
|
-
|
|
449
|
+
$ignore: killParent,
|
|
441
450
|
// Still used in flattenStructuredElements - in db/flattening.js
|
|
442
|
-
|
|
451
|
+
_flatElementNameWithDots: killProp,
|
|
443
452
|
// Set when setting default string/binary length - used in copyTypeProperties and fixBorkedElementsOfLocalized
|
|
444
453
|
// to not copy the .length property if it was only set via default
|
|
445
|
-
|
|
454
|
+
$default: killProp,
|
|
446
455
|
// Set when we turn UUID into String, checked during generateDraftForHana
|
|
447
|
-
|
|
456
|
+
$renamed: killProp,
|
|
448
457
|
// Set when we remove .key from temporal things, used in localized.js
|
|
449
|
-
|
|
458
|
+
$key: killProp,
|
|
450
459
|
// We need .elements easily for rendering - otherwise we have to compute it then
|
|
451
460
|
// Does not fit in the "killers" theme - TODO: Find a better place
|
|
452
461
|
SET: (parent, prop, SET) => {
|
|
453
|
-
if(!SET.elements) {
|
|
454
|
-
const stack = [parent];
|
|
455
|
-
while(stack.length > 0) {
|
|
462
|
+
if (!SET.elements) {
|
|
463
|
+
const stack = [ parent ];
|
|
464
|
+
while (stack.length > 0) {
|
|
456
465
|
const query = stack.pop();
|
|
457
466
|
|
|
458
|
-
if(query.SET)
|
|
467
|
+
if (query.SET)
|
|
459
468
|
stack.push(query.SET.args[0]);
|
|
460
|
-
else if(query.SELECT)
|
|
469
|
+
else if (query.SELECT)
|
|
461
470
|
setProp(SET, 'elements', query.SELECT.elements);
|
|
462
471
|
}
|
|
463
472
|
}
|
|
@@ -465,34 +474,33 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
465
474
|
includes: killProp,
|
|
466
475
|
masked: killProp,
|
|
467
476
|
localized: killProp,
|
|
468
|
-
}
|
|
477
|
+
};
|
|
469
478
|
|
|
470
|
-
if(options.sqlDialect === 'postgres') {
|
|
479
|
+
if (options.sqlDialect === 'postgres') {
|
|
471
480
|
killers.length = (parent) => {
|
|
472
|
-
if (parent.type === 'cds.Binary')
|
|
481
|
+
if (parent.type === 'cds.Binary')
|
|
473
482
|
delete parent.length;
|
|
474
|
-
|
|
475
|
-
}
|
|
483
|
+
};
|
|
476
484
|
}
|
|
477
485
|
|
|
478
|
-
if(options.sqlDialect === 'hana' && options.withHanaAssociations === false && doA2J)
|
|
486
|
+
if (options.sqlDialect === 'hana' && options.withHanaAssociations === false && doA2J)
|
|
479
487
|
killers.target = killParent;
|
|
480
|
-
|
|
488
|
+
|
|
481
489
|
|
|
482
490
|
const killTypes = [];
|
|
483
491
|
|
|
484
|
-
if(doA2J) { // replace types and aspects with dummies to shrink overall CSN size
|
|
492
|
+
if (doA2J) { // replace types and aspects with dummies to shrink overall CSN size
|
|
485
493
|
killers.kind = (parent, prop, kind, path) => {
|
|
486
|
-
if(kind === 'type' || kind === 'aspect') {
|
|
494
|
+
if (kind === 'type' || kind === 'aspect') {
|
|
487
495
|
const artifactName = path[1];
|
|
488
496
|
killTypes.push(() => {
|
|
489
497
|
csn.definitions[artifactName] = {
|
|
490
498
|
kind,
|
|
491
|
-
type: 'cds.Integer'
|
|
499
|
+
type: 'cds.Integer',
|
|
492
500
|
};
|
|
493
|
-
})
|
|
501
|
+
});
|
|
494
502
|
}
|
|
495
|
-
}
|
|
503
|
+
};
|
|
496
504
|
}
|
|
497
505
|
|
|
498
506
|
applyTransformations(csn, killers, [], { skipIgnore: false });
|
|
@@ -506,19 +514,20 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
506
514
|
|
|
507
515
|
/* ----------------------------------- Functions start here -----------------------------------------------*/
|
|
508
516
|
|
|
509
|
-
function bindCsnReference(){
|
|
517
|
+
function bindCsnReference() {
|
|
510
518
|
messageFunctions.setModel(csn);
|
|
511
519
|
({ error, throwWithAnyError } = messageFunctions);
|
|
512
520
|
|
|
513
|
-
({
|
|
521
|
+
({
|
|
522
|
+
flattenStructuredElement,
|
|
514
523
|
flattenStructStepsInRef,
|
|
515
524
|
addDefaultTypeFacets,
|
|
516
525
|
expandStructsInExpression,
|
|
517
|
-
csnUtils
|
|
526
|
+
csnUtils,
|
|
518
527
|
} = transformUtils.getTransformers(csn, options, messageFunctions, pathDelimiter));
|
|
519
528
|
}
|
|
520
529
|
|
|
521
|
-
function bindCsnReferenceOnly(){
|
|
530
|
+
function bindCsnReferenceOnly() {
|
|
522
531
|
// invalidate caches for CSN ref API
|
|
523
532
|
const csnRefApi = csnRefs(csn);
|
|
524
533
|
Object.assign(csnUtils, csnRefApi);
|
|
@@ -533,45 +542,42 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
533
542
|
if (mixin) {
|
|
534
543
|
query.SELECT.columns
|
|
535
544
|
// filter for associations which are used in the SELECT
|
|
536
|
-
.filter(
|
|
537
|
-
return c.ref && c.ref.length > 1;
|
|
538
|
-
})
|
|
545
|
+
.filter(c => c.ref && c.ref.length > 1)
|
|
539
546
|
.forEach((usedAssoc) => {
|
|
540
547
|
const assocName = pathId(usedAssoc.ref[0]);
|
|
541
548
|
const mixinAssociation = mixin[assocName];
|
|
542
549
|
if (mixinAssociation)
|
|
543
|
-
mixinAssociation.on = getResolvedMixinOnCondition(mixinAssociation, query, assocName, path.concat(['mixin', assocName]));
|
|
544
|
-
})
|
|
550
|
+
mixinAssociation.on = getResolvedMixinOnCondition(mixinAssociation, query, assocName, path.concat([ 'mixin', assocName ]));
|
|
551
|
+
});
|
|
545
552
|
}
|
|
546
|
-
}, ['definitions', artifactName, 'query']);
|
|
553
|
+
}, [ 'definitions', artifactName, 'query' ]);
|
|
547
554
|
}
|
|
548
555
|
|
|
549
556
|
// For non-A2J only
|
|
550
557
|
function getResolvedMixinOnCondition(mixinAssociation, query, assocName, path) {
|
|
551
|
-
const referencedThroughStar = query.SELECT.columns.some(
|
|
558
|
+
const referencedThroughStar = query.SELECT.columns.some(column => column === '*');
|
|
552
559
|
return mixinAssociation.on.map(handeMixinOnConditionPart);
|
|
553
560
|
|
|
554
|
-
function handeMixinOnConditionPart(onConditionPart, i)
|
|
561
|
+
function handeMixinOnConditionPart(onConditionPart, i) {
|
|
555
562
|
let columnToReplace;
|
|
556
|
-
if (onConditionPart.ref && (onConditionPart.ref[0] === '$projection' || onConditionPart.ref[0] === '$self')){
|
|
557
|
-
const { links } = csnUtils.inspectRef(path.concat(['on', i]));
|
|
558
|
-
if (links)
|
|
563
|
+
if (onConditionPart.ref && (onConditionPart.ref[0] === '$projection' || onConditionPart.ref[0] === '$self')) {
|
|
564
|
+
const { links } = csnUtils.inspectRef(path.concat([ 'on', i ]));
|
|
565
|
+
if (links)
|
|
559
566
|
columnToReplace = onConditionPart.ref[links.length - 1];
|
|
560
|
-
}
|
|
561
567
|
}
|
|
562
568
|
if (!columnToReplace)
|
|
563
569
|
return onConditionPart;
|
|
564
570
|
|
|
565
571
|
const replaceWith = query.SELECT.columns.find(col => columnAlias(col) === columnToReplace);
|
|
566
572
|
if (!replaceWith && referencedThroughStar) {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
573
|
+
// not explicitly in column list, check query sources
|
|
574
|
+
// get$combined also includes elements which are part of "excluding {}"
|
|
575
|
+
// this shouldn't be an issue here, as such references get rejected
|
|
570
576
|
const elementsOfQuerySources = csnUtils.get$combined(query);
|
|
571
577
|
forEach(elementsOfQuerySources, (id, element) => {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
if(id === columnToReplace)
|
|
578
|
+
// if the ref points to an element which is not explicitly exposed in the column list,
|
|
579
|
+
// but through the '*' operator -> replace the $projection / $self with the correct source entity
|
|
580
|
+
if (id === columnToReplace)
|
|
575
581
|
onConditionPart.ref[0] = element[0].parent;
|
|
576
582
|
});
|
|
577
583
|
return onConditionPart;
|
|
@@ -582,9 +588,8 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
582
588
|
delete clone.as;
|
|
583
589
|
return clone;
|
|
584
590
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
591
|
+
|
|
592
|
+
return onConditionPart;
|
|
588
593
|
}
|
|
589
594
|
}
|
|
590
595
|
|
|
@@ -597,29 +602,29 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
597
602
|
if (!artifact.$ignore) {
|
|
598
603
|
// Do things specific for entities and views (pass 2)
|
|
599
604
|
if ((artifact.kind === 'entity') && artifact.query) {
|
|
600
|
-
|
|
601
605
|
// First pass: Set alias name for SELECTs without table alias. Required for setting proper table aliases
|
|
602
606
|
// for HDBCDS in naming mode HDBCDS. We use the same schema as the core-compiler, so duplicates should
|
|
603
607
|
// have already been reported.
|
|
604
|
-
if(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds') {
|
|
608
|
+
if (options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds') {
|
|
605
609
|
let selectDepth = 0;
|
|
606
610
|
traverseQuery(artifact.query, null, null, (query, fromSelect) => {
|
|
607
611
|
if (!query.ref && !query.as && fromSelect) {
|
|
608
612
|
// Use +1; for UNION, it's the next select, for SELECT, it's increased later.
|
|
609
|
-
query.as = `$_select_${selectDepth + 1}__`;
|
|
613
|
+
query.as = `$_select_${ selectDepth + 1 }__`;
|
|
610
614
|
}
|
|
611
|
-
if (query.SELECT)
|
|
615
|
+
if (query.SELECT)
|
|
616
|
+
++selectDepth;
|
|
612
617
|
});
|
|
613
618
|
}
|
|
614
619
|
|
|
615
620
|
const process = (parent, prop, query, path) => {
|
|
616
|
-
transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop))
|
|
621
|
+
transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop));
|
|
617
622
|
replaceAssociationsInGroupByOrderBy(parent, options, csnUtils.inspectRef, error, path.concat(prop));
|
|
618
623
|
return query;
|
|
619
|
-
}
|
|
624
|
+
};
|
|
620
625
|
applyTransformationsOnNonDictionary(csn.definitions, artifactName, {
|
|
621
|
-
SELECT: process
|
|
622
|
-
}, {}, [ 'definitions']);
|
|
626
|
+
SELECT: process,
|
|
627
|
+
}, {}, [ 'definitions' ]);
|
|
623
628
|
}
|
|
624
629
|
}
|
|
625
630
|
}
|
|
@@ -654,9 +659,9 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
654
659
|
|
|
655
660
|
// restore all (non-enumerable) properties that wouldn't survive reaugmentation/compactification into the new compact model
|
|
656
661
|
forEachDefinition(csn, (art, artName) => {
|
|
657
|
-
if (art
|
|
658
|
-
newCsn.definitions[artName].$tableConstraints = art
|
|
659
|
-
|
|
662
|
+
if (art.$tableConstraints)
|
|
663
|
+
newCsn.definitions[artName].$tableConstraints = art.$tableConstraints;
|
|
664
|
+
|
|
660
665
|
if (art.technicalConfig)
|
|
661
666
|
newCsn.definitions[artName].technicalConfig = art.technicalConfig;
|
|
662
667
|
});
|
|
@@ -677,7 +682,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
677
682
|
// assert key === 'type'
|
|
678
683
|
const hanaNamesMap = {
|
|
679
684
|
__proto__: null,
|
|
680
|
-
'cds.UUID': 'cds.String'
|
|
685
|
+
'cds.UUID': 'cds.String',
|
|
681
686
|
};
|
|
682
687
|
node[key] = hanaNamesMap[val] || val;
|
|
683
688
|
if (val === 'cds.UUID' && !node.length) {
|
|
@@ -685,9 +690,9 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
685
690
|
setProp(node, '$renamed', 'cds.UUID');
|
|
686
691
|
}
|
|
687
692
|
|
|
688
|
-
if(options.sqlDialect === 'h2' && val === 'cds.Decimal' && node.scale === undefined)
|
|
693
|
+
if (options.sqlDialect === 'h2' && val === 'cds.Decimal' && node.scale === undefined)
|
|
689
694
|
node[key] = 'cds.DecimalFloat'; // cds.Decimal and cds.Decimal(p) should map do DECFLOAT for h2
|
|
690
|
-
|
|
695
|
+
|
|
691
696
|
|
|
692
697
|
// Length/Precision/Scale is done in addDefaultTypeFacets
|
|
693
698
|
}
|
|
@@ -742,23 +747,27 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
742
747
|
if (options.sqlDialect === 'plain')
|
|
743
748
|
error('ref-unsupported-type', path, { '#': 'dialect', type: node.type, value: 'plain' });
|
|
744
749
|
else if (options.transformation === 'hdbcds')
|
|
745
|
-
error('ref-unsupported-type', path, {'#': 'hdbcds', type: node.type, value: options.sqlDialect });
|
|
750
|
+
error('ref-unsupported-type', path, { '#': 'hdbcds', type: node.type, value: options.sqlDialect });
|
|
746
751
|
break;
|
|
747
752
|
}
|
|
748
753
|
case 'cds.Vector': {
|
|
749
754
|
if (options.sqlDialect !== 'hana') {
|
|
750
755
|
error('ref-unsupported-type', path, {
|
|
751
|
-
'#': 'hana',
|
|
752
|
-
|
|
756
|
+
'#': 'hana',
|
|
757
|
+
type: node.type,
|
|
758
|
+
value: 'hana',
|
|
759
|
+
othervalue: options.sqlDialect,
|
|
753
760
|
});
|
|
754
761
|
}
|
|
755
762
|
else if (options.transformation === 'hdbcds') {
|
|
756
763
|
error('ref-unsupported-type', path, {
|
|
757
|
-
'#': 'hdbcds', type: node.type, value: options.sqlDialect
|
|
764
|
+
'#': 'hdbcds', type: node.type, value: options.sqlDialect,
|
|
758
765
|
});
|
|
759
766
|
}
|
|
760
767
|
break;
|
|
761
768
|
}
|
|
769
|
+
default:
|
|
770
|
+
break; // nothing to check for unknown types
|
|
762
771
|
}
|
|
763
772
|
}
|
|
764
773
|
|
|
@@ -767,21 +776,22 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
767
776
|
function checkTypeParamValue(node, paramName, range = null, path = null) {
|
|
768
777
|
const paramValue = node[paramName];
|
|
769
778
|
if (paramValue == null) {
|
|
770
|
-
if(options.toSql || artifact.query || !['cds.Binary','cds.hana.BINARY', 'cds.hana.NCHAR','cds.hana.CHAR'].includes(node.type))
|
|
779
|
+
if (options.toSql || artifact.query || ![ 'cds.Binary', 'cds.hana.BINARY', 'cds.hana.NCHAR', 'cds.hana.CHAR' ].includes(node.type))
|
|
771
780
|
return true;
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
781
|
+
|
|
782
|
+
return error('type-missing-argument', path, { name: paramName, id: node.type, $reviewed: false });
|
|
775
783
|
}
|
|
776
784
|
if (range) {
|
|
777
785
|
if (isMaxParameterLengthRestricted(node.type) && range.max && paramValue > range.max) {
|
|
778
|
-
error('type-unexpected-argument', path,
|
|
779
|
-
|
|
786
|
+
error('type-unexpected-argument', path, {
|
|
787
|
+
'#': 'max', prop: paramName, type: node.type, number: range.max, $reviewed: false,
|
|
788
|
+
});
|
|
780
789
|
return false;
|
|
781
790
|
}
|
|
782
791
|
if (range.min && paramValue < range.min) {
|
|
783
|
-
error('type-unexpected-argument', path,
|
|
784
|
-
|
|
792
|
+
error('type-unexpected-argument', path, {
|
|
793
|
+
'#': 'min', prop: paramName, type: node.type, number: range.min, $reviewed: false,
|
|
794
|
+
});
|
|
785
795
|
return false;
|
|
786
796
|
}
|
|
787
797
|
}
|
|
@@ -816,9 +826,10 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
816
826
|
if (Array.isArray(index)) {
|
|
817
827
|
const flattenedIndex = [];
|
|
818
828
|
const isFulltextIndex = (index[0] === 'fulltext');
|
|
819
|
-
index.
|
|
829
|
+
for (let idx = 0; idx < index.length; idx++) {
|
|
830
|
+
const val = index[idx];
|
|
820
831
|
if (typeof val === 'object' && val.ref) {
|
|
821
|
-
// Replace a reference by references to
|
|
832
|
+
// Replace a reference by references to its elements, if it is structured
|
|
822
833
|
const path = [ 'definitions', artName, 'technicalConfig', dialect, 'indexes', name, idx ];
|
|
823
834
|
const { art } = csnUtils.inspectRef(path);
|
|
824
835
|
if (!art) {
|
|
@@ -832,7 +843,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
832
843
|
// First, compute the name from the path, e.g ['s', 's1', 's2' ] will result in 'S_s1_s2' ...
|
|
833
844
|
const [ refPath ] = flattenStructStepsInRef(val.ref, path);
|
|
834
845
|
// ... and take this as the prefix for all elements
|
|
835
|
-
const flattenedElems = flattenStructuredElement(art, refPath, [], ['definitions', artName, 'elements']);
|
|
846
|
+
const flattenedElems = flattenStructuredElement(art, refPath, [], [ 'definitions', artName, 'elements' ]);
|
|
836
847
|
Object.keys(flattenedElems).forEach((elem, i, elems) => {
|
|
837
848
|
// if it's not the first entry, add a ',' ...
|
|
838
849
|
if (i)
|
|
@@ -850,11 +861,10 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
850
861
|
flattenedIndex.push({ ref: refPath });
|
|
851
862
|
}
|
|
852
863
|
}
|
|
853
|
-
else // it's just some token like 'index', '(' etc. so we copy it over
|
|
854
|
-
{
|
|
864
|
+
else { // it's just some token like 'index', '(' etc. so we copy it over
|
|
855
865
|
flattenedIndex.push(val);
|
|
856
866
|
}
|
|
857
|
-
}
|
|
867
|
+
}
|
|
858
868
|
// Replace index by the flattened one
|
|
859
869
|
tc[dialect].indexes[name] = flattenedIndex;
|
|
860
870
|
}
|
|
@@ -862,7 +872,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
862
872
|
}
|
|
863
873
|
}
|
|
864
874
|
}
|
|
865
|
-
|
|
866
875
|
}
|
|
867
876
|
|
|
868
877
|
module.exports = {
|