@sap/cds-compiler 3.4.2 → 3.4.4
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 +8 -0
- package/bin/cdsc.js +3 -4
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/lib/api/main.js +6 -3
- package/lib/base/message-registry.js +60 -37
- package/lib/base/messages.js +7 -3
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -6
- package/lib/compiler/builtins.js +13 -13
- package/lib/compiler/checks.js +50 -33
- package/lib/compiler/define.js +9 -6
- package/lib/compiler/extend.js +71 -45
- package/lib/compiler/finalize-parse-cdl.js +3 -3
- package/lib/compiler/populate.js +16 -5
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +4 -4
- package/lib/compiler/utils.js +14 -0
- package/lib/edm/annotations/genericTranslation.js +68 -56
- package/lib/edm/csn2edm.js +214 -174
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +1 -1
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/Dictionary.json +176 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4776 -4513
- package/lib/json/from-csn.js +21 -16
- package/lib/json/to-csn.js +37 -41
- package/lib/language/.eslintrc.json +4 -1
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +54 -95
- package/lib/language/language.g4 +92 -66
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +2 -2
- package/lib/model/csnRefs.js +5 -0
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +99 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/toCdl.js +96 -127
- package/lib/render/toHdbcds.js +38 -35
- package/lib/render/toSql.js +75 -161
- package/lib/render/utils/common.js +133 -83
- package/lib/render/utils/delta.js +227 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/forOdataNew.js +26 -19
- package/lib/transform/localized.js +9 -8
- package/lib/transform/odata/typesExposure.js +26 -4
- package/lib/transform/transformUtilsNew.js +15 -8
- package/package.json +2 -3
- package/lib/modelCompare/filter.js +0 -83
|
@@ -72,7 +72,6 @@ function beautifyExprArray(tokens) {
|
|
|
72
72
|
return result;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
76
75
|
/**
|
|
77
76
|
* Get the part that is really the name of this artifact and not just prefix caused by a context/service
|
|
78
77
|
*
|
|
@@ -86,7 +85,6 @@ function getRealName(csn, artifactName) {
|
|
|
86
85
|
if (parts.length === 1)
|
|
87
86
|
return artifactName;
|
|
88
87
|
|
|
89
|
-
|
|
90
88
|
const namespace = getNamespace(csn, artifactName);
|
|
91
89
|
const startIndex = namespace ? namespace.split('.').length : 0;
|
|
92
90
|
let indexOfLastParent = startIndex;
|
|
@@ -129,7 +127,6 @@ function getParentContextName(csn, artifactName) {
|
|
|
129
127
|
if (art && (art.kind === 'context' || art.kind === 'service'))
|
|
130
128
|
return name;
|
|
131
129
|
}
|
|
132
|
-
|
|
133
130
|
return null;
|
|
134
131
|
}
|
|
135
132
|
|
|
@@ -404,8 +401,7 @@ function getSqlSnippets(options, obj) {
|
|
|
404
401
|
*
|
|
405
402
|
* @callback renderPart
|
|
406
403
|
* @param {object|array} expression
|
|
407
|
-
* @
|
|
408
|
-
* @this {{inline: Boolean, nestedExpr: Boolean}}
|
|
404
|
+
* @this {ExpressionRenderer}
|
|
409
405
|
* @returns {string}
|
|
410
406
|
*/
|
|
411
407
|
|
|
@@ -415,7 +411,27 @@ function getSqlSnippets(options, obj) {
|
|
|
415
411
|
*
|
|
416
412
|
* @typedef {object} ExpressionConfiguration
|
|
417
413
|
* @property {(x: any) => string} finalize The final function to call on the expression(-string) before returning
|
|
418
|
-
* @property {renderPart}
|
|
414
|
+
* @property {renderPart} typeCast
|
|
415
|
+
* @property {renderPart} val
|
|
416
|
+
* @property {renderPart} enum
|
|
417
|
+
* @property {renderPart} ref
|
|
418
|
+
* @property {renderPart} aliasOnly
|
|
419
|
+
* @property {renderPart} windowFunction
|
|
420
|
+
* @property {renderPart} func
|
|
421
|
+
* @property {renderPart} xpr
|
|
422
|
+
* @property {renderPart} SELECT
|
|
423
|
+
* @property {renderPart} SET
|
|
424
|
+
* @property {Function} [visitExpr]
|
|
425
|
+
* @property {Function} [renderExpr]
|
|
426
|
+
* @property {Function} [renderSubExpr]
|
|
427
|
+
* @property {boolean} [isNestedXpr]
|
|
428
|
+
* @property {CdlRenderEnvironment} [env]
|
|
429
|
+
*/
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @typedef {object} ExpressionRenderer
|
|
433
|
+
* @property {(x: any) => string} finalize The final function to call on the expression(-string) before returning
|
|
434
|
+
* @property {renderPart} typeCast
|
|
419
435
|
* @property {renderPart} val
|
|
420
436
|
* @property {renderPart} enum
|
|
421
437
|
* @property {renderPart} ref
|
|
@@ -425,94 +441,128 @@ function getSqlSnippets(options, obj) {
|
|
|
425
441
|
* @property {renderPart} xpr
|
|
426
442
|
* @property {renderPart} SELECT
|
|
427
443
|
* @property {renderPart} SET
|
|
428
|
-
* @property {
|
|
429
|
-
* @property {
|
|
444
|
+
* @property {Function} visitExpr
|
|
445
|
+
* @property {Function} renderExpr
|
|
446
|
+
* @property {Function} renderSubExpr
|
|
447
|
+
* @property {boolean} isNestedXpr
|
|
448
|
+
* @property {CdlRenderEnvironment} env
|
|
430
449
|
*/
|
|
431
450
|
|
|
451
|
+
/**
|
|
452
|
+
* If `xpr` has a `cast` property, return a copy without it, otherwise return `xpr`.
|
|
453
|
+
* Useful for removing e.g. top-level CDL-style casts that should not be rendered as CAST().
|
|
454
|
+
*
|
|
455
|
+
* @param xpr
|
|
456
|
+
*/
|
|
457
|
+
function withoutCast(xpr) {
|
|
458
|
+
return !xpr.cast ? xpr : { ...xpr, cast: undefined };
|
|
459
|
+
}
|
|
460
|
+
|
|
432
461
|
/**
|
|
433
462
|
* Render an expression (including paths and values) or condition 'x'.
|
|
434
463
|
* (no trailing LF, don't indent if inline)
|
|
435
464
|
*
|
|
436
|
-
* @param {ExpressionConfiguration}
|
|
437
|
-
* @returns {
|
|
465
|
+
* @param {ExpressionConfiguration} rendererBase
|
|
466
|
+
* @returns {ExpressionRenderer} Expression rendering utility
|
|
438
467
|
*/
|
|
439
|
-
function
|
|
468
|
+
function createExpressionRenderer(rendererBase) {
|
|
469
|
+
const renderer = Object.create(rendererBase);
|
|
470
|
+
renderer.visitExpr = visitExpr;
|
|
440
471
|
/**
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
* @todo Reuse this with toCdl
|
|
445
|
-
* @param {Array|object|string} expr Expression to render
|
|
446
|
-
* @param {object} env Render environment
|
|
447
|
-
* @param {boolean} [inline=true] Whether to render the expression inline
|
|
448
|
-
* @param {boolean} [nestedExpr=false] Whether to treat the expression as nested
|
|
449
|
-
* @param {boolean} [alwaysRenderCast=false] Whether to _always_ render SQL-style casts, even if `nestedExpr === false`.
|
|
450
|
-
* Note: This is a hack for casts() inside groupBy.
|
|
451
|
-
* @returns {string} Rendered expression
|
|
472
|
+
* @param {any} x
|
|
473
|
+
* @param {CdlRenderEnvironment} env
|
|
452
474
|
*/
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
475
|
+
renderer.renderExpr = function renderExpr(x, env) {
|
|
476
|
+
/** @type {ExpressionRenderer} */
|
|
477
|
+
const renderObj = Object.create(renderer);
|
|
478
|
+
renderObj.env = env || this?.env;
|
|
479
|
+
// The outermost expression is not nested. All `.xpr` inside `expr`
|
|
480
|
+
// are nested. This information is used for adding parentheses around
|
|
481
|
+
// expressions (see `this.xpr()`).
|
|
482
|
+
renderObj.isNestedXpr = false;
|
|
483
|
+
return renderObj.visitExpr(x);
|
|
484
|
+
};
|
|
485
|
+
/**
|
|
486
|
+
* @param {any} x
|
|
487
|
+
* @param {CdlRenderEnvironment} env
|
|
488
|
+
*/
|
|
489
|
+
renderer.renderSubExpr = function renderSubExpr(x, env) {
|
|
490
|
+
/** @type {ExpressionRenderer} */
|
|
491
|
+
const renderObj = Object.create(renderer);
|
|
492
|
+
renderObj.env = env || this?.env;
|
|
493
|
+
renderObj.isNestedXpr = true;
|
|
494
|
+
return renderObj.visitExpr(x);
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
return renderer;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Render an expression (including paths and values) or condition 'x'.
|
|
503
|
+
* (no trailing LF, don't indent if inline)
|
|
504
|
+
*
|
|
505
|
+
* `this` must refer to an object of type `ExpressionRenderer`, see
|
|
506
|
+
* `createExpressionRenderer()`
|
|
507
|
+
*
|
|
508
|
+
* @param {any} x (Sub-)Expression to render
|
|
509
|
+
*
|
|
510
|
+
* @this ExpressionRenderer
|
|
511
|
+
* @returns {string} Rendered expression
|
|
512
|
+
*/
|
|
513
|
+
function visitExpr(x) {
|
|
514
|
+
if (Array.isArray(x)) {
|
|
515
|
+
// Compound expression, e.g. for on- or where-conditions.
|
|
516
|
+
// If xpr is part of an array, it's always a nested xpr,
|
|
517
|
+
// e.g. CSN for `(1=1 or 2=2) and 3=3`.
|
|
518
|
+
const tokens = x.map(item => this.renderSubExpr(item));
|
|
519
|
+
return beautifyExprArray(tokens);
|
|
520
|
+
}
|
|
521
|
+
else if (typeof x !== 'object' || x === null) {
|
|
464
522
|
// Not a literal value but part of an operator, function etc - just leave as it is
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
else if (x.SET) {
|
|
505
|
-
return renderer.SET.call({ inline, nestedExpr }, x, env);
|
|
506
|
-
}
|
|
507
|
-
else if (x.as && x.cast && x.cast.type && x.cast.target) {
|
|
508
|
-
return renderer.aliasOnly.call({ inline, nestedExpr }, x, env);
|
|
509
|
-
}
|
|
523
|
+
return this.finalize(x);
|
|
524
|
+
}
|
|
525
|
+
else if (x.cast?.type && !x.cast.target) {
|
|
526
|
+
return this.typeCast(x);
|
|
527
|
+
}
|
|
528
|
+
else if (x.list) {
|
|
529
|
+
// Render as non-nested expr.
|
|
530
|
+
return `(${x.list.map(item => this.renderExpr(item)).join(', ')})`;
|
|
531
|
+
}
|
|
532
|
+
else if (x.val !== undefined) {
|
|
533
|
+
return this.val(x);
|
|
534
|
+
}
|
|
535
|
+
else if (x['#']) {
|
|
536
|
+
// Enum symbol
|
|
537
|
+
return this.enum(x);
|
|
538
|
+
}
|
|
539
|
+
else if (x.ref) {
|
|
540
|
+
// Reference: Array of path steps, possibly preceded by ':'
|
|
541
|
+
return this.ref(x);
|
|
542
|
+
}
|
|
543
|
+
else if (x.func) {
|
|
544
|
+
// Function call, possibly with args (use '=>' for named args)
|
|
545
|
+
if (x.xpr)
|
|
546
|
+
return this.windowFunction(x);
|
|
547
|
+
return this.func(x);
|
|
548
|
+
}
|
|
549
|
+
else if (x.xpr) {
|
|
550
|
+
return this.xpr(x);
|
|
551
|
+
}
|
|
552
|
+
else if (x.SELECT) {
|
|
553
|
+
return this.SELECT(x);
|
|
554
|
+
}
|
|
555
|
+
else if (x.SET) {
|
|
556
|
+
return this.SET(x);
|
|
557
|
+
}
|
|
558
|
+
else if (x.as) {
|
|
559
|
+
return this.aliasOnly(x);
|
|
560
|
+
}
|
|
510
561
|
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
};
|
|
562
|
+
throw new ModelError(`renderExpr(): Unknown expression: ${JSON.stringify(x)}`);
|
|
514
563
|
}
|
|
515
564
|
|
|
565
|
+
|
|
516
566
|
/**
|
|
517
567
|
* @typedef CdlRenderEnvironment Rendering environment used throughout the render process.
|
|
518
568
|
*
|
|
@@ -531,8 +581,7 @@ function getExpressionRenderer(renderer) {
|
|
|
531
581
|
|
|
532
582
|
module.exports = {
|
|
533
583
|
renderFunc,
|
|
534
|
-
|
|
535
|
-
beautifyExprArray,
|
|
584
|
+
createExpressionRenderer,
|
|
536
585
|
getNamespace,
|
|
537
586
|
getRealName,
|
|
538
587
|
addIntermediateContexts,
|
|
@@ -544,4 +593,5 @@ module.exports = {
|
|
|
544
593
|
findElement,
|
|
545
594
|
funcWithoutParen,
|
|
546
595
|
getSqlSnippets,
|
|
596
|
+
withoutCast,
|
|
547
597
|
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Encapsulate all the functions needed to render SQL ALTER/DROP/ADD statements.
|
|
5
|
+
*/
|
|
6
|
+
class DeltaRenderer {
|
|
7
|
+
constructor(options, scopedFunctions) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.scopedFunctions = scopedFunctions;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Render column additions as SQL. Checks for duplicate elements.
|
|
14
|
+
*/
|
|
15
|
+
addColumnsFromElementStrings(artifactName, eltStrings) {
|
|
16
|
+
return eltStrings.map(eltString => `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ADD ${eltString};`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Render column additions as SQL. Checks for duplicate elements.
|
|
21
|
+
*/
|
|
22
|
+
addColumnsFromElementsObj(artifactName, elementsObj, env, duplicateChecker) {
|
|
23
|
+
// Only extend with 'ADD' for elements/associations
|
|
24
|
+
// TODO: May also include 'RENAME' at a later stage
|
|
25
|
+
const alterEnv = this.scopedFunctions.activateAlterMode(env);
|
|
26
|
+
const elements = Object.entries(elementsObj)
|
|
27
|
+
.map(([ name, elt ]) => this.scopedFunctions.renderElement(artifactName, name, elt, duplicateChecker, null, alterEnv))
|
|
28
|
+
.filter(s => s !== '');
|
|
29
|
+
|
|
30
|
+
if (elements.length)
|
|
31
|
+
return this.addColumnsFromElementStrings(artifactName, elements);
|
|
32
|
+
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* By default, we don't support rendering association-alters - only for HANA
|
|
38
|
+
*/
|
|
39
|
+
addAssociations() {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Render key addition as SQL.
|
|
45
|
+
*/
|
|
46
|
+
addKey(artifactName, elementsObj) {
|
|
47
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ADD ${this.primaryKey(elementsObj)};` ];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Render column removals as SQL.
|
|
52
|
+
*/
|
|
53
|
+
dropColumns(artifactName, sqlIds) {
|
|
54
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} DROP ${sqlIds.join(', ')};` ];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* No associations by default - only for HANA.
|
|
59
|
+
*/
|
|
60
|
+
dropAssociation() {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Render primary-key removals as SQL.
|
|
66
|
+
*/
|
|
67
|
+
dropKey(artifactName) {
|
|
68
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} DROP PRIMARY KEY;` ];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Render column modifications as SQL.
|
|
73
|
+
*/
|
|
74
|
+
alterColumns(artifactName, columnName, delta, definitionsStr) {
|
|
75
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER (${definitionsStr});` ];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Render primary keys as SQL.
|
|
80
|
+
*/
|
|
81
|
+
primaryKey(elementsObj) {
|
|
82
|
+
const primaryKeys = Object.keys(elementsObj)
|
|
83
|
+
.filter(name => elementsObj[name].key && !elementsObj[name].virtual)
|
|
84
|
+
.map(name => this.scopedFunctions.quoteSqlId(name))
|
|
85
|
+
.join(', ');
|
|
86
|
+
return primaryKeys && `PRIMARY KEY(${primaryKeys})`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Render entity-comment modifications as SQL.
|
|
91
|
+
*/
|
|
92
|
+
alterEntityComment(artifactName, comment) {
|
|
93
|
+
return [ `COMMENT ON TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} IS ${this.comment(comment)};` ];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Render column-comment modifications as SQL.
|
|
98
|
+
*/
|
|
99
|
+
alterColumnComment(artifactName, columnName, comment) {
|
|
100
|
+
return [ `COMMENT ON COLUMN ${this.scopedFunctions.renderArtifactName(artifactName)}.${columnName} IS ${this.comment(comment)};` ];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Render comment string.
|
|
105
|
+
*/
|
|
106
|
+
comment(comment) {
|
|
107
|
+
return comment && this.scopedFunctions.renderStringForSql(this.scopedFunctions.getHanaComment({ doc: comment }), this.options.sqlDialect) || 'NULL';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Alter SQL snippet for entity.
|
|
112
|
+
*/
|
|
113
|
+
alterEntitySqlSnippet(artifactName, snippet) {
|
|
114
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ${snippet};` ];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Concatenate multiple statements which are to be treated as one by the API caller.
|
|
119
|
+
*/
|
|
120
|
+
concat(...statements) {
|
|
121
|
+
return [ statements.join('\n') ];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
class DeltaRendererHana extends DeltaRenderer {
|
|
126
|
+
/**
|
|
127
|
+
* Render column additions as HANA SQL. Checks for duplicate elements.
|
|
128
|
+
*/
|
|
129
|
+
addColumnsFromElementStrings(artifactName, eltStrings) {
|
|
130
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ADD (${eltStrings.join(', ')});` ];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Render association additions as HANA SQL.
|
|
135
|
+
* TODO duplicity check
|
|
136
|
+
*/
|
|
137
|
+
addAssociations(artifactName, elementsObj, env) {
|
|
138
|
+
return Object.entries(elementsObj)
|
|
139
|
+
.map(([ name, elt ]) => this.scopedFunctions.renderAssociationElement(name, elt, env))
|
|
140
|
+
.filter(s => s !== '')
|
|
141
|
+
.map(eltStr => `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ADD ASSOCIATION (${eltStr});`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Render column removals as HANA SQL.
|
|
146
|
+
*/
|
|
147
|
+
dropColumns(artifactName, sqlIds) {
|
|
148
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} DROP (${sqlIds.join(', ')});` ];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Render association removals as HANA SQL.
|
|
153
|
+
*/
|
|
154
|
+
dropAssociation(artifactName, sqlId) {
|
|
155
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} DROP ASSOCIATION ${sqlId};` ];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class DeltaRendererPostgres extends DeltaRenderer {
|
|
160
|
+
/**
|
|
161
|
+
* Render primary-key removals as SQL.
|
|
162
|
+
* @todo tableName is escaped - we cannot simply add _pkey
|
|
163
|
+
*/
|
|
164
|
+
dropKey(artifactName) {
|
|
165
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} DROP CONSTRAINT ${this.scopedFunctions.renderArtifactName(`${artifactName}_pkey`)};` ];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Render column removals as SQL.
|
|
170
|
+
*/
|
|
171
|
+
dropColumns(artifactName, sqlIds) {
|
|
172
|
+
return sqlIds.map(sqlId => `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} DROP ${sqlId};`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Render column modifications as Postgres SQL - no ( ), special NOT NULL.
|
|
177
|
+
*/
|
|
178
|
+
alterColumns(artifactName, columnName, delta, definitionsStr) {
|
|
179
|
+
const sqls = [];
|
|
180
|
+
if (delta.new.notNull === true || delta.new.key === true)
|
|
181
|
+
definitionsStr = definitionsStr.replace(' NOT NULL', ''); // TODO: Is this robust enough?
|
|
182
|
+
else if (delta.new.notNull === false || delta.new.$notNull === false)
|
|
183
|
+
definitionsStr = definitionsStr.replace(' NULL', ''); // TODO: Is this robust enough?
|
|
184
|
+
|
|
185
|
+
sqls.push(`ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER ${definitionsStr};`);
|
|
186
|
+
if (delta.new.notNull && !delta.old.notNull)
|
|
187
|
+
sqls.push(`ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER ${columnName} SET NOT NULL;`);
|
|
188
|
+
else if (delta.old.notNull && !delta.new.notNull)
|
|
189
|
+
sqls.push(`ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER ${columnName} DROP NOT NULL;`);
|
|
190
|
+
|
|
191
|
+
return sqls;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
class DeltaRendererH2 extends DeltaRenderer {
|
|
196
|
+
/**
|
|
197
|
+
* Render column modifications as H2 SQL - no ().
|
|
198
|
+
*/
|
|
199
|
+
alterColumns(artifactName, columnName, delta, definitionsStr) {
|
|
200
|
+
return [ `ALTER TABLE ${this.scopedFunctions.renderArtifactName(artifactName)} ALTER ${definitionsStr};` ];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Return an object encapsulating the render-functions for ALTER/DROP for a given db dialect.
|
|
206
|
+
*
|
|
207
|
+
* @param {CSN.Options} options
|
|
208
|
+
* @param {object} scopedFunctions
|
|
209
|
+
* @returns {DeltaRenderer}
|
|
210
|
+
*/
|
|
211
|
+
function getDeltaRenderer(options, scopedFunctions) {
|
|
212
|
+
switch (options.sqlDialect) {
|
|
213
|
+
case 'hana':
|
|
214
|
+
return new DeltaRendererHana(options, scopedFunctions);
|
|
215
|
+
case 'h2':
|
|
216
|
+
return new DeltaRendererH2(options, scopedFunctions);
|
|
217
|
+
case 'postgres':
|
|
218
|
+
return new DeltaRendererPostgres(options, scopedFunctions);
|
|
219
|
+
default:
|
|
220
|
+
return new DeltaRenderer(options, scopedFunctions);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
module.exports = {
|
|
226
|
+
getDeltaRenderer,
|
|
227
|
+
};
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"template-curly-spacing":["error", "never"],
|
|
11
11
|
"complexity": ["warn", 40],
|
|
12
12
|
"max-len": "off",
|
|
13
|
+
// there seem to be false positives
|
|
14
|
+
"jsdoc/require-returns-check": "off",
|
|
13
15
|
// Don't enforce stupid descriptions
|
|
14
16
|
"jsdoc/require-param-description": "off",
|
|
15
17
|
"jsdoc/require-returns-description": "off",
|
|
@@ -1,38 +1,4 @@
|
|
|
1
1
|
{
|
|
2
2
|
"root": true,
|
|
3
|
-
"
|
|
4
|
-
"extends": ["../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended", "plugin:jsdoc/recommended"],
|
|
5
|
-
"rules": {
|
|
6
|
-
"prefer-const": "error",
|
|
7
|
-
"quotes": ["error", "single", "avoid-escape"],
|
|
8
|
-
"prefer-template": "error",
|
|
9
|
-
"no-trailing-spaces": "error",
|
|
10
|
-
"template-curly-spacing":["error", "never"],
|
|
11
|
-
"complexity": ["warn", 30],
|
|
12
|
-
"max-len": "off",
|
|
13
|
-
// Don't enforce stupid descriptions
|
|
14
|
-
"jsdoc/require-param-description": "off",
|
|
15
|
-
"jsdoc/require-returns-description": "off",
|
|
16
|
-
// Sometimes if-else's are more specific
|
|
17
|
-
"sonarjs/prefer-single-boolean-return": "off",
|
|
18
|
-
// Very whiny and nitpicky
|
|
19
|
-
"sonarjs/cognitive-complexity": "off",
|
|
20
|
-
// Does not recognize TS types
|
|
21
|
-
"jsdoc/no-undefined-types": "off",
|
|
22
|
-
// Whiny and annoying
|
|
23
|
-
"sonarjs/no-duplicate-string": "off"
|
|
24
|
-
},
|
|
25
|
-
"parserOptions": {
|
|
26
|
-
"ecmaVersion": 2020,
|
|
27
|
-
"sourceType": "script"
|
|
28
|
-
},
|
|
29
|
-
"env": {
|
|
30
|
-
"es2020": true,
|
|
31
|
-
"node": true
|
|
32
|
-
},
|
|
33
|
-
"settings": {
|
|
34
|
-
"jsdoc": {
|
|
35
|
-
"mode": "typescript"
|
|
36
|
-
}
|
|
37
|
-
}
|
|
3
|
+
"extends": ["../db/.eslintrc.json"]
|
|
38
4
|
}
|
|
@@ -213,14 +213,14 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
213
213
|
const skipPersNameKinds = {'service':1, 'context':1, 'namespace':1, 'annotation':1, 'action':1, 'function':1};
|
|
214
214
|
forEachDefinition(csn, (def, defName) => {
|
|
215
215
|
// Resolve annotation shorthands for entities, types, annotations, ...
|
|
216
|
-
renameShorthandAnnotations(def);
|
|
216
|
+
renameShorthandAnnotations(def, ['definitions', defName ]);
|
|
217
217
|
|
|
218
218
|
// Annotate artifacts with their DB names if requested.
|
|
219
219
|
// Skip artifacts that have no DB equivalent anyway
|
|
220
220
|
if (options.sqlMapping && !(def.kind in skipPersNameKinds))
|
|
221
221
|
def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
|
|
222
222
|
|
|
223
|
-
forEachMemberRecursively(def, (member, memberName, propertyName) => {
|
|
223
|
+
forEachMemberRecursively(def, (member, memberName, propertyName, path) => {
|
|
224
224
|
// Annotate elements, foreign keys, parameters, etc. with their DB names if requested
|
|
225
225
|
// Only these are actually required and don't annotate virtual elements in entities or types
|
|
226
226
|
// as they have no DB representation (although in views)
|
|
@@ -233,7 +233,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
233
233
|
annotateCoreComputed(member);
|
|
234
234
|
|
|
235
235
|
// Resolve annotation shorthands for elements, actions, action parameters
|
|
236
|
-
renameShorthandAnnotations(member);
|
|
236
|
+
renameShorthandAnnotations(member, path);
|
|
237
237
|
|
|
238
238
|
// - If the association target is annotated with @cds.odata.valuelist, annotate the
|
|
239
239
|
// association with @Common.ValueList.viaAssociation
|
|
@@ -298,7 +298,7 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
298
298
|
|
|
299
299
|
// Rename shorthand annotations within artifact or element 'node' according to a builtin
|
|
300
300
|
// list.
|
|
301
|
-
function renameShorthandAnnotations(node) {
|
|
301
|
+
function renameShorthandAnnotations(node, path) {
|
|
302
302
|
// FIXME: Verify this list - are they all still required? Do we need any more?
|
|
303
303
|
const mappings = {
|
|
304
304
|
'@label': '@Common.Label',
|
|
@@ -368,22 +368,29 @@ function transform4odataWithCsn(inputModel, options) {
|
|
|
368
368
|
if(!node.enum && node.type && !isBuiltinType(node.type))
|
|
369
369
|
typeDef = csn.definitions[node.type];
|
|
370
370
|
if(typeDef.enum) {
|
|
371
|
-
|
|
371
|
+
const enumValue = [];
|
|
372
|
+
for(const enumSymbol in typeDef.enum) {
|
|
372
373
|
const enumSymbolDef = typeDef.enum[enumSymbol];
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
result
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
374
|
+
if(enumSymbolDef.val === null)
|
|
375
|
+
info('odata-enum-value-type', path,
|
|
376
|
+
{name: enumSymbol, value: null, anno: '@Valiation.AllowedValues' },
|
|
377
|
+
'Value $(VALUE) for enum element $(NAME) not added to $(ANNO)');
|
|
378
|
+
else {
|
|
379
|
+
const result = { '@Core.SymbolicName': enumSymbol };
|
|
380
|
+
if (enumSymbolDef.val !== undefined)
|
|
381
|
+
result.Value = enumSymbolDef.val;
|
|
382
|
+
else if (node.type && node.type === 'cds.String')
|
|
383
|
+
// the symbol is used as value only for type 'cds.String'
|
|
384
|
+
result.Value = enumSymbol;
|
|
385
|
+
// Can't rely that @description has already been renamed to @Core.Description
|
|
386
|
+
// Eval description according to precedence (doc comment must be considered already in Odata transformer
|
|
387
|
+
// as in contrast to the other doc commments as it is used to annotate the @Validation.AllowedValues)
|
|
388
|
+
const desc = enumSymbolDef['@Core.Description'] || enumSymbolDef['@description'] || enumSymbolDef.doc;
|
|
389
|
+
if (desc)
|
|
390
|
+
result['@Core.Description'] = desc;
|
|
391
|
+
enumValue.push(result);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
387
394
|
setAnnotation(node, '@Validation.AllowedValues', enumValue);
|
|
388
395
|
}
|
|
389
396
|
}
|