@sap/cds-compiler 2.5.2 → 2.11.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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- 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 +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
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, getVariableReplacement,
|
|
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,
|
|
@@ -17,7 +17,9 @@ const DuplicateChecker = require('./DuplicateChecker');
|
|
|
17
17
|
const { isDeprecatedEnabled, forEachDefinition } = require('../base/model');
|
|
18
18
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
19
19
|
const { makeMessageFunction } = require('../base/messages');
|
|
20
|
-
const timetrace = require('../utils/timetrace');
|
|
20
|
+
const { timetrace } = require('../utils/timetrace');
|
|
21
|
+
|
|
22
|
+
const { smartId, delimitedId } = require('../sql-identifier');
|
|
21
23
|
|
|
22
24
|
const $PROJECTION = '$projection';
|
|
23
25
|
const $SELF = '$self';
|
|
@@ -39,8 +41,7 @@ function getEscapedHanaComment(obj) {
|
|
|
39
41
|
* { "foo" : "using XY; context foo {...};",
|
|
40
42
|
* "bar::wiz" : "namespace bar::; entity wiz {...};"
|
|
41
43
|
* }
|
|
42
|
-
*
|
|
43
|
-
* only affects translation of '$self.foo' in paths and ::-ish namespace declarations)
|
|
44
|
+
*
|
|
44
45
|
* FIXME: This comment no longer tells the whole truth
|
|
45
46
|
*
|
|
46
47
|
* @param {CSN.Model} csn HANA transformed CSN
|
|
@@ -49,9 +50,9 @@ function getEscapedHanaComment(obj) {
|
|
|
49
50
|
*/
|
|
50
51
|
function toHdbcdsSource(csn, options) {
|
|
51
52
|
timetrace.start('HDBCDS rendering');
|
|
52
|
-
const plainNames = options.
|
|
53
|
-
const quotedNames = options.
|
|
54
|
-
const hdbcdsNames = options.
|
|
53
|
+
const plainNames = options.sqlMapping === 'plain';
|
|
54
|
+
const quotedNames = options.sqlMapping === 'quoted';
|
|
55
|
+
const hdbcdsNames = options.sqlMapping === 'hdbcds';
|
|
55
56
|
|
|
56
57
|
const {
|
|
57
58
|
info, warning, error, throwWithError,
|
|
@@ -62,7 +63,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
62
63
|
const result = Object.create(null);
|
|
63
64
|
|
|
64
65
|
|
|
65
|
-
const globalDuplicateChecker = new DuplicateChecker(options.
|
|
66
|
+
const globalDuplicateChecker = new DuplicateChecker(options.sqlMapping); // registry for all artifact names and element names
|
|
66
67
|
|
|
67
68
|
const killList = [];
|
|
68
69
|
if (quotedNames)
|
|
@@ -389,7 +390,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
389
390
|
const childEnv = increaseIndent(env);
|
|
390
391
|
const normalizedArtifactName = renderArtifactName(artifactName, env);
|
|
391
392
|
|
|
392
|
-
globalDuplicateChecker.addArtifact(art['@cds.persistence.name'],
|
|
393
|
+
globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], env.path, artifactName);
|
|
393
394
|
|
|
394
395
|
if (hasHanaComment(art, options))
|
|
395
396
|
result += `${env.indent}@Comment: '${getEscapedHanaComment(art)}'\n`;
|
|
@@ -401,7 +402,11 @@ function toHdbcdsSource(csn, options) {
|
|
|
401
402
|
}
|
|
402
403
|
result += ' {\n';
|
|
403
404
|
const duplicateChecker = new DuplicateChecker(); // registry for all artifact names and element names
|
|
404
|
-
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);
|
|
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,9 +548,9 @@ 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
|
-
duplicateChecker.addElement(quotedElementName,
|
|
553
|
+
duplicateChecker.addElement(quotedElementName, env.path, elementName);
|
|
522
554
|
|
|
523
555
|
if (hasHanaComment(elm, options))
|
|
524
556
|
result += `${env.indent}@Comment: '${getEscapedHanaComment(elm)}'\n`;
|
|
@@ -526,7 +558,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
526
558
|
result += env.indent + (elm.key && !isSubElement ? 'key ' : '') +
|
|
527
559
|
(elm.masked ? 'masked ' : '') +
|
|
528
560
|
quotedElementName + (omitColon ? ' ' : ' : ') +
|
|
529
|
-
renderTypeReference(elm, env
|
|
561
|
+
renderTypeReference(elm, env) +
|
|
530
562
|
renderNullability(elm);
|
|
531
563
|
if (elm.default)
|
|
532
564
|
result += ` default ${renderExpr(elm.default, env)}`;
|
|
@@ -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
|
}
|
|
@@ -598,7 +630,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
598
630
|
|
|
599
631
|
// Even the first step might have parameters and/or a filter
|
|
600
632
|
if (path.ref[0].args)
|
|
601
|
-
result += `(${renderArgs(path.ref[0]
|
|
633
|
+
result += `(${renderArgs(path.ref[0], ':', env)})`;
|
|
602
634
|
|
|
603
635
|
if (path.ref[0].where)
|
|
604
636
|
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env, true, true)}]`;
|
|
@@ -624,14 +656,14 @@ function toHdbcdsSource(csn, options) {
|
|
|
624
656
|
function renderAbsolutePathWithAlias(path, env) {
|
|
625
657
|
let result = renderAbsolutePath(path, env);
|
|
626
658
|
// Take care of aliases - for artifact references, use the resulting name (multi-dot joined with _)
|
|
627
|
-
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);
|
|
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
|
}
|
|
@@ -648,14 +680,14 @@ function toHdbcdsSource(csn, options) {
|
|
|
648
680
|
*/
|
|
649
681
|
function renderViewColumn(col, env, element) {
|
|
650
682
|
// Annotations and column
|
|
651
|
-
let result =
|
|
683
|
+
let result = '';
|
|
652
684
|
|
|
653
685
|
const leaf = col.as || col.ref && col.ref[col.ref.length - 1];
|
|
654
686
|
// Render 'null as <alias>' only for database and if element is virtual
|
|
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) {
|
|
@@ -710,7 +742,8 @@ function toHdbcdsSource(csn, options) {
|
|
|
710
742
|
*/
|
|
711
743
|
function renderView(artifactName, art, env) {
|
|
712
744
|
let result = '';
|
|
713
|
-
|
|
745
|
+
const artifactPath = [ 'definitions', artifactName ];
|
|
746
|
+
globalDuplicateChecker.addArtifact(art['@cds.persistence.name'], artifactPath, artifactName);
|
|
714
747
|
|
|
715
748
|
if (hasHanaComment(art, options))
|
|
716
749
|
result += `${env.indent}@Comment: '${getEscapedHanaComment(art)}'\n`;
|
|
@@ -726,7 +759,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
726
759
|
result += ' as ';
|
|
727
760
|
}
|
|
728
761
|
env._artifact = art;
|
|
729
|
-
result += renderQuery(getNormalizedQuery(art).query, true, env,
|
|
762
|
+
result += renderQuery(getNormalizedQuery(art).query, true, env, artifactPath.concat(art.projection ? 'projection' : 'query'), art.elements);
|
|
730
763
|
result += ';\n';
|
|
731
764
|
return result;
|
|
732
765
|
}
|
|
@@ -776,7 +809,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
776
809
|
const childEnv = increaseIndent(env);
|
|
777
810
|
childEnv.currentArtifactName = $PROJECTION; // $self to be replaced by $projection
|
|
778
811
|
result += `select from ${renderViewSource(select.from, env)}`;
|
|
779
|
-
|
|
780
812
|
if (select.mixin) {
|
|
781
813
|
let elems = '';
|
|
782
814
|
for (const name in select.mixin)
|
|
@@ -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
|
/**
|
|
@@ -927,7 +959,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
927
959
|
}
|
|
928
960
|
else {
|
|
929
961
|
// Derived type or annotation with non-anonymous type
|
|
930
|
-
result += ` : ${renderTypeReference(art, env
|
|
962
|
+
result += ` : ${renderTypeReference(art, env)};\n`;
|
|
931
963
|
}
|
|
932
964
|
return result;
|
|
933
965
|
}
|
|
@@ -938,10 +970,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
938
970
|
*
|
|
939
971
|
* @param {CSN.Element} elm Element using the type reference
|
|
940
972
|
* @param {CdlRenderEnvironment} env Environment
|
|
941
|
-
* @param {boolean} [noEnum=false] If true, do not render enums
|
|
942
973
|
* @returns {string} Rendered type reference
|
|
943
974
|
*/
|
|
944
|
-
function renderTypeReference(elm, env
|
|
975
|
+
function renderTypeReference(elm, env) {
|
|
945
976
|
let result = '';
|
|
946
977
|
|
|
947
978
|
// Array type: Render items instead
|
|
@@ -979,11 +1010,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
979
1010
|
// Reference to another element
|
|
980
1011
|
if (elm.type.ref) {
|
|
981
1012
|
// For HANA CDS, we need a 'type of'
|
|
982
|
-
|
|
983
|
-
if (elm.enum)
|
|
984
|
-
result += renderEnum(elm.enum, env);
|
|
985
|
-
|
|
986
|
-
return result;
|
|
1013
|
+
return `type of ${renderAbsolutePath(elm.type, env)}`;
|
|
987
1014
|
}
|
|
988
1015
|
|
|
989
1016
|
// If we get here, it must be a named type
|
|
@@ -995,8 +1022,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
995
1022
|
// Type names are never flattened (derived types are unraveled in HANA)
|
|
996
1023
|
result += renderAbsoluteNameWithQuotes(elm.type, env);
|
|
997
1024
|
}
|
|
998
|
-
if (elm.enum && !noEnum)
|
|
999
|
-
result += renderEnum(elm.enum, env);
|
|
1000
1025
|
|
|
1001
1026
|
return result;
|
|
1002
1027
|
}
|
|
@@ -1012,10 +1037,24 @@ function toHdbcdsSource(csn, options) {
|
|
|
1012
1037
|
|
|
1013
1038
|
result += `${renderCardinality(elm.cardinality)} to `;
|
|
1014
1039
|
|
|
1040
|
+
|
|
1015
1041
|
// normal target or named aspect
|
|
1016
1042
|
if (elm.target || elm.targetAspect && typeof elm.targetAspect === 'string') {
|
|
1017
|
-
|
|
1018
|
-
|
|
1043
|
+
// we might have a "using target as __target"
|
|
1044
|
+
const targetArtifact = csn.definitions[elm.target];
|
|
1045
|
+
const targetAlias = env.topLevelAliases[targetArtifact['@cds.persistence.name']];
|
|
1046
|
+
if (targetAlias) {
|
|
1047
|
+
result += targetAlias.quotedAlias;
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
result += plainNames ? renderAbsoluteNamePlain(elm.target || elm.targetAspect, env)
|
|
1051
|
+
: renderAbsoluteNameWithQuotes(elm.target || elm.targetAspect, env);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// ON-condition (if any)
|
|
1056
|
+
if (elm.on) {
|
|
1057
|
+
result += ` on ${renderExpr(elm.on, env, true, true)}`;
|
|
1019
1058
|
}
|
|
1020
1059
|
else if (elm.targetAspect && elm.targetAspect.elements) { // anonymous aspect
|
|
1021
1060
|
const childEnv = increaseIndent(env);
|
|
@@ -1027,11 +1066,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1027
1066
|
}
|
|
1028
1067
|
|
|
1029
1068
|
|
|
1030
|
-
// ON-condition (if any)
|
|
1031
|
-
if (elm.on)
|
|
1032
|
-
result += ` on ${renderExpr(elm.on, env, true, true)}`;
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
1069
|
// Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
|
|
1036
1070
|
if (elm.keys && !elm.on)
|
|
1037
1071
|
result += ` { ${Object.keys(elm.keys).map(name => renderForeignKey(elm.keys[name], env)).join(', ')} }`;
|
|
@@ -1053,27 +1087,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1053
1087
|
return elm.type.replace(/^cds\./, '') + renderTypeParameters(elm);
|
|
1054
1088
|
}
|
|
1055
1089
|
|
|
1056
|
-
/**
|
|
1057
|
-
* Render the 'enum { ... } part of a type declaration
|
|
1058
|
-
*
|
|
1059
|
-
* @param {CSN.EnumElements} enumPart Enum part of a type declaration
|
|
1060
|
-
* @param {CdlRenderEnvironment} env Environment
|
|
1061
|
-
* @returns {string} Rendered enum
|
|
1062
|
-
*/
|
|
1063
|
-
function renderEnum(enumPart, env) {
|
|
1064
|
-
let result = ' enum {\n';
|
|
1065
|
-
const childEnv = increaseIndent(env);
|
|
1066
|
-
for (const name in enumPart) {
|
|
1067
|
-
const enumConst = enumPart[name];
|
|
1068
|
-
result += childEnv.indent + quoteId(name);
|
|
1069
|
-
if (enumConst.val !== undefined)
|
|
1070
|
-
result += ` = ${renderExpr(enumConst, childEnv)}`;
|
|
1071
|
-
result += ';\n';
|
|
1072
|
-
}
|
|
1073
|
-
result += `${env.indent}}`;
|
|
1074
|
-
return result;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
1090
|
/**
|
|
1078
1091
|
* Render an expression (including paths and values) or condition 'x'.
|
|
1079
1092
|
* (no trailing LF, don't indent if inline)
|
|
@@ -1087,7 +1100,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1087
1100
|
function renderExpr(x, env, inline = true, inExpr = false) {
|
|
1088
1101
|
// Compound expression
|
|
1089
1102
|
if (Array.isArray(x))
|
|
1090
|
-
return
|
|
1103
|
+
return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
|
|
1091
1104
|
|
|
1092
1105
|
if (typeof x === 'object' && x !== null) {
|
|
1093
1106
|
if (inExpr && x.cast && x.cast.type)
|
|
@@ -1129,6 +1142,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
1129
1142
|
|
|
1130
1143
|
const regex = RegExp(/^[a-zA-Z][\w#$]*$/, 'g');
|
|
1131
1144
|
const funcName = regex.test(x.func) ? x.func : quoteId(x.func);
|
|
1145
|
+
// we can't quote functions with parens, issue warning if it is a reserved keyword
|
|
1146
|
+
if (!funcWithoutParen(x, 'hana') && keywords.hdbcds.includes(uppercaseAndUnderscore(funcName)))
|
|
1147
|
+
warning(null, x.$location, `The identifier “${uppercaseAndUnderscore(funcName)}“ is a SAP HANA keyword`);
|
|
1132
1148
|
return renderFunc( funcName, x, 'hana', a => renderArgs(a, '=>', env) );
|
|
1133
1149
|
}
|
|
1134
1150
|
// Nested expression
|
|
@@ -1185,14 +1201,19 @@ function toHdbcdsSource(csn, options) {
|
|
|
1185
1201
|
*/
|
|
1186
1202
|
function renderExpressionRef(x) {
|
|
1187
1203
|
if (!x.param && !x.global) {
|
|
1204
|
+
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1188
1205
|
if (x.ref[0] === '$user') {
|
|
1206
|
+
if (magicReplacement !== null)
|
|
1207
|
+
return `'${magicReplacement}'`;
|
|
1208
|
+
|
|
1209
|
+
// Keep old way of solving this to remain backwards compatible
|
|
1189
1210
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1190
1211
|
if (x.ref[1] === 'id') {
|
|
1191
|
-
if (options.
|
|
1192
|
-
return `'${options.
|
|
1212
|
+
if (options.magicVars && options.magicVars.user && (typeof options.magicVars.user === 'string' || options.magicVars.user instanceof String))
|
|
1213
|
+
return `'${options.magicVars.user}'`;
|
|
1193
1214
|
|
|
1194
|
-
else if ((options.
|
|
1195
|
-
return `'${options.
|
|
1215
|
+
else if ((options.magicVars && options.magicVars.user && options.magicVars.user.id) && (typeof options.magicVars.user.id === 'string' || options.magicVars.user.id instanceof String))
|
|
1216
|
+
return `'${options.magicVars.user.id}'`;
|
|
1196
1217
|
|
|
1197
1218
|
return 'SESSION_CONTEXT(\'APPLICATIONUSER\')';
|
|
1198
1219
|
}
|
|
@@ -1207,6 +1228,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
1207
1228
|
else if (x.ref[1] === 'to')
|
|
1208
1229
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-TO\'))';
|
|
1209
1230
|
}
|
|
1231
|
+
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1232
|
+
return `'${magicReplacement}'`;
|
|
1233
|
+
}
|
|
1210
1234
|
}
|
|
1211
1235
|
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, x.ref)).join('.')}`;
|
|
1212
1236
|
}
|
|
@@ -1218,7 +1242,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1218
1242
|
* @returns {string} Rendered cast()
|
|
1219
1243
|
*/
|
|
1220
1244
|
function renderExplicitTypeCast(value) {
|
|
1221
|
-
let typeRef = renderTypeReference(x.cast, env
|
|
1245
|
+
let typeRef = renderTypeReference(x.cast, env);
|
|
1222
1246
|
|
|
1223
1247
|
// inside a cast expression, the cds and hana cds types need to be mapped to hana sql types
|
|
1224
1248
|
const hanaSqlType = cdsToSqlTypes.hana[x.cast.type] || cdsToSqlTypes.standard[x.cast.type];
|
|
@@ -1267,7 +1291,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1267
1291
|
[ $SELF, $PROJECTION, '$session' ].includes(s))
|
|
1268
1292
|
return s;
|
|
1269
1293
|
|
|
1270
|
-
return
|
|
1294
|
+
return formatIdentifier(s);
|
|
1271
1295
|
}
|
|
1272
1296
|
// ID with filters or parameters
|
|
1273
1297
|
else if (typeof s === 'object') {
|
|
@@ -1277,13 +1301,13 @@ function toHdbcdsSource(csn, options) {
|
|
|
1277
1301
|
|
|
1278
1302
|
// Not really a path step but an object-like function call
|
|
1279
1303
|
if (s.func)
|
|
1280
|
-
return `${s.func}(${renderArgs(s
|
|
1304
|
+
return `${s.func}(${renderArgs(s, '=>', env)})`;
|
|
1281
1305
|
|
|
1282
1306
|
// Path step, possibly with view parameters and/or filters
|
|
1283
|
-
let result = `${
|
|
1307
|
+
let result = `${formatIdentifier(s.id)}`;
|
|
1284
1308
|
if (s.args) {
|
|
1285
1309
|
// View parameters
|
|
1286
|
-
result += `(${renderArgs(s
|
|
1310
|
+
result += `(${renderArgs(s, ':', env)})`;
|
|
1287
1311
|
}
|
|
1288
1312
|
if (s.where) {
|
|
1289
1313
|
// Filter, possibly with cardinality
|
|
@@ -1300,19 +1324,21 @@ function toHdbcdsSource(csn, options) {
|
|
|
1300
1324
|
* Render function arguments or view parameters (positional if array, named if object/dict),
|
|
1301
1325
|
* using 'sep' as separator for positional parameters
|
|
1302
1326
|
*
|
|
1303
|
-
* @param {object
|
|
1327
|
+
* @param {object} node with `args` to render
|
|
1304
1328
|
* @param {string} sep Seperator between arguments
|
|
1305
1329
|
* @param {CdlRenderEnvironment} env Environment
|
|
1306
1330
|
* @returns {string} Rendered arguments
|
|
1307
1331
|
*/
|
|
1308
|
-
function renderArgs(
|
|
1332
|
+
function renderArgs(node, sep, env) {
|
|
1333
|
+
const args = node.args ? node.args : {};
|
|
1309
1334
|
// Positional arguments
|
|
1310
1335
|
if (Array.isArray(args))
|
|
1311
1336
|
return args.map(arg => renderExpr(arg, env)).join(', ');
|
|
1312
1337
|
|
|
1313
1338
|
// Named arguments (object/dict)
|
|
1314
1339
|
else if (typeof args === 'object')
|
|
1315
|
-
|
|
1340
|
+
// if this is a function param which is not a reference to the model, we must not quote it
|
|
1341
|
+
return Object.keys(args).map(key => `${node.func ? key : formatIdentifier(key)} ${sep} ${renderExpr(args[key], env)}`).join(', ');
|
|
1316
1342
|
|
|
1317
1343
|
|
|
1318
1344
|
throw new Error(`Unknown args: ${JSON.stringify(args)}`);
|
|
@@ -1405,10 +1431,10 @@ function toHdbcdsSource(csn, options) {
|
|
|
1405
1431
|
function renderAbsoluteNamePlain(absName, env) {
|
|
1406
1432
|
// Add using declaration
|
|
1407
1433
|
env.topLevelAliases[absName] = {
|
|
1408
|
-
quotedName: uppercaseAndUnderscore(absName),
|
|
1409
|
-
quotedAlias: uppercaseAndUnderscore(absName),
|
|
1434
|
+
quotedName: formatIdentifier(uppercaseAndUnderscore(absName)),
|
|
1435
|
+
quotedAlias: formatIdentifier(uppercaseAndUnderscore(absName)),
|
|
1410
1436
|
};
|
|
1411
|
-
return uppercaseAndUnderscore(absName);
|
|
1437
|
+
return formatIdentifier(uppercaseAndUnderscore(absName));
|
|
1412
1438
|
}
|
|
1413
1439
|
|
|
1414
1440
|
/**
|
|
@@ -1493,7 +1519,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1493
1519
|
function renderUsings(artifactName, env) {
|
|
1494
1520
|
const distinct = {};
|
|
1495
1521
|
Object.keys(env.topLevelAliases)
|
|
1496
|
-
.filter(name =>
|
|
1522
|
+
.filter(name => env.topLevelAliases[name].quotedAlias !== formatIdentifier(uppercaseAndUnderscore(artifactName))) // avoid "using FOO as FOO" in FOO.cds
|
|
1497
1523
|
.forEach((name) => {
|
|
1498
1524
|
distinct[`using ${env.topLevelAliases[name].quotedName} as ${env.topLevelAliases[name].quotedAlias};\n`] = '';
|
|
1499
1525
|
});
|
|
@@ -1564,6 +1590,8 @@ function toHdbcdsSource(csn, options) {
|
|
|
1564
1590
|
topLevelAliases: Object.create(null),
|
|
1565
1591
|
// Current name prefix (including trailing dot if not empty)
|
|
1566
1592
|
namePrefix: '',
|
|
1593
|
+
// CSN path - should at least point to the correct artifact
|
|
1594
|
+
path: [],
|
|
1567
1595
|
};
|
|
1568
1596
|
}
|
|
1569
1597
|
|
|
@@ -1608,7 +1636,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1608
1636
|
*/
|
|
1609
1637
|
function quoteAbsolutePathString(abspath) {
|
|
1610
1638
|
const namespace = getNamespace(csn, abspath);
|
|
1611
|
-
const resultingName = getResultingName(csn, options.
|
|
1639
|
+
const resultingName = getResultingName(csn, options.sqlMapping, abspath);
|
|
1612
1640
|
|
|
1613
1641
|
if (hdbcdsNames && namespace)
|
|
1614
1642
|
return `${quotePathString(namespace)}::${quotePathString(resultingName.slice(namespace.length + 2))}`;
|
|
@@ -1633,10 +1661,19 @@ function toHdbcdsSource(csn, options) {
|
|
|
1633
1661
|
return id;
|
|
1634
1662
|
|
|
1635
1663
|
|
|
1636
|
-
|
|
1664
|
+
switch (options.forHana.names) {
|
|
1665
|
+
case 'plain':
|
|
1666
|
+
return smartId(id, 'hdbcds');
|
|
1667
|
+
case 'quoted':
|
|
1668
|
+
return delimitedId(id, 'hdbcds');
|
|
1669
|
+
case 'hdbcds':
|
|
1670
|
+
return delimitedId(id, 'hdbcds');
|
|
1671
|
+
default:
|
|
1672
|
+
return null;
|
|
1673
|
+
}
|
|
1637
1674
|
}
|
|
1638
1675
|
|
|
1639
|
-
|
|
1676
|
+
/*
|
|
1640
1677
|
* Return an absolute name 'absname', with '::' inserted if required by naming strategy 'hdbcds', quoted
|
|
1641
1678
|
* as if it was a single identifier (required only for native USINGs)
|
|
1642
1679
|
*
|
|
@@ -1654,21 +1691,34 @@ function toHdbcdsSource(csn, options) {
|
|
|
1654
1691
|
}
|
|
1655
1692
|
|
|
1656
1693
|
/**
|
|
1657
|
-
* Quote or uppercase an identifier 'id', depending on naming strategy
|
|
1694
|
+
* Quote and/or uppercase an identifier 'id', depending on naming strategy
|
|
1658
1695
|
*
|
|
1659
1696
|
* @param {string} id Identifier
|
|
1660
|
-
* @param {CSN.Location} [location] Optional location for the warning.
|
|
1661
1697
|
* @returns {string} Quoted/uppercased id
|
|
1662
1698
|
*/
|
|
1663
|
-
function
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1699
|
+
function formatIdentifier(id) {
|
|
1700
|
+
id = options.forHana.names === 'plain' ? id.toUpperCase() : id;
|
|
1701
|
+
return quoteId(id);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* Quote or uppercase a parameter identifier 'id', depending on naming strategy
|
|
1706
|
+
* Smart quoting cannot be applied to the parameter identifiers, issue warning instead.
|
|
1707
|
+
*
|
|
1708
|
+
*
|
|
1709
|
+
* @param {string} id Identifier
|
|
1710
|
+
* @param {CSN.Path} [location] Optional location for the warning.
|
|
1711
|
+
* @returns {string} Quoted/uppercased id
|
|
1712
|
+
*/
|
|
1713
|
+
function formatParamIdentifier(id, location) {
|
|
1714
|
+
// Warn if colliding with HANA keyword, but do not quote for plain
|
|
1715
|
+
// --> quoted reserved words as param lead to a weird deployment error
|
|
1716
|
+
if (keywords.hdbcds.includes(uppercaseAndUnderscore(id)))
|
|
1717
|
+
warning(null, location, { id }, 'The identifier $(ID) is a SAP HANA keyword');
|
|
1718
|
+
|
|
1719
|
+
if (plainNames)
|
|
1720
|
+
return uppercaseAndUnderscore(id);
|
|
1669
1721
|
|
|
1670
|
-
return result;
|
|
1671
|
-
}
|
|
1672
1722
|
return quoteId(id);
|
|
1673
1723
|
}
|
|
1674
1724
|
|
|
@@ -1691,7 +1741,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1691
1741
|
*/
|
|
1692
1742
|
function renderArtifactName(artifactName, env, fallthrough = false) {
|
|
1693
1743
|
if (plainNames && !fallthrough)
|
|
1694
|
-
return uppercaseAndUnderscore(artifactName);
|
|
1744
|
+
return formatIdentifier(uppercaseAndUnderscore(artifactName));
|
|
1695
1745
|
// hdbcds with quoted or hdbcds naming
|
|
1696
1746
|
return env.namePrefix + quoteId(getRealName(csn, artifactName).replace(/\./g, '_'));
|
|
1697
1747
|
}
|