@sap/cds-compiler 2.7.0 → 2.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/lib/api/main.js +8 -10
- package/lib/api/options.js +13 -9
- package/lib/api/validate.js +11 -8
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +16 -0
- package/lib/base/messages.js +2 -0
- package/lib/base/model.js +1 -0
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +3 -1
- package/lib/compiler/definer.js +87 -29
- package/lib/compiler/resolver.js +75 -16
- package/lib/compiler/shared.js +29 -9
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +93 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +274 -83
- package/lib/edm/edmUtils.js +29 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4727 -4323
- package/lib/json/from-csn.js +52 -23
- package/lib/json/to-csn.js +185 -71
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +9 -0
- package/lib/language/language.g4 +90 -31
- package/lib/main.js +4 -0
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +7 -1
- package/lib/model/csnUtils.js +5 -4
- package/lib/optionProcessor.js +7 -1
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/toCdl.js +45 -9
- package/lib/render/toHdbcds.js +100 -34
- package/lib/render/toSql.js +12 -4
- package/lib/render/utils/common.js +5 -9
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +14 -4
- package/lib/transform/db/flattening.js +13 -5
- package/lib/transform/db/transformExists.js +252 -58
- package/lib/transform/forHanaNew.js +7 -1
- package/lib/transform/forOdataNew.js +12 -8
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +33 -1
- package/lib/transform/translateAssocsToJoins.js +6 -4
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/package.json +1 -1
package/lib/render/toHdbcds.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
const {
|
|
4
4
|
getParentNameOf, getLastPartOf, getLastPartOfRef,
|
|
5
5
|
hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
|
|
6
|
-
getRootArtifactName, getResultingName, getNamespace,
|
|
6
|
+
getRootArtifactName, getResultingName, getNamespace, forEachMember,
|
|
7
7
|
} = require('../model/csnUtils');
|
|
8
8
|
const keywords = require('../base/keywords');
|
|
9
9
|
const {
|
|
10
|
-
renderFunc,
|
|
11
|
-
hasHanaComment, getHanaComment, findElement,
|
|
10
|
+
renderFunc, beautifyExprArray, getRealName, addContextMarkers, addIntermediateContexts, cdsToSqlTypes,
|
|
11
|
+
hasHanaComment, getHanaComment, findElement, funcWithoutParen,
|
|
12
12
|
} = require('./utils/common');
|
|
13
13
|
const {
|
|
14
14
|
renderReferentialConstraint,
|
|
@@ -19,6 +19,8 @@ const { checkCSNVersion } = require('../json/csnVersion');
|
|
|
19
19
|
const { makeMessageFunction } = require('../base/messages');
|
|
20
20
|
const timetrace = require('../utils/timetrace');
|
|
21
21
|
|
|
22
|
+
const { smartId, delimitedId } = require('../sql-identifier');
|
|
23
|
+
|
|
22
24
|
const $PROJECTION = '$projection';
|
|
23
25
|
const $SELF = '$self';
|
|
24
26
|
|
|
@@ -402,6 +404,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
402
404
|
const duplicateChecker = new DuplicateChecker(); // registry for all artifact names and element names
|
|
403
405
|
duplicateChecker.addArtifact(artifactName, env.path, artifactName);
|
|
404
406
|
childEnv.path = env.path.concat('elements');
|
|
407
|
+
// calculate __aliases which must be used in case an association
|
|
408
|
+
// has the same identifier as it's target
|
|
409
|
+
createTopLevelAliasesForArtifact(artifactName, art, env);
|
|
405
410
|
for (const name in art.elements)
|
|
406
411
|
result += renderElement(name, art.elements[name], childEnv, duplicateChecker);
|
|
407
412
|
|
|
@@ -411,6 +416,33 @@ function toHdbcdsSource(csn, options) {
|
|
|
411
416
|
return result;
|
|
412
417
|
}
|
|
413
418
|
|
|
419
|
+
/**
|
|
420
|
+
* If an association/composition has the same identifier as it's target
|
|
421
|
+
* we must render an "using target as __target" and use the alias to refer to the target
|
|
422
|
+
*
|
|
423
|
+
* @param {string} artName
|
|
424
|
+
* @param {CSN.Artifact} art
|
|
425
|
+
* @param {CdlRenderEnvironment} env
|
|
426
|
+
*/
|
|
427
|
+
function createTopLevelAliasesForArtifact(artName, art, env) {
|
|
428
|
+
forEachMember(art, (element) => {
|
|
429
|
+
if (!element.target)
|
|
430
|
+
return;
|
|
431
|
+
|
|
432
|
+
let alias = element['@cds.persistence.name'];
|
|
433
|
+
if (uppercaseAndUnderscore(element.target) === element['@cds.persistence.name']) {
|
|
434
|
+
alias = createTopLevelAliasName(element['@cds.persistence.name']);
|
|
435
|
+
// calculate new alias if it would conflict with other csn.Artifact
|
|
436
|
+
while (csn.definitions[alias])
|
|
437
|
+
alias = createTopLevelAliasName(alias);
|
|
438
|
+
env.topLevelAliases[element['@cds.persistence.name']] = {
|
|
439
|
+
quotedName: formatIdentifier(element['@cds.persistence.name']),
|
|
440
|
+
quotedAlias: formatIdentifier(alias),
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
414
446
|
/**
|
|
415
447
|
* Render the 'technical configuration { ... }' section 'tc' of an entity.
|
|
416
448
|
*
|
|
@@ -516,7 +548,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
516
548
|
// Special handling for HANA CDS: Must omit the ':' before anonymous structured types (for historical reasons)
|
|
517
549
|
const omitColon = (!elm.type && elm.elements);
|
|
518
550
|
let result = '';
|
|
519
|
-
const quotedElementName =
|
|
551
|
+
const quotedElementName = formatIdentifier(elementName);
|
|
520
552
|
if (duplicateChecker)
|
|
521
553
|
duplicateChecker.addElement(quotedElementName, env.path, elementName);
|
|
522
554
|
|
|
@@ -548,7 +580,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
548
580
|
if (source.SELECT || source.SET) {
|
|
549
581
|
let result = `(${renderQuery(source, false, increaseIndent(env))})`;
|
|
550
582
|
if (source.as)
|
|
551
|
-
result += ` as ${
|
|
583
|
+
result += ` as ${formatIdentifier(source.as)}`;
|
|
552
584
|
|
|
553
585
|
return result;
|
|
554
586
|
}
|
|
@@ -627,11 +659,11 @@ function toHdbcdsSource(csn, options) {
|
|
|
627
659
|
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.sqlMapping, path.ref[0])) : getLastPartOfRef(path.ref);
|
|
628
660
|
if (path.as) {
|
|
629
661
|
// Source had an alias - render it
|
|
630
|
-
result += ` as ${
|
|
662
|
+
result += ` as ${formatIdentifier(path.as)}`;
|
|
631
663
|
}
|
|
632
|
-
else if (getLastPartOf(result) !==
|
|
664
|
+
else if (getLastPartOf(result) !== formatIdentifier(implicitAlias)) {
|
|
633
665
|
// Render an artificial alias if the result would produce a different one
|
|
634
|
-
result += ` as ${
|
|
666
|
+
result += ` as ${formatIdentifier(implicitAlias)}`;
|
|
635
667
|
}
|
|
636
668
|
return result;
|
|
637
669
|
}
|
|
@@ -655,7 +687,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
655
687
|
|
|
656
688
|
if (element && element.virtual || env._artifact.elements[leaf] && env._artifact.elements[leaf].virtual) {
|
|
657
689
|
if (isDeprecatedEnabled(options, 'renderVirtualElements'))
|
|
658
|
-
return `${result}${env.indent}null as ${
|
|
690
|
+
return `${result}${env.indent}null as ${formatIdentifier(leaf)}`;
|
|
659
691
|
}
|
|
660
692
|
else {
|
|
661
693
|
return renderNonVirtualColumn();
|
|
@@ -683,7 +715,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
683
715
|
alias = leaf;
|
|
684
716
|
|
|
685
717
|
if (alias)
|
|
686
|
-
result += ` as ${
|
|
718
|
+
result += ` as ${formatIdentifier(alias)}`;
|
|
687
719
|
|
|
688
720
|
// Explicit type provided for the view element?
|
|
689
721
|
if (col.cast && col.cast.target) {
|
|
@@ -797,7 +829,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
797
829
|
result += `${env.indent}}`;
|
|
798
830
|
}
|
|
799
831
|
if (select.excluding) {
|
|
800
|
-
result += ` excluding {\n${select.excluding.map(id => `${childEnv.indent}${
|
|
832
|
+
result += ` excluding {\n${select.excluding.map(id => `${childEnv.indent}${formatIdentifier(id)}`).join(',\n')}\n`;
|
|
801
833
|
result += `${env.indent}}`;
|
|
802
834
|
}
|
|
803
835
|
|
|
@@ -895,7 +927,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
895
927
|
function renderParameter(parName, par, env) {
|
|
896
928
|
if (par.notNull === true || par.notNull === false)
|
|
897
929
|
info(null, env.path.concat([ 'params', parName ]), 'Not Null constraints on HDBCDS view parameters are not allowed and are ignored');
|
|
898
|
-
return `${env.indent +
|
|
930
|
+
return `${env.indent + formatParamIdentifier(parName, env.path.concat([ 'params', parName ]))} : ${renderTypeReference(par, env)}`;
|
|
899
931
|
}
|
|
900
932
|
|
|
901
933
|
/**
|
|
@@ -1012,10 +1044,19 @@ function toHdbcdsSource(csn, options) {
|
|
|
1012
1044
|
|
|
1013
1045
|
result += `${renderCardinality(elm.cardinality)} to `;
|
|
1014
1046
|
|
|
1047
|
+
|
|
1015
1048
|
// normal target or named aspect
|
|
1016
1049
|
if (elm.target || elm.targetAspect && typeof elm.targetAspect === 'string') {
|
|
1017
|
-
|
|
1018
|
-
|
|
1050
|
+
// we might have a "using target as __target"
|
|
1051
|
+
const targetArtifact = csn.definitions[elm.target];
|
|
1052
|
+
const targetAlias = env.topLevelAliases[targetArtifact['@cds.persistence.name']];
|
|
1053
|
+
if (targetAlias) {
|
|
1054
|
+
result += targetAlias.quotedAlias;
|
|
1055
|
+
}
|
|
1056
|
+
else {
|
|
1057
|
+
result += plainNames ? renderAbsoluteNamePlain(elm.target || elm.targetAspect, env)
|
|
1058
|
+
: renderAbsoluteNameWithQuotes(elm.target || elm.targetAspect, env);
|
|
1059
|
+
}
|
|
1019
1060
|
}
|
|
1020
1061
|
|
|
1021
1062
|
// ON-condition (if any)
|
|
@@ -1087,7 +1128,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1087
1128
|
function renderExpr(x, env, inline = true, inExpr = false) {
|
|
1088
1129
|
// Compound expression
|
|
1089
1130
|
if (Array.isArray(x))
|
|
1090
|
-
return
|
|
1131
|
+
return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
|
|
1091
1132
|
|
|
1092
1133
|
if (typeof x === 'object' && x !== null) {
|
|
1093
1134
|
if (inExpr && x.cast && x.cast.type)
|
|
@@ -1129,6 +1170,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
1129
1170
|
|
|
1130
1171
|
const regex = RegExp(/^[a-zA-Z][\w#$]*$/, 'g');
|
|
1131
1172
|
const funcName = regex.test(x.func) ? x.func : quoteId(x.func);
|
|
1173
|
+
// we can't quote functions with parens, issue warning if it is a reserved keyword
|
|
1174
|
+
if (!funcWithoutParen(x, 'hana') && keywords.hdbcds.includes(uppercaseAndUnderscore(funcName)))
|
|
1175
|
+
warning(null, x.$location, `The identifier “${uppercaseAndUnderscore(funcName)}“ is a SAP HANA keyword`);
|
|
1132
1176
|
return renderFunc( funcName, x, 'hana', a => renderArgs(a, '=>', env) );
|
|
1133
1177
|
}
|
|
1134
1178
|
// Nested expression
|
|
@@ -1267,7 +1311,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1267
1311
|
[ $SELF, $PROJECTION, '$session' ].includes(s))
|
|
1268
1312
|
return s;
|
|
1269
1313
|
|
|
1270
|
-
return
|
|
1314
|
+
return formatIdentifier(s);
|
|
1271
1315
|
}
|
|
1272
1316
|
// ID with filters or parameters
|
|
1273
1317
|
else if (typeof s === 'object') {
|
|
@@ -1280,7 +1324,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1280
1324
|
return `${s.func}(${renderArgs(s.args, '=>', env)})`;
|
|
1281
1325
|
|
|
1282
1326
|
// Path step, possibly with view parameters and/or filters
|
|
1283
|
-
let result = `${
|
|
1327
|
+
let result = `${formatIdentifier(s.id)}`;
|
|
1284
1328
|
if (s.args) {
|
|
1285
1329
|
// View parameters
|
|
1286
1330
|
result += `(${renderArgs(s.args, ':', env)})`;
|
|
@@ -1312,7 +1356,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1312
1356
|
|
|
1313
1357
|
// Named arguments (object/dict)
|
|
1314
1358
|
else if (typeof args === 'object')
|
|
1315
|
-
return Object.keys(args).map(key => `${
|
|
1359
|
+
return Object.keys(args).map(key => `${formatIdentifier(key)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
1316
1360
|
|
|
1317
1361
|
|
|
1318
1362
|
throw new Error(`Unknown args: ${JSON.stringify(args)}`);
|
|
@@ -1405,10 +1449,10 @@ function toHdbcdsSource(csn, options) {
|
|
|
1405
1449
|
function renderAbsoluteNamePlain(absName, env) {
|
|
1406
1450
|
// Add using declaration
|
|
1407
1451
|
env.topLevelAliases[absName] = {
|
|
1408
|
-
quotedName: uppercaseAndUnderscore(absName),
|
|
1409
|
-
quotedAlias: uppercaseAndUnderscore(absName),
|
|
1452
|
+
quotedName: formatIdentifier(uppercaseAndUnderscore(absName)),
|
|
1453
|
+
quotedAlias: formatIdentifier(uppercaseAndUnderscore(absName)),
|
|
1410
1454
|
};
|
|
1411
|
-
return uppercaseAndUnderscore(absName);
|
|
1455
|
+
return formatIdentifier(uppercaseAndUnderscore(absName));
|
|
1412
1456
|
}
|
|
1413
1457
|
|
|
1414
1458
|
/**
|
|
@@ -1493,7 +1537,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1493
1537
|
function renderUsings(artifactName, env) {
|
|
1494
1538
|
const distinct = {};
|
|
1495
1539
|
Object.keys(env.topLevelAliases)
|
|
1496
|
-
.filter(name =>
|
|
1540
|
+
.filter(name => env.topLevelAliases[name].quotedAlias !== formatIdentifier(uppercaseAndUnderscore(artifactName))) // avoid "using FOO as FOO" in FOO.cds
|
|
1497
1541
|
.forEach((name) => {
|
|
1498
1542
|
distinct[`using ${env.topLevelAliases[name].quotedName} as ${env.topLevelAliases[name].quotedAlias};\n`] = '';
|
|
1499
1543
|
});
|
|
@@ -1635,7 +1679,16 @@ function toHdbcdsSource(csn, options) {
|
|
|
1635
1679
|
return id;
|
|
1636
1680
|
|
|
1637
1681
|
|
|
1638
|
-
|
|
1682
|
+
switch (options.forHana.names) {
|
|
1683
|
+
case 'plain':
|
|
1684
|
+
return smartId(id, 'hdbcds');
|
|
1685
|
+
case 'quoted':
|
|
1686
|
+
return delimitedId(id, 'hdbcds');
|
|
1687
|
+
case 'hdbcds':
|
|
1688
|
+
return delimitedId(id, 'hdbcds');
|
|
1689
|
+
default:
|
|
1690
|
+
return null;
|
|
1691
|
+
}
|
|
1639
1692
|
}
|
|
1640
1693
|
|
|
1641
1694
|
/*
|
|
@@ -1656,21 +1709,34 @@ function toHdbcdsSource(csn, options) {
|
|
|
1656
1709
|
}
|
|
1657
1710
|
|
|
1658
1711
|
/**
|
|
1659
|
-
* Quote or uppercase an identifier 'id', depending on naming strategy
|
|
1712
|
+
* Quote and/or uppercase an identifier 'id', depending on naming strategy
|
|
1660
1713
|
*
|
|
1661
1714
|
* @param {string} id Identifier
|
|
1662
|
-
* @param {CSN.Location} [location] Optional location for the warning.
|
|
1663
1715
|
* @returns {string} Quoted/uppercased id
|
|
1664
1716
|
*/
|
|
1665
|
-
function
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1717
|
+
function formatIdentifier(id) {
|
|
1718
|
+
id = options.forHana.names === 'plain' ? id.toUpperCase() : id;
|
|
1719
|
+
return quoteId(id);
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* Quote or uppercase a parameter identifier 'id', depending on naming strategy
|
|
1724
|
+
* Smart quoting cannot be applied to the parameter identifiers, issue warning instead.
|
|
1725
|
+
*
|
|
1726
|
+
*
|
|
1727
|
+
* @param {string} id Identifier
|
|
1728
|
+
* @param {CSN.Path} [location] Optional location for the warning.
|
|
1729
|
+
* @returns {string} Quoted/uppercased id
|
|
1730
|
+
*/
|
|
1731
|
+
function formatParamIdentifier(id, location) {
|
|
1732
|
+
// Warn if colliding with HANA keyword, but do not quote for plain
|
|
1733
|
+
// --> quoted reserved words as param lead to a weird deployment error
|
|
1734
|
+
if (keywords.hdbcds.includes(uppercaseAndUnderscore(id)))
|
|
1735
|
+
warning(null, location, { id }, 'The identifier $(ID) is a SAP HANA keyword');
|
|
1736
|
+
|
|
1737
|
+
if (plainNames)
|
|
1738
|
+
return uppercaseAndUnderscore(id);
|
|
1671
1739
|
|
|
1672
|
-
return result;
|
|
1673
|
-
}
|
|
1674
1740
|
return quoteId(id);
|
|
1675
1741
|
}
|
|
1676
1742
|
|
|
@@ -1693,7 +1759,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1693
1759
|
*/
|
|
1694
1760
|
function renderArtifactName(artifactName, env, fallthrough = false) {
|
|
1695
1761
|
if (plainNames && !fallthrough)
|
|
1696
|
-
return uppercaseAndUnderscore(artifactName);
|
|
1762
|
+
return formatIdentifier(uppercaseAndUnderscore(artifactName));
|
|
1697
1763
|
// hdbcds with quoted or hdbcds naming
|
|
1698
1764
|
return env.namePrefix + quoteId(getRealName(csn, artifactName).replace(/\./g, '_'));
|
|
1699
1765
|
}
|
package/lib/render/toSql.js
CHANGED
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
forEachDefinition, getResultingName,
|
|
8
8
|
} = require('../model/csnUtils');
|
|
9
9
|
const {
|
|
10
|
-
renderFunc,
|
|
10
|
+
renderFunc, beautifyExprArray, cdsToSqlTypes, getHanaComment, hasHanaComment,
|
|
11
11
|
} = require('./utils/common');
|
|
12
12
|
const {
|
|
13
13
|
renderReferentialConstraint, getIdentifierUtils,
|
|
@@ -1088,8 +1088,15 @@ function toSqlDdl(csn, options) {
|
|
|
1088
1088
|
const p = params[pn];
|
|
1089
1089
|
if (p.notNull === true || p.notNull === false)
|
|
1090
1090
|
info(null, [ 'definitions', artifactName, 'params', pn ], 'Not Null constraints on SQL view parameters are not allowed and are ignored');
|
|
1091
|
-
|
|
1092
|
-
|
|
1091
|
+
// do not quote parameter identifiers for naming mode "quoted" / "hdbcds"
|
|
1092
|
+
// this would be an incompatible change, as non-uppercased, quoted identifiers
|
|
1093
|
+
// are rejected by the HANA compiler.
|
|
1094
|
+
let pIdentifier;
|
|
1095
|
+
if (options.toSql.names === 'quoted' || options.toSql.names === 'hdbcds')
|
|
1096
|
+
pIdentifier = prepareIdentifier(pn);
|
|
1097
|
+
else
|
|
1098
|
+
pIdentifier = quoteSqlId(pn);
|
|
1099
|
+
let pstr = `IN ${pIdentifier} ${renderTypeReference(artifactName, pn, p)}`;
|
|
1093
1100
|
if (p.default)
|
|
1094
1101
|
pstr += ` DEFAULT ${renderExpr(p.default)}`;
|
|
1095
1102
|
|
|
@@ -1336,7 +1343,8 @@ function toSqlDdl(csn, options) {
|
|
|
1336
1343
|
function renderExpr(x, env, inline = true, nestedExpr = false) {
|
|
1337
1344
|
// Compound expression
|
|
1338
1345
|
if (Array.isArray(x)) {
|
|
1339
|
-
|
|
1346
|
+
const tokens = x.map(item => renderExpr(item, env, inline, nestedExpr));
|
|
1347
|
+
return beautifyExprArray(tokens);
|
|
1340
1348
|
}
|
|
1341
1349
|
else if (typeof x === 'object' && x !== null) {
|
|
1342
1350
|
if (nestedExpr && x.cast && x.cast.type)
|
|
@@ -53,19 +53,14 @@ function funcWithoutParen( node, dialect ) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* Process
|
|
56
|
+
* Process already rendered expression parts by joining them nicely
|
|
57
57
|
*
|
|
58
|
-
* @param {Array}
|
|
59
|
-
* @param {Function} renderExpr Function to render expressions
|
|
60
|
-
* @param {CdlRenderEnvironment} env Environment for rendering
|
|
61
|
-
* @param {boolean} inline Wether to render the expression inline
|
|
62
|
-
* @param {boolean} inExpr Wether to render as if a subexpression
|
|
58
|
+
* @param {Array} tokens Array of expression tokens
|
|
63
59
|
*
|
|
64
60
|
* @returns {string} The rendered xpr
|
|
65
61
|
*/
|
|
66
|
-
function
|
|
62
|
+
function beautifyExprArray(tokens) {
|
|
67
63
|
// Simply concatenate array parts with spaces (with a tiny bit of beautification)
|
|
68
|
-
const tokens = xpr.map(item => renderExpr(item, env, inline, inExpr));
|
|
69
64
|
let result = '';
|
|
70
65
|
for (let i = 0; i < tokens.length; i++) {
|
|
71
66
|
result += tokens[i];
|
|
@@ -378,7 +373,7 @@ function getHanaComment(obj) {
|
|
|
378
373
|
|
|
379
374
|
module.exports = {
|
|
380
375
|
renderFunc,
|
|
381
|
-
|
|
376
|
+
beautifyExprArray,
|
|
382
377
|
getNamespace,
|
|
383
378
|
getRealName,
|
|
384
379
|
addIntermediateContexts,
|
|
@@ -387,4 +382,5 @@ module.exports = {
|
|
|
387
382
|
hasHanaComment,
|
|
388
383
|
getHanaComment,
|
|
389
384
|
findElement,
|
|
385
|
+
funcWithoutParen,
|
|
390
386
|
};
|
package/lib/sql-identifier.js
CHANGED
|
@@ -51,7 +51,12 @@ const sqlDialects = {
|
|
|
51
51
|
effectiveName: name => name.toUpperCase(),
|
|
52
52
|
asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
|
|
53
53
|
},
|
|
54
|
-
|
|
54
|
+
hdbcds: {
|
|
55
|
+
regularRegex: /^[A-Za-z_][A-Za-z_0-9]*$/,
|
|
56
|
+
reservedWords: keywords.hdbcds,
|
|
57
|
+
effectiveName: name => name,
|
|
58
|
+
asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
|
|
59
|
+
},
|
|
55
60
|
};
|
|
56
61
|
|
|
57
62
|
function smartId( name, dialect ) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
hasAnnotationValue,
|
|
5
|
-
getResultingName,
|
|
4
|
+
hasAnnotationValue, getUtils, getServiceNames, forEachDefinition,
|
|
5
|
+
getResultingName, forEachMemberRecursively,
|
|
6
6
|
} = require('../../model/csnUtils');
|
|
7
7
|
const { setProp, isDeprecatedEnabled } = require('../../base/model');
|
|
8
8
|
const { getTransformers } = require('../transformUtilsNew');
|
|
@@ -117,10 +117,12 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
117
117
|
|
|
118
118
|
// extract keys for UUID inspection
|
|
119
119
|
const keys = [];
|
|
120
|
-
|
|
120
|
+
forEachMemberRecursively(artifact, (elt, name, prop, path) => {
|
|
121
|
+
if (!elt.elements && !elt.type && !elt.virtual) // only check leafs
|
|
122
|
+
error(null, path, 'Expecting element to have a type when used in a draft-enabled artifact');
|
|
121
123
|
if (elt.key && elt.key === true && !elt.virtual)
|
|
122
124
|
keys.push(elt);
|
|
123
|
-
});
|
|
125
|
+
}, [ 'definitions', artifactName ], true, { elementsOnly: true });
|
|
124
126
|
|
|
125
127
|
// In contrast to EDM, the DB entity may have more than one technical keys but should have idealy exactly one key of type cds.UUID
|
|
126
128
|
if (keys.length !== 1)
|
|
@@ -446,11 +446,15 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
446
446
|
if (col.ref && col.$scope !== '$magic') {
|
|
447
447
|
const _art = col._art || inspectRef(path.concat(i)).art;
|
|
448
448
|
if (_art && isStructured(_art))
|
|
449
|
-
newThing.push(...expandRef(_art, col.ref,
|
|
449
|
+
newThing.push(...expandRef(_art, col.ref, col.as, col.key || false, withAlias));
|
|
450
450
|
|
|
451
451
|
else
|
|
452
452
|
newThing.push(col);
|
|
453
453
|
}
|
|
454
|
+
else if (col.ref && col.$scope === '$magic' && col.ref[0] === '$user' && !col.as) {
|
|
455
|
+
col.as = implicitAs(col.ref);
|
|
456
|
+
newThing.push(col);
|
|
457
|
+
}
|
|
454
458
|
else {
|
|
455
459
|
newThing.push(col);
|
|
456
460
|
}
|
|
@@ -473,7 +477,7 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
473
477
|
*/
|
|
474
478
|
function expandRef(art, ref, alias, isKey, withAlias) {
|
|
475
479
|
const expanded = [];
|
|
476
|
-
const stack = [ [ art, ref, alias ] ];
|
|
480
|
+
const stack = [ [ art, ref, [ alias || ref[ref.length - 1] ] ] ];
|
|
477
481
|
while (stack.length > 0) {
|
|
478
482
|
const [ current, currentRef, currentAlias ] = stack.pop();
|
|
479
483
|
if (isStructured(current)) {
|
|
@@ -482,8 +486,14 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
482
486
|
}
|
|
483
487
|
else {
|
|
484
488
|
const obj = { ref: currentRef };
|
|
485
|
-
if (withAlias)
|
|
486
|
-
|
|
489
|
+
if (withAlias) {
|
|
490
|
+
const newAlias = currentAlias.join(pathDelimiter);
|
|
491
|
+
// if (alias !== undefined) // explicit alias
|
|
492
|
+
obj.as = newAlias;
|
|
493
|
+
// alias was implicit - to later distinguish expanded s -> s.a from explicitly written s.a
|
|
494
|
+
if (alias === undefined)
|
|
495
|
+
setProp(obj, '$implicitAlias', true);
|
|
496
|
+
}
|
|
487
497
|
if (isKey)
|
|
488
498
|
obj.key = true;
|
|
489
499
|
expanded.push(obj);
|
|
@@ -69,8 +69,12 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
const { toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
72
|
-
// (030) - For all elements, replace derived types by final base type
|
|
73
72
|
applyTransformations(csn, {
|
|
73
|
+
cast: (parent) => {
|
|
74
|
+
// Resolve cast already - we otherwise lose .localized
|
|
75
|
+
if (parent.cast.type && !isBuiltinType(parent.cast.type))
|
|
76
|
+
toFinalBaseType(parent.cast, resolved, true);
|
|
77
|
+
},
|
|
74
78
|
type: (parent, prop, type) => {
|
|
75
79
|
if (!isBuiltinType(type)) {
|
|
76
80
|
const directLocalized = parent.localized || false;
|
|
@@ -143,13 +147,17 @@ function flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter) {
|
|
|
143
147
|
|
|
144
148
|
parent.ref = flattenStructStepsInRef(ref, scopedPath, links, scope, resolvedLinkTypes );
|
|
145
149
|
resolved.set(parent, { links, art, scope });
|
|
146
|
-
// Because of expansion we added alias to everything - strip off superflous alias
|
|
147
|
-
if (parent.as && parent.as === parent.ref[parent.ref.length - 1] && scope !== '$magic')
|
|
148
|
-
delete parent.as;
|
|
149
150
|
// Explicitly set implicit alias for things that are now flattened - but only in columns
|
|
150
151
|
// TODO: Can this be done elegantly during expand phase already?
|
|
151
|
-
|
|
152
|
+
if (parent.$implicitAlias) { // an expanded s -> s.a is marked with this - do not add implicit alias "a" there, we want s_a
|
|
153
|
+
if (parent.ref[parent.ref.length - 1] === parent.as) // for a simple s that was expanded - for s.substructure this would not apply
|
|
154
|
+
delete parent.as;
|
|
155
|
+
delete parent.$implicitAlias;
|
|
156
|
+
}
|
|
157
|
+
// To handle explicitly written s.a - add implicit alias a, since after flattening it would otherwise be s_a
|
|
158
|
+
else if (parent.ref[parent.ref.length - 1] !== lastRef && (insideColumns(scopedPath) || insideKeys(scopedPath)) && !parent.as) {
|
|
152
159
|
parent.as = lastRef;
|
|
160
|
+
}
|
|
153
161
|
};
|
|
154
162
|
// adapt queries later
|
|
155
163
|
adaptRefs.push(fn);
|