@sap/cds-compiler 2.7.0 → 2.11.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 +167 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +17 -33
- package/lib/api/options.js +25 -13
- package/lib/api/validate.js +33 -9
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +26 -2
- package/lib/base/messages.js +25 -9
- package/lib/base/model.js +5 -3
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +18 -5
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +5 -2
- package/lib/compiler/definer.js +145 -120
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +207 -47
- package/lib/compiler/shared.js +47 -200
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +94 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +302 -115
- package/lib/edm/edmUtils.js +31 -12
- 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 +5308 -4308
- package/lib/json/from-csn.js +59 -30
- package/lib/json/to-csn.js +354 -105
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +81 -14
- package/lib/language/language.g4 +163 -31
- package/lib/main.d.ts +136 -17
- package/lib/main.js +7 -1
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +115 -32
- package/lib/model/csnUtils.js +71 -33
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -16
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +60 -17
- package/lib/render/toHdbcds.js +122 -74
- package/lib/render/toSql.js +57 -32
- package/lib/render/utils/common.js +6 -10
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +9 -6
- package/lib/transform/db/expansion.js +19 -7
- package/lib/transform/db/flattening.js +31 -7
- package/lib/transform/db/transformExists.js +344 -66
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +65 -436
- package/lib/transform/forOdataNew.js +21 -10
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +55 -9
- package/lib/transform/translateAssocsToJoins.js +11 -17
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
package/lib/render/toCdl.js
CHANGED
|
@@ -8,11 +8,13 @@ const {
|
|
|
8
8
|
isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
|
|
9
9
|
} = require('../model/csnUtils');
|
|
10
10
|
const keywords = require('../base/keywords');
|
|
11
|
-
const { renderFunc,
|
|
11
|
+
const { renderFunc, beautifyExprArray, findElement } = require('./utils/common');
|
|
12
12
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
13
|
-
const timetrace = require('../utils/timetrace');
|
|
13
|
+
const { timetrace } = require('../utils/timetrace');
|
|
14
14
|
const { csnRefs } = require('../model/csnRefs');
|
|
15
15
|
const { forEachDefinition } = require('../model/csnUtils');
|
|
16
|
+
const enrichUniversalCsn = require('../transform/universalCsnEnricher');
|
|
17
|
+
const { isBetaEnabled } = require('../base/model');
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Render the CSN model 'model' to CDS source text. One source is created per
|
|
@@ -33,6 +35,9 @@ function toCdsSourceCsn(csn, options) {
|
|
|
33
35
|
// Skip compactModel if already using CSN
|
|
34
36
|
// const csn = cloneCsn(model, options);
|
|
35
37
|
|
|
38
|
+
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
39
|
+
enrichUniversalCsn(csn, options);
|
|
40
|
+
|
|
36
41
|
checkCSNVersion(csn, options);
|
|
37
42
|
|
|
38
43
|
const result = Object.create(null);
|
|
@@ -50,16 +55,20 @@ function toCdsSourceCsn(csn, options) {
|
|
|
50
55
|
result[main] += `${sourceStr}\n`;
|
|
51
56
|
});
|
|
52
57
|
|
|
53
|
-
// Apply possible subelement annotations with an "annotate X with"
|
|
58
|
+
// Apply possible subelement/action return annotations with an "annotate X with"
|
|
59
|
+
// Some of them appear in csn.extensions, some not...
|
|
54
60
|
if (subelementAnnotates.length > 0) {
|
|
55
|
-
for (const [ artName, element, elementName ] of subelementAnnotates) {
|
|
56
|
-
|
|
57
|
-
sourceStr
|
|
61
|
+
for (const [ artName, element, elementName, suffix ] of subelementAnnotates) {
|
|
62
|
+
// Suffix is used with action return annotations
|
|
63
|
+
let sourceStr = `annotate ${artName} with ${suffix ? `${suffix} ` : ''}{\n`;
|
|
64
|
+
if (elementName) // action returns do not have element name - we need less {} there
|
|
65
|
+
sourceStr += ` ${elementName} {\n`;
|
|
58
66
|
const env = increaseIndent(increaseIndent(createEnv()));
|
|
59
67
|
const subelements = renderSubelementAnnotates(element, env);
|
|
60
68
|
if (subelements !== '') {
|
|
61
69
|
sourceStr += `${subelements}\n`;
|
|
62
|
-
|
|
70
|
+
if (elementName) // action returns do not have element name - we need less {} there
|
|
71
|
+
sourceStr += ' }\n';
|
|
63
72
|
sourceStr += '}\n';
|
|
64
73
|
result[main] += `${sourceStr}\n`;
|
|
65
74
|
}
|
|
@@ -145,6 +154,12 @@ function toCdsSourceCsn(csn, options) {
|
|
|
145
154
|
if (ext.elements)
|
|
146
155
|
result += renderElementExtensions(ext.elements, env);
|
|
147
156
|
|
|
157
|
+
// Returns annotations
|
|
158
|
+
if (ext.returns) {
|
|
159
|
+
const childEnv = increaseIndent(env);
|
|
160
|
+
result += ` with returns${renderElementExtensions(ext.returns.elements, childEnv)}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
148
163
|
// Action annotations
|
|
149
164
|
if (ext.actions) {
|
|
150
165
|
result += ' actions {\n';
|
|
@@ -162,10 +177,19 @@ function toCdsSourceCsn(csn, options) {
|
|
|
162
177
|
|
|
163
178
|
result += `${paramAnnotations.join(',\n')}\n${childEnv.indent})`;
|
|
164
179
|
}
|
|
180
|
+
// Annotations on action returns
|
|
181
|
+
if (action.returns && action.returns.elements) {
|
|
182
|
+
const grandChildEnv = increaseIndent(childEnv);
|
|
183
|
+
result += ` returns${renderElementExtensions(action.returns.elements, grandChildEnv)}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
165
187
|
result += ';\n';
|
|
166
188
|
}
|
|
167
189
|
result += `${env.indent}}`;
|
|
168
190
|
}
|
|
191
|
+
|
|
192
|
+
|
|
169
193
|
result += ';';
|
|
170
194
|
return result;
|
|
171
195
|
}).join('\n');
|
|
@@ -205,6 +229,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
205
229
|
function renderArtifact(artifactName, art, env) {
|
|
206
230
|
// FIXME: Correctly build the paths during runtime to give better locations
|
|
207
231
|
env.path = [ 'definitions', artifactName ];
|
|
232
|
+
env.artifactName = artifactName;
|
|
208
233
|
|
|
209
234
|
switch (art.kind) {
|
|
210
235
|
case 'entity':
|
|
@@ -509,6 +534,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
509
534
|
* @param {Boolean} [isSubElement]
|
|
510
535
|
*/
|
|
511
536
|
function renderElement(elementName, elm, env, isSubElement) {
|
|
537
|
+
env.elementName = elementName;
|
|
512
538
|
let result = renderDocComment(elm, env) + renderAnnotationAssignments(elm, env);
|
|
513
539
|
const quotedElementName = quoteOrUppercaseId(elementName);
|
|
514
540
|
result += `${env.indent + (elm.virtual ? 'virtual ' : '') +
|
|
@@ -516,10 +542,11 @@ function toCdsSourceCsn(csn, options) {
|
|
|
516
542
|
((elm.masked && !elm._ignoreMasked) ? 'masked ' : '') +
|
|
517
543
|
quotedElementName} : ${
|
|
518
544
|
renderTypeReference(elm, env, undefined)
|
|
519
|
-
}${renderNullability(elm)}`;
|
|
545
|
+
}${elm.on ? '' : renderNullability(elm)}`;
|
|
520
546
|
if (elm.default)
|
|
521
547
|
result += ` default ${renderExpr(elm.default, env)}`;
|
|
522
548
|
|
|
549
|
+
delete env.elementName;
|
|
523
550
|
return `${result};\n`;
|
|
524
551
|
}
|
|
525
552
|
|
|
@@ -552,7 +579,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
552
579
|
* @return {string}
|
|
553
580
|
*/
|
|
554
581
|
function renderQueryActionsAndFunctions(artifactName, art, env) {
|
|
555
|
-
let result =
|
|
582
|
+
let result = renderActionsAndFunctions(art, env);
|
|
556
583
|
// Even if we have seen actions/functions, they might all have been ignored
|
|
557
584
|
if (result !== '')
|
|
558
585
|
result = `${env.indent}extend entity ${artifactName} with${result};`;
|
|
@@ -585,7 +612,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
585
612
|
}
|
|
586
613
|
// Now iterate elements - render an annotation if it is different from the column's
|
|
587
614
|
const childEnv = increaseIndent(env);
|
|
588
|
-
let result =
|
|
615
|
+
let result = '';
|
|
589
616
|
for (const elemName in art.elements) {
|
|
590
617
|
let elemAnnotations = '';
|
|
591
618
|
const elem = art.elements[elemName];
|
|
@@ -688,7 +715,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
688
715
|
|
|
689
716
|
// Even the first step might have parameters and/or a filter
|
|
690
717
|
if (path.ref[0].args)
|
|
691
|
-
result += `(${renderArgs(path.ref[0]
|
|
718
|
+
result += `(${renderArgs(path.ref[0], ':', env)})`;
|
|
692
719
|
|
|
693
720
|
if (path.ref[0].where)
|
|
694
721
|
result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env, true, true)}]`;
|
|
@@ -1056,8 +1083,11 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1056
1083
|
const childEnv = increaseIndent(env);
|
|
1057
1084
|
const parameters = Object.keys(act.params || []).map(name => renderParameter(name, act.params[name], childEnv)).join(',\n');
|
|
1058
1085
|
result += (parameters === '') ? '()' : `(\n${parameters}\n${env.indent})`;
|
|
1059
|
-
if (act.returns)
|
|
1086
|
+
if (act.returns) {
|
|
1087
|
+
if (act.returns.type && act.returns.elements) // action returns annotations
|
|
1088
|
+
subelementAnnotates.push([ actionName, act.returns, '', 'returns' ]);
|
|
1060
1089
|
result += ` returns ${renderTypeReference(act.returns, env)}${renderNullability(act.returns)}`;
|
|
1090
|
+
}
|
|
1061
1091
|
|
|
1062
1092
|
result += ';\n';
|
|
1063
1093
|
return result;
|
|
@@ -1133,6 +1163,10 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1133
1163
|
let rc = `many ${renderTypeReference(elm.items, env)}`;
|
|
1134
1164
|
if (elm.items.notNull != null)
|
|
1135
1165
|
rc += elm.items.notNull ? ' not null' : ' null';
|
|
1166
|
+
// many sub element annotates
|
|
1167
|
+
if (elm.items.type && elm.items.elements && env.artifactName)
|
|
1168
|
+
subelementAnnotates.push([ env.artifactName, elm.items, env.elementName ]);
|
|
1169
|
+
|
|
1136
1170
|
return rc;
|
|
1137
1171
|
}
|
|
1138
1172
|
|
|
@@ -1316,7 +1350,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1316
1350
|
function renderExpr(x, env, inline = true, inExpr = false) {
|
|
1317
1351
|
// Compound expression
|
|
1318
1352
|
if (Array.isArray(x))
|
|
1319
|
-
return
|
|
1353
|
+
return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
|
|
1320
1354
|
|
|
1321
1355
|
if (typeof x === 'object' && x !== null) {
|
|
1322
1356
|
if (inExpr && x.cast && x.cast.type)
|
|
@@ -1370,6 +1404,12 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1370
1404
|
// FIXME: no extra magic with x.param or x.global
|
|
1371
1405
|
return `${(x.param || x.global) ? ':' : ''}${x.ref.map(renderPathStep).join('.')}`;
|
|
1372
1406
|
}
|
|
1407
|
+
else if (x.xpr && x.func) {
|
|
1408
|
+
// window function
|
|
1409
|
+
const funcDef = renderFunc( x.func, x, null, a => renderArgs(a, '=>', env) );
|
|
1410
|
+
const windowFunctionOperator = x.xpr.shift(); // OVER ...
|
|
1411
|
+
return `${funcDef} ${windowFunctionOperator} ( ${renderExpr(x.xpr, env, true)} )`;
|
|
1412
|
+
}
|
|
1373
1413
|
// Function call, possibly with args (use '=>' for named args)
|
|
1374
1414
|
else if (x.func) {
|
|
1375
1415
|
// test for non-regular HANA identifier that needs to be quoted
|
|
@@ -1440,13 +1480,13 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1440
1480
|
|
|
1441
1481
|
// Not really a path step but an object-like function call
|
|
1442
1482
|
if (s.func)
|
|
1443
|
-
return `${s.func}(${renderArgs(s
|
|
1483
|
+
return `${s.func}(${renderArgs(s, '=>', env)})`;
|
|
1444
1484
|
|
|
1445
1485
|
// Path step, possibly with view parameters and/or filters
|
|
1446
1486
|
let result = `${quoteOrUppercaseId(s.id)}`;
|
|
1447
1487
|
if (s.args) {
|
|
1448
1488
|
// View parameters
|
|
1449
|
-
result += `(${renderArgs(s
|
|
1489
|
+
result += `(${renderArgs(s, ':', env)})`;
|
|
1450
1490
|
}
|
|
1451
1491
|
if (s.where) {
|
|
1452
1492
|
// Filter, possibly with cardinality
|
|
@@ -1464,12 +1504,13 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1464
1504
|
* Render function arguments or view parameters (positional if array, named if object/dict),
|
|
1465
1505
|
* using 'sep' as separator for positional parameters
|
|
1466
1506
|
*
|
|
1467
|
-
* @param {object
|
|
1507
|
+
* @param {object} node with `args` to render
|
|
1468
1508
|
* @param {string} sep
|
|
1469
1509
|
* @param {CdlRenderEnvironment} env
|
|
1470
1510
|
* @returns {string}
|
|
1471
1511
|
*/
|
|
1472
|
-
function renderArgs(
|
|
1512
|
+
function renderArgs(node, sep, env) {
|
|
1513
|
+
const args = node.args ? node.args : {};
|
|
1473
1514
|
// Positional arguments
|
|
1474
1515
|
if (Array.isArray(args))
|
|
1475
1516
|
return args.map(arg => renderExpr(arg, env)).join(', ');
|
|
@@ -1861,6 +1902,8 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1861
1902
|
*
|
|
1862
1903
|
* @property {string} indent Current indentation as a string, e.g. ' ' for two spaces.
|
|
1863
1904
|
* @property {CSN.Path} [path] CSN path to the current artifact
|
|
1905
|
+
* @property {string} artifactName Name of the artifact - set in renderArtifact
|
|
1906
|
+
* @property {string} elementName Name of the element being rendered - set in renderElement
|
|
1864
1907
|
* @property {{[name: string]: {
|
|
1865
1908
|
quotedName: string,
|
|
1866
1909
|
quotedAlias: string
|
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';
|
|
@@ -402,6 +404,9 @@ function toHdbcdsSource(csn, options) {
|
|
|
402
404
|
const duplicateChecker = new DuplicateChecker(); // registry for all artifact names and element names
|
|
403
405
|
duplicateChecker.addArtifact(artifactName, env.path, artifactName);
|
|
404
406
|
childEnv.path = env.path.concat('elements');
|
|
407
|
+
// calculate __aliases which must be used in case an association
|
|
408
|
+
// has the same identifier as it's target
|
|
409
|
+
createTopLevelAliasesForArtifact(artifactName, art, env);
|
|
405
410
|
for (const name in art.elements)
|
|
406
411
|
result += renderElement(name, art.elements[name], childEnv, duplicateChecker);
|
|
407
412
|
|
|
@@ -411,6 +416,33 @@ function toHdbcdsSource(csn, options) {
|
|
|
411
416
|
return result;
|
|
412
417
|
}
|
|
413
418
|
|
|
419
|
+
/**
|
|
420
|
+
* If an association/composition has the same identifier as it's target
|
|
421
|
+
* we must render an "using target as __target" and use the alias to refer to the target
|
|
422
|
+
*
|
|
423
|
+
* @param {string} artName
|
|
424
|
+
* @param {CSN.Artifact} art
|
|
425
|
+
* @param {CdlRenderEnvironment} env
|
|
426
|
+
*/
|
|
427
|
+
function createTopLevelAliasesForArtifact(artName, art, env) {
|
|
428
|
+
forEachMember(art, (element) => {
|
|
429
|
+
if (!element.target)
|
|
430
|
+
return;
|
|
431
|
+
|
|
432
|
+
let alias = element['@cds.persistence.name'];
|
|
433
|
+
if (uppercaseAndUnderscore(element.target) === element['@cds.persistence.name']) {
|
|
434
|
+
alias = createTopLevelAliasName(element['@cds.persistence.name']);
|
|
435
|
+
// calculate new alias if it would conflict with other csn.Artifact
|
|
436
|
+
while (csn.definitions[alias])
|
|
437
|
+
alias = createTopLevelAliasName(alias);
|
|
438
|
+
env.topLevelAliases[element['@cds.persistence.name']] = {
|
|
439
|
+
quotedName: formatIdentifier(element['@cds.persistence.name']),
|
|
440
|
+
quotedAlias: formatIdentifier(alias),
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
414
446
|
/**
|
|
415
447
|
* Render the 'technical configuration { ... }' section 'tc' of an entity.
|
|
416
448
|
*
|
|
@@ -516,7 +548,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
516
548
|
// Special handling for HANA CDS: Must omit the ':' before anonymous structured types (for historical reasons)
|
|
517
549
|
const omitColon = (!elm.type && elm.elements);
|
|
518
550
|
let result = '';
|
|
519
|
-
const quotedElementName =
|
|
551
|
+
const quotedElementName = formatIdentifier(elementName);
|
|
520
552
|
if (duplicateChecker)
|
|
521
553
|
duplicateChecker.addElement(quotedElementName, env.path, elementName);
|
|
522
554
|
|
|
@@ -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)}]`;
|
|
@@ -627,11 +659,11 @@ function toHdbcdsSource(csn, options) {
|
|
|
627
659
|
const implicitAlias = path.ref.length === 0 ? getLastPartOf(getResultingName(csn, options.sqlMapping, path.ref[0])) : getLastPartOfRef(path.ref);
|
|
628
660
|
if (path.as) {
|
|
629
661
|
// Source had an alias - render it
|
|
630
|
-
result += ` as ${
|
|
662
|
+
result += ` as ${formatIdentifier(path.as)}`;
|
|
631
663
|
}
|
|
632
|
-
else if (getLastPartOf(result) !==
|
|
664
|
+
else if (getLastPartOf(result) !== formatIdentifier(implicitAlias)) {
|
|
633
665
|
// Render an artificial alias if the result would produce a different one
|
|
634
|
-
result += ` as ${
|
|
666
|
+
result += ` as ${formatIdentifier(implicitAlias)}`;
|
|
635
667
|
}
|
|
636
668
|
return result;
|
|
637
669
|
}
|
|
@@ -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) {
|
|
@@ -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,19 @@ 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
|
+
}
|
|
1019
1053
|
}
|
|
1020
1054
|
|
|
1021
1055
|
// ON-condition (if any)
|
|
@@ -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,7 +1201,12 @@ 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
1212
|
if (options.magicVars && options.magicVars.user && (typeof options.magicVars.user === 'string' || options.magicVars.user instanceof String))
|
|
@@ -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
|
});
|
|
@@ -1635,7 +1661,16 @@ function toHdbcdsSource(csn, options) {
|
|
|
1635
1661
|
return id;
|
|
1636
1662
|
|
|
1637
1663
|
|
|
1638
|
-
|
|
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
|
+
}
|
|
1639
1674
|
}
|
|
1640
1675
|
|
|
1641
1676
|
/*
|
|
@@ -1656,21 +1691,34 @@ function toHdbcdsSource(csn, options) {
|
|
|
1656
1691
|
}
|
|
1657
1692
|
|
|
1658
1693
|
/**
|
|
1659
|
-
* Quote or uppercase an identifier 'id', depending on naming strategy
|
|
1694
|
+
* Quote and/or uppercase an identifier 'id', depending on naming strategy
|
|
1660
1695
|
*
|
|
1661
1696
|
* @param {string} id Identifier
|
|
1662
|
-
* @param {CSN.Location} [location] Optional location for the warning.
|
|
1663
1697
|
* @returns {string} Quoted/uppercased id
|
|
1664
1698
|
*/
|
|
1665
|
-
function
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
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);
|
|
1671
1721
|
|
|
1672
|
-
return result;
|
|
1673
|
-
}
|
|
1674
1722
|
return quoteId(id);
|
|
1675
1723
|
}
|
|
1676
1724
|
|
|
@@ -1693,7 +1741,7 @@ function toHdbcdsSource(csn, options) {
|
|
|
1693
1741
|
*/
|
|
1694
1742
|
function renderArtifactName(artifactName, env, fallthrough = false) {
|
|
1695
1743
|
if (plainNames && !fallthrough)
|
|
1696
|
-
return uppercaseAndUnderscore(artifactName);
|
|
1744
|
+
return formatIdentifier(uppercaseAndUnderscore(artifactName));
|
|
1697
1745
|
// hdbcds with quoted or hdbcds naming
|
|
1698
1746
|
return env.namePrefix + quoteId(getRealName(csn, artifactName).replace(/\./g, '_'));
|
|
1699
1747
|
}
|