@sap/cds-compiler 3.4.0 → 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.
Files changed (60) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/bin/cdsc.js +3 -4
  3. package/bin/cdshi.js +19 -6
  4. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  5. package/lib/api/main.js +6 -3
  6. package/lib/base/message-registry.js +61 -38
  7. package/lib/base/messages.js +7 -3
  8. package/lib/checks/.eslintrc.json +2 -0
  9. package/lib/compiler/.eslintrc.json +4 -1
  10. package/lib/compiler/assert-consistency.js +8 -6
  11. package/lib/compiler/builtins.js +13 -13
  12. package/lib/compiler/checks.js +50 -33
  13. package/lib/compiler/define.js +9 -6
  14. package/lib/compiler/extend.js +83 -55
  15. package/lib/compiler/finalize-parse-cdl.js +3 -3
  16. package/lib/compiler/populate.js +16 -5
  17. package/lib/compiler/propagator.js +22 -29
  18. package/lib/compiler/resolve.js +2 -15
  19. package/lib/compiler/shared.js +4 -4
  20. package/lib/compiler/utils.js +14 -0
  21. package/lib/edm/annotations/genericTranslation.js +68 -56
  22. package/lib/edm/csn2edm.js +214 -174
  23. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  24. package/lib/edm/edmInboundChecks.js +2 -2
  25. package/lib/edm/edmPreprocessor.js +1 -1
  26. package/lib/edm/edmUtils.js +3 -3
  27. package/lib/gen/Dictionary.json +176 -8
  28. package/lib/gen/language.checksum +1 -1
  29. package/lib/gen/language.interp +2 -1
  30. package/lib/gen/languageParser.js +4776 -4513
  31. package/lib/json/from-csn.js +21 -16
  32. package/lib/json/to-csn.js +37 -41
  33. package/lib/language/.eslintrc.json +4 -1
  34. package/lib/language/antlrParser.js +5 -2
  35. package/lib/language/docCommentParser.js +6 -6
  36. package/lib/language/errorStrategy.js +43 -23
  37. package/lib/language/genericAntlrParser.js +54 -95
  38. package/lib/language/language.g4 +92 -66
  39. package/lib/language/multiLineStringParser.js +2 -2
  40. package/lib/language/textUtils.js +2 -2
  41. package/lib/model/csnRefs.js +5 -0
  42. package/lib/modelCompare/compare.js +2 -2
  43. package/lib/modelCompare/utils/.eslintrc.json +22 -0
  44. package/lib/modelCompare/utils/filter.js +99 -0
  45. package/lib/render/.eslintrc.json +1 -0
  46. package/lib/render/toCdl.js +96 -127
  47. package/lib/render/toHdbcds.js +38 -35
  48. package/lib/render/toSql.js +75 -161
  49. package/lib/render/utils/common.js +133 -83
  50. package/lib/render/utils/delta.js +227 -0
  51. package/lib/transform/db/.eslintrc.json +2 -0
  52. package/lib/transform/draft/.eslintrc.json +1 -35
  53. package/lib/transform/forOdataNew.js +33 -22
  54. package/lib/transform/forRelationalDB.js +1 -1
  55. package/lib/transform/localized.js +9 -8
  56. package/lib/transform/odata/typesExposure.js +26 -4
  57. package/lib/transform/transformUtilsNew.js +15 -8
  58. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
  59. package/package.json +2 -3
  60. 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
- * @param {CdlRenderEnvironment} env
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} explicitTypeCast
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 {boolean} [inline]
429
- * @property {boolean} [nestedExpr]
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} renderer
437
- * @returns {Function} Rendered expression
465
+ * @param {ExpressionConfiguration} rendererBase
466
+ * @returns {ExpressionRenderer} Expression rendering utility
438
467
  */
439
- function getExpressionRenderer(renderer) {
468
+ function createExpressionRenderer(rendererBase) {
469
+ const renderer = Object.create(rendererBase);
470
+ renderer.visitExpr = visitExpr;
440
471
  /**
441
- * Render an expression (including paths and values) or condition 'x'.
442
- * (no trailing LF, don't indent if inline)
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
- return function renderExpr(expr, env, inline = true, nestedExpr = false, alwaysRenderCast = false) {
454
- // Compound expression
455
- if (Array.isArray(expr)) {
456
- const tokens = expr.map(item => renderExpr(item, env, inline, nestedExpr));
457
- return beautifyExprArray(tokens);
458
- }
459
- else if (typeof expr === 'object' && expr !== null) {
460
- if ((nestedExpr || alwaysRenderCast) && expr.cast && expr.cast.type && !expr.cast.target)
461
- return renderer.explicitTypeCast.call({ inline, nestedExpr }, expr, env);
462
- return renderExprObject(expr);
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
- // FIXME: For the sake of simplicity, we should get away from all this uppercasing in toSql
466
- return renderer.finalize.call({ inline, nestedExpr }, expr, env);
467
-
468
-
469
- /**
470
- * Various special cases represented as objects
471
- *
472
- * @param {object} x Expression
473
- * @returns {string} String representation of the expression
474
- */
475
- function renderExprObject(x) {
476
- if (x.list) { // TODO: Does this still exist?
477
- return `(${x.list.map(item => renderExpr(item, env, inline, false)).join(', ')})`;
478
- }
479
- else if (x.val !== undefined) {
480
- return renderer.val.call({ inline, nestedExpr }, x, env);
481
- }
482
- // Enum symbol
483
- else if (x['#']) {
484
- return renderer.enum.call({ inline, nestedExpr }, x, env);
485
- }
486
- // Reference: Array of path steps, possibly preceded by ':'
487
- else if (x.ref) {
488
- return renderer.ref.call({ inline, nestedExpr }, x, env);
489
- }
490
- // Function call, possibly with args (use '=>' for named args)
491
- else if (x.func) {
492
- if (x.xpr)
493
- return renderer.windowFunction.call({ inline, nestedExpr }, x, env);
494
- return renderer.func.call({ inline, nestedExpr }, x, env);
495
- }
496
- // Nested expression
497
- else if (x.xpr) {
498
- return renderer.xpr.call({ inline, nestedExpr }, x, env);
499
- }
500
- // Sub-select
501
- else if (x.SELECT) {
502
- return renderer.SELECT.call({ inline, nestedExpr }, x, env);
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
- throw new ModelError(`Unknown expression: ${JSON.stringify(x)}`);
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
- getExpressionRenderer,
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
- "plugins": ["sonarjs", "jsdoc"],
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
  }
@@ -109,7 +109,9 @@ function transform4odataWithCsn(inputModel, options) {
109
109
  // @ts-ignore
110
110
  const externalServices = services.filter(serviceName => csn.definitions[serviceName]['@cds.external']);
111
111
  // @ts-ignore
112
- const isExternalServiceMember = (_art, name) => externalServices.includes(getServiceName(name));
112
+ const isExternalServiceMember = (art, name) => {
113
+ return !!(externalServices.includes(getServiceName(name)) || (art && art['@cds.external']))
114
+ }
113
115
 
114
116
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
115
117
  enrichUniversalCsn(csn, options);
@@ -163,10 +165,12 @@ function transform4odataWithCsn(inputModel, options) {
163
165
  // Needs to happen exactly between flattenAllStructStepsInRefs and flattenElements to keep model resolvable.
164
166
  // OData doesn't resolve type chains after the first 'items'
165
167
  flattening.resolveTypeReferences(csn, options, resolved, '_',
166
- { skip: [ 'action', 'aspect', 'event', 'function', 'type'], skipArtifact: isExternalServiceMember, skipStandard: { items: true } });
168
+ { skip: [ 'action', 'aspect', 'event', 'function', 'type'],
169
+ skipArtifact: isExternalServiceMember, skipStandard: { items: true } });
167
170
  // No structured elements exists anymore
168
171
  flattening.flattenElements(csn, options, '_', error,
169
- { skip: ['action', 'aspect', 'event', 'function', 'type'], skipArtifact: isExternalServiceMember,
172
+ { skip: ['action', 'aspect', 'event', 'function', 'type'],
173
+ skipArtifact: isExternalServiceMember,
170
174
  skipStandard: { items: true }, // don't drill further into .items
171
175
  skipDict: { actions: true } }); // don't drill further into .actions -> bound actions, action-artifacts are handled by skip
172
176
  }
@@ -209,14 +213,14 @@ function transform4odataWithCsn(inputModel, options) {
209
213
  const skipPersNameKinds = {'service':1, 'context':1, 'namespace':1, 'annotation':1, 'action':1, 'function':1};
210
214
  forEachDefinition(csn, (def, defName) => {
211
215
  // Resolve annotation shorthands for entities, types, annotations, ...
212
- renameShorthandAnnotations(def);
216
+ renameShorthandAnnotations(def, ['definitions', defName ]);
213
217
 
214
218
  // Annotate artifacts with their DB names if requested.
215
219
  // Skip artifacts that have no DB equivalent anyway
216
220
  if (options.sqlMapping && !(def.kind in skipPersNameKinds))
217
221
  def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
218
222
 
219
- forEachMemberRecursively(def, (member, memberName, propertyName) => {
223
+ forEachMemberRecursively(def, (member, memberName, propertyName, path) => {
220
224
  // Annotate elements, foreign keys, parameters, etc. with their DB names if requested
221
225
  // Only these are actually required and don't annotate virtual elements in entities or types
222
226
  // as they have no DB representation (although in views)
@@ -229,7 +233,7 @@ function transform4odataWithCsn(inputModel, options) {
229
233
  annotateCoreComputed(member);
230
234
 
231
235
  // Resolve annotation shorthands for elements, actions, action parameters
232
- renameShorthandAnnotations(member);
236
+ renameShorthandAnnotations(member, path);
233
237
 
234
238
  // - If the association target is annotated with @cds.odata.valuelist, annotate the
235
239
  // association with @Common.ValueList.viaAssociation
@@ -294,7 +298,7 @@ function transform4odataWithCsn(inputModel, options) {
294
298
 
295
299
  // Rename shorthand annotations within artifact or element 'node' according to a builtin
296
300
  // list.
297
- function renameShorthandAnnotations(node) {
301
+ function renameShorthandAnnotations(node, path) {
298
302
  // FIXME: Verify this list - are they all still required? Do we need any more?
299
303
  const mappings = {
300
304
  '@label': '@Common.Label',
@@ -364,22 +368,29 @@ function transform4odataWithCsn(inputModel, options) {
364
368
  if(!node.enum && node.type && !isBuiltinType(node.type))
365
369
  typeDef = csn.definitions[node.type];
366
370
  if(typeDef.enum) {
367
- let enumValue = Object.keys(typeDef.enum).map(enumSymbol => {
371
+ const enumValue = [];
372
+ for(const enumSymbol in typeDef.enum) {
368
373
  const enumSymbolDef = typeDef.enum[enumSymbol];
369
- let result = { '@Core.SymbolicName': enumSymbol };
370
- if (enumSymbolDef.val !== undefined)
371
- result.Value = enumSymbolDef.val;
372
- else if (node.type && node.type === 'cds.String')
373
- // the symbol is used as value only for type 'cds.String'
374
- result.Value = enumSymbol;
375
- // Can't rely that @description has already been renamed to @Core.Description
376
- // Eval description according to precedence (doc comment must be considered already in Odata transformer
377
- // as in contrast to the other doc commments as it is used to annotate the @Validation.AllowedValues)
378
- const desc = enumSymbolDef['@Core.Description'] || enumSymbolDef['@description'] || enumSymbolDef.doc;
379
- if (desc)
380
- result['@Core.Description'] = desc;
381
- return result;
382
- });
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
+ }
383
394
  setAnnotation(node, '@Validation.AllowedValues', enumValue);
384
395
  }
385
396
  }
@@ -705,7 +705,7 @@ function transformForRelationalDBWithCsn(inputModel, options, moduleName) {
705
705
  setProp(node, '$renamed', 'cds.UUID');
706
706
  }
707
707
 
708
- if(options.sqlDialect === 'h2' && val === 'cds.Decimal' && !node.scale) {
708
+ if(options.sqlDialect === 'h2' && val === 'cds.Decimal' && node.scale === undefined) {
709
709
  node[key] = 'cds.DecimalFloat'; // cds.Decimal and cds.Decimal(p) should map do DECFLOAT for h2
710
710
  }
711
711