@sap/cds-compiler 4.6.2 → 4.7.4
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 +37 -0
- package/bin/cds_update_identifiers.js +6 -2
- package/bin/cdsc.js +1 -1
- package/doc/CHANGELOG_ARCHIVE.md +9 -9
- package/doc/CHANGELOG_BETA.md +6 -0
- package/lib/api/main.js +56 -9
- package/lib/api/options.js +6 -3
- package/lib/api/validate.js +20 -29
- package/lib/base/message-registry.js +27 -3
- package/lib/base/messages.js +8 -3
- package/lib/base/model.js +2 -0
- package/lib/checks/dbFeatureFlags.js +28 -0
- package/lib/checks/elements.js +81 -13
- package/lib/checks/enricher.js +3 -2
- package/lib/checks/validator.js +38 -4
- package/lib/compiler/assert-consistency.js +4 -4
- package/lib/compiler/checks.js +5 -4
- package/lib/compiler/define.js +2 -2
- package/lib/compiler/generate.js +2 -1
- package/lib/compiler/propagator.js +3 -11
- package/lib/compiler/shared.js +2 -1
- package/lib/compiler/tweak-assocs.js +43 -24
- package/lib/edm/annotations/edmJson.js +3 -0
- package/lib/edm/annotations/genericTranslation.js +156 -106
- package/lib/edm/annotations/preprocessAnnotations.js +11 -14
- package/lib/edm/csn2edm.js +27 -24
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +135 -37
- package/lib/edm/edmUtils.js +20 -7
- package/lib/gen/Dictionary.json +1 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -11
- package/lib/gen/languageParser.js +5942 -5446
- package/lib/json/to-csn.js +7 -114
- package/lib/language/genericAntlrParser.js +106 -48
- package/lib/model/cloneCsn.js +203 -0
- package/lib/model/csnRefs.js +11 -3
- package/lib/model/csnUtils.js +42 -85
- package/lib/optionProcessor.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +133 -88
- package/lib/render/toHdbcds.js +1 -5
- package/lib/render/toSql.js +7 -9
- package/lib/render/utils/common.js +9 -16
- package/lib/transform/addTenantFields.js +277 -102
- package/lib/transform/db/applyTransformations.js +14 -9
- package/lib/transform/db/backlinks.js +2 -1
- package/lib/transform/db/constraints.js +60 -82
- package/lib/transform/db/expansion.js +6 -6
- package/lib/transform/db/featureFlags.js +5 -0
- package/lib/transform/db/flattening.js +4 -4
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/rewriteCalculatedElements.js +2 -2
- package/lib/transform/db/transformExists.js +12 -0
- package/lib/transform/db/views.js +5 -2
- package/lib/transform/draft/odata.js +7 -6
- package/lib/transform/effective/associations.js +2 -1
- package/lib/transform/effective/main.js +3 -2
- package/lib/transform/effective/types.js +6 -3
- package/lib/transform/forOdata.js +39 -24
- package/lib/transform/forRelationalDB.js +34 -27
- package/lib/transform/localized.js +29 -9
- package/lib/transform/odata/flattening.js +419 -0
- package/lib/transform/odata/toFinalBaseType.js +95 -15
- package/lib/transform/odata/typesExposure.js +9 -7
- package/lib/transform/transformUtils.js +7 -6
- package/lib/transform/translateAssocsToJoins.js +3 -3
- package/lib/utils/objectUtils.js +14 -0
- package/package.json +1 -1
package/lib/model/csnUtils.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { csnRefs, implicitAs, pathId } = require('./csnRefs');
|
|
4
|
-
const { applyTransformations, applyTransformationsOnNonDictionary, applyTransformationsOnDictionary } = require('../transform/db/applyTransformations');
|
|
5
|
-
const { isBuiltinType, isMagicVariable, isAnnotationExpression } = require('../compiler/builtins.js');
|
|
6
4
|
const {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
transformExpression,
|
|
6
|
+
applyTransformations,
|
|
7
|
+
applyTransformationsOnNonDictionary,
|
|
8
|
+
applyTransformationsOnDictionary,
|
|
9
|
+
} = require('../transform/db/applyTransformations');
|
|
10
|
+
const { isBuiltinType, isMagicVariable, isAnnotationExpression } = require('../compiler/builtins.js');
|
|
11
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');
|
|
15
|
+
const { cloneAnnotationValue } = require('./cloneCsn');
|
|
15
16
|
|
|
16
17
|
// Low-level utility functions to work with compact CSN.
|
|
17
18
|
|
|
@@ -442,52 +443,6 @@ function getUtils( model, universalReady ) {
|
|
|
442
443
|
}
|
|
443
444
|
}
|
|
444
445
|
|
|
445
|
-
/**
|
|
446
|
-
* Deeply clone the given CSN model and return it.
|
|
447
|
-
* In testMode (or with testSortCsn), definitions are sorted.
|
|
448
|
-
*
|
|
449
|
-
* This function is CSN aware! Don't put annotation values into it, or
|
|
450
|
-
* keys such as "elements" will be interpreted according to CSN rules!
|
|
451
|
-
*
|
|
452
|
-
* @see cloneAnnotationValue
|
|
453
|
-
* @see cloneCsnDictionary
|
|
454
|
-
*
|
|
455
|
-
* @param {object} csn Top-level CSN. You can pass non-dictionary values.
|
|
456
|
-
* @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
|
|
457
|
-
*/
|
|
458
|
-
function cloneCsnNonDict( csn, options ) {
|
|
459
|
-
return sortCsn(csn, options);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
/**
|
|
463
|
-
* Deeply clone the given CSN dictionary and return it.
|
|
464
|
-
* This function does _not_ sort the given dictionary.
|
|
465
|
-
* See cloneCsnNonDict() if you want sorted definitions.
|
|
466
|
-
*
|
|
467
|
-
* This function is CSN aware! Don't put annotation values into it, or
|
|
468
|
-
* keys such as "elements" will be interpreted according to CSN rules!
|
|
469
|
-
*
|
|
470
|
-
* @see cloneAnnotationValue
|
|
471
|
-
* @see cloneCsnNonDict
|
|
472
|
-
*
|
|
473
|
-
* @param {object} csn
|
|
474
|
-
* @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
|
|
475
|
-
* used and cloneOptions are passed to sortCsn().
|
|
476
|
-
*/
|
|
477
|
-
function cloneCsnDictionary( csn, options ) {
|
|
478
|
-
return _cloneCsnDictionary(csn, options);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Clones the given annotation _value_. `value` must not be an object with annotations,
|
|
483
|
-
* but the annotation value itself, e.g. `[ { a: 1 } ]`, not `@anno: [...]`.
|
|
484
|
-
*
|
|
485
|
-
* @param {any} value
|
|
486
|
-
* @returns {any}
|
|
487
|
-
*/
|
|
488
|
-
function cloneAnnotationValue( value ) {
|
|
489
|
-
return _cloneAnnotationValue( value );
|
|
490
|
-
}
|
|
491
446
|
|
|
492
447
|
/**
|
|
493
448
|
* Apply function `callback` to all artifacts in dictionary
|
|
@@ -1092,17 +1047,19 @@ function getLastPartOfRef( ref ) {
|
|
|
1092
1047
|
* @returns {array} copiedAnnoNames
|
|
1093
1048
|
*/
|
|
1094
1049
|
function copyAnnotations( fromNode, toNode, overwrite = false, excludes = {}, annoNames = undefined ) {
|
|
1095
|
-
|
|
1096
|
-
if (
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
annoNames
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1050
|
+
const copiedAnnoNames = [];
|
|
1051
|
+
if (toNode) {
|
|
1052
|
+
if (annoNames == null)
|
|
1053
|
+
annoNames = Object.keys(fromNode).filter(key => key.startsWith('@'));
|
|
1054
|
+
|
|
1055
|
+
annoNames.forEach((anno) => {
|
|
1056
|
+
if ((toNode[anno] === undefined || overwrite) && !excludes[anno]) {
|
|
1057
|
+
toNode[anno] = cloneAnnotationValue(fromNode[anno]);
|
|
1058
|
+
copiedAnnoNames.push(anno);
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
return copiedAnnoNames;
|
|
1106
1063
|
}
|
|
1107
1064
|
|
|
1108
1065
|
|
|
@@ -1275,23 +1232,6 @@ function getNamespace( csn, name, ns = undefined ) {
|
|
|
1275
1232
|
return ns;
|
|
1276
1233
|
}
|
|
1277
1234
|
|
|
1278
|
-
|
|
1279
|
-
/**
|
|
1280
|
-
* Sorts the definition dictionary in tests mode.
|
|
1281
|
-
*
|
|
1282
|
-
* @param {CSN.Model} csn
|
|
1283
|
-
* @param {CSN.Options} options
|
|
1284
|
-
*/
|
|
1285
|
-
function sortCsnDefinitionsForTests( csn, options ) {
|
|
1286
|
-
if (!options.testMode && !options.testSortCsn)
|
|
1287
|
-
return;
|
|
1288
|
-
const sorted = Object.create(null);
|
|
1289
|
-
Object.keys(csn.definitions || {}).sort().forEach((name) => {
|
|
1290
|
-
sorted[name] = csn.definitions[name];
|
|
1291
|
-
});
|
|
1292
|
-
csn.definitions = sorted;
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
1235
|
/**
|
|
1296
1236
|
* Return an array of non-abstract service names contained in CSN
|
|
1297
1237
|
*
|
|
@@ -1452,12 +1392,28 @@ function pathName( ref ) {
|
|
|
1452
1392
|
return ref ? ref.map( pathId ).join( '.' ) : '';
|
|
1453
1393
|
}
|
|
1454
1394
|
|
|
1395
|
+
/**
|
|
1396
|
+
* Return true if prop is an annotation and the annotation value has an expression
|
|
1397
|
+
*
|
|
1398
|
+
* @param {object} node
|
|
1399
|
+
* @param {string} prop
|
|
1400
|
+
* @returns {boolean}
|
|
1401
|
+
*/
|
|
1402
|
+
function findAnnotationExpression( node, prop ) {
|
|
1403
|
+
let isExpr = false;
|
|
1404
|
+
if (prop[0] === '@') {
|
|
1405
|
+
transformExpression(node, prop, {
|
|
1406
|
+
'=': (p) => {
|
|
1407
|
+
isExpr ||= isAnnotationExpression(p);
|
|
1408
|
+
},
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
return isExpr;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1455
1415
|
module.exports = {
|
|
1456
1416
|
getUtils,
|
|
1457
|
-
cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
|
|
1458
|
-
cloneCsnNonDict,
|
|
1459
|
-
cloneCsnDictionary,
|
|
1460
|
-
cloneAnnotationValue,
|
|
1461
1417
|
isBuiltinType,
|
|
1462
1418
|
isMagicVariable,
|
|
1463
1419
|
isAnnotationExpression,
|
|
@@ -1473,6 +1429,7 @@ module.exports = {
|
|
|
1473
1429
|
getResultingName,
|
|
1474
1430
|
getUnderscoredName,
|
|
1475
1431
|
getElementDatabaseNameOf,
|
|
1432
|
+
transformExpression,
|
|
1476
1433
|
applyTransformations,
|
|
1477
1434
|
applyTransformationsOnNonDictionary,
|
|
1478
1435
|
applyTransformationsOnDictionary,
|
|
@@ -1491,7 +1448,6 @@ module.exports = {
|
|
|
1491
1448
|
isAspect,
|
|
1492
1449
|
hasValidSkipOrExists,
|
|
1493
1450
|
getNamespace,
|
|
1494
|
-
sortCsnDefinitionsForTests,
|
|
1495
1451
|
getServiceNames,
|
|
1496
1452
|
walkCsnPath,
|
|
1497
1453
|
getVariableReplacement,
|
|
@@ -1502,4 +1458,5 @@ module.exports = {
|
|
|
1502
1458
|
isAssociationOperand,
|
|
1503
1459
|
isDollarSelfOrProjectionOperand,
|
|
1504
1460
|
pathName,
|
|
1461
|
+
findAnnotationExpression,
|
|
1505
1462
|
};
|
package/lib/optionProcessor.js
CHANGED
|
@@ -41,7 +41,7 @@ optionProcessor
|
|
|
41
41
|
.option(' --doc-comment')
|
|
42
42
|
.option(' --add-texts-language-assoc')
|
|
43
43
|
.option(' --localized-without-coalesce')
|
|
44
|
-
.option(' --tenant-
|
|
44
|
+
.option(' --tenant-discriminator')
|
|
45
45
|
.option(' --default-binary-length <length>')
|
|
46
46
|
.option(' --default-string-length <length>')
|
|
47
47
|
.option(' --no-recompile')
|
|
@@ -101,7 +101,7 @@ optionProcessor
|
|
|
101
101
|
-E, --enrich-csn Show non-enumerable CSN properties and locations of references
|
|
102
102
|
-R, --raw-output <name> Write XSN for definition "name" and error output to <stdout>,
|
|
103
103
|
with name = "+", write complete XSN, long!
|
|
104
|
-
--tenant-
|
|
104
|
+
--tenant-discriminator Add tenant fields to entities
|
|
105
105
|
--internal-msg Write raw messages with call stack to <stdout>/<stderr>
|
|
106
106
|
--beta-mode Enable all unsupported, incomplete (beta) features
|
|
107
107
|
--beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
|
|
@@ -11,7 +11,7 @@ const { transformForRelationalDBWithCsn } = require('../transform/forRelationalD
|
|
|
11
11
|
const {
|
|
12
12
|
renderReferentialConstraint, getIdentifierUtils,
|
|
13
13
|
} = require('./utils/sql');
|
|
14
|
-
const { sortCsn } = require('../
|
|
14
|
+
const { sortCsn } = require('../model/cloneCsn');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Used only by `cdsc manageConstraints`.
|
package/lib/render/toCdl.js
CHANGED
|
@@ -15,8 +15,8 @@ const {
|
|
|
15
15
|
isBuiltinType,
|
|
16
16
|
generatedByCompilerVersion,
|
|
17
17
|
getNormalizedQuery,
|
|
18
|
-
cloneCsnNonDict,
|
|
19
18
|
} = require('../model/csnUtils');
|
|
19
|
+
const { cloneFullCsn } = require('../model/cloneCsn');
|
|
20
20
|
|
|
21
21
|
const identifierRegex = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
|
|
22
22
|
const specialFunctionKeywords = Object.create(null);
|
|
@@ -36,7 +36,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
36
36
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn')) {
|
|
37
37
|
// Since the expander modifies the CSN, we need to clone it first or
|
|
38
38
|
// toCdl can't guarantee that the input CSN is not modified.
|
|
39
|
-
csn =
|
|
39
|
+
csn = cloneFullCsn(csn, options);
|
|
40
40
|
enrichUniversalCsn(csn, options);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -319,29 +319,38 @@ function csnToCdl( csn, options, msg ) {
|
|
|
319
319
|
result += renderAnnotateParamsInParentheses(ext, env);
|
|
320
320
|
|
|
321
321
|
// Element extensions and annotations (possibly nested)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
result += ` ${renderAnnotateStatementElements(ext.elements, env
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
else if (ext.
|
|
322
|
+
if (ext.elements) {
|
|
323
|
+
env.path.push('elements');
|
|
324
|
+
result += ` ${renderAnnotateStatementElements(ext.elements, env)}`;
|
|
325
|
+
env.path.length -= 1;
|
|
326
|
+
}
|
|
327
|
+
else if (ext.enum) {
|
|
328
|
+
env.path.push('enum');
|
|
329
|
+
result += ` ${renderAnnotateStatementElements(ext.enum, env)}`;
|
|
330
|
+
env.path.length -= 1;
|
|
331
|
+
}
|
|
332
|
+
else if (ext.returns) {
|
|
328
333
|
result += renderAnnotateReturns(ext, env);
|
|
334
|
+
}
|
|
329
335
|
|
|
330
336
|
if (ext.actions) { // Bound action annotations
|
|
331
337
|
result += ' actions {\n';
|
|
338
|
+
env.increaseIndent();
|
|
339
|
+
env.path.push('actions', '');
|
|
332
340
|
for (const name in ext.actions) {
|
|
333
|
-
|
|
341
|
+
env.path[env.path.length - 1] = name;
|
|
334
342
|
const action = ext.actions[name];
|
|
335
|
-
result += renderAnnotationAssignmentsAndDocComment(action,
|
|
343
|
+
result += renderAnnotationAssignmentsAndDocComment(action, env) + env.indent + quoteNonIdentifierOrKeyword(name, env);
|
|
336
344
|
// Action parameter annotations
|
|
337
345
|
if (action.params)
|
|
338
|
-
result += renderAnnotateParamsInParentheses(action,
|
|
346
|
+
result += renderAnnotateParamsInParentheses(action, env);
|
|
339
347
|
if (action.returns)
|
|
340
|
-
result += renderAnnotateReturns(action,
|
|
348
|
+
result += renderAnnotateReturns(action, env);
|
|
341
349
|
|
|
342
350
|
result = removeTrailingNewline(result);
|
|
343
351
|
result += ';\n';
|
|
344
352
|
}
|
|
353
|
+
env.decreaseIndent();
|
|
345
354
|
result += `${env.indent}}`;
|
|
346
355
|
}
|
|
347
356
|
|
|
@@ -361,18 +370,28 @@ function csnToCdl( csn, options, msg ) {
|
|
|
361
370
|
*/
|
|
362
371
|
function renderAnnotateStatementElements( elements, env ) {
|
|
363
372
|
let result = '{\n';
|
|
373
|
+
env.increaseIndent();
|
|
374
|
+
env.path.push('');
|
|
364
375
|
for (const name in elements) {
|
|
365
|
-
|
|
376
|
+
env.path[env.path.length - 1] = name;
|
|
366
377
|
const elem = elements[name];
|
|
367
|
-
result += renderAnnotationAssignmentsAndDocComment(elem,
|
|
368
|
-
result +=
|
|
369
|
-
if (elem.elements)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
378
|
+
result += renderAnnotationAssignmentsAndDocComment(elem, env);
|
|
379
|
+
result += env.indent + quoteNonIdentifierOrKeyword(name, env);
|
|
380
|
+
if (elem.elements) {
|
|
381
|
+
env.path.push('elements');
|
|
382
|
+
result += ` ${renderAnnotateStatementElements(elem.elements, env)}`;
|
|
383
|
+
env.path.pop();
|
|
384
|
+
}
|
|
385
|
+
else if (elem.enum) {
|
|
386
|
+
env.path.push('enum');
|
|
387
|
+
result += ` ${renderAnnotateStatementElements(elem.enum, env)}`;
|
|
388
|
+
env.path.pop();
|
|
389
|
+
}
|
|
373
390
|
|
|
374
391
|
result += ';\n';
|
|
375
392
|
}
|
|
393
|
+
env.path.length -= 1;
|
|
394
|
+
env.decreaseIndent();
|
|
376
395
|
result += `${env.indent}}`;
|
|
377
396
|
return result;
|
|
378
397
|
}
|
|
@@ -456,8 +475,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
456
475
|
return renderEvent(artifactName, art, env);
|
|
457
476
|
|
|
458
477
|
default:
|
|
459
|
-
|
|
460
|
-
throw new ModelError(`to.cdl: Unknown artifact kind: ${art.kind}`);
|
|
478
|
+
throw new ModelError(`to.cdl: Unknown artifact kind: '${art.kind}' at ${JSON.stringify(env.path)}`);
|
|
461
479
|
}
|
|
462
480
|
}
|
|
463
481
|
|
|
@@ -606,12 +624,14 @@ function csnToCdl( csn, options, msg ) {
|
|
|
606
624
|
|
|
607
625
|
if (isCalcElement) { // calculated element // @ts-ignore
|
|
608
626
|
result += ' = ';
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
627
|
+
env.path.push('value');
|
|
628
|
+
const isSubExpr = (element.value.xpr && xprContainsCondition(element.value.xpr));
|
|
629
|
+
result += isSubExpr
|
|
630
|
+
? exprRenderer.renderSubExpr(element.value, env)
|
|
631
|
+
: exprRenderer.renderExpr(element.value, env);
|
|
613
632
|
if (element.value.stored === true)
|
|
614
633
|
result += ' stored';
|
|
634
|
+
env.path.length -= 1;
|
|
615
635
|
}
|
|
616
636
|
|
|
617
637
|
return `${result};\n`;
|
|
@@ -660,7 +680,6 @@ function csnToCdl( csn, options, msg ) {
|
|
|
660
680
|
|
|
661
681
|
// Based on the current path, create a correctly nested structure
|
|
662
682
|
// of elements for which we collect annotations.
|
|
663
|
-
// TODO: More properties?
|
|
664
683
|
let obj = annotate;
|
|
665
684
|
for (let i = 2; i < env.path.length; ++i) {
|
|
666
685
|
const key = env.path[i];
|
|
@@ -746,13 +765,19 @@ function csnToCdl( csn, options, msg ) {
|
|
|
746
765
|
// JOIN
|
|
747
766
|
else if (source.join) {
|
|
748
767
|
// One join operation, possibly with ON-condition
|
|
749
|
-
|
|
768
|
+
env.path.push('args', 0);
|
|
769
|
+
let result = `(${renderViewSource(source.args[0], env)}`;
|
|
750
770
|
for (let i = 1; i < source.args.length; i++) {
|
|
771
|
+
env.path[env.path.length - 1] = i;
|
|
751
772
|
result += ` ${source.join} `;
|
|
752
773
|
result += renderJoinCardinality(source.cardinality);
|
|
753
|
-
result += `join ${renderViewSource(source.args[i], env
|
|
754
|
-
|
|
755
|
-
|
|
774
|
+
result += `join ${renderViewSource(source.args[i], env)}`;
|
|
775
|
+
}
|
|
776
|
+
env.path.length -= 2;
|
|
777
|
+
if (source.on) {
|
|
778
|
+
env.path.push('on');
|
|
779
|
+
result += ` on ${exprRenderer.renderExpr(source.on, env.withSubPath([ 'on' ]))}`;
|
|
780
|
+
env.path.length -= 1;
|
|
756
781
|
}
|
|
757
782
|
result += ')';
|
|
758
783
|
return result;
|
|
@@ -798,18 +823,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
798
823
|
let result = renderDefinitionReference(firstArtifactName, env);
|
|
799
824
|
|
|
800
825
|
// Even the first step might have parameters and/or a filter
|
|
826
|
+
env.path.push('ref', 0);
|
|
801
827
|
if (path.ref[0].args)
|
|
802
|
-
result += `(${renderArguments(path.ref[0], ':', env
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
|
|
807
|
-
const expr = exprRenderer.renderExpr(path.ref[0].where, env.withSubPath([ 'ref', 0, 'where' ]));
|
|
808
|
-
if (expr.endsWith(']')) // for cases such as [… ![id] ]
|
|
809
|
-
result += `[ ${cardinality}${expr} ]`;
|
|
810
|
-
else
|
|
811
|
-
result += `[${cardinality}${expr}]`;
|
|
812
|
-
}
|
|
828
|
+
result += `(${renderArguments(path.ref[0], ':', env)})`;
|
|
829
|
+
if (path.ref[0].where)
|
|
830
|
+
result += renderFilterAndCardinality(path.ref[0], env);
|
|
831
|
+
env.path.length -= 2;
|
|
813
832
|
|
|
814
833
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
815
834
|
if (path.ref.length > 1)
|
|
@@ -847,8 +866,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
847
866
|
* @return {string}
|
|
848
867
|
*/
|
|
849
868
|
function renderViewColumns( art, env, elements = Object.create(null) ) {
|
|
850
|
-
|
|
851
|
-
|
|
869
|
+
env.path.push( 'columns', -1 );
|
|
870
|
+
const result = art.columns.map((col, i) => {
|
|
871
|
+
env.path[env.path.length - 1] = i;
|
|
872
|
+
return renderViewColumn(col, env, findElement(elements, col));
|
|
873
|
+
}).join(',\n');
|
|
874
|
+
env.path.length -= 2;
|
|
852
875
|
return `${result}\n`;
|
|
853
876
|
}
|
|
854
877
|
|
|
@@ -869,8 +892,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
869
892
|
// of an `annotate` statement. That may change in the future.
|
|
870
893
|
result += renderDocComment(element, env);
|
|
871
894
|
}
|
|
872
|
-
|
|
873
|
-
result += renderAnnotationAssignmentsAndDocComment(col, env, { parentheses: true });
|
|
895
|
+
result += renderAnnotationAssignmentsAndDocComment(col, env);
|
|
874
896
|
result += env.indent;
|
|
875
897
|
|
|
876
898
|
// only if column is virtual, keyword virtual was present in the source text
|
|
@@ -893,11 +915,13 @@ function csnToCdl( csn, options, msg ) {
|
|
|
893
915
|
|
|
894
916
|
// Explicit type provided for the view element?
|
|
895
917
|
if (col.cast) {
|
|
918
|
+
env.path.push('cast');
|
|
896
919
|
// Special case: Explicit association type is actually a redirect
|
|
897
920
|
if (col.cast.target && !col.cast.type)
|
|
898
|
-
result += ` : ${renderRedirectedTo(col.cast, env
|
|
921
|
+
result += ` : ${renderRedirectedTo(col.cast, env)}`;
|
|
899
922
|
else
|
|
900
|
-
result += ` : ${renderTypeReferenceAndProps(col.cast, env
|
|
923
|
+
result += ` : ${renderTypeReferenceAndProps(col.cast, env, { typeRefOnly: true, noAnnoCollect: true })}`;
|
|
924
|
+
env.path.length -= 1;
|
|
901
925
|
}
|
|
902
926
|
return result;
|
|
903
927
|
}
|
|
@@ -920,10 +944,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
920
944
|
|
|
921
945
|
// We found a leaf - no further drilling
|
|
922
946
|
if (!obj.inline && !obj.expand) {
|
|
947
|
+
env.path.push('cast');
|
|
923
948
|
if (obj.cast && obj.cast.type)
|
|
924
|
-
result += ` : ${renderTypeReferenceAndProps(obj.cast, env
|
|
949
|
+
result += ` : ${renderTypeReferenceAndProps(obj.cast, env, { noAnnoCollect: true })}`;
|
|
925
950
|
else if (obj.cast && obj.cast.target) // test tbd
|
|
926
|
-
result += ` : ${renderRedirectedTo(obj.cast, env
|
|
951
|
+
result += ` : ${renderRedirectedTo(obj.cast, env)}`;
|
|
952
|
+
env.path.length -= 1;
|
|
927
953
|
return result;
|
|
928
954
|
}
|
|
929
955
|
|
|
@@ -1023,17 +1049,24 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1023
1049
|
|
|
1024
1050
|
let result = '';
|
|
1025
1051
|
const select = query.SELECT;
|
|
1026
|
-
const childEnv = env.withIncreasedIndent();
|
|
1027
1052
|
|
|
1028
1053
|
// If not a projection, must be view/entity.
|
|
1029
1054
|
result += (syntax === 'projection') ? 'projection on ' : 'select from ';
|
|
1030
|
-
|
|
1055
|
+
|
|
1056
|
+
env.path.push('from');
|
|
1057
|
+
result += renderViewSource(select.from, env);
|
|
1058
|
+
env.path.length -= 1;
|
|
1031
1059
|
|
|
1032
1060
|
if (select.mixin) {
|
|
1033
1061
|
let elems = '';
|
|
1062
|
+
env.path.push('mixin', '');
|
|
1063
|
+
env.increaseIndent();
|
|
1034
1064
|
forEach(select.mixin, (name, mixin) => {
|
|
1035
|
-
|
|
1065
|
+
env.path[env.path.length - 1] = name;
|
|
1066
|
+
elems += renderElement(name, mixin, env);
|
|
1036
1067
|
});
|
|
1068
|
+
env.decreaseIndent();
|
|
1069
|
+
env.path.length -= 2;
|
|
1037
1070
|
if (elems) {
|
|
1038
1071
|
result += ' mixin {\n';
|
|
1039
1072
|
result += elems;
|
|
@@ -1043,10 +1076,13 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1043
1076
|
result += select.distinct ? ' distinct' : '';
|
|
1044
1077
|
if (select.columns) {
|
|
1045
1078
|
result += ' {\n';
|
|
1046
|
-
|
|
1079
|
+
env.increaseIndent();
|
|
1080
|
+
result += renderViewColumns(select, env, elements);
|
|
1081
|
+
env.decreaseIndent();
|
|
1047
1082
|
result += `${env.indent}}`;
|
|
1048
1083
|
}
|
|
1049
1084
|
|
|
1085
|
+
const childEnv = env.withIncreasedIndent();
|
|
1050
1086
|
if (select.excluding) {
|
|
1051
1087
|
const excludes = select.excluding.map(id => `${childEnv.indent}${quoteNonIdentifierOrKeyword(id, env)}`).join(',\n');
|
|
1052
1088
|
result += ` excluding {\n${excludes}\n`;
|
|
@@ -1568,35 +1604,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1568
1604
|
result += `(${renderArguments(s, ':', env)})`;
|
|
1569
1605
|
}
|
|
1570
1606
|
|
|
1571
|
-
|
|
1572
|
-
let filter = '';
|
|
1573
|
-
|
|
1574
|
-
// TODO: Unify with other filter rendering for SELECT
|
|
1575
|
-
if (s.groupBy)
|
|
1576
|
-
filter += ` group by ${s.groupBy.map((expr, i) => exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
1577
|
-
if (s.having)
|
|
1578
|
-
filter += ` having ${exprRenderer.renderExpr(s.having, env.withSubPath([ 'having' ]))}`;
|
|
1579
|
-
if (s.orderBy)
|
|
1580
|
-
filter += ` order by ${s.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
1581
|
-
if (s.limit)
|
|
1582
|
-
filter += ` ${renderLimit(s.limit, env.withSubPath([ 'limit' ]))}`;
|
|
1583
|
-
|
|
1584
|
-
if (s.where) {
|
|
1585
|
-
let where = exprRenderer.renderExpr(s.where, env.withSubPath([ 'where' ]));
|
|
1586
|
-
// Special rules in CDS parser: If filter starts with one of these SQL keywords, WHERE is mandatory.
|
|
1587
|
-
if (filter || /^(?:group|having|order|limit)\s/i.test(where))
|
|
1588
|
-
where = ` where ${where}`;
|
|
1589
|
-
filter = `${where} ${filter}`;
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
filter = filter.trim();
|
|
1593
|
-
|
|
1594
|
-
if (cardinality || filter) {
|
|
1595
|
-
if (filter.endsWith(']')) // for cases such as [… ![id] ]
|
|
1596
|
-
result += `[ ${cardinality}${filter} ]`;
|
|
1597
|
-
else
|
|
1598
|
-
result += `[${cardinality}${filter}]`;
|
|
1599
|
-
}
|
|
1607
|
+
result += renderFilterAndCardinality(s, env);
|
|
1600
1608
|
|
|
1601
1609
|
return result;
|
|
1602
1610
|
}
|
|
@@ -1690,8 +1698,6 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1690
1698
|
/**
|
|
1691
1699
|
* Render a cardinality (only those parts that were actually provided)
|
|
1692
1700
|
*
|
|
1693
|
-
* TODO: Check srcmin as well?
|
|
1694
|
-
*
|
|
1695
1701
|
* @param {CSN.Artifact} art
|
|
1696
1702
|
* @return {string}
|
|
1697
1703
|
*/
|
|
@@ -1706,13 +1712,11 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1706
1712
|
let result = '[';
|
|
1707
1713
|
if (card.src !== undefined)
|
|
1708
1714
|
result += `${card.src}, `;
|
|
1709
|
-
|
|
1710
1715
|
if (card.min !== undefined)
|
|
1711
1716
|
result += `${card.min}..`;
|
|
1712
|
-
|
|
1713
1717
|
if (card.max !== undefined)
|
|
1714
1718
|
result += card.max;
|
|
1715
|
-
|
|
1719
|
+
// srcmin can't be represented in CDL
|
|
1716
1720
|
return `${result}]${suffix}`;
|
|
1717
1721
|
}
|
|
1718
1722
|
|
|
@@ -1750,6 +1754,41 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1750
1754
|
return result;
|
|
1751
1755
|
}
|
|
1752
1756
|
|
|
1757
|
+
|
|
1758
|
+
function renderFilterAndCardinality( s, env ) {
|
|
1759
|
+
let result = '';
|
|
1760
|
+
const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
|
|
1761
|
+
let filter = '';
|
|
1762
|
+
|
|
1763
|
+
// TODO: Unify with other filter rendering for SELECT
|
|
1764
|
+
if (s.groupBy)
|
|
1765
|
+
filter += ` group by ${s.groupBy.map((expr, i) => exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
1766
|
+
if (s.having)
|
|
1767
|
+
filter += ` having ${exprRenderer.renderExpr(s.having, env.withSubPath([ 'having' ]))}`;
|
|
1768
|
+
if (s.orderBy)
|
|
1769
|
+
filter += ` order by ${s.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
1770
|
+
if (s.limit)
|
|
1771
|
+
filter += ` ${renderLimit(s.limit, env.withSubPath([ 'limit' ]))}`;
|
|
1772
|
+
|
|
1773
|
+
if (s.where) {
|
|
1774
|
+
let where = exprRenderer.renderExpr(s.where, env.withSubPath([ 'where' ]));
|
|
1775
|
+
// Special rules in CDS parser: If filter starts with one of these SQL keywords, WHERE is mandatory.
|
|
1776
|
+
if (filter || /^(?:group|having|order|limit)\s/i.test(where))
|
|
1777
|
+
where = ` where ${where}`;
|
|
1778
|
+
filter = `${where} ${filter}`;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
filter = filter.trim();
|
|
1782
|
+
|
|
1783
|
+
if (cardinality || filter) {
|
|
1784
|
+
if (filter.endsWith(']')) // for cases such as [… ![id] ]
|
|
1785
|
+
result += `[ ${cardinality}${filter} ]`;
|
|
1786
|
+
else
|
|
1787
|
+
result += `[${cardinality}${filter}]`;
|
|
1788
|
+
}
|
|
1789
|
+
return result;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1753
1792
|
function renderDefaultExpr( art, env ) {
|
|
1754
1793
|
if (!art.default)
|
|
1755
1794
|
return '';
|
|
@@ -1977,7 +2016,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1977
2016
|
SELECT(x) {
|
|
1978
2017
|
return `(${renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
|
|
1979
2018
|
},
|
|
1980
|
-
}
|
|
2019
|
+
});
|
|
1981
2020
|
}
|
|
1982
2021
|
|
|
1983
2022
|
// checks -------------------------------------------------------------------
|
|
@@ -2135,6 +2174,12 @@ class CdlRenderEnvironment {
|
|
|
2135
2174
|
Object.assign(this, values);
|
|
2136
2175
|
}
|
|
2137
2176
|
|
|
2177
|
+
increaseIndent() {
|
|
2178
|
+
this.indent = ` ${this.indent}`;
|
|
2179
|
+
}
|
|
2180
|
+
decreaseIndent() {
|
|
2181
|
+
this.indent = this.indent.substring(0, this.indent.length - 2);
|
|
2182
|
+
}
|
|
2138
2183
|
withIncreasedIndent() {
|
|
2139
2184
|
return new CdlRenderEnvironment({ ...this, indent: ` ${this.indent}` });
|
|
2140
2185
|
}
|
package/lib/render/toHdbcds.js
CHANGED
|
@@ -103,10 +103,6 @@ function toHdbcdsSource( csn, options, messageFunctions ) {
|
|
|
103
103
|
const {
|
|
104
104
|
info, warning, error, throwWithAnyError, message,
|
|
105
105
|
} = messageFunctions;
|
|
106
|
-
if (options.tenantAsColumn) {
|
|
107
|
-
error('api-unexpected-option', null, { option: 'tenantAsColumn', module: 'to.hdbcds' });
|
|
108
|
-
throwWithAnyError();
|
|
109
|
-
}
|
|
110
106
|
|
|
111
107
|
const reportedMissingReplacements = Object.create(null);
|
|
112
108
|
|
|
@@ -140,7 +136,7 @@ function toHdbcdsSource( csn, options, messageFunctions ) {
|
|
|
140
136
|
SET(x) {
|
|
141
137
|
return `${renderQuery(x, false, this.env.withIncreasedIndent())}`;
|
|
142
138
|
},
|
|
143
|
-
}
|
|
139
|
+
});
|
|
144
140
|
|
|
145
141
|
function renderExpr( x, env ) {
|
|
146
142
|
return exprRenderer.renderExpr(x, env);
|