@sap/cds-compiler 3.6.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +109 -1
  2. package/README.md +3 -0
  3. package/bin/cdsc.js +12 -5
  4. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  5. package/doc/CHANGELOG_BETA.md +35 -2
  6. package/doc/CHANGELOG_DEPRECATED.md +2 -2
  7. package/doc/DeprecatedOptions_v2.md +1 -1
  8. package/doc/NameResolution.md +1 -1
  9. package/lib/api/main.js +63 -23
  10. package/lib/api/options.js +1 -0
  11. package/lib/api/validate.js +5 -0
  12. package/lib/base/dictionaries.js +15 -3
  13. package/lib/base/keywords.js +2 -0
  14. package/lib/base/message-registry.js +120 -34
  15. package/lib/base/messages.js +51 -27
  16. package/lib/base/model.js +4 -2
  17. package/lib/base/shuffle.js +2 -1
  18. package/lib/checks/arrayOfs.js +1 -1
  19. package/lib/checks/defaultValues.js +1 -1
  20. package/lib/checks/elements.js +29 -1
  21. package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/onConditions.js +15 -9
  25. package/lib/checks/sql-snippets.js +2 -2
  26. package/lib/checks/types.js +5 -1
  27. package/lib/checks/validator.js +7 -3
  28. package/lib/compiler/assert-consistency.js +42 -26
  29. package/lib/compiler/base.js +50 -4
  30. package/lib/compiler/builtins.js +17 -8
  31. package/lib/compiler/checks.js +241 -246
  32. package/lib/compiler/define.js +113 -146
  33. package/lib/compiler/extend.js +889 -383
  34. package/lib/compiler/finalize-parse-cdl.js +5 -58
  35. package/lib/compiler/index.js +1 -1
  36. package/lib/compiler/kick-start.js +7 -8
  37. package/lib/compiler/populate.js +297 -293
  38. package/lib/compiler/propagator.js +27 -18
  39. package/lib/compiler/resolve.js +146 -463
  40. package/lib/compiler/shared.js +36 -79
  41. package/lib/compiler/tweak-assocs.js +30 -28
  42. package/lib/compiler/utils.js +31 -5
  43. package/lib/edm/annotations/genericTranslation.js +131 -59
  44. package/lib/edm/annotations/preprocessAnnotations.js +3 -0
  45. package/lib/edm/csn2edm.js +22 -5
  46. package/lib/edm/edm.js +6 -4
  47. package/lib/edm/edmAnnoPreprocessor.js +1 -0
  48. package/lib/edm/edmPreprocessor.js +42 -26
  49. package/lib/gen/Dictionary.json +38 -2
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -1
  52. package/lib/gen/languageLexer.js +1 -1
  53. package/lib/gen/languageParser.js +4828 -4472
  54. package/lib/inspect/inspectPropagation.js +20 -34
  55. package/lib/json/from-csn.js +140 -44
  56. package/lib/json/to-csn.js +114 -122
  57. package/lib/language/errorStrategy.js +2 -0
  58. package/lib/language/genericAntlrParser.js +156 -36
  59. package/lib/language/language.g4 +100 -58
  60. package/lib/language/textUtils.js +13 -0
  61. package/lib/main.d.ts +43 -3
  62. package/lib/main.js +4 -2
  63. package/lib/model/csnRefs.js +15 -3
  64. package/lib/model/csnUtils.js +12 -74
  65. package/lib/model/revealInternalProperties.js +4 -2
  66. package/lib/modelCompare/compare.js +2 -1
  67. package/lib/optionProcessor.js +3 -0
  68. package/lib/render/manageConstraints.js +5 -2
  69. package/lib/render/toCdl.js +216 -104
  70. package/lib/render/toHdbcds.js +2 -9
  71. package/lib/render/toRename.js +14 -51
  72. package/lib/render/toSql.js +4 -3
  73. package/lib/render/utils/common.js +9 -5
  74. package/lib/transform/braceExpression.js +6 -0
  75. package/lib/transform/db/assertUnique.js +2 -1
  76. package/lib/transform/db/expansion.js +2 -0
  77. package/lib/transform/db/flattening.js +37 -36
  78. package/lib/transform/db/rewriteCalculatedElements.js +600 -0
  79. package/lib/transform/db/transformExists.js +4 -0
  80. package/lib/transform/db/views.js +40 -37
  81. package/lib/transform/forOdataNew.js +20 -15
  82. package/lib/transform/forRelationalDB.js +58 -41
  83. package/lib/transform/odata/typesExposure.js +50 -15
  84. package/lib/transform/parseExpr.js +16 -8
  85. package/lib/transform/transformUtilsNew.js +42 -14
  86. package/lib/transform/translateAssocsToJoins.js +60 -37
  87. package/lib/transform/universalCsn/coreComputed.js +15 -7
  88. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  89. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,106 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 3.8.0 - 2023-03-27
11
+
12
+ ### Added
13
+
14
+ - compiler:
15
+ + Table aliases for sub-queries are no longer required.
16
+ + A time zone designator can now be used in time literals, e.g. `time'16:41:01+01:30'`or `time'16:41:01Z'`.
17
+ - Calculated elements ("on-read") are now enabled per default.
18
+ When used in views, they are replaced by their value, for example:
19
+ ```cds
20
+ entity E { one: Integer; two = one + 1; };
21
+ entity P as projection on E { two };
22
+ // P is the same as:
23
+ entity P as projection on E { one + 1 as two };
24
+ ```
25
+ This allows to define calculations centrally at the entity, which can be used by
26
+ other views.
27
+ - In CDL, a ternary operator was added as a shortcut for `CASE` expressions:
28
+ `a ? b : c` is a shortcut for `CASE WHEN a THEN b ELSE c END`. There is no CSN
29
+ representation. The ternary operator is rendered as a `CASE` expression in CSN.
30
+ - In CDL and CSN, `not null` can now also be used in type definitions.
31
+ - In CDL (and CSN as before), elements can be defined without specifying a type.
32
+
33
+ ### Changed
34
+
35
+ - API: We now report an error for most backends, if the input CSN has
36
+ `meta.flavor == 'xtended'`, because only client/inferred CSN is supported.
37
+ - Update OData vocabularies 'PersonalData', 'UI'
38
+ - for.odata: Shortcut annotations `@label`, `@title`, `@description`, `@readonly` are no longer
39
+ removed from the OData processed CSN.
40
+ - to.cdl:
41
+ + Annotation arrays are split into multiple lines, if a single line would be too long.
42
+ + Nested `SELECT`s are put into separate lines to make them more readable.
43
+ + (Annotation) paths are quoted less often.
44
+ - to.sql: The list of reserved SAP HANA identifiers was updated (for smart quoting).
45
+
46
+ ### Fixed
47
+
48
+ - The CSN parser now accepts bare `list`s in `columns[]`, similar to the CDL parser.
49
+ - to.cdl:
50
+ + Delimited identifiers in filters are now surrounded by spaces if necessary, to avoid `]]`
51
+ being interpreted as an escaped bracket.
52
+ - to.edm(x):
53
+ + Remove empty `Edm.EntityContainer` again. Removal of an empty entity container has been
54
+ revoked with [3.5.0](#fixed-7) which was wrong. An empty container must not be rendered
55
+ as it is not spec compliant.
56
+ + Correctly resolve chained enum symbols.
57
+ + Fix a program abort during structured rendering in combination with `--odata-foreign-keys`
58
+ and foreign keys in structured types.
59
+ + Correctly render paths to nested foreign keys as primary key in structured mode with
60
+ `--odata-foreign-keys`.
61
+ - to.hdi/to.sql/to.edm(x):
62
+ + Reject unmanaged associations as ON-condition path end points.
63
+ + Fix bug in message rendering for tuple expansion.
64
+ + Correctly detect invalid @sql.append/prepend in projections.
65
+ - to.hdi/to.sql: The list of SAP HANA keywords was updated to the latest version.
66
+
67
+ ### Removed
68
+
69
+ - for.odata: Undocumented shortcut annotation `@important` has been removed.
70
+
71
+
72
+ ## Version 3.7.2 - 2023-02-24
73
+
74
+ ### Fixed
75
+
76
+ - CSN parser: Structured annotations containing `=` were accidentally interpreted as expressions,
77
+ even though the corresponding beta flag was not set.
78
+
79
+
80
+ ## Version 3.7.0 - 2023-02-22
81
+
82
+ ### Added
83
+
84
+ - Several `annotate` statement can append/prepend values
85
+ to the same array-valued annotation without an `anno-duplicate` error,
86
+ even if there is no `using from` dependency between the involved sources
87
+ - SQL methods such as `point.ST_X()` can be used in views.
88
+ - The SQL `new` keyword can be used for `ST_*` types such as `new ST_POINT('Point(0.5 0.5)') )`
89
+
90
+ ### Changed
91
+
92
+ - Update OData vocabularies 'Common', 'Core', 'Measures', 'PDF', 'UI'.
93
+ - to.edm(x): Empty complex types are no longer warned about as they are allowed.
94
+
95
+ ### Fixed
96
+
97
+ - `parse.cql` and `parse.expr` no longer ignore type arguments such as `cast(field as String(12))`.
98
+ One argument is interpreted as `length` and two are interpreted as `precision` and `scale`, similar to
99
+ how custom types and their arguments are interpreted.
100
+ - Previously, the compiler could not always find a unique redirection target if there were
101
+ one direct projection on the model target and two or more projections on that projection.
102
+ - The performance of compiler-checks for deeply nested expressions/queries has been improved
103
+ - Fix various bugs in Association to Join translation:
104
+ + Recursive `$self` dereferencing
105
+ + Correct resolution of table alias in non-bijective `$self` backlink associations in combination with
106
+ explicit redirection.
107
+ - to.edm(x): Process value help list convenience annotations on unbound action parameters.
108
+
109
+
10
110
  ## Version 3.6.2 - 2023-02-06
11
111
 
12
112
  ### Fixed
@@ -365,6 +465,14 @@ The compiler behavior concerning `beta` features can change at any time without
365
465
  it is now only allowed for `count`, `min`, `max`, `sum`, `avg`, `stddev`, `var`.
366
466
  - All non-SNAPI options.
367
467
 
468
+ ## Version 2.15.10 - 2023-01-26
469
+
470
+ ### Fixed
471
+
472
+ - If an entity with parameters is auto-exposed, the generated projection now has
473
+ the same formal parameters and its query forwards these parameters to the origin entity.
474
+ - to.edm(x): Respect record type hint `$Type` in EDM JSON as full qualified `@type` URI property.
475
+
368
476
  ## Version 2.15.8 - 2022-08-02
369
477
 
370
478
  ### Fixed
@@ -406,7 +514,7 @@ The compiler behavior concerning `beta` features can change at any time without
406
514
  + Enforce `odata-spec-violation-key-null` on explicit foreign keys of managed primary key associations.
407
515
  + Proxies/service cross references are no longer created for associations with arbitrary ON conditions.
408
516
  Only managed or `$self` backlink association targets are proxy/service cross reference candidates.
409
- + Explicit foreign keys of a managed association that are not a primary key in the target are exposed in the the proxy.
517
+ + Explicit foreign keys of a managed association that are not a primary key in the target are exposed in the proxy.
410
518
  + If an association is primary key, the resulting navigation property is set to `Nullable:false` in structured mode.
411
519
 
412
520
  ## Version 2.15.0 - 2022-05-06
package/README.md CHANGED
@@ -29,6 +29,9 @@ Or maintain your package.json dependencies as follows:
29
29
 
30
30
  Please refer to the [official CDS documentation](https://cap.cloud.sap/docs/cds/).
31
31
 
32
+ ## How to Obtain Support
33
+
34
+ In case you find a bug, please report an [incident](https://cap.cloud.sap/docs/resources/#reporting-incidents) on SAP Support Portal.
32
35
 
33
36
  ## License
34
37
 
package/bin/cdsc.js CHANGED
@@ -239,10 +239,13 @@ function displayUsage( error, helpText, code ) {
239
239
  // Display help text first, error at the end (more readable, no scrolling)
240
240
  out.write(`${helpText}\n`);
241
241
  if (error) {
242
- if (error instanceof Array)
243
- out.write(`${error.map(err => `cdsc: ERROR: ${err}`).join('\n')}\n`);
244
- else
242
+ if (error instanceof Array) {
243
+ const errors = error.map(err => `cdsc: ERROR: ${err}`).join('\n');
244
+ out.write(`${errors}\n`);
245
+ }
246
+ else {
245
247
  out.write(`cdsc: ERROR: ${error}\n`);
248
+ }
246
249
  }
247
250
  throw new ProcessExitError(code);
248
251
  }
@@ -350,6 +353,9 @@ function executeCommandLine( command, options, args ) {
350
353
  options.odataFormat = 'structured';
351
354
  options.odataContainment = true;
352
355
  }
356
+ if (options.odataVocRefs && typeof options.odataVocRefs === 'string')
357
+ options.odataVocRefs = JSON.parse(options.odataVocRefs);
358
+
353
359
  const csn = options.directBackend ? model : compactModel(model, options);
354
360
  if (options.csn) {
355
361
  const odataCsn = main.for.odata(csn, options);
@@ -389,8 +395,8 @@ function executeCommandLine( command, options, args ) {
389
395
  // Execute the command line option 'manageConstraints' and display the results.
390
396
  function manageConstraints( model ) {
391
397
  const csn = options.directBackend ? model : compactModel(model, options);
392
- const alterConstraintsResult = alterConstraintsWithCsn(csn, options);
393
398
  const { src } = options || {};
399
+ const alterConstraintsResult = alterConstraintsWithCsn(csn, options);
394
400
  Object.keys(alterConstraintsResult).forEach((id) => {
395
401
  const renderedConstraintStatement = alterConstraintsResult[id];
396
402
  if (src === 'hdi')
@@ -499,8 +505,9 @@ function executeCommandLine( command, options, args ) {
499
505
  .forEach(msg => log(msg));
500
506
  }
501
507
  else if (options.noMessageContext) {
508
+ // TODO: currently, ‹↓› = "downgradable" is only shown here
502
509
  messages.filter(msg => (messageLevels[msg.severity] <= options.warning))
503
- .forEach(msg => log(main.messageString(msg, normalizeFilename, !!options.noMessageId)));
510
+ .forEach(msg => log(main.messageString(msg, normalizeFilename, !!options.noMessageId, false, options.testMode && 'compile'))); // TODO: use module name
504
511
  }
505
512
  else {
506
513
  // Contains file-contents that are split at '\n'. Try to avoid multiple `.split()` calls.
@@ -777,7 +777,7 @@ synchronously.
777
777
 
778
778
  - Introduce annotation `@cds.redirection.target`.
779
779
  With value `false`, the projection is not considered an implicit redirection target;
780
- with value `true`, is is considered a “preferred” redirection target.
780
+ with value `true`, is considered a “preferred” redirection target.
781
781
 
782
782
  ## Version 1.49.2 - 2021-02-16
783
783
 
@@ -1313,7 +1313,7 @@ synchronously.
1313
1313
 
1314
1314
  - HANA/SQL: Validate ON conditions of mixin association definitions in all subqueries
1315
1315
 
1316
- - OData V2: Assign various `@sap` annotations to the `<edmx:EnitySet>` and `<edmx:AssociationSet>`
1316
+ - OData V2: Assign various `@sap` annotations to the `<edmx:entitySet>` and `<edmx:AssociationSet>`
1317
1317
  if such annotations are assigned to CDS entities or associations.
1318
1318
 
1319
1319
  - OData V4 Structured: Omit foreign keys of managed associations that establish the containment relationship to
@@ -1445,7 +1445,7 @@ synchronously.
1445
1445
 
1446
1446
  ### Fixed
1447
1447
 
1448
- - Compiler: `type of <unmanaged assocation>` is now handled correctly by raising an error.
1448
+ - Compiler: `type of <unmanaged association>` is now handled correctly by raising an error.
1449
1449
 
1450
1450
  ## Version 1.25.0 - 2020-04-09
1451
1451
 
@@ -1629,7 +1629,7 @@ Fixes
1629
1629
  * In the Hana/Sql backend, correctly resolve forward `on` condition when using
1630
1630
  mixin association that backlinks to an unrelated 3rd party entity and association.
1631
1631
  * Raise a warning if the element of the forward association and the element of
1632
- the query source do not not originate from the same defining entity. Raise an
1632
+ the query source do not originate from the same defining entity. Raise an
1633
1633
  error if the element of the forward association cannot be found in the query
1634
1634
  source or is ambiguous.
1635
1635
  * Correctly create localization views with compiled model as input;
@@ -2267,7 +2267,7 @@ Changes
2267
2267
  _Update to v1.12.1_ if you experience problems – an inherited `@cds.autoexpose` had not been considered.
2268
2268
  * In `toSql` and `toHana` errors are raised
2269
2269
  + for duplicate definitions of elements that differ only in spelling,
2270
- + if the the entity is not `abstract` or annotated with any `@cds.persistence` set to true and
2270
+ + if the entity is not `abstract` or annotated with any `@cds.persistence` set to true and
2271
2271
  - an element is typed to be an `array of` a `type`,
2272
2272
  - an implicit managed composition has cardinality to many.
2273
2273
  * Raise a warning if an element is to be `localized` which is not of type `cds.String`.
@@ -2489,7 +2489,7 @@ Fixes
2489
2489
  while rewriting the `on` conditition of a projected association.
2490
2490
  * Apply OData specific checks (e.g. that all elements of an entity must have a type)
2491
2491
  applied only to objects that are exposed in a service.
2492
- * When generating SQL for SQLite, replace the the special variables `$now`, `$user.id`
2492
+ * When generating SQL for SQLite, replace the special variables `$now`, `$user.id`
2493
2493
  and `$user.locale` by `CURRENT_TIMESTAMP`, `'$user.id'`, and `'EN'`, respectively.
2494
2494
  * Issue a warning for conflicting cardinality declarations (e.g. `association[1] to many ...`).
2495
2495
  * Handle filters with cardinality correctly when translating associations to joins.
@@ -8,6 +8,39 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
  **Don't use `beta` fixes, changes and features in productive mode.**
10
10
 
11
+ ## Version 3.8.0 - 2023-03-27
12
+
13
+ ### Added `v4preview`
14
+
15
+ This beta flags tries to imitate cds-compiler v4 behavior.
16
+ This includes new compiler messages as well as potentially breaking changes.
17
+
18
+ Enable this beta flag to ease upgrading to cds-compiler v4.
19
+
20
+ This flag does not guarantee full compatibility with v4, but only
21
+ helps to identify potential issues as soon as possible.
22
+
23
+ ### Removed `calculatedElements`
24
+
25
+ Now enabled per default.
26
+
27
+ ## Version 3.7.0 - 2023-02-22
28
+
29
+ ### Added `calculatedElements`
30
+
31
+ Allows to define calculated elements in entities and aspects. When used in views, they
32
+ are replaced by their value, for example:
33
+
34
+ ```cds
35
+ entity E { one: Integer; two = one + 1; };
36
+ entity P as projection on E { two };
37
+ // P is the same as:
38
+ entity P as projection on E { one + 1 as two };
39
+ ```
40
+
41
+ This allows to define calculations centrally at the entity, which can be used by
42
+ other views.
43
+
11
44
  ## Version 3.5.0 - 2022-12-07
12
45
 
13
46
  ### Added `odataTerms`
@@ -250,7 +283,7 @@ Render JOIN cardinality in native HANA association if provided. If no cardinalit
250
283
 
251
284
  ### Removed `aspectCompositions`
252
285
 
253
- Aspect compositions aka managed compositions are now avaible without beta option.
286
+ Aspect compositions aka managed compositions are now available without beta option.
254
287
  _Warning_: the CSN representation can still change.
255
288
 
256
289
  ## Version 1.31.0 - 2020-06-26
@@ -286,5 +319,5 @@ This option does not enable:
286
319
  ### Added `keyRefError`
287
320
 
288
321
  Always signal an error (instead of just a warning in some cases),
289
- if not all references in the `keys` of an managed associations
322
+ if not all references in the `keys` of a managed associations
290
323
  are projected in the new target.
@@ -15,7 +15,7 @@ and several new features are not available.**
15
15
 
16
16
  ### Added `autoCorrectOrderBySourceRefs`
17
17
 
18
- When this option is set, calling `compile` auto-corrects direct `order by`
18
+ When this option is set, calling `compile` autocorrects direct `order by`
19
19
  source element references without table alias for SELECT queries by adding the
20
20
  table alias to the `ref`.
21
21
 
@@ -153,7 +153,7 @@ Render old and broken temporal EDM API.
153
153
  ### Added `noElementsExpansion`
154
154
 
155
155
  When setting it, association in sub elements are not automatically redirected,
156
- and the sub elements cannot be annotated indivually.
156
+ and the sub elements cannot be annotated individually.
157
157
 
158
158
  Do not use this. Setting it might avoid some compile errors,
159
159
  but in most cases the reported errors are rightly reported.
@@ -51,7 +51,7 @@ The compiled model contains the following generated entities:
51
51
  * the auto-exposed projection `our.Service.Proj.texts`
52
52
  which is a composition target of `our.Service.Proj:texts`
53
53
 
54
- For the following sub sections (and in general), is is important to understand that
54
+ For the following subsections (and in general), it is important to understand that
55
55
  you can define all auto-exposed entities yourself (well, they are not
56
56
  _auto_-exposed anymore)
57
57
  _without_ any difference in the compiled model
@@ -600,7 +600,7 @@ The list of search environments is created as follows:
600
600
 
601
601
  The above mentioned `:`-escape mechanism leads to the following name resolution:
602
602
 
603
- * The first lexical search environment is the the environment containing all parameter names of the current view.
603
+ * The first lexical search environment is the environment containing all parameter names of the current view.
604
604
  * The following search environments are the usual ones from the "main artifact name resolution";
605
605
  constant values can be accessed this way (_TODO_: probably not now).
606
606
 
package/lib/api/main.js CHANGED
@@ -63,6 +63,7 @@ function attachTransformerCharacteristics( csn, transformation, options,
63
63
  relevant[name] = options[name];
64
64
  }
65
65
 
66
+ // eslint-disable-next-line sonarjs/no-empty-collection
66
67
  for (const name of relevantGeneralOptions ) {
67
68
  if (options[name] !== undefined)
68
69
  relevant[name] = options[name];
@@ -334,7 +335,7 @@ function remapName( key, csn, filter = () => true ) {
334
335
  * @param {CSN.Model} csn A clean input CSN representing the desired "after-image"
335
336
  * @param {HdiOptions} options Options
336
337
  * @param {CSN.Model} beforeImage A db-transformed CSN representing the "before-image", or null in case no such image
337
- * is known, i.e. for the very first migration step
338
+ * is known, i.e. for the very first migration step.
338
339
  * @returns {object} An object with three properties:
339
340
  * - afterImage: A db-transformed CSN representing the "after-image"
340
341
  * - drops: An array of SQL statements to drop views/tables
@@ -763,7 +764,7 @@ function flattenResultStructure( toProcess ) {
763
764
  /**
764
765
  * Print args to stderr if CDSC_TRACE_API is set
765
766
  *
766
- * @param {...any} args
767
+ * @param {...any} args to be logged to stderr
767
768
  */
768
769
  function traceApi( ...args ) {
769
770
  if (process?.env?.CDSC_TRACE_API !== undefined) {
@@ -811,18 +812,21 @@ function publishCsnProcessor( processor, _name ) {
811
812
  /**
812
813
  * Function that calls the processor and re-compiles in case of internal errors
813
814
  *
814
- * @param {object} csn CSN
815
- * @param {object} options Options
815
+ * @param {CSN.Model} csn CSN
816
+ * @param {CSN.Options} options Options
816
817
  * @param {any} args Any additional arguments
817
818
  * @returns {any} What ever the processor returns
818
819
  */
819
820
  function api( csn, options = {}, ...args ) {
820
821
  try {
821
- if (options.deprecated) {
822
- const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
822
+ const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
823
+ if (options.deprecated)
823
824
  checkRemovedDeprecatedFlags( options, messageFunctions );
824
- }
825
- checkOutdatedOptions( options );
825
+
826
+ checkOutdatedOptions( options, messageFunctions );
827
+ checkCsnFlavor( csn, options, messageFunctions, _name );
828
+
829
+ messageFunctions.throwWithError();
826
830
 
827
831
  timetrace.timetrace.start(_name);
828
832
  const result = processor( csn, options, ...args );
@@ -860,37 +864,73 @@ const oldBackendOptionNames = [ 'toSql', 'toOdata', 'toHana', 'forHana' ];
860
864
  * - toOdata/toSql/toHana/forHana -> now flat options
861
865
  *
862
866
  * @param {CSN.Options} options Backend options
867
+ * @param {object} messageFunctions Functions returned by makeMessageFunction()
863
868
  */
864
- function checkOutdatedOptions( options ) {
865
- const { error, throwWithError } = messages.makeMessageFunction(null, options, 'api');
866
-
869
+ function checkOutdatedOptions( options, messageFunctions ) {
867
870
  // This error has been emitted once, we don't need to emit it again.
868
- if (options.messages?.some(m => m.messageId === 'api-invalid-option')) {
869
- throwWithError();
871
+ if (options.messages?.some(m => m.messageId === 'api-invalid-option' || m.messageId === 'api-invalid-variable-replacement'))
870
872
  return;
871
- }
873
+
872
874
 
873
875
  for (const name of oldBackendOptionNames) {
874
876
  if (typeof options[name] === 'object') // may be a boolean due to internal options
875
- error('api-invalid-option', null, { '#': 'std', name });
877
+ messageFunctions.error('api-invalid-option', null, { '#': 'deprecated', name });
876
878
  }
877
879
 
878
880
  if (options.magicVars)
879
- error('api-invalid-option', null, { '#': 'magicVars' });
881
+ messageFunctions.error('api-invalid-option', null, { '#': 'magicVars', prop: 'magicVars', otherprop: 'variableReplacements' });
880
882
 
881
883
  // Don't check `options.magicVars`. It's likely that the user renamed `magicVars` but
882
884
  // forgot about user -> $user and locale -> $user.locale
883
- if (options.variableReplacements?.user)
884
- error('api-invalid-option', null, { '#': 'user' });
885
- if (options.variableReplacements?.locale)
886
- error('api-invalid-option', null, { '#': 'locale' });
885
+ if (options.variableReplacements?.user) {
886
+ messageFunctions.error('api-invalid-variable-replacement', null, {
887
+ '#': 'user', option: 'variableReplacements', prop: '$user', otherprop: 'user',
888
+ });
889
+ }
890
+ if (options.variableReplacements?.locale) {
891
+ messageFunctions.error('api-invalid-variable-replacement', null, {
892
+ '#': 'locale', option: 'variableReplacements', prop: '$user.locale', otherprop: 'locale',
893
+ });
894
+ }
887
895
 
888
896
  forEachKey(options.variableReplacements || {}, (name) => {
889
- if (!name.startsWith('$') && name !== 'user' && name !== 'locale')
890
- error('api-invalid-option', null, { '#': 'noDollar', name });
897
+ if (!name.startsWith('$') && name !== 'user' && name !== 'locale') {
898
+ messageFunctions.error('api-invalid-variable-replacement', null, {
899
+ '#': 'noDollar', option: 'variableReplacements', code: '$', name,
900
+ });
901
+ }
891
902
  });
903
+ }
892
904
 
893
- throwWithError();
905
+ /**
906
+ * Checks that the given CSN is usable by our backends, e.g. that
907
+ * the CSN is not a gensrc (a.k.a. xtended) for most backends.
908
+ *
909
+ * For reference, cds-compiler/cds-dk CSN flavor map:
910
+ * - client -> inferred
911
+ * - gensrc -> xtended
912
+ * - parseCdl -> parsed
913
+ *
914
+ * If this function becomes more complex (e.g. more module conditions),
915
+ * move it from then generic api wrapper to the individual module.
916
+ *
917
+ * TODO: The compiler does not set any marker in `meta`; we use the umbrella one
918
+ * for easier debugging.
919
+ *
920
+ * @param {CSN.Model} csn User CSN
921
+ * @param {CSN.Options} options User options
922
+ * @param {object} messageFunctions Functions returned by makeMessageFunction()
923
+ * @param {string} module Backend module, e.g. to.cdl or to.sql
924
+ */
925
+ function checkCsnFlavor( csn, options, messageFunctions, module ) {
926
+ if (module === 'to.cdl' || !csn)
927
+ return; // to.cdl allows every CSN flavor
928
+
929
+ if (csn.meta?.flavor === 'xtended') {
930
+ // TODO: csn.meta?.flavor === 'parsed'; currently used by `@sap/cds` tests.
931
+ messageFunctions.error('api-unsupported-csn-flavor', null, { name: module, option: csn.meta?.flavor },
932
+ 'Module $(NAME) expects a client/inferred CSN, not $(OPTION)');
933
+ }
894
934
  }
895
935
 
896
936
  /**
@@ -38,6 +38,7 @@ const publicOptionsNewAPI = [
38
38
  'odataProxies',
39
39
  'odataXServiceRefs',
40
40
  'odataV2PartialConstr',
41
+ 'odataVocRefs',
41
42
  'service',
42
43
  'serviceNames',
43
44
  //
@@ -82,6 +82,11 @@ const validators = {
82
82
  sqlMapping: generateStringValidator([ 'plain', 'quoted', 'hdbcds' ]),
83
83
  odataVersion: generateStringValidator([ 'v2', 'v4' ]),
84
84
  odataFormat: generateStringValidator([ 'flat', 'structured' ]),
85
+ odataVocRefs: {
86
+ validate: val => (typeof val === 'object' && !Array.isArray(val)),
87
+ expected: () => 'type JSON object',
88
+ found: val => `type ${ Array.isArray(val) ? 'JSON array' : typeof val }`,
89
+ },
85
90
  service: {
86
91
  validate: val => typeof val === 'string',
87
92
  expected: () => 'type string',
@@ -81,17 +81,29 @@ function dictAddArray( dict, name, entry, messageCallback ) {
81
81
  return entry;
82
82
  }
83
83
 
84
+ function dictFirst( dict ) {
85
+ if (!dict)
86
+ return dict;
87
+ for (const name in dict) {
88
+ return dict[name];
89
+ }
90
+ return undefined;
91
+ }
92
+
84
93
  // Push `entry` to the array value with key `name` in the dictionary `dict`.
85
- function pushToDict( dict, name, entry ) {
94
+ function pushToDict( dict, name, ...entries ) {
86
95
  if (dict[name])
87
- dict[name].push( entry );
96
+ dict[name].push( ...entries );
97
+ else if (name.charAt(0) !== '_')
98
+ dict[name] = entries;
88
99
  else
89
- dict[name] = [ entry ];
100
+ Object.defineProperty( dict, name, { value: entries, configurable: true, writable: true } );
90
101
  }
91
102
 
92
103
  module.exports = {
93
104
  dictAdd,
94
105
  dictForEach,
95
106
  dictAddArray,
107
+ dictFirst,
96
108
  pushToDict,
97
109
  };
@@ -579,6 +579,7 @@ module.exports = {
579
579
  'ST_ALPHASHAPEAGGR',
580
580
  'ST_ALPHASHAPEAREAAGGR',
581
581
  'ST_ALPHASHAPEEDGEAGGR',
582
+ 'ST_ASGEOJSON',
582
583
  'ST_ASMVT',
583
584
  'ST_ASSVGAGGR',
584
585
  'ST_CIRCULARSTRING',
@@ -606,6 +607,7 @@ module.exports = {
606
607
  'ST_INTERSECTIONAGGR',
607
608
  'ST_LINESTRING',
608
609
  'ST_MAKELINE',
610
+ 'ST_MAKELINEAGGR',
609
611
  'ST_MAKEPOLYGON',
610
612
  'ST_MEMORY_LOB',
611
613
  'ST_MULTILINESTRING',