@sap/cds-compiler 2.10.4 → 2.12.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 +136 -0
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +58 -35
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +10 -36
- package/lib/api/options.js +17 -8
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +64 -11
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +6 -4
- package/lib/base/optionProcessorHelper.js +148 -86
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +14 -5
- package/lib/compiler/base.js +64 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +34 -10
- package/lib/compiler/definer.js +91 -112
- package/lib/compiler/index.js +30 -30
- package/lib/compiler/propagator.js +8 -4
- package/lib/compiler/resolver.js +279 -63
- package/lib/compiler/shared.js +65 -230
- package/lib/compiler/utils.js +191 -0
- package/lib/edm/annotations/genericTranslation.js +35 -18
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +4 -3
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +61 -59
- package/lib/edm/edmUtils.js +14 -15
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +19 -1
- package/lib/gen/language.tokens +80 -73
- package/lib/gen/languageLexer.interp +27 -1
- package/lib/gen/languageLexer.js +925 -826
- package/lib/gen/languageLexer.tokens +72 -65
- package/lib/gen/languageParser.js +4817 -4102
- package/lib/json/from-csn.js +57 -26
- package/lib/json/to-csn.js +244 -51
- package/lib/language/antlrParser.js +12 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +106 -30
- package/lib/language/language.g4 +200 -70
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +220 -21
- package/lib/main.js +6 -3
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +218 -86
- package/lib/model/csnUtils.js +99 -178
- package/lib/model/enrichCsn.js +84 -43
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +202 -82
- package/lib/render/toHdbcds.js +194 -135
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +91 -51
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +275 -119
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +10 -9
- package/lib/transform/db/flattening.js +23 -8
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +106 -25
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +90 -1036
- package/lib/transform/forOdataNew.js +11 -3
- package/lib/transform/localized.js +5 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +34 -20
- package/lib/transform/translateAssocsToJoins.js +15 -23
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +13 -6
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +55 -27
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/render/toHdbcds.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
|
|
4
|
+
getLastPartOf, getLastPartOfRef,
|
|
5
5
|
hasValidSkipOrExists, isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
|
|
6
|
-
getRootArtifactName, getResultingName, getNamespace, forEachMember,
|
|
6
|
+
getRootArtifactName, getResultingName, getNamespace, forEachMember, getVariableReplacement, hasAnnotationValue,
|
|
7
7
|
} = require('../model/csnUtils');
|
|
8
8
|
const keywords = require('../base/keywords');
|
|
9
9
|
const {
|
|
10
10
|
renderFunc, beautifyExprArray, getRealName, addContextMarkers, addIntermediateContexts, cdsToSqlTypes,
|
|
11
|
-
hasHanaComment, getHanaComment, findElement, funcWithoutParen,
|
|
11
|
+
hasHanaComment, getHanaComment, findElement, funcWithoutParen, getSqlSnippets,
|
|
12
12
|
} = require('./utils/common');
|
|
13
13
|
const {
|
|
14
14
|
renderReferentialConstraint,
|
|
@@ -17,7 +17,7 @@ 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
21
|
|
|
22
22
|
const { smartId, delimitedId } = require('../sql-identifier');
|
|
23
23
|
|
|
@@ -60,8 +60,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
60
60
|
|
|
61
61
|
checkCSNVersion(csn, options);
|
|
62
62
|
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
const hdbcdsResult = Object.create(null);
|
|
65
64
|
|
|
66
65
|
const globalDuplicateChecker = new DuplicateChecker(options.sqlMapping); // registry for all artifact names and element names
|
|
67
66
|
|
|
@@ -100,17 +99,17 @@ function toHdbcdsSource(csn, options) {
|
|
|
100
99
|
Object.entries(art.$tableConstraints.referential)
|
|
101
100
|
.forEach(([ fileName, referentialConstraint ]) => {
|
|
102
101
|
referentialConstraints[fileName] = renderReferentialConstraint(
|
|
103
|
-
referentialConstraint, '', renderToUppercase, csn, options
|
|
102
|
+
referentialConstraint, '', renderToUppercase, csn, options
|
|
104
103
|
);
|
|
105
104
|
});
|
|
106
105
|
Object.entries(referentialConstraints)
|
|
107
|
-
.forEach(
|
|
106
|
+
.forEach(([ fileName, constraint ]) => {
|
|
108
107
|
hdbconstraint[fileName] = constraint;
|
|
109
108
|
});
|
|
110
109
|
}
|
|
111
110
|
});
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
hdbcdsResult.hdbcds = hdbcds;
|
|
112
|
+
hdbcdsResult.hdbconstraint = hdbconstraint;
|
|
114
113
|
|
|
115
114
|
if (globalDuplicateChecker)
|
|
116
115
|
globalDuplicateChecker.check(error, options); // perform duplicates check
|
|
@@ -119,7 +118,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
119
118
|
|
|
120
119
|
throwWithError();
|
|
121
120
|
timetrace.stop();
|
|
122
|
-
return options.testMode ? sort(
|
|
121
|
+
return options.testMode ? sort(hdbcdsResult) : hdbcdsResult;
|
|
123
122
|
|
|
124
123
|
/**
|
|
125
124
|
* Sort the given object alphabetically
|
|
@@ -224,7 +223,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
224
223
|
const parts = containee.split('.');
|
|
225
224
|
const prefixLength = contextName.split('.').length;
|
|
226
225
|
|
|
227
|
-
for (let i = parts.length - 1; i > prefixLength; i--
|
|
226
|
+
for (let i = parts.length - 1; i > prefixLength; i--) {
|
|
228
227
|
const prefix = parts.slice(0, i).join('.');
|
|
229
228
|
const art = csn.definitions[prefix];
|
|
230
229
|
if (art && (art.kind === 'context' || art.kind === 'service'))
|
|
@@ -291,7 +290,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
291
290
|
function renderContext(artifactName, art, env, isShadowed) {
|
|
292
291
|
let result = '';
|
|
293
292
|
if (!isShadowed)
|
|
294
|
-
isShadowed = contextIsShadowed(artifactName
|
|
293
|
+
isShadowed = contextIsShadowed(artifactName);
|
|
295
294
|
if (isShadowed) {
|
|
296
295
|
const subArtifacts = getSubArtifacts(artifactName);
|
|
297
296
|
for (const name in subArtifacts)
|
|
@@ -318,10 +317,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
318
317
|
* non-context/service/namespace definition
|
|
319
318
|
*
|
|
320
319
|
* @param {string} artifactName
|
|
321
|
-
* @param {CSN.Model} csn
|
|
322
320
|
* @returns {boolean}
|
|
323
321
|
*/
|
|
324
|
-
function contextIsShadowed(artifactName
|
|
322
|
+
function contextIsShadowed(artifactName) {
|
|
325
323
|
if (artifactName.indexOf('.') === -1)
|
|
326
324
|
return false;
|
|
327
325
|
|
|
@@ -395,6 +393,12 @@ function toHdbcdsSource(csn, options) {
|
|
|
395
393
|
if (hasHanaComment(art, options))
|
|
396
394
|
result += `${env.indent}@Comment: '${getEscapedHanaComment(art)}'\n`;
|
|
397
395
|
|
|
396
|
+
// tables can have @sql.prepend and @sql.append
|
|
397
|
+
const { front, back } = getSqlSnippets(options, art);
|
|
398
|
+
|
|
399
|
+
if (front) // attach @sql.prepend after adding @Comment annotation
|
|
400
|
+
result += front;
|
|
401
|
+
|
|
398
402
|
result += `${env.indent + (art.abstract ? 'abstract ' : '')}entity ${normalizedArtifactName}`;
|
|
399
403
|
if (art.includes) {
|
|
400
404
|
// Includes are never flattened (don't exist in HANA)
|
|
@@ -412,8 +416,12 @@ function toHdbcdsSource(csn, options) {
|
|
|
412
416
|
|
|
413
417
|
duplicateChecker.check(error);
|
|
414
418
|
result += `${env.indent}}`;
|
|
415
|
-
result += `${renderTechnicalConfiguration(art.technicalConfig, env)}
|
|
416
|
-
|
|
419
|
+
result += `${renderTechnicalConfiguration(art.technicalConfig, env)}`;
|
|
420
|
+
|
|
421
|
+
if (back)
|
|
422
|
+
result += back;
|
|
423
|
+
|
|
424
|
+
return `${result};\n`;
|
|
417
425
|
}
|
|
418
426
|
|
|
419
427
|
/**
|
|
@@ -429,9 +437,8 @@ function toHdbcdsSource(csn, options) {
|
|
|
429
437
|
if (!element.target)
|
|
430
438
|
return;
|
|
431
439
|
|
|
432
|
-
let alias = element['@cds.persistence.name'];
|
|
433
440
|
if (uppercaseAndUnderscore(element.target) === element['@cds.persistence.name']) {
|
|
434
|
-
alias = createTopLevelAliasName(element['@cds.persistence.name']);
|
|
441
|
+
let alias = createTopLevelAliasName(element['@cds.persistence.name']);
|
|
435
442
|
// calculate new alias if it would conflict with other csn.Artifact
|
|
436
443
|
while (csn.definitions[alias])
|
|
437
444
|
alias = createTopLevelAliasName(alias);
|
|
@@ -556,13 +563,19 @@ function toHdbcdsSource(csn, options) {
|
|
|
556
563
|
result += `${env.indent}@Comment: '${getEscapedHanaComment(elm)}'\n`;
|
|
557
564
|
|
|
558
565
|
result += env.indent + (elm.key && !isSubElement ? 'key ' : '') +
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
566
|
+
(elm.masked ? 'masked ' : '') +
|
|
567
|
+
quotedElementName + (omitColon ? ' ' : ' : ') +
|
|
568
|
+
renderTypeReference(elm, env) +
|
|
569
|
+
renderNullability(elm);
|
|
563
570
|
if (elm.default)
|
|
564
571
|
result += ` default ${renderExpr(elm.default, env)}`;
|
|
565
572
|
|
|
573
|
+
// (table) elements can only have a @sql.append
|
|
574
|
+
const { back } = getSqlSnippets(options, elm);
|
|
575
|
+
|
|
576
|
+
if (back)
|
|
577
|
+
result += back;
|
|
578
|
+
|
|
566
579
|
return `${result};\n`;
|
|
567
580
|
}
|
|
568
581
|
|
|
@@ -680,7 +693,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
680
693
|
*/
|
|
681
694
|
function renderViewColumn(col, env, element) {
|
|
682
695
|
// Annotations and column
|
|
683
|
-
let result =
|
|
696
|
+
let result = '';
|
|
684
697
|
|
|
685
698
|
const leaf = col.as || col.ref && col.ref[col.ref.length - 1];
|
|
686
699
|
// Render 'null as <alias>' only for database and if element is virtual
|
|
@@ -760,7 +773,14 @@ function toHdbcdsSource(csn, options) {
|
|
|
760
773
|
}
|
|
761
774
|
env._artifact = art;
|
|
762
775
|
result += renderQuery(getNormalizedQuery(art).query, true, env, artifactPath.concat(art.projection ? 'projection' : 'query'), art.elements);
|
|
776
|
+
|
|
777
|
+
// views can only have a @sql.append
|
|
778
|
+
const { back } = getSqlSnippets(options, art);
|
|
779
|
+
if (back)
|
|
780
|
+
result += back;
|
|
781
|
+
|
|
763
782
|
result += ';\n';
|
|
783
|
+
|
|
764
784
|
return result;
|
|
765
785
|
}
|
|
766
786
|
|
|
@@ -829,72 +849,73 @@ function toHdbcdsSource(csn, options) {
|
|
|
829
849
|
result += `${env.indent}}`;
|
|
830
850
|
}
|
|
831
851
|
if (select.excluding) {
|
|
832
|
-
|
|
852
|
+
const excludingList = select.excluding.map(id => `${childEnv.indent}${formatIdentifier(id)}`).join(',\n');
|
|
853
|
+
result += ` excluding {\n${excludingList}\n`;
|
|
833
854
|
result += `${env.indent}}`;
|
|
834
855
|
}
|
|
835
856
|
|
|
836
|
-
return renderSelectProperties(select, result);
|
|
837
|
-
|
|
838
|
-
/**
|
|
839
|
-
* Render WHERE, GROUP BY, HAVING, ORDER BY and LIMIT clause
|
|
840
|
-
*
|
|
841
|
-
* @param {CSN.QuerySelect} select
|
|
842
|
-
* @param {string} alreadyRendered The query as it has been rendered so far
|
|
843
|
-
* @returns {string} The query with WHERE etc. added
|
|
844
|
-
*/
|
|
845
|
-
function renderSelectProperties(select, alreadyRendered) {
|
|
846
|
-
if (select.where)
|
|
847
|
-
alreadyRendered += `${continueIndent(alreadyRendered, env)}where ${renderExpr(select.where, env, true, true)}`;
|
|
857
|
+
return renderSelectProperties(select, result, env);
|
|
858
|
+
}
|
|
848
859
|
|
|
849
|
-
|
|
850
|
-
|
|
860
|
+
/**
|
|
861
|
+
* Render WHERE, GROUP BY, HAVING, ORDER BY and LIMIT clause
|
|
862
|
+
*
|
|
863
|
+
* @param {CSN.QuerySelect} select
|
|
864
|
+
* @param {string} alreadyRendered The query as it has been rendered so far
|
|
865
|
+
* @param {CdlRenderEnvironment} env Environment
|
|
866
|
+
* @returns {string} The query with WHERE etc. added
|
|
867
|
+
*/
|
|
868
|
+
function renderSelectProperties(select, alreadyRendered, env) {
|
|
869
|
+
if (select.where)
|
|
870
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}where ${renderExpr(select.where, env, true, true)}`;
|
|
851
871
|
|
|
852
|
-
|
|
853
|
-
|
|
872
|
+
if (select.groupBy)
|
|
873
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}group by ${select.groupBy.map(expr => renderExpr(expr, env)).join(', ')}`;
|
|
854
874
|
|
|
855
|
-
|
|
856
|
-
|
|
875
|
+
if (select.having)
|
|
876
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}having ${renderExpr(select.having, env, true, true)}`;
|
|
857
877
|
|
|
858
|
-
|
|
859
|
-
|
|
878
|
+
if (select.orderBy)
|
|
879
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}order by ${select.orderBy.map(entry => renderOrderByEntry(entry, env)).join(', ')}`;
|
|
860
880
|
|
|
861
|
-
|
|
862
|
-
|
|
881
|
+
if (select.limit)
|
|
882
|
+
alreadyRendered += `${continueIndent(alreadyRendered, env)}${renderLimit(select.limit, env)}`;
|
|
863
883
|
|
|
884
|
+
return alreadyRendered;
|
|
885
|
+
}
|
|
864
886
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
}
|
|
877
|
-
// Otherwise, start new line and indent normally
|
|
878
|
-
return `\n${increaseIndent(env).indent}`;
|
|
887
|
+
/**
|
|
888
|
+
* Utility function to make sure that we continue with the same indentation in WHERE, GROUP BY, ... after a closing curly brace and beyond
|
|
889
|
+
*
|
|
890
|
+
* @param {string} result Result of a previous render step
|
|
891
|
+
* @param {CdlRenderEnvironment} env Environment
|
|
892
|
+
* @returns {string} String to join with
|
|
893
|
+
*/
|
|
894
|
+
function continueIndent(result, env) {
|
|
895
|
+
if (result.endsWith('}') || result.endsWith('})')) {
|
|
896
|
+
// The preceding clause ended with '}', just append after that
|
|
897
|
+
return ' ';
|
|
879
898
|
}
|
|
899
|
+
// Otherwise, start new line and indent normally
|
|
900
|
+
return `\n${increaseIndent(env).indent}`;
|
|
901
|
+
}
|
|
880
902
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
903
|
+
/**
|
|
904
|
+
* Render a query's LIMIT clause, which may have also have OFFSET.
|
|
905
|
+
*
|
|
906
|
+
* @param {CSN.QueryLimit} limit CSN limit clause
|
|
907
|
+
* @param {CdlRenderEnvironment} env Environment
|
|
908
|
+
* @returns {string} Rendered limit clause
|
|
909
|
+
*/
|
|
910
|
+
function renderLimit(limit, env) {
|
|
911
|
+
let result = '';
|
|
912
|
+
if (limit.rows !== undefined)
|
|
913
|
+
result += `limit ${renderExpr(limit.rows, env)}`;
|
|
892
914
|
|
|
893
|
-
|
|
894
|
-
|
|
915
|
+
if (limit.offset !== undefined)
|
|
916
|
+
result += `${result !== '' ? `\n${increaseIndent(env).indent}` : ''}offset ${renderExpr(limit.offset, env)}`;
|
|
895
917
|
|
|
896
|
-
|
|
897
|
-
}
|
|
918
|
+
return result;
|
|
898
919
|
}
|
|
899
920
|
|
|
900
921
|
/**
|
|
@@ -926,7 +947,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
926
947
|
*/
|
|
927
948
|
function renderParameter(parName, par, env) {
|
|
928
949
|
if (par.notNull === true || par.notNull === false)
|
|
929
|
-
info(
|
|
950
|
+
info('query-ignoring-param-nullability', env.path.concat([ 'params', parName ]), { '#': 'std' });
|
|
930
951
|
return `${env.indent + formatParamIdentifier(parName, env.path.concat([ 'params', parName ]))} : ${renderTypeReference(par, env)}`;
|
|
931
952
|
}
|
|
932
953
|
|
|
@@ -943,7 +964,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
943
964
|
if (art.kind === 'aspect' || art.kind === 'type' && !hdbcdsNames || art.kind === 'type' && hdbcdsNames && !art.elements)
|
|
944
965
|
return '';
|
|
945
966
|
let result = '';
|
|
946
|
-
result += `${env.indent + (art.kind
|
|
967
|
+
result += `${env.indent + (art.kind)} ${renderArtifactName(artifactName, env, true)}`;
|
|
947
968
|
if (art.includes) {
|
|
948
969
|
// Includes are never flattened (don't exist in HANA)
|
|
949
970
|
result += ` : ${art.includes.map(name => renderAbsoluteNameWithQuotes(name, env)).join(', ')}`;
|
|
@@ -959,7 +980,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
959
980
|
}
|
|
960
981
|
else {
|
|
961
982
|
// Derived type or annotation with non-anonymous type
|
|
962
|
-
result += ` : ${renderTypeReference(art, env
|
|
983
|
+
result += ` : ${renderTypeReference(art, env)};\n`;
|
|
963
984
|
}
|
|
964
985
|
return result;
|
|
965
986
|
}
|
|
@@ -968,12 +989,11 @@ function toHdbcdsSource(csn, options) {
|
|
|
968
989
|
* Render a reference to a type used by 'elm' (named or inline)
|
|
969
990
|
* Allow suppressing enum-rendering - used in columns for example
|
|
970
991
|
*
|
|
971
|
-
* @param {
|
|
992
|
+
* @param {object} elm Element using the type reference
|
|
972
993
|
* @param {CdlRenderEnvironment} env Environment
|
|
973
|
-
* @param {boolean} [noEnum=false] If true, do not render enums
|
|
974
994
|
* @returns {string} Rendered type reference
|
|
975
995
|
*/
|
|
976
|
-
function renderTypeReference(elm, env
|
|
996
|
+
function renderTypeReference(elm, env) {
|
|
977
997
|
let result = '';
|
|
978
998
|
|
|
979
999
|
// Array type: Render items instead
|
|
@@ -1011,11 +1031,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1011
1031
|
// Reference to another element
|
|
1012
1032
|
if (elm.type.ref) {
|
|
1013
1033
|
// For HANA CDS, we need a 'type of'
|
|
1014
|
-
|
|
1015
|
-
if (elm.enum)
|
|
1016
|
-
result += renderEnum(elm.enum, env);
|
|
1017
|
-
|
|
1018
|
-
return result;
|
|
1034
|
+
return `type of ${renderAbsolutePath(elm.type, env)}`;
|
|
1019
1035
|
}
|
|
1020
1036
|
|
|
1021
1037
|
// If we get here, it must be a named type
|
|
@@ -1027,8 +1043,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1027
1043
|
// Type names are never flattened (derived types are unraveled in HANA)
|
|
1028
1044
|
result += renderAbsoluteNameWithQuotes(elm.type, env);
|
|
1029
1045
|
}
|
|
1030
|
-
if (elm.enum && !noEnum)
|
|
1031
|
-
result += renderEnum(elm.enum, env);
|
|
1032
1046
|
|
|
1033
1047
|
return result;
|
|
1034
1048
|
}
|
|
@@ -1094,58 +1108,38 @@ function toHdbcdsSource(csn, options) {
|
|
|
1094
1108
|
return elm.type.replace(/^cds\./, '') + renderTypeParameters(elm);
|
|
1095
1109
|
}
|
|
1096
1110
|
|
|
1097
|
-
/**
|
|
1098
|
-
* Render the 'enum { ... } part of a type declaration
|
|
1099
|
-
*
|
|
1100
|
-
* @param {CSN.EnumElements} enumPart Enum part of a type declaration
|
|
1101
|
-
* @param {CdlRenderEnvironment} env Environment
|
|
1102
|
-
* @returns {string} Rendered enum
|
|
1103
|
-
*/
|
|
1104
|
-
function renderEnum(enumPart, env) {
|
|
1105
|
-
let result = ' enum {\n';
|
|
1106
|
-
const childEnv = increaseIndent(env);
|
|
1107
|
-
for (const name in enumPart) {
|
|
1108
|
-
const enumConst = enumPart[name];
|
|
1109
|
-
result += childEnv.indent + quoteId(name);
|
|
1110
|
-
if (enumConst.val !== undefined)
|
|
1111
|
-
result += ` = ${renderExpr(enumConst, childEnv)}`;
|
|
1112
|
-
result += ';\n';
|
|
1113
|
-
}
|
|
1114
|
-
result += `${env.indent}}`;
|
|
1115
|
-
return result;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
1111
|
/**
|
|
1119
1112
|
* Render an expression (including paths and values) or condition 'x'.
|
|
1120
1113
|
* (no trailing LF, don't indent if inline)
|
|
1121
1114
|
*
|
|
1122
|
-
* @param {any}
|
|
1115
|
+
* @param {any} expr Expression to render
|
|
1123
1116
|
* @param {CdlRenderEnvironment} env Environment
|
|
1124
|
-
* @param {boolean} [inline=true]
|
|
1117
|
+
* @param {boolean} [inline=true] Whether to render inline
|
|
1125
1118
|
* @param {boolean} [inExpr=false] Whether the expression is already inside another expression
|
|
1126
1119
|
* @returns {string} Rendered expression
|
|
1127
1120
|
*/
|
|
1128
|
-
function renderExpr(
|
|
1121
|
+
function renderExpr(expr, env, inline = true, inExpr = false) {
|
|
1129
1122
|
// Compound expression
|
|
1130
|
-
if (Array.isArray(
|
|
1131
|
-
return beautifyExprArray(
|
|
1123
|
+
if (Array.isArray(expr))
|
|
1124
|
+
return beautifyExprArray(expr.map(item => renderExpr(item, env, inline, inExpr)));
|
|
1132
1125
|
|
|
1133
|
-
if (typeof
|
|
1134
|
-
if (inExpr &&
|
|
1135
|
-
return renderExplicitTypeCast(renderExprObject());
|
|
1136
|
-
return renderExprObject();
|
|
1126
|
+
if (typeof expr === 'object' && expr !== null) {
|
|
1127
|
+
if (inExpr && expr.cast && expr.cast.type)
|
|
1128
|
+
return renderExplicitTypeCast(renderExprObject(expr));
|
|
1129
|
+
return renderExprObject(expr);
|
|
1137
1130
|
}
|
|
1138
1131
|
|
|
1139
1132
|
// Not a literal value but part of an operator, function etc - just leave as it is
|
|
1140
|
-
return
|
|
1133
|
+
return expr;
|
|
1141
1134
|
|
|
1142
1135
|
|
|
1143
1136
|
/**
|
|
1144
1137
|
* Various special cases represented as objects
|
|
1145
1138
|
*
|
|
1139
|
+
* @param {object} x Expression
|
|
1146
1140
|
* @returns {string} Rendered expression object
|
|
1147
1141
|
*/
|
|
1148
|
-
function renderExprObject() {
|
|
1142
|
+
function renderExprObject(x) {
|
|
1149
1143
|
if (x.list) {
|
|
1150
1144
|
// set "inExpr" to false: treat list elements as new expressions
|
|
1151
1145
|
return `(${x.list.map(item => renderExpr(item, env, inline, false)).join(', ')})`;
|
|
@@ -1173,7 +1167,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1173
1167
|
// we can't quote functions with parens, issue warning if it is a reserved keyword
|
|
1174
1168
|
if (!funcWithoutParen(x, 'hana') && keywords.hdbcds.includes(uppercaseAndUnderscore(funcName)))
|
|
1175
1169
|
warning(null, x.$location, `The identifier “${uppercaseAndUnderscore(funcName)}“ is a SAP HANA keyword`);
|
|
1176
|
-
return renderFunc(
|
|
1170
|
+
return renderFunc(funcName, x, 'hana', a => renderArgs(a, '=>', env));
|
|
1177
1171
|
}
|
|
1178
1172
|
// Nested expression
|
|
1179
1173
|
else if (x.xpr) {
|
|
@@ -1229,7 +1223,12 @@ function toHdbcdsSource(csn, options) {
|
|
|
1229
1223
|
*/
|
|
1230
1224
|
function renderExpressionRef(x) {
|
|
1231
1225
|
if (!x.param && !x.global) {
|
|
1226
|
+
const magicReplacement = getVariableReplacement(x.ref, options);
|
|
1232
1227
|
if (x.ref[0] === '$user') {
|
|
1228
|
+
if (magicReplacement !== null)
|
|
1229
|
+
return `'${magicReplacement}'`;
|
|
1230
|
+
|
|
1231
|
+
// Keep old way of solving this to remain backwards compatible
|
|
1233
1232
|
// FIXME: this is all not enough: we might need an explicit select item alias
|
|
1234
1233
|
if (x.ref[1] === 'id') {
|
|
1235
1234
|
if (options.magicVars && options.magicVars.user && (typeof options.magicVars.user === 'string' || options.magicVars.user instanceof String))
|
|
@@ -1251,6 +1250,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
1251
1250
|
else if (x.ref[1] === 'to')
|
|
1252
1251
|
return 'TO_TIMESTAMP(SESSION_CONTEXT(\'VALID-TO\'))';
|
|
1253
1252
|
}
|
|
1253
|
+
else if (x.ref[0] === '$session' && magicReplacement !== null) {
|
|
1254
|
+
return `'${magicReplacement}'`;
|
|
1255
|
+
}
|
|
1254
1256
|
}
|
|
1255
1257
|
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, x.ref)).join('.')}`;
|
|
1256
1258
|
}
|
|
@@ -1262,10 +1264,10 @@ function toHdbcdsSource(csn, options) {
|
|
|
1262
1264
|
* @returns {string} Rendered cast()
|
|
1263
1265
|
*/
|
|
1264
1266
|
function renderExplicitTypeCast(value) {
|
|
1265
|
-
let typeRef = renderTypeReference(
|
|
1267
|
+
let typeRef = renderTypeReference(expr.cast, env);
|
|
1266
1268
|
|
|
1267
1269
|
// inside a cast expression, the cds and hana cds types need to be mapped to hana sql types
|
|
1268
|
-
const hanaSqlType = cdsToSqlTypes.hana[
|
|
1270
|
+
const hanaSqlType = cdsToSqlTypes.hana[expr.cast.type] || cdsToSqlTypes.standard[expr.cast.type];
|
|
1269
1271
|
if (hanaSqlType) {
|
|
1270
1272
|
const typeRefWithoutParams = typeRef.substring(0, typeRef.indexOf('(')) || typeRef;
|
|
1271
1273
|
typeRef = typeRef.replace(typeRefWithoutParams, hanaSqlType);
|
|
@@ -1288,7 +1290,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1288
1290
|
// (see FIXME at renderArtifact)
|
|
1289
1291
|
if (idx === 0 && s === $SELF) {
|
|
1290
1292
|
// do not produce USING for $projection
|
|
1291
|
-
if (env.currentArtifactName === $PROJECTION
|
|
1293
|
+
if (env.currentArtifactName === $PROJECTION)
|
|
1292
1294
|
return env.currentArtifactName;
|
|
1293
1295
|
|
|
1294
1296
|
return plainNames ? renderAbsoluteNamePlain(env.currentArtifactName, env)
|
|
@@ -1541,9 +1543,70 @@ function toHdbcdsSource(csn, options) {
|
|
|
1541
1543
|
Object.keys(env.topLevelAliases)
|
|
1542
1544
|
.filter(name => env.topLevelAliases[name].quotedAlias !== formatIdentifier(uppercaseAndUnderscore(artifactName))) // avoid "using FOO as FOO" in FOO.cds
|
|
1543
1545
|
.forEach((name) => {
|
|
1546
|
+
const nativeObjectExists = csn.definitions[name] && hasAnnotationValue(csn.definitions[name], '@cds.persistence.exists');
|
|
1547
|
+
if (!plainNames && nativeObjectExists)
|
|
1548
|
+
checkForNameClashesWithNativeObject(name);
|
|
1544
1549
|
distinct[`using ${env.topLevelAliases[name].quotedName} as ${env.topLevelAliases[name].quotedAlias};\n`] = '';
|
|
1545
1550
|
});
|
|
1551
|
+
/**
|
|
1552
|
+
* If we generate a `using <native object> from <bar>` clause,
|
|
1553
|
+
* we warn if we generate a SAP HANA CDS artifact which would hide the
|
|
1554
|
+
* native DB object from being found by the SAP HANA CDS compiler
|
|
1555
|
+
* see cap/cds-compiler#8269 for details
|
|
1556
|
+
* @param {string} name of the native db object
|
|
1557
|
+
*/
|
|
1558
|
+
function checkForNameClashesWithNativeObject(name) {
|
|
1559
|
+
const possibleShadowName = getNamePrefix(env.topLevelAliases[name].quotedName);
|
|
1560
|
+
const mightBeShadowedBy = csn.definitions[possibleShadowName];
|
|
1561
|
+
if (mightBeShadowedBy) {
|
|
1562
|
+
const artifactWillBeRendered = isArtifactRendered(mightBeShadowedBy, possibleShadowName);
|
|
1563
|
+
// only warn if actually rendered to HANA CDS
|
|
1564
|
+
if (artifactWillBeRendered)
|
|
1565
|
+
warning('anno-hidden-exists', [ 'definitions', name ], { name: possibleShadowName }, 'Native database object is hidden by a definition starting with $(NAME)');
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1546
1568
|
|
|
1569
|
+
function isArtifactRendered(art, artName) {
|
|
1570
|
+
const isHanaCdsContext = art.kind === 'service' || art.kind === 'context';
|
|
1571
|
+
if (isHanaCdsContext)
|
|
1572
|
+
return isContextRendered(artName);
|
|
1573
|
+
if ([ 'action', 'function', 'event' ].includes(art.kind) || options.sqlMapping !== 'hdbcds' && art.kind === 'type')
|
|
1574
|
+
return false;
|
|
1575
|
+
return !(hasAnnotationValue(art, '@cds.persistence.exists') || hasAnnotationValue(art, '@cds.persistence.skip'));
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
/**
|
|
1579
|
+
* Check if there is at least one entity which will be rendered as SAP HANA CDS entity
|
|
1580
|
+
* inside the given context (or in its sub-contexts).
|
|
1581
|
+
* Or in other words: If the context will be rendered as a SAP HANA CDS context in the end.
|
|
1582
|
+
*
|
|
1583
|
+
* @param {string} contextName
|
|
1584
|
+
* @returns {boolean} true if a context/service will be rendered as a SAP HANA CDS context.
|
|
1585
|
+
*/
|
|
1586
|
+
function isContextRendered(contextName) {
|
|
1587
|
+
const subArtifacts = getSubArtifacts(contextName);
|
|
1588
|
+
return Object.entries(subArtifacts).some(([ artName, art ]) => {
|
|
1589
|
+
if (art.kind === 'context')
|
|
1590
|
+
return isContextRendered(`${contextName}.${artName}`);
|
|
1591
|
+
return isArtifactRendered(art, artName);
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* @param {string} usingName the string which appears in the `using <string> from ..` including the quotes
|
|
1597
|
+
* @returns the prefix of the `using` name.
|
|
1598
|
+
* @example
|
|
1599
|
+
* "com.sap.foo.native.object" --> com
|
|
1600
|
+
* "com.sap.foo::native.object" --> com.sap.foo.native
|
|
1601
|
+
*/
|
|
1602
|
+
function getNamePrefix(usingName) {
|
|
1603
|
+
usingName = usingName.replace(/"/g, '');
|
|
1604
|
+
if (usingName.indexOf('::') !== -1) {
|
|
1605
|
+
const parts = usingName.split('::');
|
|
1606
|
+
return `${parts[0]}.${parts[1].split('.')[0]}`;
|
|
1607
|
+
}
|
|
1608
|
+
return usingName.split('.')[0];
|
|
1609
|
+
}
|
|
1547
1610
|
return Object.keys(distinct).join('');
|
|
1548
1611
|
}
|
|
1549
1612
|
|
|
@@ -1675,11 +1738,6 @@ function toHdbcdsSource(csn, options) {
|
|
|
1675
1738
|
if (id.indexOf('.') !== -1)
|
|
1676
1739
|
throw new Error(id);
|
|
1677
1740
|
|
|
1678
|
-
// FIXME: Somewhat arbitrary magic: Do not quote $projection (because HANA CDS doesn't recognize it otherwise). Similar for $self.
|
|
1679
|
-
// FIXME: The test should not be on the name, but by checking the _artifact.
|
|
1680
|
-
if (id === $PROJECTION || id === $SELF)
|
|
1681
|
-
return id;
|
|
1682
|
-
|
|
1683
1741
|
|
|
1684
1742
|
switch (options.forHana.names) {
|
|
1685
1743
|
case 'plain':
|
|
@@ -1701,13 +1759,14 @@ function toHdbcdsSource(csn, options) {
|
|
|
1701
1759
|
* @returns {string} Correctly quoted absname
|
|
1702
1760
|
*/
|
|
1703
1761
|
function quoteAbsoluteNameAsId(absname) {
|
|
1762
|
+
const resultingName = getResultingName(csn, options.sqlMapping, absname);
|
|
1763
|
+
|
|
1704
1764
|
if (hdbcdsNames) {
|
|
1705
|
-
const
|
|
1706
|
-
const namespace = getParentNameOf(topLevelName);
|
|
1765
|
+
const namespace = getNamespace(csn, absname);
|
|
1707
1766
|
if (namespace)
|
|
1708
|
-
return `"${(`${namespace}::${
|
|
1767
|
+
return `"${(`${namespace}::${resultingName.substring(namespace.length + 2)}`).replace(/"/g, '""')}"`;
|
|
1709
1768
|
}
|
|
1710
|
-
return `"${
|
|
1769
|
+
return `"${resultingName.replace(/"/g, '""')}"`;
|
|
1711
1770
|
}
|
|
1712
1771
|
|
|
1713
1772
|
/**
|
|
@@ -1762,7 +1821,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1762
1821
|
function renderArtifactName(artifactName, env, fallthrough = false) {
|
|
1763
1822
|
if (plainNames && !fallthrough)
|
|
1764
1823
|
return formatIdentifier(uppercaseAndUnderscore(artifactName));
|
|
1765
|
-
|
|
1824
|
+
// hdbcds with quoted or hdbcds naming
|
|
1766
1825
|
return env.namePrefix + quoteId(getRealName(csn, artifactName).replace(/\./g, '_'));
|
|
1767
1826
|
}
|
|
1768
1827
|
|
package/lib/render/toRename.js
CHANGED
|
@@ -70,22 +70,19 @@ function toRenameDdl(csn, options) {
|
|
|
70
70
|
|
|
71
71
|
resultStr += Object.keys(art.elements).map((name) => {
|
|
72
72
|
const e = art.elements[name];
|
|
73
|
-
let
|
|
73
|
+
let str = '';
|
|
74
74
|
|
|
75
75
|
const beforeColumnName = quoteSqlId(name);
|
|
76
76
|
const afterColumnName = plainSqlId(name);
|
|
77
77
|
|
|
78
78
|
if (!e._ignore) {
|
|
79
|
-
if (e.target)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
resultStr += ' ';
|
|
85
|
-
result = ` EXEC 'RENAME COLUMN ${afterTableName}.${beforeColumnName} TO ${afterColumnName}';\n`;
|
|
86
|
-
}
|
|
79
|
+
if (e.target)
|
|
80
|
+
str = ` EXEC 'ALTER TABLE ${afterTableName} DROP ASSOCIATION ${beforeColumnName}';\n`;
|
|
81
|
+
|
|
82
|
+
else if (beforeColumnName !== afterColumnName)
|
|
83
|
+
str = ` EXEC 'RENAME COLUMN ${afterTableName}.${beforeColumnName} TO ${afterColumnName}';\n`;
|
|
87
84
|
}
|
|
88
|
-
return
|
|
85
|
+
return str;
|
|
89
86
|
}).join('');
|
|
90
87
|
}
|
|
91
88
|
return resultStr;
|