@sap/cds-compiler 2.4.4 → 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 +241 -1
- package/bin/.eslintrc.json +17 -0
- package/bin/cds_update_identifiers.js +8 -7
- package/bin/cdsc.js +180 -132
- package/bin/cdshi.js +18 -11
- package/bin/cdsse.js +38 -32
- package/bin/cdsv2m.js +8 -7
- package/doc/CHANGELOG_BETA.md +36 -1
- package/lib/api/main.js +81 -100
- package/lib/api/options.js +17 -11
- package/lib/api/validate.js +12 -8
- package/lib/backends.js +0 -81
- package/lib/base/keywords.js +32 -2
- package/lib/base/location.js +2 -2
- package/lib/base/message-registry.js +66 -4
- package/lib/base/messages.js +84 -27
- package/lib/base/model.js +2 -61
- package/lib/checks/arrayOfs.js +0 -1
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/enricher.js +8 -2
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +27 -9
- package/lib/checks/selectItems.js +25 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +66 -13
- package/lib/compiler/assert-consistency.js +24 -12
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +6 -4
- package/lib/compiler/definer.js +101 -39
- package/lib/compiler/index.js +88 -59
- package/lib/compiler/resolver.js +455 -209
- package/lib/compiler/shared.js +57 -33
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +128 -99
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +361 -127
- package/lib/edm/edmUtils.js +103 -33
- package/lib/gen/Dictionary.json +74 -28
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +18 -4
- package/lib/gen/language.tokens +124 -118
- package/lib/gen/languageLexer.interp +13 -1
- package/lib/gen/languageLexer.js +870 -839
- package/lib/gen/languageLexer.tokens +116 -111
- package/lib/gen/languageParser.js +5894 -5614
- package/lib/json/from-csn.js +152 -67
- package/lib/json/to-csn.js +334 -135
- package/lib/language/antlrParser.js +4 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +24 -14
- package/lib/language/language.g4 +188 -128
- package/lib/main.d.ts +435 -0
- package/lib/main.js +31 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +463 -187
- package/lib/model/csnUtils.js +280 -136
- package/lib/model/enrichCsn.js +75 -4
- package/lib/model/revealInternalProperties.js +2 -1
- package/lib/modelCompare/compare.js +70 -25
- package/lib/optionProcessor.js +13 -10
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +8 -5
- package/lib/render/toCdl.js +123 -40
- package/lib/render/toHdbcds.js +156 -65
- package/lib/render/toSql.js +87 -11
- package/lib/render/utils/common.js +55 -9
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/{sql → db}/.eslintrc.json +0 -0
- package/lib/transform/{sql → db}/assertUnique.js +7 -8
- package/lib/transform/{sql → db}/constraints.js +35 -20
- package/lib/transform/db/draft.js +353 -0
- package/lib/transform/db/expansion.js +582 -0
- package/lib/transform/db/flattening.js +325 -0
- package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
- package/lib/transform/{sql → db}/helpers.js +0 -0
- package/lib/transform/{sql → db}/transformExists.js +256 -60
- package/lib/transform/forHanaNew.js +216 -765
- package/lib/transform/forOdataNew.js +60 -56
- package/lib/transform/localized.js +48 -26
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
- package/lib/transform/odata/generateForeignKeyElements.js +13 -12
- package/lib/transform/odata/referenceFlattener.js +60 -36
- package/lib/transform/odata/sortByAssociationDependency.js +4 -4
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +21 -22
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +27 -17
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +141 -77
- package/lib/transform/translateAssocsToJoins.js +17 -14
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +0 -11
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/timetrace.js +6 -1
- package/package.json +2 -1
- package/lib/base/deepCopy.js +0 -66
- package/lib/json/walker.js +0 -26
- package/lib/utils/string.js +0 -17
package/lib/render/toHdbcds.js
CHANGED
|
@@ -3,11 +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,
|
|
10
|
+
renderFunc, beautifyExprArray, getRealName, addContextMarkers, addIntermediateContexts, cdsToSqlTypes,
|
|
11
|
+
hasHanaComment, getHanaComment, findElement, funcWithoutParen,
|
|
11
12
|
} = require('./utils/common');
|
|
12
13
|
const {
|
|
13
14
|
renderReferentialConstraint,
|
|
@@ -18,9 +19,21 @@ const { checkCSNVersion } = require('../json/csnVersion');
|
|
|
18
19
|
const { makeMessageFunction } = require('../base/messages');
|
|
19
20
|
const timetrace = require('../utils/timetrace');
|
|
20
21
|
|
|
22
|
+
const { smartId, delimitedId } = require('../sql-identifier');
|
|
23
|
+
|
|
21
24
|
const $PROJECTION = '$projection';
|
|
22
25
|
const $SELF = '$self';
|
|
23
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Get the comment and in addition escape \n so HANA CDS can handle it.
|
|
29
|
+
*
|
|
30
|
+
* @param {CSN.Artifact} obj
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function getEscapedHanaComment(obj) {
|
|
34
|
+
return getHanaComment(obj).replace(/\n/g, '\\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
/**
|
|
25
38
|
* Render the CSN model 'model' to CDS source text. One source is created per
|
|
26
39
|
* top-level artifact. Return a dictionary of top-level artifacts
|
|
@@ -28,8 +41,7 @@ const $SELF = '$self';
|
|
|
28
41
|
* { "foo" : "using XY; context foo {...};",
|
|
29
42
|
* "bar::wiz" : "namespace bar::; entity wiz {...};"
|
|
30
43
|
* }
|
|
31
|
-
*
|
|
32
|
-
* only affects translation of '$self.foo' in paths and ::-ish namespace declarations)
|
|
44
|
+
*
|
|
33
45
|
* FIXME: This comment no longer tells the whole truth
|
|
34
46
|
*
|
|
35
47
|
* @param {CSN.Model} csn HANA transformed CSN
|
|
@@ -38,9 +50,9 @@ const $SELF = '$self';
|
|
|
38
50
|
*/
|
|
39
51
|
function toHdbcdsSource(csn, options) {
|
|
40
52
|
timetrace.start('HDBCDS rendering');
|
|
41
|
-
const plainNames = options.
|
|
42
|
-
const quotedNames = options.
|
|
43
|
-
const hdbcdsNames = options.
|
|
53
|
+
const plainNames = options.sqlMapping === 'plain';
|
|
54
|
+
const quotedNames = options.sqlMapping === 'quoted';
|
|
55
|
+
const hdbcdsNames = options.sqlMapping === 'hdbcds';
|
|
44
56
|
|
|
45
57
|
const {
|
|
46
58
|
info, warning, error, throwWithError,
|
|
@@ -51,7 +63,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
51
63
|
const result = Object.create(null);
|
|
52
64
|
|
|
53
65
|
|
|
54
|
-
const globalDuplicateChecker = new DuplicateChecker(options.
|
|
66
|
+
const globalDuplicateChecker = new DuplicateChecker(options.sqlMapping); // registry for all artifact names and element names
|
|
55
67
|
|
|
56
68
|
const killList = [];
|
|
57
69
|
if (quotedNames)
|
|
@@ -378,7 +390,10 @@ function toHdbcdsSource(csn, options) {
|
|
|
378
390
|
const childEnv = increaseIndent(env);
|
|
379
391
|
const normalizedArtifactName = renderArtifactName(artifactName, env);
|
|
380
392
|
|
|
381
|
-
globalDuplicateChecker.addArtifact(art['@cds.persistence.name'],
|
|
393
|
+
globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], env.path, artifactName);
|
|
394
|
+
|
|
395
|
+
if (hasHanaComment(art, options))
|
|
396
|
+
result += `${env.indent}@Comment: '${getEscapedHanaComment(art)}'\n`;
|
|
382
397
|
|
|
383
398
|
result += `${env.indent + (art.abstract ? 'abstract ' : '')}entity ${normalizedArtifactName}`;
|
|
384
399
|
if (art.includes) {
|
|
@@ -387,7 +402,11 @@ function toHdbcdsSource(csn, options) {
|
|
|
387
402
|
}
|
|
388
403
|
result += ' {\n';
|
|
389
404
|
const duplicateChecker = new DuplicateChecker(); // registry for all artifact names and element names
|
|
390
|
-
duplicateChecker.addArtifact(artifactName,
|
|
405
|
+
duplicateChecker.addArtifact(artifactName, env.path, artifactName);
|
|
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);
|
|
391
410
|
for (const name in art.elements)
|
|
392
411
|
result += renderElement(name, art.elements[name], childEnv, duplicateChecker);
|
|
393
412
|
|
|
@@ -397,6 +416,33 @@ function toHdbcdsSource(csn, options) {
|
|
|
397
416
|
return result;
|
|
398
417
|
}
|
|
399
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
|
+
|
|
400
446
|
/**
|
|
401
447
|
* Render the 'technical configuration { ... }' section 'tc' of an entity.
|
|
402
448
|
*
|
|
@@ -502,9 +548,13 @@ function toHdbcdsSource(csn, options) {
|
|
|
502
548
|
// Special handling for HANA CDS: Must omit the ':' before anonymous structured types (for historical reasons)
|
|
503
549
|
const omitColon = (!elm.type && elm.elements);
|
|
504
550
|
let result = '';
|
|
505
|
-
const quotedElementName =
|
|
551
|
+
const quotedElementName = formatIdentifier(elementName);
|
|
506
552
|
if (duplicateChecker)
|
|
507
|
-
duplicateChecker.addElement(quotedElementName,
|
|
553
|
+
duplicateChecker.addElement(quotedElementName, env.path, elementName);
|
|
554
|
+
|
|
555
|
+
if (hasHanaComment(elm, options))
|
|
556
|
+
result += `${env.indent}@Comment: '${getEscapedHanaComment(elm)}'\n`;
|
|
557
|
+
|
|
508
558
|
result += env.indent + (elm.key && !isSubElement ? 'key ' : '') +
|
|
509
559
|
(elm.masked ? 'masked ' : '') +
|
|
510
560
|
quotedElementName + (omitColon ? ' ' : ' : ') +
|
|
@@ -530,7 +580,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
530
580
|
if (source.SELECT || source.SET) {
|
|
531
581
|
let result = `(${renderQuery(source, false, increaseIndent(env))})`;
|
|
532
582
|
if (source.as)
|
|
533
|
-
result += ` as ${
|
|
583
|
+
result += ` as ${formatIdentifier(source.as)}`;
|
|
534
584
|
|
|
535
585
|
return result;
|
|
536
586
|
}
|
|
@@ -606,14 +656,14 @@ function toHdbcdsSource(csn, options) {
|
|
|
606
656
|
function renderAbsolutePathWithAlias(path, env) {
|
|
607
657
|
let result = renderAbsolutePath(path, env);
|
|
608
658
|
// Take care of aliases - for artifact references, use the resulting name (multi-dot joined with _)
|
|
609
|
-
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.
|
|
659
|
+
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.sqlMapping, path.ref[0])) : getLastPartOfRef(path.ref);
|
|
610
660
|
if (path.as) {
|
|
611
661
|
// Source had an alias - render it
|
|
612
|
-
result += ` as ${
|
|
662
|
+
result += ` as ${formatIdentifier(path.as)}`;
|
|
613
663
|
}
|
|
614
|
-
else if (getLastPartOf(result) !==
|
|
664
|
+
else if (getLastPartOf(result) !== formatIdentifier(implicitAlias)) {
|
|
615
665
|
// Render an artificial alias if the result would produce a different one
|
|
616
|
-
result += ` as ${
|
|
666
|
+
result += ` as ${formatIdentifier(implicitAlias)}`;
|
|
617
667
|
}
|
|
618
668
|
return result;
|
|
619
669
|
}
|
|
@@ -625,19 +675,19 @@ function toHdbcdsSource(csn, options) {
|
|
|
625
675
|
*
|
|
626
676
|
* @param {object} col Column to render
|
|
627
677
|
* @param {CdlRenderEnvironment} env Environment
|
|
678
|
+
* @param {CSN.Element} [element] Element (non-enum from subquery possibly) corresponding to the column ref
|
|
628
679
|
* @returns {string} Rendered column
|
|
629
680
|
*/
|
|
630
|
-
function renderViewColumn(col, env) {
|
|
681
|
+
function renderViewColumn(col, env, element) {
|
|
631
682
|
// Annotations and column
|
|
632
|
-
let result = '';
|
|
683
|
+
let result = element && hasHanaComment(element, options) ? `${env.indent}@Comment: '${getEscapedHanaComment(element)}'\n` : '';
|
|
633
684
|
|
|
634
685
|
const leaf = col.as || col.ref && col.ref[col.ref.length - 1];
|
|
635
|
-
const element = env._artifact.elements[leaf];
|
|
636
686
|
// Render 'null as <alias>' only for database and if element is virtual
|
|
637
687
|
|
|
638
|
-
if (element && element.virtual) {
|
|
688
|
+
if (element && element.virtual || env._artifact.elements[leaf] && env._artifact.elements[leaf].virtual) {
|
|
639
689
|
if (isDeprecatedEnabled(options, 'renderVirtualElements'))
|
|
640
|
-
return `${result}${env.indent}null as ${
|
|
690
|
+
return `${result}${env.indent}null as ${formatIdentifier(leaf)}`;
|
|
641
691
|
}
|
|
642
692
|
else {
|
|
643
693
|
return renderNonVirtualColumn();
|
|
@@ -652,7 +702,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
652
702
|
result += 'virtual ';
|
|
653
703
|
// If key is explicitly set in a non-leading query, issue an error.
|
|
654
704
|
if (col.key && env.skipKeys)
|
|
655
|
-
error(null, env.path, '
|
|
705
|
+
error(null, env.path, { tokensymbol: 'key', $reviewed: true }, 'Unexpected $(TOKENSYMBOL) in subquery');
|
|
656
706
|
|
|
657
707
|
const key = (!env.skipKeys && (col.key || (element && element.key)) ? 'key ' : '');
|
|
658
708
|
result += key + renderExpr(col, env, true);
|
|
@@ -665,7 +715,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
665
715
|
alias = leaf;
|
|
666
716
|
|
|
667
717
|
if (alias)
|
|
668
|
-
result += ` as ${
|
|
718
|
+
result += ` as ${formatIdentifier(alias)}`;
|
|
669
719
|
|
|
670
720
|
// Explicit type provided for the view element?
|
|
671
721
|
if (col.cast && col.cast.target) {
|
|
@@ -692,7 +742,12 @@ function toHdbcdsSource(csn, options) {
|
|
|
692
742
|
*/
|
|
693
743
|
function renderView(artifactName, art, env) {
|
|
694
744
|
let result = '';
|
|
695
|
-
|
|
745
|
+
const artifactPath = [ 'definitions', artifactName ];
|
|
746
|
+
globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], artifactPath, artifactName);
|
|
747
|
+
|
|
748
|
+
if (hasHanaComment(art, options))
|
|
749
|
+
result += `${env.indent}@Comment: '${getEscapedHanaComment(art)}'\n`;
|
|
750
|
+
|
|
696
751
|
result += `${env.indent}${art.abstract ? 'abstract ' : ''}view ${renderArtifactName(artifactName, env)}`;
|
|
697
752
|
if (art.params) {
|
|
698
753
|
const childEnv = increaseIndent(env);
|
|
@@ -704,7 +759,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
704
759
|
result += ' as ';
|
|
705
760
|
}
|
|
706
761
|
env._artifact = art;
|
|
707
|
-
result += renderQuery(getNormalizedQuery(art).query, true, env,
|
|
762
|
+
result += renderQuery(getNormalizedQuery(art).query, true, env, artifactPath.concat(art.projection ? 'projection' : 'query'), art.elements);
|
|
708
763
|
result += ';\n';
|
|
709
764
|
return result;
|
|
710
765
|
}
|
|
@@ -719,15 +774,16 @@ function toHdbcdsSource(csn, options) {
|
|
|
719
774
|
* @param {boolean} isLeadingQuery Wether the query is the leading query or not
|
|
720
775
|
* @param {CdlRenderEnvironment} env Environment
|
|
721
776
|
* @param {CSN.Path} [path=[]] CSN path to the query
|
|
777
|
+
* @param {object} [elements] For leading query, the elements of the artifact
|
|
722
778
|
* @returns {string} The rendered query
|
|
723
779
|
*/
|
|
724
|
-
function renderQuery(query, isLeadingQuery, env, path = []) {
|
|
780
|
+
function renderQuery(query, isLeadingQuery, env, path = [], elements) {
|
|
725
781
|
let result = '';
|
|
726
782
|
env.skipKeys = !isLeadingQuery;
|
|
727
783
|
// Set operator, like UNION, INTERSECT, ...
|
|
728
784
|
if (query.SET) {
|
|
729
785
|
// First arg may be leading query
|
|
730
|
-
result += `(${renderQuery(query.SET.args[0], isLeadingQuery, env, path.concat([ 'SET', 'args', 0 ]))}`;
|
|
786
|
+
result += `(${renderQuery(query.SET.args[0], isLeadingQuery, env, path.concat([ 'SET', 'args', 0 ]), elements)}`;
|
|
731
787
|
// FIXME: Clarify if set operators can be n-ary (assuming binary here)
|
|
732
788
|
if (query.SET.op) {
|
|
733
789
|
// Loop over all other arguments, i.e. for A UNION B UNION C UNION D ...
|
|
@@ -753,7 +809,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
753
809
|
const childEnv = increaseIndent(env);
|
|
754
810
|
childEnv.currentArtifactName = $PROJECTION; // $self to be replaced by $projection
|
|
755
811
|
result += `select from ${renderViewSource(select.from, env)}`;
|
|
756
|
-
|
|
757
812
|
if (select.mixin) {
|
|
758
813
|
let elems = '';
|
|
759
814
|
for (const name in select.mixin)
|
|
@@ -768,13 +823,13 @@ function toHdbcdsSource(csn, options) {
|
|
|
768
823
|
result += select.distinct ? ' distinct' : '';
|
|
769
824
|
if (select.columns) {
|
|
770
825
|
result += ' {\n';
|
|
771
|
-
result += `${select.columns.map(col => renderViewColumn(col, childEnv))
|
|
826
|
+
result += `${select.columns.map(col => renderViewColumn(col, childEnv, findElement(elements, col)))
|
|
772
827
|
.filter(s => s !== '')
|
|
773
828
|
.join(',\n')}\n`;
|
|
774
829
|
result += `${env.indent}}`;
|
|
775
830
|
}
|
|
776
831
|
if (select.excluding) {
|
|
777
|
-
result += ` excluding {\n${select.excluding.map(id => `${childEnv.indent}${
|
|
832
|
+
result += ` excluding {\n${select.excluding.map(id => `${childEnv.indent}${formatIdentifier(id)}`).join(',\n')}\n`;
|
|
778
833
|
result += `${env.indent}}`;
|
|
779
834
|
}
|
|
780
835
|
|
|
@@ -872,7 +927,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
872
927
|
function renderParameter(parName, par, env) {
|
|
873
928
|
if (par.notNull === true || par.notNull === false)
|
|
874
929
|
info(null, env.path.concat([ 'params', parName ]), 'Not Null constraints on HDBCDS view parameters are not allowed and are ignored');
|
|
875
|
-
return `${env.indent +
|
|
930
|
+
return `${env.indent + formatParamIdentifier(parName, env.path.concat([ 'params', parName ]))} : ${renderTypeReference(par, env)}`;
|
|
876
931
|
}
|
|
877
932
|
|
|
878
933
|
/**
|
|
@@ -989,10 +1044,24 @@ function toHdbcdsSource(csn, options) {
|
|
|
989
1044
|
|
|
990
1045
|
result += `${renderCardinality(elm.cardinality)} to `;
|
|
991
1046
|
|
|
1047
|
+
|
|
992
1048
|
// normal target or named aspect
|
|
993
1049
|
if (elm.target || elm.targetAspect && typeof elm.targetAspect === 'string') {
|
|
994
|
-
|
|
995
|
-
|
|
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
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// ON-condition (if any)
|
|
1063
|
+
if (elm.on) {
|
|
1064
|
+
result += ` on ${renderExpr(elm.on, env, true, true)}`;
|
|
996
1065
|
}
|
|
997
1066
|
else if (elm.targetAspect && elm.targetAspect.elements) { // anonymous aspect
|
|
998
1067
|
const childEnv = increaseIndent(env);
|
|
@@ -1004,11 +1073,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1004
1073
|
}
|
|
1005
1074
|
|
|
1006
1075
|
|
|
1007
|
-
// ON-condition (if any)
|
|
1008
|
-
if (elm.on)
|
|
1009
|
-
result += ` on ${renderExpr(elm.on, env, true, true)}`;
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
1076
|
// Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
|
|
1013
1077
|
if (elm.keys && !elm.on)
|
|
1014
1078
|
result += ` { ${Object.keys(elm.keys).map(name => renderForeignKey(elm.keys[name], env)).join(', ')} }`;
|
|
@@ -1064,7 +1128,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1064
1128
|
function renderExpr(x, env, inline = true, inExpr = false) {
|
|
1065
1129
|
// Compound expression
|
|
1066
1130
|
if (Array.isArray(x))
|
|
1067
|
-
return
|
|
1131
|
+
return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
|
|
1068
1132
|
|
|
1069
1133
|
if (typeof x === 'object' && x !== null) {
|
|
1070
1134
|
if (inExpr && x.cast && x.cast.type)
|
|
@@ -1106,6 +1170,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
1106
1170
|
|
|
1107
1171
|
const regex = RegExp(/^[a-zA-Z][\w#$]*$/, 'g');
|
|
1108
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`);
|
|
1109
1176
|
return renderFunc( funcName, x, 'hana', a => renderArgs(a, '=>', env) );
|
|
1110
1177
|
}
|
|
1111
1178
|
// Nested expression
|
|
@@ -1165,11 +1232,11 @@ function toHdbcdsSource(csn, options) {
|
|
|
1165
1232
|
if (x.ref[0] === '$user') {
|
|
1166
1233
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1167
1234
|
if (x.ref[1] === 'id') {
|
|
1168
|
-
if (options.
|
|
1169
|
-
return `'${options.
|
|
1235
|
+
if (options.magicVars && options.magicVars.user && (typeof options.magicVars.user === 'string' || options.magicVars.user instanceof String))
|
|
1236
|
+
return `'${options.magicVars.user}'`;
|
|
1170
1237
|
|
|
1171
|
-
else if ((options.
|
|
1172
|
-
return `'${options.
|
|
1238
|
+
else if ((options.magicVars && options.magicVars.user && options.magicVars.user.id) && (typeof options.magicVars.user.id === 'string' || options.magicVars.user.id instanceof String))
|
|
1239
|
+
return `'${options.magicVars.user.id}'`;
|
|
1173
1240
|
|
|
1174
1241
|
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1175
1242
|
}
|
|
@@ -1244,7 +1311,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1244
1311
|
[ $SELF, $PROJECTION, '$session' ].includes(s))
|
|
1245
1312
|
return s;
|
|
1246
1313
|
|
|
1247
|
-
return
|
|
1314
|
+
return formatIdentifier(s);
|
|
1248
1315
|
}
|
|
1249
1316
|
// ID with filters or parameters
|
|
1250
1317
|
else if (typeof s === 'object') {
|
|
@@ -1257,7 +1324,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1257
1324
|
return `${s.func}(${renderArgs(s.args, '=>', env)})`;
|
|
1258
1325
|
|
|
1259
1326
|
// Path step, possibly with view parameters and/or filters
|
|
1260
|
-
let result = `${
|
|
1327
|
+
let result = `${formatIdentifier(s.id)}`;
|
|
1261
1328
|
if (s.args) {
|
|
1262
1329
|
// View parameters
|
|
1263
1330
|
result += `(${renderArgs(s.args, ':', env)})`;
|
|
@@ -1289,7 +1356,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1289
1356
|
|
|
1290
1357
|
// Named arguments (object/dict)
|
|
1291
1358
|
else if (typeof args === 'object')
|
|
1292
|
-
return Object.keys(args).map(key => `${
|
|
1359
|
+
return Object.keys(args).map(key => `${formatIdentifier(key)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
1293
1360
|
|
|
1294
1361
|
|
|
1295
1362
|
throw new Error(`Unknown args: ${JSON.stringify(args)}`);
|
|
@@ -1382,10 +1449,10 @@ function toHdbcdsSource(csn, options) {
|
|
|
1382
1449
|
function renderAbsoluteNamePlain(absName, env) {
|
|
1383
1450
|
// Add using declaration
|
|
1384
1451
|
env.topLevelAliases[absName] = {
|
|
1385
|
-
quotedName: uppercaseAndUnderscore(absName),
|
|
1386
|
-
quotedAlias: uppercaseAndUnderscore(absName),
|
|
1452
|
+
quotedName: formatIdentifier(uppercaseAndUnderscore(absName)),
|
|
1453
|
+
quotedAlias: formatIdentifier(uppercaseAndUnderscore(absName)),
|
|
1387
1454
|
};
|
|
1388
|
-
return uppercaseAndUnderscore(absName);
|
|
1455
|
+
return formatIdentifier(uppercaseAndUnderscore(absName));
|
|
1389
1456
|
}
|
|
1390
1457
|
|
|
1391
1458
|
/**
|
|
@@ -1470,7 +1537,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1470
1537
|
function renderUsings(artifactName, env) {
|
|
1471
1538
|
const distinct = {};
|
|
1472
1539
|
Object.keys(env.topLevelAliases)
|
|
1473
|
-
.filter(name =>
|
|
1540
|
+
.filter(name => env.topLevelAliases[name].quotedAlias !== formatIdentifier(uppercaseAndUnderscore(artifactName))) // avoid "using FOO as FOO" in FOO.cds
|
|
1474
1541
|
.forEach((name) => {
|
|
1475
1542
|
distinct[`using ${env.topLevelAliases[name].quotedName} as ${env.topLevelAliases[name].quotedAlias};\n`] = '';
|
|
1476
1543
|
});
|
|
@@ -1541,6 +1608,8 @@ function toHdbcdsSource(csn, options) {
|
|
|
1541
1608
|
topLevelAliases: Object.create(null),
|
|
1542
1609
|
// Current name prefix (including trailing dot if not empty)
|
|
1543
1610
|
namePrefix: '',
|
|
1611
|
+
// CSN path - should at least point to the correct artifact
|
|
1612
|
+
path: [],
|
|
1544
1613
|
};
|
|
1545
1614
|
}
|
|
1546
1615
|
|
|
@@ -1585,7 +1654,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1585
1654
|
*/
|
|
1586
1655
|
function quoteAbsolutePathString(abspath) {
|
|
1587
1656
|
const namespace = getNamespace(csn, abspath);
|
|
1588
|
-
const resultingName = getResultingName(csn, options.
|
|
1657
|
+
const resultingName = getResultingName(csn, options.sqlMapping, abspath);
|
|
1589
1658
|
|
|
1590
1659
|
if (hdbcdsNames && namespace)
|
|
1591
1660
|
return `${quotePathString(namespace)}::${quotePathString(resultingName.slice(namespace.length + 2))}`;
|
|
@@ -1610,10 +1679,19 @@ function toHdbcdsSource(csn, options) {
|
|
|
1610
1679
|
return id;
|
|
1611
1680
|
|
|
1612
1681
|
|
|
1613
|
-
|
|
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
|
+
}
|
|
1614
1692
|
}
|
|
1615
1693
|
|
|
1616
|
-
|
|
1694
|
+
/*
|
|
1617
1695
|
* Return an absolute name 'absname', with '::' inserted if required by naming strategy 'hdbcds', quoted
|
|
1618
1696
|
* as if it was a single identifier (required only for native USINGs)
|
|
1619
1697
|
*
|
|
@@ -1631,21 +1709,34 @@ function toHdbcdsSource(csn, options) {
|
|
|
1631
1709
|
}
|
|
1632
1710
|
|
|
1633
1711
|
/**
|
|
1634
|
-
* Quote or uppercase an identifier 'id', depending on naming strategy
|
|
1712
|
+
* Quote and/or uppercase an identifier 'id', depending on naming strategy
|
|
1635
1713
|
*
|
|
1636
1714
|
* @param {string} id Identifier
|
|
1637
|
-
* @param {CSN.Location} [location] Optional location for the warning.
|
|
1638
1715
|
* @returns {string} Quoted/uppercased id
|
|
1639
1716
|
*/
|
|
1640
|
-
function
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
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);
|
|
1646
1739
|
|
|
1647
|
-
return result;
|
|
1648
|
-
}
|
|
1649
1740
|
return quoteId(id);
|
|
1650
1741
|
}
|
|
1651
1742
|
|
|
@@ -1668,7 +1759,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1668
1759
|
*/
|
|
1669
1760
|
function renderArtifactName(artifactName, env, fallthrough = false) {
|
|
1670
1761
|
if (plainNames && !fallthrough)
|
|
1671
|
-
return uppercaseAndUnderscore(artifactName);
|
|
1762
|
+
return formatIdentifier(uppercaseAndUnderscore(artifactName));
|
|
1672
1763
|
// hdbcds with quoted or hdbcds naming
|
|
1673
1764
|
return env.namePrefix + quoteId(getRealName(csn, artifactName).replace(/\./g, '_'));
|
|
1674
1765
|
}
|