@sap/cds-compiler 3.5.4 → 3.6.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 +56 -2
- package/bin/cdsc.js +14 -6
- package/doc/CHANGELOG_ARCHIVE.md +10 -10
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +32 -55
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +104 -32
- package/lib/base/messages.js +277 -212
- package/lib/base/optionProcessorHelper.js +9 -2
- package/lib/base/shuffle.js +50 -0
- package/lib/checks/actionsFunctions.js +37 -20
- package/lib/checks/foreignKeys.js +13 -6
- package/lib/checks/nonexpandableStructured.js +1 -2
- package/lib/checks/onConditions.js +21 -19
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -0
- package/lib/checks/types.js +16 -22
- package/lib/compiler/assert-consistency.js +31 -28
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +72 -63
- package/lib/compiler/define.js +396 -314
- package/lib/compiler/extend.js +55 -49
- package/lib/compiler/index.js +5 -0
- package/lib/compiler/populate.js +28 -11
- package/lib/compiler/propagator.js +2 -1
- package/lib/compiler/resolve.js +28 -13
- package/lib/compiler/shared.js +15 -10
- package/lib/compiler/utils.js +7 -7
- package/lib/edm/annotations/genericTranslation.js +51 -46
- package/lib/edm/annotations/preprocessAnnotations.js +37 -40
- package/lib/edm/csn2edm.js +69 -21
- package/lib/edm/edm.js +2 -2
- package/lib/edm/edmInboundChecks.js +6 -8
- package/lib/edm/edmPreprocessor.js +88 -80
- package/lib/edm/edmUtils.js +6 -15
- package/lib/gen/Dictionary.json +81 -13
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4680 -4484
- package/lib/inspect/inspectModelStatistics.js +2 -1
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +131 -78
- package/lib/json/to-csn.js +39 -23
- package/lib/language/antlrParser.js +0 -3
- package/lib/language/docCommentParser.js +7 -3
- package/lib/language/errorStrategy.js +3 -2
- package/lib/language/genericAntlrParser.js +96 -41
- package/lib/language/language.g4 +112 -128
- package/lib/language/multiLineStringParser.js +2 -1
- package/lib/main.d.ts +115 -2
- package/lib/main.js +16 -3
- package/lib/model/csnRefs.js +3 -3
- package/lib/model/csnUtils.js +109 -179
- package/lib/model/enrichCsn.js +13 -8
- package/lib/model/revealInternalProperties.js +4 -3
- package/lib/optionProcessor.js +19 -3
- package/lib/render/manageConstraints.js +11 -15
- package/lib/render/toCdl.js +144 -47
- package/lib/render/toHdbcds.js +22 -22
- package/lib/render/toRename.js +3 -4
- package/lib/render/toSql.js +29 -20
- package/lib/render/utils/delta.js +3 -1
- package/lib/render/utils/sql.js +2 -14
- package/lib/transform/db/associations.js +6 -6
- package/lib/transform/db/cdsPersistence.js +3 -3
- package/lib/transform/db/constraints.js +4 -6
- package/lib/transform/db/expansion.js +4 -4
- package/lib/transform/db/flattening.js +12 -15
- package/lib/transform/db/temporal.js +4 -3
- package/lib/transform/db/transformExists.js +2 -1
- package/lib/transform/draft/db.js +7 -7
- package/lib/transform/forOdataNew.js +15 -4
- package/lib/transform/forRelationalDB.js +53 -39
- package/lib/transform/odata/toFinalBaseType.js +106 -82
- package/lib/transform/odata/typesExposure.js +26 -17
- package/lib/transform/odata/utils.js +1 -1
- package/lib/transform/parseExpr.js +1 -1
- package/lib/transform/transformUtilsNew.js +33 -10
- package/lib/transform/translateAssocsToJoins.js +8 -7
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
- package/lib/utils/timetrace.js +2 -2
- package/package.json +1 -2
package/lib/model/csnUtils.js
CHANGED
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
cloneCsnDictionary: _cloneCsnDictionary,
|
|
9
9
|
cloneAnnotationValue: _cloneAnnotationValue,
|
|
10
10
|
} = require('../json/to-csn');
|
|
11
|
-
const { ModelError } = require('../base/error');
|
|
11
|
+
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
12
12
|
const { typeParameters } = require('../compiler/builtins');
|
|
13
13
|
const { forEach } = require('../utils/objectUtils');
|
|
14
14
|
const { version } = require('../../package.json');
|
|
@@ -44,6 +44,7 @@ const { version } = require('../../package.json');
|
|
|
44
44
|
* @param {CSN.Model} model (Compact) CSN model
|
|
45
45
|
*/
|
|
46
46
|
function getUtils( model, universalReady ) {
|
|
47
|
+
const special$self = !model?.definitions?.$self && '$self';
|
|
47
48
|
const _csnRefs = csnRefs(model, universalReady);
|
|
48
49
|
const { artifactRef } = _csnRefs;
|
|
49
50
|
/** Cache for getFinalBaseTypeWithProps(). Specific to the current model. */
|
|
@@ -53,13 +54,11 @@ function getUtils( model, universalReady ) {
|
|
|
53
54
|
getCsnDef,
|
|
54
55
|
isStructured,
|
|
55
56
|
getFinalType,
|
|
56
|
-
getFinalTypeDef,
|
|
57
57
|
isManagedAssociation,
|
|
58
58
|
isAssocOrComposition,
|
|
59
59
|
isAssociation,
|
|
60
60
|
isComposition,
|
|
61
61
|
getArtifactDatabaseNameOf,
|
|
62
|
-
getNamespaceOfArtifact,
|
|
63
62
|
getContextOfArtifact,
|
|
64
63
|
addStringAnnotationTo,
|
|
65
64
|
getServiceName,
|
|
@@ -219,31 +218,6 @@ function getUtils( model, universalReady ) {
|
|
|
219
218
|
return undefined;
|
|
220
219
|
}
|
|
221
220
|
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Create an object to track visited objects identified by a unique string.
|
|
225
|
-
* @param {string} [initialId] Initial entry (optional)
|
|
226
|
-
*/
|
|
227
|
-
function createVisited( initialId ) {
|
|
228
|
-
const visited = Object.create(null);
|
|
229
|
-
check(initialId);
|
|
230
|
-
return { check };
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Check if an identifier has already been visited and
|
|
234
|
-
* add it to the list of visited identifiers.
|
|
235
|
-
* @param {string} id unique identifier
|
|
236
|
-
*/
|
|
237
|
-
function check( id ) {
|
|
238
|
-
if (!id)
|
|
239
|
-
return;
|
|
240
|
-
if (visited[id])
|
|
241
|
-
throw new ModelError('Circular dependency');
|
|
242
|
-
|
|
243
|
-
visited[id] = true;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
221
|
/**
|
|
248
222
|
* Get the CSN definition for an artifact name.
|
|
249
223
|
* @param {string} defName Absolute name of the artifact
|
|
@@ -265,41 +239,15 @@ function getUtils( model, universalReady ) {
|
|
|
265
239
|
return !!(obj.elements || (obj.type && getFinalBaseTypeWithProps(obj.type)?.elements));
|
|
266
240
|
}
|
|
267
241
|
|
|
268
|
-
/**
|
|
269
|
-
* Resolves typedefs to its final typedef which is returned.
|
|
270
|
-
* If the artifact for typename isn't a typedef, the name itself is returned.
|
|
271
|
-
*
|
|
272
|
-
* @param {string} typeName Absolute type name
|
|
273
|
-
* @returns {object}
|
|
274
|
-
*/
|
|
275
|
-
function getFinalTypeDef( typeName ) {
|
|
276
|
-
const visited = createVisited(typeName);
|
|
277
|
-
let type = model.definitions[typeName];
|
|
278
|
-
if (!type)
|
|
279
|
-
return typeName;
|
|
280
|
-
|
|
281
|
-
for (let nextType = type; nextType;) {
|
|
282
|
-
type = nextType;
|
|
283
|
-
visited.check(type.type);
|
|
284
|
-
nextType = model.definitions[nextType.type];
|
|
285
|
-
}
|
|
286
|
-
return type;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
242
|
/**
|
|
290
243
|
* Resolves typedefs to its final type (name) which is returned.
|
|
291
|
-
* @param {string} typeName Absolute type name
|
|
292
|
-
* @returns {string}
|
|
244
|
+
* @param {string|object} typeName Absolute type name or type ref ({ref: [...]}).
|
|
245
|
+
* @returns {string|object}
|
|
293
246
|
*/
|
|
294
247
|
function getFinalType( typeName ) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
typeName = type.type;
|
|
299
|
-
visited.check(typeName);
|
|
300
|
-
type = model.definitions[typeName];
|
|
301
|
-
}
|
|
302
|
-
return typeName;
|
|
248
|
+
if (!typeName)
|
|
249
|
+
return typeName;
|
|
250
|
+
return getFinalBaseTypeWithProps(typeName)?.type || null;
|
|
303
251
|
}
|
|
304
252
|
|
|
305
253
|
// Return true if 'node' is a managed association element
|
|
@@ -309,77 +257,33 @@ function getUtils( model, universalReady ) {
|
|
|
309
257
|
}
|
|
310
258
|
|
|
311
259
|
/**
|
|
312
|
-
* Returns if a type is an association or a composition
|
|
313
|
-
*
|
|
260
|
+
* Returns if a type is an association or a composition (possibly via type chain).
|
|
261
|
+
*
|
|
314
262
|
* @param {string} typeName Absolute type name
|
|
315
263
|
*/
|
|
316
264
|
function isAssocOrComposition( typeName ) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const visited = createVisited(typeName);
|
|
320
|
-
let type = model.definitions[typeName];
|
|
321
|
-
while (type) {
|
|
322
|
-
if (type.type === 'cds.Association' || type.type === 'cds.Composition')
|
|
323
|
-
return true;
|
|
324
|
-
visited.check(type.type);
|
|
325
|
-
type = model.definitions[type.type];
|
|
326
|
-
}
|
|
327
|
-
return false;
|
|
265
|
+
const finalType = getFinalType( typeName );
|
|
266
|
+
return (finalType === 'cds.Association' || finalType === 'cds.Composition');
|
|
328
267
|
}
|
|
329
268
|
|
|
330
269
|
/**
|
|
331
|
-
* Returns if a type is an association
|
|
270
|
+
* Returns true if a type is an association (possibly via type chain).
|
|
271
|
+
*
|
|
332
272
|
* @param {string} typeName Absolute type name
|
|
333
273
|
*/
|
|
334
274
|
function isAssociation( typeName ) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const visited = createVisited(typeName);
|
|
338
|
-
let type = model.definitions[typeName];
|
|
339
|
-
while (type) {
|
|
340
|
-
if (type.type === 'cds.Association')
|
|
341
|
-
return true;
|
|
342
|
-
visited.check(type.type);
|
|
343
|
-
type = model.definitions[type.type];
|
|
344
|
-
}
|
|
345
|
-
return false;
|
|
275
|
+
const finalType = getFinalType( typeName );
|
|
276
|
+
return (finalType === 'cds.Association');
|
|
346
277
|
}
|
|
347
278
|
|
|
348
279
|
/**
|
|
349
|
-
* Returns if a type is
|
|
280
|
+
* Returns if a type is a composition (possibly via type chain).
|
|
281
|
+
*
|
|
350
282
|
* @param {string} typeName Absolute type name
|
|
351
283
|
*/
|
|
352
284
|
function isComposition( typeName ) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const visited = createVisited(typeName);
|
|
356
|
-
let type = model.definitions[typeName];
|
|
357
|
-
while (type) {
|
|
358
|
-
if (type.type === 'cds.Composition')
|
|
359
|
-
return true;
|
|
360
|
-
visited.check(type.type);
|
|
361
|
-
type = model.definitions[type.type];
|
|
362
|
-
}
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Return the namespace part of the artifact name.
|
|
368
|
-
* @param {string} name Absolute name of artifact
|
|
369
|
-
*/
|
|
370
|
-
function getNamespaceOfArtifact( name ) {
|
|
371
|
-
let lastDotIdx = name.lastIndexOf('.');
|
|
372
|
-
if (lastDotIdx === -1)
|
|
373
|
-
return undefined;
|
|
374
|
-
while (model.definitions[name]) {
|
|
375
|
-
if (model.definitions[name].kind === 'namespace')
|
|
376
|
-
return name;
|
|
377
|
-
lastDotIdx = name.lastIndexOf('.');
|
|
378
|
-
if (lastDotIdx === -1)
|
|
379
|
-
return undefined;
|
|
380
|
-
name = name.substring(0, lastDotIdx);
|
|
381
|
-
}
|
|
382
|
-
return name;
|
|
285
|
+
const finalType = getFinalType( typeName );
|
|
286
|
+
return (finalType === 'cds.Composition');
|
|
383
287
|
}
|
|
384
288
|
|
|
385
289
|
/**
|
|
@@ -409,7 +313,7 @@ function getUtils( model, universalReady ) {
|
|
|
409
313
|
function addStringAnnotationTo( absoluteName, theValue, node ) {
|
|
410
314
|
// Sanity check
|
|
411
315
|
if (!absoluteName.startsWith('@'))
|
|
412
|
-
throw
|
|
316
|
+
throw new CompilerAssertion(`Annotation name should start with "@": ${ absoluteName }`);
|
|
413
317
|
|
|
414
318
|
// Only overwrite if undefined or null
|
|
415
319
|
if (node[absoluteName] === undefined || node[absoluteName] === null) {
|
|
@@ -488,12 +392,6 @@ function getUtils( model, universalReady ) {
|
|
|
488
392
|
}
|
|
489
393
|
}
|
|
490
394
|
|
|
491
|
-
function _normalizeTypeRef( type ) {
|
|
492
|
-
if (type && typeof type === 'object' && type.ref?.length === 1)
|
|
493
|
-
type = type.ref[0]; // simplify type: no element -> simple string can be used
|
|
494
|
-
return type;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
395
|
/**
|
|
498
396
|
* Resolve to the final type of a type, that means follow type chains, references, etc.
|
|
499
397
|
* Input is a fully qualified type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
|
|
@@ -501,30 +399,32 @@ function getUtils( model, universalReady ) {
|
|
|
501
399
|
* Returns `null` if the type can't be resolved or if the referenced element has no type,
|
|
502
400
|
* e.g. `typeof V:calculated`.
|
|
503
401
|
* Otherwise, if scalar, returns an object that has a `type` property and all collected type
|
|
504
|
-
* properties, or
|
|
402
|
+
* properties, or an object with `type` and `elements` or `items` property if structured/arrayed.
|
|
505
403
|
*
|
|
506
404
|
* Notes:
|
|
507
405
|
* - Caches type lookups. If the CSN changes drastically, you will need to re-call
|
|
508
|
-
* `
|
|
509
|
-
* - Does _not_ return the underlying type definition!
|
|
406
|
+
* `getUtils()` and use the newly returned `getFinalBaseTypeWithProps()`.
|
|
407
|
+
* - Does _not_ return the underlying type definition! It is an object with all relevant
|
|
510
408
|
* type properties collected while traversing the type chain!
|
|
511
409
|
*
|
|
410
|
+
* @todo Rename to e.g. getFinalTypeInfo()
|
|
411
|
+
*
|
|
512
412
|
* @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
|
|
513
413
|
* @returns {object|null}
|
|
514
414
|
*/
|
|
515
415
|
function getFinalBaseTypeWithProps( type ) {
|
|
516
|
-
type =
|
|
416
|
+
type = normalizeTypeRef(type);
|
|
517
417
|
if (!type)
|
|
518
418
|
return null;
|
|
519
419
|
|
|
520
|
-
// Nothing to copy from builtin type name.
|
|
521
|
-
if (typeof type === 'string' && isBuiltinType( type ))
|
|
522
|
-
return { type };
|
|
523
|
-
|
|
524
|
-
|
|
525
420
|
// We differentiate between ref and type to avoid collisions due to dict key.
|
|
526
421
|
// Delimiter chosen arbitrarily; just one that is rarely used.
|
|
527
|
-
|
|
422
|
+
// Need to take care of clashes:
|
|
423
|
+
// - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\', '\\'] })
|
|
424
|
+
// - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\2:\\\\'] })
|
|
425
|
+
const resolvedKey = (typeof type === 'object')
|
|
426
|
+
? `ref[${ type.ref.length }]:${ type.ref.map((val, i) => `${ i }:${ val }`).join('\\') }`
|
|
427
|
+
: `type:${ type }`;
|
|
528
428
|
|
|
529
429
|
if (finalBaseTypeCache[resolvedKey]) {
|
|
530
430
|
if (finalBaseTypeCache[resolvedKey] === true)
|
|
@@ -532,8 +432,12 @@ function getUtils( model, universalReady ) {
|
|
|
532
432
|
return finalBaseTypeCache[resolvedKey];
|
|
533
433
|
}
|
|
534
434
|
|
|
435
|
+
// Nothing to copy from builtin type name.
|
|
436
|
+
if (typeof type === 'string' && (isBuiltinType( type ) || type === special$self))
|
|
437
|
+
return _cacheResolved({ type });
|
|
438
|
+
|
|
535
439
|
const typeRef = artifactRef(type); // throws if not found
|
|
536
|
-
const isNonScalar = _cacheNonScalar(typeRef);
|
|
440
|
+
const isNonScalar = _cacheNonScalar({ ...typeRef, type });
|
|
537
441
|
if (isNonScalar)
|
|
538
442
|
return finalBaseTypeCache[resolvedKey];
|
|
539
443
|
|
|
@@ -541,7 +445,8 @@ function getUtils( model, universalReady ) {
|
|
|
541
445
|
_copyTypeProps(props, typeRef);
|
|
542
446
|
|
|
543
447
|
// If the resolved type is a builtin, stop and use its type arguments.
|
|
544
|
-
type =
|
|
448
|
+
type = normalizeTypeRef(typeRef.type);
|
|
449
|
+
props.type = type;
|
|
545
450
|
if (typeof type === 'string' && isBuiltinType(type))
|
|
546
451
|
return _cacheResolved(props);
|
|
547
452
|
|
|
@@ -552,11 +457,13 @@ function getUtils( model, universalReady ) {
|
|
|
552
457
|
const finalBase = getFinalBaseTypeWithProps(type);
|
|
553
458
|
if (!finalBase) // Reference has no proper type, e.g. due to `type of View:calculated`.
|
|
554
459
|
return _cacheResolved(null);
|
|
460
|
+
|
|
555
461
|
const nonScalar = _cacheNonScalar(finalBase);
|
|
556
462
|
if (nonScalar)
|
|
557
463
|
return finalBaseTypeCache[resolvedKey];
|
|
558
464
|
|
|
559
465
|
// If not a non-scalar, must be resolved type.
|
|
466
|
+
props.type = finalBase.type;
|
|
560
467
|
_copyTypeProps(props, finalBase);
|
|
561
468
|
_cacheResolved(props);
|
|
562
469
|
return props;
|
|
@@ -578,10 +485,6 @@ function getUtils( model, universalReady ) {
|
|
|
578
485
|
* @returns {boolean} True, if structured/arrayed/invalid, false if scalar.
|
|
579
486
|
*/
|
|
580
487
|
function _cacheNonScalar( obj ) {
|
|
581
|
-
if (!obj) { // Reference has no proper type, e.g. due to `type of View:calculated`.
|
|
582
|
-
_cacheResolved(null);
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
488
|
if (obj.elements || obj.items) {
|
|
586
489
|
_cacheResolved(obj);
|
|
587
490
|
return true;
|
|
@@ -690,20 +593,10 @@ function forEachMember( construct, callback, path = [], ignoreIgnore = true, ite
|
|
|
690
593
|
if (ignoreIgnore && construct._ignore)
|
|
691
594
|
return;
|
|
692
595
|
|
|
693
|
-
|
|
694
596
|
// `items` itself is a structure that can contain "elements", and more.
|
|
695
597
|
if (construct.items)
|
|
696
598
|
forEachMember( construct.items, callback, [ ...path, 'items' ], ignoreIgnore, iterateOptions, constructCallback );
|
|
697
599
|
|
|
698
|
-
|
|
699
|
-
// Unlike XSN, we don't make "returns" a "params" in the callback.
|
|
700
|
-
// Backends rely on the fact that `forEachElement` also goes through all
|
|
701
|
-
// `elements` of the return type (if structured).
|
|
702
|
-
// TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
|
|
703
|
-
if (construct.returns && !iterateOptions.elementsOnly)
|
|
704
|
-
forEachMember( construct.returns, callback, [ ...path, 'returns' ], ignoreIgnore, iterateOptions, constructCallback );
|
|
705
|
-
|
|
706
|
-
|
|
707
600
|
path = [ ...path ]; // Copy
|
|
708
601
|
const propsWithMembers = (iterateOptions.elementsOnly ? [ 'elements' ] : [ 'elements', 'enum', 'actions', 'params' ]);
|
|
709
602
|
propsWithMembers.forEach((prop) => {
|
|
@@ -715,6 +608,19 @@ function forEachMember( construct, callback, path = [], ignoreIgnore = true, ite
|
|
|
715
608
|
constructCallback(construct, prop, path);
|
|
716
609
|
}
|
|
717
610
|
});
|
|
611
|
+
|
|
612
|
+
if (construct.returns) {
|
|
613
|
+
if (construct.returns.items || construct.returns.elements) {
|
|
614
|
+
if (!iterateOptions.elementsOnly)
|
|
615
|
+
forEachMember(construct.returns, callback, [ ...path, 'returns' ], ignoreIgnore, iterateOptions, constructCallback);
|
|
616
|
+
}
|
|
617
|
+
else if (Array.isArray(callback)) {
|
|
618
|
+
callback.forEach(cb => cb(construct.returns, '', 'params', [ ...path, 'returns' ], construct));
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
callback(construct.returns, '', 'params', [ ...path, 'returns' ], construct);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
718
624
|
}
|
|
719
625
|
|
|
720
626
|
/**
|
|
@@ -965,10 +871,10 @@ function getArtifactDatabaseNameOf( artifactName, sqlMapping, csn, sqlDialect =
|
|
|
965
871
|
else if (sqlMapping === 'plain')
|
|
966
872
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
967
873
|
|
|
968
|
-
throw new
|
|
874
|
+
throw new CompilerAssertion(`Unknown naming mode: ${ sqlMapping }`);
|
|
969
875
|
}
|
|
970
876
|
else {
|
|
971
|
-
throw new
|
|
877
|
+
throw new CompilerAssertion('A valid CSN model is required to correctly calculate the database name of an artifact.');
|
|
972
878
|
}
|
|
973
879
|
}
|
|
974
880
|
|
|
@@ -1054,7 +960,7 @@ function getUnderscoredName( startIndex, parts, csn ) {
|
|
|
1054
960
|
|
|
1055
961
|
function isValidMappingDialectCombi( sqlDialect, sqlMapping ) {
|
|
1056
962
|
if (sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
|
|
1057
|
-
throw new
|
|
963
|
+
throw new CompilerAssertion(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${ sqlDialect }`);
|
|
1058
964
|
return true;
|
|
1059
965
|
}
|
|
1060
966
|
|
|
@@ -1087,7 +993,7 @@ function getElementDatabaseNameOf( elemName, sqlMapping, sqlDialect = 'plain' )
|
|
|
1087
993
|
else if (sqlMapping === 'quoted')
|
|
1088
994
|
return elemName.replace(/\./g, '_');
|
|
1089
995
|
|
|
1090
|
-
throw new
|
|
996
|
+
throw new CompilerAssertion(`Unknown naming mode: ${ sqlMapping }`);
|
|
1091
997
|
}
|
|
1092
998
|
|
|
1093
999
|
const _dependencies = Symbol('_dependencies');
|
|
@@ -1220,6 +1126,19 @@ function getNormalizedQuery( art ) {
|
|
|
1220
1126
|
return art;
|
|
1221
1127
|
}
|
|
1222
1128
|
|
|
1129
|
+
/**
|
|
1130
|
+
* If `art.type` is an object with `ref` of length 1, normalize it to
|
|
1131
|
+
* just the type reference string, e.g. `{ ref: [ 'T'] }` becomes `'T'`.
|
|
1132
|
+
*
|
|
1133
|
+
* @param {string|object} type
|
|
1134
|
+
* @return {string|object}
|
|
1135
|
+
*/
|
|
1136
|
+
function normalizeTypeRef( type ) {
|
|
1137
|
+
if (type && typeof type === 'object' && type.ref?.length === 1)
|
|
1138
|
+
type = type.ref[0]; // simplify type: no element -> simple string can be used
|
|
1139
|
+
return type;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1223
1142
|
/**
|
|
1224
1143
|
* If the artifact with the name given is part of a context (or multiple), return the top-most context.
|
|
1225
1144
|
* Else, return the artifact itself. Namespaces are not of concern here.
|
|
@@ -1390,36 +1309,46 @@ function hasValidSkipOrExists( artifact ) {
|
|
|
1390
1309
|
}
|
|
1391
1310
|
|
|
1392
1311
|
/**
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1312
|
+
* Return the namespace part of the artifact name.
|
|
1313
|
+
*
|
|
1314
|
+
* Return the longest undefined prefix path.
|
|
1315
|
+
* If the first path segment is defined (that is name has
|
|
1316
|
+
* no dots and is defined), return undefined.
|
|
1317
|
+
*
|
|
1318
|
+
* Example:
|
|
1319
|
+
* model.definitions {
|
|
1320
|
+
* a.b: 1,
|
|
1321
|
+
* a.b.c.d: 1
|
|
1322
|
+
* }
|
|
1323
|
+
* getNamespace('foo.bar') = 'foo.bar'
|
|
1324
|
+
* getNamespace('a.b.c.d') = 'a'
|
|
1325
|
+
*
|
|
1326
|
+
* model.definitions {
|
|
1327
|
+
* a: 1,
|
|
1328
|
+
* a.b.c.d: 1
|
|
1329
|
+
* }
|
|
1330
|
+
* getNamespace('a.b.c.d') = undefined
|
|
1331
|
+
*
|
|
1332
|
+
* @param {string} name Absolute name of artifact
|
|
1333
|
+
* @param {CSN.Model} csn CSN model
|
|
1334
|
+
* @param {any} ns default namespace return value
|
|
1335
|
+
* @returns {any} The namespace or the value of ns
|
|
1336
|
+
*/
|
|
1337
|
+
function getNamespace( csn, name, ns = undefined ) {
|
|
1338
|
+
let dotpos = -1;
|
|
1339
|
+
while (dotpos < name.length) {
|
|
1340
|
+
for (dotpos++; dotpos < name.length && name[dotpos] !== '.'; dotpos++)
|
|
1341
|
+
;
|
|
1342
|
+
const tns = name.substring(0, dotpos);
|
|
1343
|
+
if (csn.definitions[tns] === undefined)
|
|
1344
|
+
ns = tns;
|
|
1345
|
+
else
|
|
1346
|
+
return ns;
|
|
1418
1347
|
}
|
|
1419
|
-
|
|
1420
|
-
return artifactName;
|
|
1348
|
+
return ns;
|
|
1421
1349
|
}
|
|
1422
1350
|
|
|
1351
|
+
|
|
1423
1352
|
/**
|
|
1424
1353
|
* Sorts the definition dictionary in tests mode.
|
|
1425
1354
|
*
|
|
@@ -1577,6 +1506,7 @@ module.exports = {
|
|
|
1577
1506
|
getRootArtifactName,
|
|
1578
1507
|
getLastPartOfRef,
|
|
1579
1508
|
getLastPartOf,
|
|
1509
|
+
normalizeTypeRef,
|
|
1580
1510
|
copyAnnotations,
|
|
1581
1511
|
copyAnnotationsAndDoc,
|
|
1582
1512
|
isAspect,
|
package/lib/model/enrichCsn.js
CHANGED
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
|
|
42
42
|
const { csnRefs, artifactProperties } = require('./csnRefs');
|
|
43
43
|
const { locationString } = require('../base/location');
|
|
44
|
+
const { CompilerAssertion } = require('../base/error');
|
|
45
|
+
const shuffleGen = require('../base/shuffle');
|
|
44
46
|
|
|
45
47
|
function enrichCsn( csn, options = {} ) {
|
|
46
48
|
const transformers = {
|
|
@@ -64,6 +66,7 @@ function enrichCsn( csn, options = {} ) {
|
|
|
64
66
|
// options.enrichCsn = 'DEBUG';
|
|
65
67
|
let $$cacheObjectNumber = 0; // for debugging
|
|
66
68
|
const debugLocationInfo = options.enrichCsn === 'DEBUG' && Object.create(null);
|
|
69
|
+
const special$self = !csn.definitions.$self && '$self';
|
|
67
70
|
|
|
68
71
|
setLocations( csn, false, null );
|
|
69
72
|
const {
|
|
@@ -74,6 +77,7 @@ function enrichCsn( csn, options = {} ) {
|
|
|
74
77
|
// eslint-disable-next-line camelcase
|
|
75
78
|
__getCache_forEnrichCsnDebugging,
|
|
76
79
|
} = csnRefs( csn, true );
|
|
80
|
+
const { shuffleArray } = shuffleGen( options.testMode );
|
|
77
81
|
|
|
78
82
|
const csnPath = [];
|
|
79
83
|
if (csn.definitions)
|
|
@@ -93,7 +97,8 @@ function enrichCsn( csn, options = {} ) {
|
|
|
93
97
|
obj.forEach( (n, i) => standard( obj, i, n ) );
|
|
94
98
|
}
|
|
95
99
|
else {
|
|
96
|
-
|
|
100
|
+
// no shuffle here; we would need to sort the new properties otherwise
|
|
101
|
+
for (const name of Object.getOwnPropertyNames( obj ) ) {
|
|
97
102
|
const trans = transformers[name] || transformers[name.charAt(0)] || standard;
|
|
98
103
|
trans( obj, name, obj[name] );
|
|
99
104
|
}
|
|
@@ -116,13 +121,13 @@ function enrichCsn( csn, options = {} ) {
|
|
|
116
121
|
if (!dict) // value null for inheritance interruption
|
|
117
122
|
return;
|
|
118
123
|
csnPath.push( prop );
|
|
119
|
-
for (const name of Object.getOwnPropertyNames( dict )) {
|
|
124
|
+
for (const name of shuffleArray( Object.getOwnPropertyNames( dict ) )) {
|
|
120
125
|
if (prop === 'definitions')
|
|
121
126
|
initDefinition( dict[name] );
|
|
122
127
|
definition( dict, name, dict[name] );
|
|
123
128
|
}
|
|
124
129
|
if (!Object.prototype.propertyIsEnumerable.call( parent, prop ))
|
|
125
|
-
parent[`$${ prop }`] = dict;
|
|
130
|
+
parent[`$${ prop }`] = dict; // for $elements of sub queries in client-CSN
|
|
126
131
|
csnPath.pop();
|
|
127
132
|
}
|
|
128
133
|
|
|
@@ -133,7 +138,7 @@ function enrichCsn( csn, options = {} ) {
|
|
|
133
138
|
? `<illegal ref = ${ art }>`
|
|
134
139
|
: `<illegal ref: ${ typeof art }>`;
|
|
135
140
|
}
|
|
136
|
-
throw new
|
|
141
|
+
throw new CompilerAssertion( 'Illegal reference' );
|
|
137
142
|
}
|
|
138
143
|
else if (art.$location) {
|
|
139
144
|
return art.$location;
|
|
@@ -141,7 +146,7 @@ function enrichCsn( csn, options = {} ) {
|
|
|
141
146
|
|
|
142
147
|
if (!options.testMode)
|
|
143
148
|
return `<${ Object.keys( art ).join('+') }+!$location>`;
|
|
144
|
-
throw new
|
|
149
|
+
throw new CompilerAssertion( 'Reference to object without $location' );
|
|
145
150
|
}
|
|
146
151
|
|
|
147
152
|
function simpleRef( parent, prop, ref ) {
|
|
@@ -151,7 +156,7 @@ function enrichCsn( csn, options = {} ) {
|
|
|
151
156
|
parent[`_${ prop }`] = ref.map( r => refLocation( artifactRef( r, notFound ) ) );
|
|
152
157
|
}
|
|
153
158
|
else if (typeof ref === 'string') {
|
|
154
|
-
if (!ref.startsWith( 'cds.'))
|
|
159
|
+
if (!ref.startsWith( 'cds.') && ref !== special$self)
|
|
155
160
|
parent[`_${ prop }`] = refLocation( artifactRef( ref, notFound ) );
|
|
156
161
|
}
|
|
157
162
|
else if (!ref.elements) {
|
|
@@ -201,7 +206,7 @@ function enrichCsn( csn, options = {} ) {
|
|
|
201
206
|
}
|
|
202
207
|
|
|
203
208
|
csnPath.push( prop );
|
|
204
|
-
path.forEach( function step( s, i ) {
|
|
209
|
+
path.forEach( function step( s, i ) { // no shuffle, need index
|
|
205
210
|
if (s && typeof s === 'object') {
|
|
206
211
|
csnPath.push( i );
|
|
207
212
|
if (s.args)
|
|
@@ -289,7 +294,7 @@ function enrichCsn( csn, options = {} ) {
|
|
|
289
294
|
|
|
290
295
|
function debugLocation( loc, userProvided ) {
|
|
291
296
|
if (debugLocationInfo && !userProvided) {
|
|
292
|
-
loc = loc.replace( /\(
|
|
297
|
+
loc = loc.replace( /\(\d+\)\^/, '^' );
|
|
293
298
|
debugLocationInfo[loc] = (debugLocationInfo[loc] || 0) + 1;
|
|
294
299
|
loc = `${ loc }(${ debugLocationInfo[loc] })`;
|
|
295
300
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
'use strict';
|
|
13
13
|
|
|
14
14
|
const msg = require('../base/messages');
|
|
15
|
+
const { CompilerAssertion } = require('../base/error');
|
|
15
16
|
|
|
16
17
|
const $inferred = Symbol.for('cds.$inferred');
|
|
17
18
|
const $location = Symbol.for('cds.$location');
|
|
@@ -132,7 +133,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
132
133
|
if (path.length === 1) {
|
|
133
134
|
const def = xsn.definitions?.[path[0]] || xsn.vocabularies?.[path[0]];
|
|
134
135
|
if (!def)
|
|
135
|
-
throw new
|
|
136
|
+
throw new CompilerAssertion(`reveal xsn: Unknown definition: “${ path[0] }”`);
|
|
136
137
|
return reveal( def );
|
|
137
138
|
}
|
|
138
139
|
|
|
@@ -143,7 +144,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
143
144
|
if (xsn[segment])
|
|
144
145
|
xsn = xsn[segment];
|
|
145
146
|
else if (segment) // huh, this should be a call error
|
|
146
|
-
throw new
|
|
147
|
+
throw new CompilerAssertion(`Raw Output: Path segment "${ segment }" could not be found. Path: ${ JSON.stringify(path) }!"`);
|
|
147
148
|
}
|
|
148
149
|
const propName = path[path.length > 1 ? path.length - 2 : 0];
|
|
149
150
|
const obj = {};
|
|
@@ -369,7 +370,7 @@ function primOrString( node ) {
|
|
|
369
370
|
|
|
370
371
|
function quoted( name, undef = '‹undefined›' ) {
|
|
371
372
|
if (typeof name === 'number')
|
|
372
|
-
return name;
|
|
373
|
+
return String(name);
|
|
373
374
|
return (name ? `“${ name }”` : undef);
|
|
374
375
|
}
|
|
375
376
|
|