@sap/cds-compiler 6.9.3 → 7.0.1
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 +76 -2
- package/bin/cdsc.js +4 -33
- package/doc/IncompatibleChanges_v7.md +639 -0
- package/lib/api/main.js +4 -56
- package/lib/api/options.js +5 -15
- package/lib/api/validate.js +1 -0
- package/lib/base/builtins.js +1 -2
- package/lib/base/csnRefs.js +2 -6
- package/lib/base/message-registry.js +82 -76
- package/lib/base/messages.js +8 -5
- package/lib/base/optionProcessor.js +2 -72
- package/lib/base/specialOptions.js +20 -17
- package/lib/checks/defaultValues.js +1 -39
- package/lib/checks/hasPersistedElements.js +19 -3
- package/lib/checks/parameters.js +0 -34
- package/lib/checks/selectItems.js +2 -38
- package/lib/checks/typeParameters.js +162 -0
- package/lib/checks/validator.js +5 -8
- package/lib/compiler/assert-consistency.js +19 -5
- package/lib/compiler/checks.js +47 -43
- package/lib/compiler/define.js +6 -6
- package/lib/compiler/extend.js +102 -111
- package/lib/compiler/generate.js +4 -8
- package/lib/compiler/populate.js +4 -7
- package/lib/compiler/propagator.js +9 -9
- package/lib/compiler/resolve.js +205 -7
- package/lib/compiler/shared.js +76 -82
- package/lib/compiler/tweak-assocs.js +102 -22
- package/lib/compiler/utils.js +57 -12
- package/lib/compiler/xpr-rewrite.js +2 -15
- package/lib/edm/annotations/edmJson.js +14 -10
- package/lib/edm/annotations/genericTranslation.js +3 -1
- package/lib/edm/annotations/preprocessAnnotations.js +9 -26
- package/lib/edm/csn2edm.js +27 -20
- package/lib/edm/edmUtils.js +25 -0
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2237 -2241
- package/lib/gen/Dictionary.json +17 -2
- package/lib/json/from-csn.js +67 -52
- package/lib/json/to-csn.js +28 -25
- package/lib/language/textUtils.js +0 -13
- package/lib/main.d.ts +22 -59
- package/lib/main.js +1 -1
- package/lib/model/csnUtils.js +9 -8
- package/lib/parsers/AstBuildingParser.js +45 -55
- package/lib/parsers/Lexer.js +2 -0
- package/lib/parsers/identifiers.js +0 -9
- package/lib/render/toCdl.js +41 -40
- package/lib/render/toSql.js +8 -1
- package/lib/render/utils/common.js +1 -1
- package/lib/render/utils/sql.js +2 -3
- package/lib/tool-lib/enrichCsn.js +1 -2
- package/lib/transform/db/applyTransformations.js +7 -5
- package/lib/transform/db/assertUnique.js +8 -51
- package/lib/transform/db/associations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -15
- package/lib/transform/db/expansion.js +9 -12
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/groupByOrderBy.js +0 -16
- package/lib/transform/db/views.js +57 -161
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdata.js +25 -14
- package/lib/transform/forRelationalDB.js +93 -301
- package/lib/transform/localized.js +33 -102
- package/lib/transform/odata/flattening.js +11 -2
- package/lib/transform/transformUtils.js +25 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
- package/package.json +2 -2
- package/lib/render/toHdbcds.js +0 -1810
|
@@ -3,20 +3,19 @@
|
|
|
3
3
|
const { isBetaEnabled } = require('../base/specialOptions');
|
|
4
4
|
const shuffleGen = require('../utils/shuffle');
|
|
5
5
|
const {
|
|
6
|
-
|
|
6
|
+
applyTransformationsOnNonDictionary,
|
|
7
7
|
getArtifactDatabaseNameOf, getElementDatabaseNameOf, applyTransformations,
|
|
8
|
-
walkCsnPath,
|
|
8
|
+
walkCsnPath,
|
|
9
9
|
} = require('../model/csnUtils');
|
|
10
10
|
const transformUtils = require('./transformUtils');
|
|
11
11
|
const { translateAssocsToJoinsCSN } = require('./translateAssocsToJoins');
|
|
12
12
|
const {
|
|
13
|
-
csnRefs,
|
|
13
|
+
csnRefs, traverseQuery,
|
|
14
14
|
} = require('../base/csnRefs');
|
|
15
15
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
16
16
|
const validate = require('../checks/validator');
|
|
17
|
-
const { rejectManagedAssociationsAndStructuresForHdbcdsNames } = require('../checks/selectItems');
|
|
18
17
|
const { addTenantFields } = require('../transform/addTenantFields');
|
|
19
|
-
const { addLocalizationViewsWithJoins
|
|
18
|
+
const { addLocalizationViewsWithJoins } = require('../transform/localized');
|
|
20
19
|
const { timetrace } = require('../utils/timetrace');
|
|
21
20
|
const { createReferentialConstraints, assertConstraintIdentifierUniqueness } = require('./db/constraints');
|
|
22
21
|
const { setProp, forEach } = require('../utils/objectUtils');
|
|
@@ -36,7 +35,7 @@ const associations = require('./db/associations');
|
|
|
36
35
|
const backlinks = require('./db/backlinks');
|
|
37
36
|
const { getDefaultTypeLengths } = require('../render/utils/common');
|
|
38
37
|
const { featureFlags, removeFeatureFlags } = require('./featureFlags');
|
|
39
|
-
const {
|
|
38
|
+
const { cloneFullCsn } = require('../base/cloneCsn');
|
|
40
39
|
const { processSqlServices, createServiceDummy } = require('./db/processSqlServices');
|
|
41
40
|
const { expandWildcard } = require('./db/expansion');
|
|
42
41
|
const removeEnums = require('../checks/enums');
|
|
@@ -105,14 +104,17 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
105
104
|
const setQueryFeatureFlags = getQueryFeatureFlagSetter(csn);
|
|
106
105
|
const setDefinitionFeatureFlags = getDefinitionFeatureFlagSetter(csn, options);
|
|
107
106
|
|
|
108
|
-
// csnRefs cache init
|
|
107
|
+
// csnRefs cache init and feature flags in one loop
|
|
109
108
|
// as preparation for the following transformations and validations
|
|
110
109
|
forEachDefinition(csn, (def) => {
|
|
111
110
|
csnUtils.initDefinition(def);
|
|
112
111
|
if (def.query || def.projection) {
|
|
113
112
|
traverseQuery(def.query || def, null, null, (query) => {
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
// Normalize implicit wildcards (no columns → ['*']) so downstream code
|
|
114
|
+
// can always rely on an explicit columns array being present.
|
|
115
|
+
const SELECT = query.SELECT ?? query.projection;
|
|
116
|
+
if (SELECT)
|
|
117
|
+
SELECT.columns ??= [ '*' ];
|
|
116
118
|
setQueryFeatureFlags(query);
|
|
117
119
|
});
|
|
118
120
|
}
|
|
@@ -120,9 +122,22 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
120
122
|
});
|
|
121
123
|
|
|
122
124
|
const dialect = options.sqlDialect;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
|
|
126
|
+
const hasCalcElements = csn.meta?.[featureFlags]?.$calculatedElements;
|
|
127
|
+
|
|
128
|
+
// Only expand wildcards early for views whose inferred elements contain calculated
|
|
129
|
+
// elements (have $calc). All other views defer expansion past validation to keep
|
|
130
|
+
// column lists compact during the expensive enrichment + validation passes.
|
|
131
|
+
if (hasCalcElements) {
|
|
132
|
+
forEachDefinition(csn, (def) => {
|
|
133
|
+
if ((def.query || def.projection) && defHasCalculatedElements(def)) {
|
|
134
|
+
traverseQuery(def.query || def, null, null, (query) => {
|
|
135
|
+
expandWildcard(query, csnUtils, options);
|
|
136
|
+
ensureColumnNames(query, csnUtils, options);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
126
141
|
|
|
127
142
|
// replace all type refs to builtin types with direct type
|
|
128
143
|
transformUtils.rewriteBuiltinTypeRef(csn);
|
|
@@ -137,7 +152,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
137
152
|
// exit if validators found errors
|
|
138
153
|
throwWithAnyError();
|
|
139
154
|
|
|
140
|
-
if (
|
|
155
|
+
if (hasCalcElements)
|
|
141
156
|
rewriteCalculatedElementsInViews(csn, options, csnUtils, pathDelimiter, messageFunctions);
|
|
142
157
|
|
|
143
158
|
timetrace.start('Where-Exists handling');
|
|
@@ -163,12 +178,21 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
163
178
|
assertUnique.prepare(csn, options, messageFunctions),
|
|
164
179
|
]);
|
|
165
180
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
// Deferred wildcard expansion for any remaining unexpanded queries.
|
|
182
|
+
// expandWildcard() returns early when no '*' exists, so already-expanded views are a no-op.
|
|
183
|
+
forEachDefinition(csn, (def) => {
|
|
184
|
+
if (def.query || def.projection) {
|
|
185
|
+
traverseQuery(def.query || def, null, null, (query) => {
|
|
186
|
+
expandWildcard(query, csnUtils, options);
|
|
187
|
+
ensureColumnNames(query, csnUtils, options);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Expand a structured thing in: keys, columns, order by, group by
|
|
193
|
+
// In addition, kill all non-sql-backend relevant annotations
|
|
194
|
+
expansion.expandStructureReferences(csn, options, pathDelimiter, messageFunctions, csnUtils, { processAnnotations: true });
|
|
195
|
+
bindCsnReference();
|
|
172
196
|
|
|
173
197
|
timetrace.stop('Expand Structures (expressions + refs)');
|
|
174
198
|
|
|
@@ -196,7 +220,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
196
220
|
// With that, we could still ensure the processing order (assuming we don't run into problems with scoping).
|
|
197
221
|
// To analyze: Increased memory vs. saved cycles
|
|
198
222
|
// Looked at it with AFC: This is only a small part of the overall processing time, enrich step of validator is just as expensive
|
|
199
|
-
|
|
223
|
+
{
|
|
200
224
|
const resolved = new WeakMap();
|
|
201
225
|
// No refs with struct-steps exist anymore
|
|
202
226
|
flattening.flattenAllStructStepsInRefs(csn, options, messageFunctions, resolved, pathDelimiter);
|
|
@@ -206,17 +230,12 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
206
230
|
// No structured elements exists anymore
|
|
207
231
|
flattening.flattenElements(csn, options, messageFunctions, pathDelimiter);
|
|
208
232
|
}
|
|
209
|
-
else {
|
|
210
|
-
// For to.hdbcds with naming mode hdbcds we also need to resolve the types
|
|
211
|
-
flattening.resolveTypeReferences(csn, options, messageFunctions, new WeakMap(), pathDelimiter);
|
|
212
|
-
}
|
|
213
233
|
timetrace.stop('Flattening (refs + elements)');
|
|
214
234
|
|
|
215
235
|
// With flattening errors, it makes little sense to continue.
|
|
216
236
|
throwWithAnyError();
|
|
217
237
|
|
|
218
|
-
|
|
219
|
-
handleAssocToJoins();
|
|
238
|
+
handleAssocToJoins();
|
|
220
239
|
|
|
221
240
|
if (options.testMode && csn.definitions)
|
|
222
241
|
csn.definitions = shuffleDict(csn.definitions);
|
|
@@ -269,25 +288,22 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
269
288
|
temporal.getAnnotationHandler(csn, options, pathDelimiter, messageFunctions),
|
|
270
289
|
]);
|
|
271
290
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
orderBy: expandManagedToFksInArray(true),
|
|
289
|
-
}, [], { allowArtifact: artifact => artifact.query !== undefined || artifact.projection !== undefined });
|
|
290
|
-
}
|
|
291
|
+
flattening.handleManagedAssociationsAndCreateForeignKeys(
|
|
292
|
+
csn, options, messageFunctions, pathDelimiter, true, csnUtils,
|
|
293
|
+
{ skipDict: { actions: true }, allowArtifact: artifact => (artifact.kind === 'entity') }
|
|
294
|
+
);
|
|
295
|
+
forEachDefinition(csn, flattenIndexes);
|
|
296
|
+
// Managed associations get an on-condition - in views and entities
|
|
297
|
+
associations.attachOnConditions(csn, csnUtils, pathDelimiter);
|
|
298
|
+
|
|
299
|
+
// Add the foreign keys also to the columns if the association itself was explicitly selected
|
|
300
|
+
// TODO: Extend the expansion to also expand managed to their foreign
|
|
301
|
+
// TODO: Remember where in .elements we had associations and do it then?
|
|
302
|
+
applyTransformations(csn, {
|
|
303
|
+
columns: expandManagedToFksInArray(),
|
|
304
|
+
groupBy: expandManagedToFksInArray(true),
|
|
305
|
+
orderBy: expandManagedToFksInArray(true),
|
|
306
|
+
}, [], { allowArtifact: artifact => artifact.query !== undefined || artifact.projection !== undefined });
|
|
291
307
|
|
|
292
308
|
{
|
|
293
309
|
// Strip all query-ish properties from views and projections annotated with '@cds.persistence.table',
|
|
@@ -296,8 +312,7 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
296
312
|
// Allow using managed associations as steps in on-conditions to access their fks
|
|
297
313
|
// To be done after handleAssociations, since then the foreign keys of the managed assocs
|
|
298
314
|
// are part of the elements
|
|
299
|
-
|
|
300
|
-
fns.push(associations.getFKAccessFinalizer(csn, options, csnUtils, pathDelimiter, true));
|
|
315
|
+
fns.push(associations.getFKAccessFinalizer(csn, options, csnUtils, pathDelimiter, true));
|
|
301
316
|
|
|
302
317
|
forEachDefinition(csn, fns);
|
|
303
318
|
}
|
|
@@ -307,33 +322,19 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
307
322
|
// handled and before handleDBChecks which removes the localized attribute.
|
|
308
323
|
// Association elements of localized convenience views do not have hidden properties
|
|
309
324
|
// like $managed set, so we cannot do this earlier on.
|
|
310
|
-
|
|
311
|
-
addLocalizationViewsWithJoins(csn, options);
|
|
312
|
-
else
|
|
313
|
-
addLocalizationViews(csn, options);
|
|
325
|
+
addLocalizationViewsWithJoins(csn, options);
|
|
314
326
|
|
|
315
327
|
forEachDefinition(csn, [
|
|
316
|
-
(definition, artName, prop, path) => {
|
|
317
|
-
if (!doA2J && definition.query && isPersistedOnDatabase(definition)) {
|
|
318
|
-
// reject managed association and structure publishing for to-hdbcds.hdbcds
|
|
319
|
-
const that = { csnUtils, options, error };
|
|
320
|
-
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(that, definition, path);
|
|
321
|
-
}
|
|
322
|
-
},
|
|
323
328
|
// Transform '$self' in backlink associations to appropriate key comparisons
|
|
324
329
|
// Must happen before draft processing because the artificial ON-conditions in generated
|
|
325
330
|
// draft shadow entities have crooked '_artifact' links, confusing the backlink processing.
|
|
326
331
|
// But it must also happen after flattenForeignKeys has been called for all artifacts,
|
|
327
332
|
// because otherwise we would produce wrong ON-conditions for the keys involved. Sigh ...
|
|
328
|
-
backlinks.getBacklinkTransformer(csnUtils, messageFunctions, options, pathDelimiter,
|
|
333
|
+
backlinks.getBacklinkTransformer(csnUtils, messageFunctions, options, pathDelimiter, true),
|
|
329
334
|
]);
|
|
330
335
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
* For to.hdbcds with naming mode "hdbcds", no foreign keys are calculated,
|
|
334
|
-
* hence we do not generate the referential constraints for them.
|
|
335
|
-
*/
|
|
336
|
-
if (options.sqlDialect !== 'plain' && options.sqlDialect !== 'h2' && doA2J)
|
|
336
|
+
// Referential Constraints are only supported for sql-dialect "hana" and "sqlite".
|
|
337
|
+
if (options.sqlDialect !== 'plain' && options.sqlDialect !== 'h2')
|
|
337
338
|
createReferentialConstraints(csn, options);
|
|
338
339
|
|
|
339
340
|
// no constraints for drafts
|
|
@@ -364,31 +365,8 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
364
365
|
generateFioriTreeViews(artifact, artifactName, messageFunctions, csnUtils, { setAnnotation, addElement, createScalarElement }, options);
|
|
365
366
|
}, transformViews ]);
|
|
366
367
|
|
|
367
|
-
if (!doA2J) {
|
|
368
|
-
forEachDefinition(csn, [
|
|
369
|
-
// Strip 'key' property from type elements
|
|
370
|
-
removeKeyPropInType,
|
|
371
|
-
(artifact, artifactName) => {
|
|
372
|
-
if (artifact.kind === 'type') {
|
|
373
|
-
forEachMemberRecursively(artifact, (member, memberName, prop, path) => {
|
|
374
|
-
// Check type parameters (length, precision, scale ...)
|
|
375
|
-
if (!member.$ignore) {
|
|
376
|
-
if (member.type)
|
|
377
|
-
checkTypeParameters(member, artifact, path);
|
|
378
|
-
if (member.items?.type)
|
|
379
|
-
checkTypeParameters(member.items, artifact, path.concat([ 'items' ]));
|
|
380
|
-
}
|
|
381
|
-
}, [ 'definitions', artifactName ]);
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
]);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
368
|
// TODO: Could we maybe merge this with the final applyTransformations?
|
|
388
369
|
applyTransformations(csn, {
|
|
389
|
-
type: (parent, prop, type, path) => {
|
|
390
|
-
checkTypeParameters(parent, csn.definitions[path[1]], path);
|
|
391
|
-
},
|
|
392
370
|
$tableConstraints: (parent, prop, tableConstraints, path) => {
|
|
393
371
|
/* assert that there will be no conflicting unique- and foreign key constraint identifiers */
|
|
394
372
|
assertConstraintIdentifierUniqueness(parent, path[1], path, error);
|
|
@@ -400,9 +378,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
400
378
|
if ((!element.virtual || artifact.query))
|
|
401
379
|
csnUtils.addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(name, options.sqlMapping, options.sqlDialect), element);
|
|
402
380
|
});
|
|
403
|
-
// Remove leading $self to keep renderer-diffs smaller
|
|
404
|
-
if (doA2J && options.transformation === 'hdbcds')
|
|
405
|
-
flattening.removeLeadingSelf(parent, prop, elements);
|
|
406
381
|
},
|
|
407
382
|
}, [ (definitions, artifactName, artifact) => {
|
|
408
383
|
// Attach @cds.persistence.name to artifacts
|
|
@@ -456,7 +431,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
456
431
|
}
|
|
457
432
|
},
|
|
458
433
|
includes: killProp,
|
|
459
|
-
masked: killProp,
|
|
460
434
|
localized: killProp,
|
|
461
435
|
};
|
|
462
436
|
|
|
@@ -467,25 +441,24 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
467
441
|
};
|
|
468
442
|
}
|
|
469
443
|
|
|
470
|
-
if (options.sqlDialect === 'hana' && options.withHanaAssociations === false
|
|
444
|
+
if (options.sqlDialect === 'hana' && options.withHanaAssociations === false)
|
|
471
445
|
killers.target = killParent;
|
|
472
446
|
|
|
473
447
|
|
|
474
448
|
const killTypes = [];
|
|
475
449
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
450
|
+
// replace types and aspects with dummies to shrink overall CSN size
|
|
451
|
+
killers.kind = (parent, prop, kind, path) => {
|
|
452
|
+
if (kind === 'type' || kind === 'aspect') {
|
|
453
|
+
const artifactName = path[1];
|
|
454
|
+
killTypes.push(() => {
|
|
455
|
+
csn.definitions[artifactName] = {
|
|
456
|
+
kind,
|
|
457
|
+
type: 'cds.Integer',
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
};
|
|
489
462
|
|
|
490
463
|
applyTransformations(csn, killers, [], { skipIgnore: false });
|
|
491
464
|
|
|
@@ -524,66 +497,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
524
497
|
Object.assign(csnUtils, csnRefApi);
|
|
525
498
|
}
|
|
526
499
|
|
|
527
|
-
// For non-A2J only
|
|
528
|
-
function handleMixinOnConditions(artifact, artifactName) {
|
|
529
|
-
if (!artifact.query) // projections can't have mixins
|
|
530
|
-
return;
|
|
531
|
-
forAllQueries(artifact.query, (query, path) => {
|
|
532
|
-
const { mixin } = query.SELECT || {};
|
|
533
|
-
if (mixin) {
|
|
534
|
-
query.SELECT.columns
|
|
535
|
-
// filter for associations which are used in the SELECT
|
|
536
|
-
.filter(c => c.ref && c.ref.length > 1)
|
|
537
|
-
.forEach((usedAssoc) => {
|
|
538
|
-
const assocName = pathId(usedAssoc.ref[0]);
|
|
539
|
-
const mixinAssociation = mixin[assocName];
|
|
540
|
-
if (mixinAssociation)
|
|
541
|
-
mixinAssociation.on = getResolvedMixinOnCondition(mixinAssociation, query, assocName, path.concat([ 'mixin', assocName ]));
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
}, [ 'definitions', artifactName, 'query' ]);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// For non-A2J only
|
|
548
|
-
function getResolvedMixinOnCondition(mixinAssociation, query, assocName, path) {
|
|
549
|
-
const referencedThroughStar = query.SELECT.columns.some(column => column === '*');
|
|
550
|
-
return mixinAssociation.on.map(handeMixinOnConditionPart);
|
|
551
|
-
|
|
552
|
-
function handeMixinOnConditionPart(onConditionPart, i) {
|
|
553
|
-
let columnToReplace;
|
|
554
|
-
if (onConditionPart.ref && (onConditionPart.ref[0] === '$projection' || onConditionPart.ref[0] === '$self')) {
|
|
555
|
-
const { links } = csnUtils.inspectRef(path.concat([ 'on', i ]));
|
|
556
|
-
if (links)
|
|
557
|
-
columnToReplace = onConditionPart.ref[links.length - 1];
|
|
558
|
-
}
|
|
559
|
-
if (!columnToReplace)
|
|
560
|
-
return onConditionPart;
|
|
561
|
-
|
|
562
|
-
const replaceWith = query.SELECT.columns.find(col => columnAlias(col) === columnToReplace);
|
|
563
|
-
if (!replaceWith && referencedThroughStar) {
|
|
564
|
-
// not explicitly in column list, check query sources
|
|
565
|
-
// get$combined also includes elements which are part of "excluding {}"
|
|
566
|
-
// this shouldn't be an issue here, as such references get rejected
|
|
567
|
-
const elementsOfQuerySources = csnUtils.get$combined(query);
|
|
568
|
-
forEach(elementsOfQuerySources, (id, element) => {
|
|
569
|
-
// if the ref points to an element which is not explicitly exposed in the column list,
|
|
570
|
-
// but through the '*' operator -> replace the $projection / $self with the correct source entity
|
|
571
|
-
if (id === columnToReplace)
|
|
572
|
-
onConditionPart.ref[0] = element[0].parent;
|
|
573
|
-
});
|
|
574
|
-
return onConditionPart;
|
|
575
|
-
}
|
|
576
|
-
else if (replaceWith) {
|
|
577
|
-
const clone = cloneCsnNonDict(replaceWith, options);
|
|
578
|
-
delete clone.cast; // No implicit CAST in on-condition
|
|
579
|
-
delete clone.as;
|
|
580
|
-
return clone;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
return onConditionPart;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
500
|
|
|
588
501
|
/**
|
|
589
502
|
* @param {CSN.Artifact} artifact
|
|
@@ -593,21 +506,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
593
506
|
if (!artifact.$ignore) {
|
|
594
507
|
// Do things specific for entities and views (pass 2)
|
|
595
508
|
if ((artifact.kind === 'entity') && artifact.query) {
|
|
596
|
-
// First pass: Set alias name for SELECTs without table alias. Required for setting proper table aliases
|
|
597
|
-
// for HDBCDS in naming mode HDBCDS. We use the same schema as the core-compiler, so duplicates should
|
|
598
|
-
// have already been reported.
|
|
599
|
-
if (options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds') {
|
|
600
|
-
let selectDepth = 0;
|
|
601
|
-
traverseQuery(artifact.query, null, null, (query, fromSelect) => {
|
|
602
|
-
if (!query.ref && !query.as && fromSelect) {
|
|
603
|
-
// Use +1; for UNION, it's the next select, for SELECT, it's increased later.
|
|
604
|
-
query.as = `$_select_${ selectDepth + 1 }__`;
|
|
605
|
-
}
|
|
606
|
-
if (query.SELECT)
|
|
607
|
-
++selectDepth;
|
|
608
|
-
});
|
|
609
|
-
}
|
|
610
|
-
|
|
611
509
|
const process = (parent, prop, query, path) => {
|
|
612
510
|
transformEntityOrViewPass2(parent, artifact, artifactName, path.concat(prop));
|
|
613
511
|
replaceAssociationsInGroupByOrderBy(parent, options, csnUtils.inspectRef, error, path.concat(prop));
|
|
@@ -620,19 +518,6 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
620
518
|
}
|
|
621
519
|
}
|
|
622
520
|
|
|
623
|
-
/**
|
|
624
|
-
* @param {CSN.Artifact} artifact
|
|
625
|
-
* @param {string} artifactName
|
|
626
|
-
*/
|
|
627
|
-
function removeKeyPropInType(artifact, artifactName) {
|
|
628
|
-
if (!doA2J && !artifact.$ignore && artifact.kind === 'type') {
|
|
629
|
-
forEachMemberRecursively(artifact, (member) => {
|
|
630
|
-
if (member.key)
|
|
631
|
-
delete member.key;
|
|
632
|
-
}, [ 'definitions', artifactName ]);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
521
|
function handleAssocToJoins() {
|
|
637
522
|
timetrace.start('A2J');
|
|
638
523
|
// the augmentor isn't able to deal with technical configurations and since assoc2join can ignore it we
|
|
@@ -688,116 +573,9 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
688
573
|
// Length/Precision/Scale is done in addDefaultTypeFacets
|
|
689
574
|
}
|
|
690
575
|
|
|
691
|
-
/**
|
|
692
|
-
* Check that required actual parameters on 'node.type' are set, that their values are in the correct range etc.
|
|
693
|
-
|
|
694
|
-
* @param {*} node
|
|
695
|
-
* @param {CSN.Artifact} artifact
|
|
696
|
-
* @param {CSN.Path} path
|
|
697
|
-
*/
|
|
698
|
-
function checkTypeParameters(node, artifact, path) {
|
|
699
|
-
if (node.type && !node.virtual) {
|
|
700
|
-
const absolute = node.type;
|
|
701
|
-
switch (absolute) {
|
|
702
|
-
case 'cds.String':
|
|
703
|
-
case 'cds.Binary':
|
|
704
|
-
case 'cds.hana.VARCHAR': {
|
|
705
|
-
checkTypeParamValue(node, 'length', { min: 1, max: 5000 }, path);
|
|
706
|
-
break;
|
|
707
|
-
}
|
|
708
|
-
case 'cds.Decimal': {
|
|
709
|
-
// Don't check with "plain"?
|
|
710
|
-
if (node.precision || node.scale) {
|
|
711
|
-
checkTypeParamValue(node, 'precision', { max: 38 }, path);
|
|
712
|
-
checkTypeParamValue(node, 'scale', { max: node.precision }, path);
|
|
713
|
-
}
|
|
714
|
-
break;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
case 'cds.hana.BINARY':
|
|
718
|
-
case 'cds.hana.NCHAR':
|
|
719
|
-
case 'cds.hana.CHAR': {
|
|
720
|
-
checkTypeParamValue(node, 'length', { min: 1, max: 2000 }, path);
|
|
721
|
-
break;
|
|
722
|
-
}
|
|
723
|
-
case 'cds.hana.ST_POINT':
|
|
724
|
-
case 'cds.hana.ST_GEOMETRY': {
|
|
725
|
-
checkTypeParamValue(node, 'srid', { max: Number.MAX_SAFE_INTEGER }, path);
|
|
726
|
-
break;
|
|
727
|
-
}
|
|
728
|
-
case 'cds.Map': {
|
|
729
|
-
if (options.sqlDialect === 'plain')
|
|
730
|
-
error('ref-unsupported-type', path, { '#': 'dialect', type: node.type, value: 'plain' });
|
|
731
|
-
else if (options.transformation === 'hdbcds')
|
|
732
|
-
error('ref-unsupported-type', path, { '#': 'hdbcds', type: node.type, value: options.sqlDialect });
|
|
733
|
-
break;
|
|
734
|
-
}
|
|
735
|
-
case 'cds.Vector': {
|
|
736
|
-
if (options.sqlDialect === 'plain') {
|
|
737
|
-
error('ref-unsupported-type', path, {
|
|
738
|
-
'#': 'dialect',
|
|
739
|
-
type: node.type,
|
|
740
|
-
value: options.sqlDialect,
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
else if (options.transformation === 'hdbcds') {
|
|
744
|
-
error('ref-unsupported-type', path, {
|
|
745
|
-
'#': 'hdbcds', type: node.type, value: options.sqlDialect,
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
// Technical limitation of SQLite vector extension
|
|
749
|
-
else if (options.sqlDialect === 'sqlite' && node.length && node.length % 4 !== 0) {
|
|
750
|
-
error('ref-unexpected-args', path, {
|
|
751
|
-
'#': 'vector_length', elemref: path.at(-1),
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
break;
|
|
755
|
-
}
|
|
756
|
-
default:
|
|
757
|
-
break; // nothing to check for unknown types
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// Check that the value of the type property `paramName` (e.g. length, precision, scale ...) is of `expectedType`
|
|
762
|
-
// (which can currently only be 'positiveInteger') and (optional) the value is in a given range
|
|
763
|
-
function checkTypeParamValue(node, paramName, range = null, path = null) {
|
|
764
|
-
const paramValue = node[paramName];
|
|
765
|
-
if (paramValue == null) {
|
|
766
|
-
if (options.toSql || artifact.query || ![ 'cds.Binary', 'cds.hana.BINARY', 'cds.hana.NCHAR', 'cds.hana.CHAR' ].includes(node.type))
|
|
767
|
-
return true;
|
|
768
|
-
|
|
769
|
-
return error('type-missing-argument', path, { name: paramName, id: node.type, $reviewed: false });
|
|
770
|
-
}
|
|
771
|
-
if (range) {
|
|
772
|
-
if (isMaxParameterLengthRestricted(node.type) && range.max && paramValue > range.max) {
|
|
773
|
-
error('type-unexpected-argument', path, {
|
|
774
|
-
'#': 'max', prop: paramName, type: node.type, number: range.max, $reviewed: false,
|
|
775
|
-
});
|
|
776
|
-
return false;
|
|
777
|
-
}
|
|
778
|
-
if (range.min && paramValue < range.min) {
|
|
779
|
-
error('type-unexpected-argument', path, {
|
|
780
|
-
'#': 'min', prop: paramName, type: node.type, number: range.min, $reviewed: false,
|
|
781
|
-
});
|
|
782
|
-
return false;
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
return true;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
576
|
|
|
789
577
|
/**
|
|
790
|
-
* Check if the maximum length of the value of the given type is restricted.
|
|
791
|
-
*
|
|
792
|
-
* @param {string} type
|
|
793
|
-
* @returns {boolean}
|
|
794
|
-
*/
|
|
795
|
-
function isMaxParameterLengthRestricted(type) {
|
|
796
|
-
return !(options.toSql && type === 'cds.String' && (options.sqlDialect === 'sqlite' || options.sqlDialect === 'plain'));
|
|
797
|
-
}
|
|
798
578
|
|
|
799
|
-
/**
|
|
800
|
-
* Flatten technical configuration stuff
|
|
801
579
|
*
|
|
802
580
|
* @param {CSN.Artifact} art
|
|
803
581
|
* @param {string} artName Artifact Name
|
|
@@ -861,6 +639,20 @@ function transformForRelationalDBWithCsn(csn, options, messageFunctions) {
|
|
|
861
639
|
}
|
|
862
640
|
}
|
|
863
641
|
|
|
642
|
+
function defHasCalculatedElements(def) {
|
|
643
|
+
return _hasCalcElement(def.elements);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function _hasCalcElement(elements) {
|
|
647
|
+
for (const name in elements) {
|
|
648
|
+
if (elements[name].$calc)
|
|
649
|
+
return true;
|
|
650
|
+
if (elements[name].elements && _hasCalcElement(elements[name].elements))
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
|
|
864
656
|
module.exports = {
|
|
865
657
|
transformForRelationalDBWithCsn,
|
|
866
658
|
};
|