@sap/cds-compiler 4.6.2 → 4.7.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 (69) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/bin/cds_update_identifiers.js +6 -2
  3. package/bin/cdsc.js +1 -1
  4. package/doc/CHANGELOG_ARCHIVE.md +9 -9
  5. package/doc/CHANGELOG_BETA.md +6 -0
  6. package/lib/api/main.js +56 -9
  7. package/lib/api/options.js +6 -3
  8. package/lib/api/validate.js +20 -29
  9. package/lib/base/message-registry.js +27 -3
  10. package/lib/base/messages.js +8 -3
  11. package/lib/base/model.js +2 -0
  12. package/lib/checks/dbFeatureFlags.js +28 -0
  13. package/lib/checks/elements.js +81 -13
  14. package/lib/checks/enricher.js +3 -2
  15. package/lib/checks/validator.js +38 -4
  16. package/lib/compiler/assert-consistency.js +4 -4
  17. package/lib/compiler/checks.js +5 -4
  18. package/lib/compiler/define.js +2 -2
  19. package/lib/compiler/generate.js +2 -1
  20. package/lib/compiler/propagator.js +3 -11
  21. package/lib/compiler/shared.js +2 -1
  22. package/lib/compiler/tweak-assocs.js +43 -24
  23. package/lib/edm/annotations/edmJson.js +3 -0
  24. package/lib/edm/annotations/genericTranslation.js +156 -106
  25. package/lib/edm/annotations/preprocessAnnotations.js +11 -14
  26. package/lib/edm/csn2edm.js +27 -24
  27. package/lib/edm/edm.js +8 -8
  28. package/lib/edm/edmPreprocessor.js +135 -37
  29. package/lib/edm/edmUtils.js +20 -7
  30. package/lib/gen/Dictionary.json +1 -0
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +9 -11
  33. package/lib/gen/languageParser.js +5942 -5446
  34. package/lib/json/to-csn.js +7 -114
  35. package/lib/language/genericAntlrParser.js +106 -48
  36. package/lib/model/cloneCsn.js +203 -0
  37. package/lib/model/csnRefs.js +11 -3
  38. package/lib/model/csnUtils.js +42 -85
  39. package/lib/optionProcessor.js +2 -2
  40. package/lib/render/manageConstraints.js +1 -1
  41. package/lib/render/toCdl.js +133 -88
  42. package/lib/render/toHdbcds.js +1 -5
  43. package/lib/render/toSql.js +7 -9
  44. package/lib/render/utils/common.js +9 -16
  45. package/lib/transform/addTenantFields.js +277 -102
  46. package/lib/transform/db/applyTransformations.js +14 -9
  47. package/lib/transform/db/backlinks.js +2 -1
  48. package/lib/transform/db/constraints.js +60 -82
  49. package/lib/transform/db/expansion.js +6 -6
  50. package/lib/transform/db/featureFlags.js +5 -0
  51. package/lib/transform/db/flattening.js +4 -4
  52. package/lib/transform/db/killAnnotations.js +1 -0
  53. package/lib/transform/db/rewriteCalculatedElements.js +2 -2
  54. package/lib/transform/db/transformExists.js +12 -0
  55. package/lib/transform/db/views.js +5 -2
  56. package/lib/transform/draft/odata.js +7 -6
  57. package/lib/transform/effective/associations.js +2 -1
  58. package/lib/transform/effective/main.js +3 -2
  59. package/lib/transform/effective/types.js +6 -3
  60. package/lib/transform/forOdata.js +39 -24
  61. package/lib/transform/forRelationalDB.js +34 -27
  62. package/lib/transform/localized.js +29 -9
  63. package/lib/transform/odata/flattening.js +419 -0
  64. package/lib/transform/odata/toFinalBaseType.js +95 -15
  65. package/lib/transform/odata/typesExposure.js +9 -7
  66. package/lib/transform/transformUtils.js +7 -6
  67. package/lib/transform/translateAssocsToJoins.js +3 -3
  68. package/lib/utils/objectUtils.js +14 -0
  69. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,42 @@
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 4.7.4 - 2024-02-27
11
+
12
+ ### Fixed
13
+
14
+ - OData: Fixed infinite recursion in draft handling for nested recursive compositions.
15
+
16
+
17
+ ## Version 4.7.2 - 2024-02-26
18
+
19
+ ### Fixed
20
+
21
+ - Restored compatibility with `@sap/cds-dk` for Java runtime
22
+
23
+ ## Version 4.7.0 - 2024-02-23
24
+
25
+ ### Added
26
+
27
+ - compiler: Virtual elements can now be referenced in expressions in annotation
28
+
29
+ ### Changed
30
+
31
+ - Update OData vocabularies: 'Authorization', 'Common', 'Hierarchy', 'UI'.
32
+ - to.edm(x): `@cds.odata.valuelist` renders all non-key elements of the value help list as `ValueListProperty`.
33
+
34
+ ### Fixed
35
+
36
+ - CDL parser: a `select` after two or more `(`s in an expression or condition
37
+ could cause some constructs in that query, such as `virtual`, to be not properly parsed.
38
+ - compiler: published associations with filters sometimes had the filter applied twice
39
+ if used in inline aspect compositions
40
+ - to.sql|hdi|hdbcds[.migration]:
41
+ + With `withHanaAssociations`: `false`, remove the association elements from the final CSN in order to correctly detect them during migration scenarios and
42
+ with generated `hdbcds`.
43
+ + Skip expensive processing (for calculated elements and nested projections) if the model doesn't use it.
44
+ + Don't greedily set alias on subqueries if not required.
45
+ + Remove bound actions and turn all non-database relevant artifacts into dummies to simplify and shrink CSN.
10
46
 
11
47
  ## Version 4.6.2 - 2024-02-02
12
48
 
@@ -15,6 +51,7 @@ The compiler behavior concerning `beta` features can change at any time without
15
51
  - compiler: Fix incorrect error about type properties if deprecated flag `ignoreSpecifiedQueryElements` is set.
16
52
  - Update OData vocabularies: 'Authorization', 'Common'.
17
53
 
54
+
18
55
  ## Version 4.6.0 - 2024-01-26
19
56
 
20
57
  ### Added
@@ -32,6 +32,9 @@ const path = require('path');
32
32
  const cliArgs = process.argv.slice(2);
33
33
  const filepath = cliArgs[0];
34
34
 
35
+ if (filepath === '--help' || filepath === '-h')
36
+ exitError();
37
+
35
38
  if (cliArgs.length !== 1)
36
39
  exitError(`Expected exactly one argument, ${cliArgs.length} given`);
37
40
 
@@ -123,10 +126,11 @@ function replaceSliceInSource( source, startIndex, endIndex, replaceWith ) {
123
126
  }
124
127
 
125
128
  /**
126
- * @param {string} msg
129
+ * @param {string} [msg]
127
130
  */
128
131
  function exitError( msg ) {
129
- console.error(msg);
132
+ if (msg)
133
+ console.error(msg);
130
134
  usage();
131
135
  process.exit(1);
132
136
  }
package/bin/cdsc.js CHANGED
@@ -559,7 +559,7 @@ function executeCommandLine( command, options, args ) {
559
559
  }
560
560
  else if (!options.parseOnly) { // no output if parseOnly but not rawOutput
561
561
  const csn = compactModel(xsn, options);
562
- if (command === 'toCsn' && options.tenantAsColumn)
562
+ if (command === 'toCsn' && options.tenantDiscriminator)
563
563
  addTenantFields(csn, options);
564
564
  if (command === 'toCsn' && options.withLocalized)
565
565
  addLocalizationViews(csn, options);
@@ -104,7 +104,7 @@ The compiler behaviour concerning `beta` features can change at any time without
104
104
  ### Added
105
105
 
106
106
  - Support arbitrary paths after `$user` - similar to `$session`.
107
- - Support scale `floating` and `variable` for `cds.Decimal` in CDL and CSN. Backend specific handling is descibed in their sections.
107
+ - Support scale `floating` and `variable` for `cds.Decimal` in CDL and CSN. Backend specific handling is described in their sections.
108
108
  - Allow select item wildcard (`*`) in a `select`/`projection` at any position, not just the first.
109
109
 
110
110
  - to.edm(x):
@@ -585,7 +585,7 @@ synchronously.
585
585
  + Localized convenience views are no longer generated by the core compiler but added by the `for.odata`
586
586
  and `to.sql/hdi/hdbcds` processing on demand.
587
587
  + Minimize name clashes when calculating names for auto-exposed entities,
588
- extends the v1 option `dependentAutoexposed` to sub artifacts of entites (see “Added”).
588
+ extends the v1 option `dependentAutoexposed` to sub artifacts of entities (see “Added”).
589
589
  + Ambiguities when redirecting associations now always lead to compile errors;
590
590
  you might want to use the new annotation `@cds.redirection.target` to solve them.
591
591
  + The association `up_` in the calculated entity for managed compositions is now managed.
@@ -1046,7 +1046,7 @@ synchronously.
1046
1046
 
1047
1047
  - OData: Allow the relational comparison of structures or managed associations in an ON condition as described in
1048
1048
  version 1.32.0 - 2020-07-10 (forHana).
1049
- - Allow `Struct:elem` with and without preceeding `type of` as type reference.
1049
+ - Allow `Struct:elem` with and without preceding `type of` as type reference.
1050
1050
 
1051
1051
  ### Fixed
1052
1052
 
@@ -2426,7 +2426,7 @@ Fixes
2426
2426
 
2427
2427
  Features
2428
2428
  * Allow entities to have parameters. They can be referred to inside the query with
2429
- `:Param`. Entites with parameters are not allowed in `toSql` for dialect "sqlite".
2429
+ `:Param`. Entities with parameters are not allowed in `toSql` for dialect "sqlite".
2430
2430
  When generating for HANA, parameters cannot be used in combination with associations:
2431
2431
  an entity with parameters cannot have associations, and an association must not point
2432
2432
  to an entity with parameters.
@@ -2446,7 +2446,7 @@ Changes
2446
2446
  _The severity of these messages will be increased if implicit redirections
2447
2447
  will have been performed by the core compiler._
2448
2448
  * `toHana` and `toSql` now reject entities that only contain unmanaged associations.
2449
- Such entites would lead to a deployment error later.
2449
+ Such entities would lead to a deployment error later.
2450
2450
  * SQL name mapping modes `quoted` and `hdbcds` are only allowed when generating for HANA.
2451
2451
  * In the csn, the csn language version is now stored in the top level attribute `$version`.
2452
2452
  The version information via `version.csn` is deprecated and will be removed in a future
@@ -2685,7 +2685,7 @@ Fixes
2685
2685
  ## Version 1.0.33
2686
2686
 
2687
2687
  Features
2688
- * Allow to extend query entites with actions.
2688
+ * Allow to extend query entities with actions.
2689
2689
  * Allow `select distinct`.
2690
2690
  * With `--tnt-flavor` only: allow to specify (a restricted version of) service include via syntax.
2691
2691
  * (Work in progress): New option `{ dialect: 'hana'|'sqlite' }` for `toSql()`, allowing generation
@@ -2874,7 +2874,7 @@ Fixes
2874
2874
  recognized and led to the generation of incorrect HANA CDS models.
2875
2875
  * We now also allow query entities and their elements to use as type, relaxing
2876
2876
  a check introduces with v1.0.26.
2877
- It needs to be seen whether we allow entites as type only for actions.
2877
+ It needs to be seen whether we allow entities as type only for actions.
2878
2878
 
2879
2879
  ## Version 1.0.27
2880
2880
 
@@ -2972,7 +2972,7 @@ Fixes
2972
2972
 
2973
2973
  Changes
2974
2974
  * The CSN element property `notNull` is not inherited anymore
2975
- if the `select`/`projection` items whose path refering the source element
2975
+ if the `select`/`projection` items whose path referring the source element
2976
2976
  navigates along associations or compositions.
2977
2977
  * Annotation assignments which are placed after the name of `context` or `service` definitions
2978
2978
  must now use the `@(...)` syntax variant if a value is supplied,
@@ -3197,7 +3197,7 @@ Features
3197
3197
 
3198
3198
  Fixes
3199
3199
  * Views are now rendered as EntitySet/EntityType in edmx
3200
- * Abstract entites do not appear as EntitySet/EntityType in the generated edmx
3200
+ * Abstract entities do not appear as EntitySet/EntityType in the generated edmx
3201
3201
  * `--to-hana` now correctly handles type casts in view definitions
3202
3202
  * In the generated edmx for OData V2, inside a `ReferentialConstraint`, the elements `Dependent` and `Principal` now have the correct order
3203
3203
  * Remove attribute `nullable` for function import parameters in edmx generation for OData V2
@@ -8,6 +8,12 @@ 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 4.x.x - 2024-mm-dd
12
+
13
+ ### Added `v5preview`
14
+
15
+ Sneak preview into incompatible changes that are about to be shipped with compiler version 5.
16
+
11
17
  ## Version 4.6.0 - 2024-01-26
12
18
 
13
19
  ### Added `vectorType`
package/lib/api/main.js CHANGED
@@ -23,10 +23,11 @@ const toHdbcds = lazyload('../render/toHdbcds');
23
23
  const baseError = lazyload('../base/error');
24
24
  const csnToEdm = lazyload('../edm/csn2edm');
25
25
  const trace = lazyload('./trace');
26
+ const cloneCsn = lazyload('../model/cloneCsn');
26
27
 
27
28
  const { forEach, forEachKey } = require('../utils/objectUtils');
28
- const { checkRemovedDeprecatedFlags } = require('../base/model');
29
29
  const { makeMessageFunction } = require('../base/messages');
30
+ const { sortCsnForTests } = require('../model/cloneCsn');
30
31
 
31
32
  /**
32
33
  * Return the artifact name for use for the hdbresult object
@@ -138,7 +139,8 @@ function isPreTransformed( csn, transformation ) {
138
139
  */
139
140
  function odataInternal( csn, internalOptions, messageFunctions ) {
140
141
  internalOptions.transformation = 'odata';
141
- const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions, messageFunctions);
142
+ let oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions, messageFunctions);
143
+ oDataCsn = sortCsnForTests(oDataCsn, internalOptions);
142
144
  messageFunctions.setModel(oDataCsn);
143
145
  attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
144
146
  return oDataCsn;
@@ -187,7 +189,7 @@ function csnForSql( csn, internalOptions, messageFunctions ) {
187
189
  const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(
188
190
  csn, internalOptions, messageFunctions
189
191
  );
190
- return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
192
+ return cloneCsn.sortCsnForTests(transformedCsn, internalOptions);
191
193
  }
192
194
 
193
195
  /**
@@ -202,7 +204,7 @@ function csnForSql( csn, internalOptions, messageFunctions ) {
202
204
  */
203
205
  function forSql( csn, options, messageFunctions ) {
204
206
  const internalOptions = prepareOptions.to.sql(options);
205
- return csnForSql(csn, internalOptions, messageFunctions);
207
+ return csnForSql(csn, internalOptions, messageFunctions); // already sorted for test mode
206
208
  }
207
209
 
208
210
  /**
@@ -220,7 +222,7 @@ function forHdi( csn, options, messageFunctions ) {
220
222
  const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(
221
223
  csn, internalOptions, messageFunctions
222
224
  );
223
- return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
225
+ return cloneCsn.sortCsnForTests(transformedCsn, internalOptions);
224
226
  }
225
227
  /**
226
228
  * Transform a CSN like to.hdbcds
@@ -237,7 +239,7 @@ function forHdbcds( csn, options, messageFunctions ) {
237
239
  const hanaCsn = forRelationalDB.transformForRelationalDBWithCsn(
238
240
  csn, internalOptions, messageFunctions
239
241
  );
240
- return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
242
+ return cloneCsn.sortCsnForTests(hanaCsn, internalOptions);
241
243
  }
242
244
 
243
245
  /**
@@ -252,9 +254,17 @@ function forHdbcds( csn, options, messageFunctions ) {
252
254
  function forEffective( csn, options, messageFunctions ) {
253
255
  const internalOptions = prepareOptions.to.sql(options);
254
256
  internalOptions.transformation = 'effective';
257
+ if (options.tenantDiscriminator) {
258
+ messageFunctions.error('api-invalid-option', null, {
259
+ '#': 'forbidden',
260
+ option: 'tenantDiscriminator',
261
+ module: 'for.effective',
262
+ });
263
+ messageFunctions.throwWithAnyError();
264
+ }
255
265
 
256
266
  const eCsn = effective.effectiveCsn(csn, internalOptions, messageFunctions);
257
- return internalOptions.testMode ? toCsn.sortCsn(eCsn, internalOptions) : eCsn;
267
+ return cloneCsn.sortCsnForTests(eCsn, internalOptions);
258
268
  }
259
269
 
260
270
  /**
@@ -270,6 +280,8 @@ function sql( csn, options, messageFunctions ) {
270
280
  const internalOptions = prepareOptions.to.sql(options);
271
281
  internalOptions.transformation = 'sql';
272
282
 
283
+ handleTenantDiscriminator(options, internalOptions, messageFunctions);
284
+
273
285
  // we need the CSN for view sorting
274
286
  const transformedCsn = csnForSql(csn, internalOptions, messageFunctions);
275
287
  messageFunctions.setModel(transformedCsn);
@@ -294,6 +306,8 @@ function hdi( csn, options, messageFunctions ) {
294
306
  trace.traceApi('to.hdi', options);
295
307
  const internalOptions = prepareOptions.to.hdi(options);
296
308
 
309
+ handleTenantDiscriminator(options, internalOptions, messageFunctions);
310
+
297
311
  // we need the CSN for view sorting
298
312
  const sqlCSN = forHdi(csn, options, messageFunctions);
299
313
  messageFunctions.setModel(sqlCSN);
@@ -398,6 +412,7 @@ function remapName( key, csn, filter = () => true ) {
398
412
  function sqlMigration( csn, options, messageFunctions, beforeImage ) {
399
413
  trace.traceApi('to.sql.migration', options);
400
414
  const internalOptions = prepareOptions.to.sql(options);
415
+ handleTenantDiscriminator(options, internalOptions, messageFunctions);
401
416
  const { error, throwWithError } = messageFunctions;
402
417
 
403
418
  // Prepare after-image.
@@ -550,6 +565,7 @@ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
550
565
  function hdiMigration( csn, options, messageFunctions, beforeImage ) {
551
566
  trace.traceApi('to.hdi.migration', options);
552
567
  const internalOptions = prepareOptions.to.hdi(options);
568
+ handleTenantDiscriminator(options, internalOptions, messageFunctions);
553
569
 
554
570
  // Prepare after-image.
555
571
  const afterImage = forHdi(csn, options, messageFunctions);
@@ -639,6 +655,15 @@ function hdbcds( csn, options, messageFunctions ) {
639
655
  const internalOptions = prepareOptions.to.hdbcds(options);
640
656
  internalOptions.transformation = 'hdbcds';
641
657
 
658
+ if (options.tenantDiscriminator) {
659
+ messageFunctions.error('api-invalid-option', null, {
660
+ '#': 'forbidden',
661
+ option: 'tenantDiscriminator',
662
+ module: 'to.hdbcds',
663
+ });
664
+ messageFunctions.throwWithAnyError();
665
+ }
666
+
642
667
  const hanaCsn = forHdbcds(csn, internalOptions, messageFunctions);
643
668
  messageFunctions.setModel(hanaCsn);
644
669
  const result = toHdbcds.toHdbcdsSource(hanaCsn, internalOptions, messageFunctions);
@@ -838,7 +863,6 @@ odata2.all = odataall;
838
863
  * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
839
864
  * @returns {object} { <protocol>: { <serviceName>: { edmx: <XML representation>, edm: <JSON representation> } } }
840
865
  */
841
- // @ts-ignore
842
866
  function odataall( csn, options = {}, messageFunctions ) {
843
867
  trace.traceApi('to.odata.all', options);
844
868
  const internalOptions = prepareOptions.to.odata(options);
@@ -1028,7 +1052,7 @@ function publishCsnProcessor( processor, _name ) {
1028
1052
  try {
1029
1053
  const messageFunctions = messages.makeMessageFunction(csn, options, _name);
1030
1054
  if (options.deprecated)
1031
- checkRemovedDeprecatedFlags( options, messageFunctions );
1055
+ baseModel.checkRemovedDeprecatedFlags( options, messageFunctions );
1032
1056
 
1033
1057
  checkOutdatedOptions( options, messageFunctions );
1034
1058
  csn = ensureClientCsn( csn, options, messageFunctions, _name );
@@ -1195,6 +1219,29 @@ function lazyload( moduleName ) {
1195
1219
  });
1196
1220
  }
1197
1221
 
1222
+ /**
1223
+ * Error when tenantDiscriminator and withHanaAssociations is set by the user.
1224
+ *
1225
+ * Set withHanaAssociations to false when tenantDiscriminator is used.
1226
+ *
1227
+ * @param {object} options Options set by the user
1228
+ * @param {object} internalOptions Options clone after we processed it
1229
+ * @param {object} messageFunctions Message functions
1230
+ */
1231
+ function handleTenantDiscriminator( options, internalOptions, messageFunctions ) {
1232
+ if (options.tenantDiscriminator && options.withHanaAssociations) {
1233
+ messageFunctions.error('api-invalid-combination', null, {
1234
+ option: 'tenantDiscriminator',
1235
+ prop: 'withHanaAssociations',
1236
+ });
1237
+
1238
+ messageFunctions.throwWithAnyError();
1239
+ }
1240
+ else if (internalOptions.tenantDiscriminator) {
1241
+ internalOptions.withHanaAssociations = false;
1242
+ }
1243
+ }
1244
+
1198
1245
 
1199
1246
  /**
1200
1247
  * Option format used by the old API, where they are grouped thematically.
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const { validate, generateStringValidator } = require('./validate');
4
+ const { makeMessageFunction } = require('../base/messages');
4
5
 
5
6
  // TODO: there should be just one place where the options are defined with
6
7
  // their types (not also in validate.js or whatever).
@@ -67,7 +68,7 @@ const privateOptions = [
67
68
  'noRecompile',
68
69
  'internalMsg',
69
70
  'disableHanaComments', // in case of issues with hana comment rendering
70
- 'tenantAsColumn', // not published yet
71
+ 'tenantDiscriminator', // not published yet
71
72
  'localizedWithoutCoalesce', // deprecated version of 'localizedLanguageFallback', TODO(v5): Remove option
72
73
  ];
73
74
 
@@ -104,14 +105,16 @@ function translateOptions( input = {}, defaults = {}, hardRequire = {},
104
105
 
105
106
  // Validate the filtered input options
106
107
  // only "new-style" options are here
108
+ const messageFunctions = makeMessageFunction(null, options, moduleName);
107
109
  validate(options,
108
- moduleName,
110
+ messageFunctions,
109
111
  // TODO: is there a better place to specify the type of option values?
110
112
  Object.assign( {
111
113
  localizedLanguageFallback: generateStringValidator([ 'none', 'coalesce' ]),
112
114
  sqlChangeMode: generateStringValidator([ 'alter', 'drop' ]),
113
115
  }, customValidators ),
114
- combinationValidators);
116
+ combinationValidators,
117
+ moduleName);
115
118
 
116
119
  // Overwrite with the hardRequire options - like src: sql in to.sql()
117
120
  Object.assign(options, hardRequire);
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { makeMessageFunction } = require('../base/messages');
4
3
  const { forEach } = require('../utils/objectUtils');
5
4
 
6
5
  /* eslint-disable arrow-body-style */
@@ -128,6 +127,7 @@ const validators = {
128
127
  found: val => (typeof val === 'string' ? val : `type ${ typeof val }`),
129
128
  },
130
129
  assertIntegrityType: generateStringValidator([ 'DB', 'RT' ]),
130
+ tenantDiscriminator: { validate: () => true }, // do it ourselves
131
131
  };
132
132
 
133
133
  // Note: if `validate()` returns true, it means the option is _invalid_!
@@ -152,47 +152,38 @@ const allCombinationValidators = {
152
152
 
153
153
  const alwaysRunValidators = [ 'beta-no-test', 'sql-dialect-and-localized' ];
154
154
 
155
- /* eslint-disable jsdoc/no-undefined-types */
156
155
  /**
157
156
  * Run the validations for each option.
158
157
  * Use a custom validator or "default" custom validator, fallback to Boolean validator.
159
158
  *
160
159
  * @param {object} options Flat options object to validate
161
- * @param {string} moduleName The called module, e.g. 'for.odata', 'to.hdi'. Needed to initialize the message functions
160
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`,
162
161
  * @param {object} [customValidators] Map of custom validators to use
163
162
  * @param {string[]} [combinationValidators] Validate option combinations
164
163
  * @returns {void}
165
164
  * @throws {CompilationError} Throws in case of invalid option usage
166
165
  */
167
- function validate( options, moduleName, customValidators = {}, combinationValidators = [] ) {
168
- // TODO: issuing messages in this function looks very strange...
169
- {
170
- const messageCollector = { messages: [] };
171
- const { error, throwWithAnyError } = makeMessageFunction(null, messageCollector, moduleName);
172
-
173
- forEach(options, (optionName, optionValue) => {
174
- const validator = customValidators[optionName] || validators[optionName] || booleanValidator;
175
-
176
- if (!validator.validate(optionValue)) {
177
- error('api-invalid-option', null, {
178
- '#': 'value',
179
- prop: optionName,
180
- value: validator.expected(optionValue),
181
- othervalue: validator.found(optionValue),
182
- });
183
- }
184
- });
185
- throwWithAnyError();
186
- }
187
-
188
- const message = makeMessageFunction(null, options, moduleName);
166
+ function validate( options, messageFunctions, customValidators = {}, combinationValidators = [] ) {
167
+ const { error, throwWithError } = messageFunctions;
168
+
169
+ forEach(options, (optionName, optionValue) => {
170
+ const validator = customValidators[optionName] || validators[optionName] || booleanValidator;
171
+
172
+ if (!validator.validate(optionValue)) {
173
+ error('api-invalid-option', null, {
174
+ '#': 'value',
175
+ prop: optionName,
176
+ value: validator.expected(optionValue),
177
+ othervalue: validator.found(optionValue),
178
+ });
179
+ }
180
+ });
181
+ throwWithError();
189
182
 
190
183
  for (const combinationValidatorName of combinationValidators.concat(alwaysRunValidators))
191
- allCombinationValidators[combinationValidatorName](options, message);
192
-
193
- message.throwWithAnyError();
184
+ allCombinationValidators[combinationValidatorName](options, messageFunctions);
185
+ throwWithError();
194
186
  }
195
- /* eslint-enable jsdoc/no-undefined-types */
196
187
 
197
188
 
198
189
  module.exports = { validate, generateStringValidator };
@@ -234,7 +234,9 @@ const centralMessageTexts = {
234
234
  deprecated: 'Option $(NAME) is no longer supported! Use latest API options instead',
235
235
  magicVars: 'Option $(PROP) is no longer supported! Use $(OTHERPROP) instead. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
236
236
  value: 'Expected option $(PROP) to have $(VALUE). Found: $(OTHERVALUE)',
237
+ value2: 'Expected option $(OPTION) to have value $(VALUE) or $(RAWVALUE); found: $(OTHERVALUE)',
237
238
  type: 'Expected option $(OPTION) to be of type $(VALUE). Found: $(OTHERVALUE)',
239
+ forbidden: 'Option $(OPTION) can\'t be used with API function $(MODULE)'
238
240
  },
239
241
 
240
242
  'api-invalid-variable-replacement': {
@@ -674,6 +676,7 @@ const centralMessageTexts = {
674
676
  std: 'Rewriting the ON-condition not supported here', // unused: merge with 'rewrite-not-supported'
675
677
  'sub-element': 'Rewriting the ON-condition of unmanaged association in sub element is not supported'
676
678
  },
679
+ 'rewrite-undefined-key': 'Expected element $(ID) to be available in $(TARGET), as it is required to match the foreign key of $(NAME)',
677
680
 
678
681
  'type-unexpected-typeof': {
679
682
  std: 'Unexpected $(KEYWORD) for the type reference here',
@@ -854,6 +857,7 @@ const centralMessageTexts = {
854
857
  'ref-invalid-include': {
855
858
  std: 'A type, entity, aspect or event with direct elements is expected here',
856
859
  bare : 'An aspect without elements is expected here',
860
+ param: 'A type, entity, aspect or event without parameters is expected here',
857
861
  },
858
862
  'ref-invalid-type': {
859
863
  std: 'A type or an element is expected here',
@@ -894,8 +898,8 @@ const centralMessageTexts = {
894
898
  },
895
899
 
896
900
  'query-undefined-element': {
897
- std: 'Element $(ID) has not been found in the elements of the query',
898
- redirected: 'Element $(ID) has not been found in the elements of the query; please use REDIRECTED TO with an explicit ON-condition',
901
+ std: 'Target $(TARGET) of $(NAME) is missing element $(ID); please use $(KEYWORD) with an explicit ON-condition',
902
+ redirected: 'Target $(TARGET) of $(NAME) is missing element $(ID); please add an ON-condition to $(KEYWORD)',
899
903
  },
900
904
  'query-unexpected-assoc-hdbcds': 'Publishing a managed association in a view is not possible for “hdbcds” naming mode',
901
905
  'query-unexpected-structure-hdbcds': 'Publishing a structured element in a view is not possible for “hdbcds” naming mode',
@@ -1136,7 +1140,27 @@ const centralMessageTexts = {
1136
1140
  'wrongval': 'Unexpected value for $(OP) in $(ANNO)',
1137
1141
  'wrongval_meta': 'Expected value for $(OP) to be a $(META) in $(ANNO)',
1138
1142
  'wrongval_meta_list': 'Expected value for $(OP) to be a $(META) or $(RAWVALUES) in $(ANNO)',
1139
- }
1143
+ },
1144
+ 'odata-anno-xpr-ref': {
1145
+ 'std': '$(ANNO) can\'t be propagated to $(NAME) because path $(ELEMREF) is not resolvable via type reference $(CODE)',
1146
+ 'flatten_builtin': 'Expected path $(ELEMREF) in $(ANNO) to resolve to a builtin type while flattening $(NAME)',
1147
+ 'notaparam': 'Unexpected path $(ELEMREF) for parameter entity in $(ANNO)',
1148
+ 'notaneelement': 'Unexpected path $(ELEMREF) for type entity in $(ANNO)'
1149
+ },
1150
+ // tenenat isolation via discriminator column:
1151
+ 'tenant-invalid-alias-name': {
1152
+ std: 'Can\'t have a table alias named $(NAME) in a tenant-dependent entity',
1153
+ implicit: 'Provide an explicit table alias name; do not use $(NAME)',
1154
+ mixin: 'Can\'t define a mixin named $(NAME) in a tenant-dependent entity',
1155
+ },
1156
+ 'tenant-invalid-composition': {
1157
+ std: 'Can\'t define a composition of a tenant-independent entity $(TARGET) in a tenant-dependent entity',
1158
+ type: 'Can\'t use type $(TYPE) with a composition of a tenant-independent entity in a tenant-dependent entity',
1159
+ },
1160
+ 'tenant-invalid-target': {
1161
+ std: 'Can\'t define an association to a tenant-dependent entity $(TARGET) in a tenant-independent entity',
1162
+ type: 'Can\'t use type $(TYPE) with an association to a tenant-dependent entity in a tenant-independent entity',
1163
+ },
1140
1164
  // -----------------------------------------------------------------------------------
1141
1165
  // OData Message section ends here, no messages below this line
1142
1166
  // -----------------------------------------------------------------------------------
@@ -814,6 +814,7 @@ function transformElementRef( arg ) {
814
814
  return quoted( arg );
815
815
  // Can be used by CSN backends or compiler to create a simple path such as E:elem
816
816
  return quoted(
817
+ (arg.param ? ':' : '') +
817
818
  ref.map(
818
819
  item => (typeof item !== 'string'
819
820
  ? `${ item.id }${item.args ? '(…)' : ''}${item.where ? '[…]' : ''}`
@@ -833,8 +834,8 @@ function transformArg( arg, r, args, texts ) {
833
834
  if (arg.ref) {
834
835
  // Can be used by CSN backends to create a simple path such as E:elem
835
836
  if (arg.ref.length > 1)
836
- return quoted(`${ arg.ref[0] }:${ arg.ref.slice(1).join('.') }`);
837
- return quoted(arg.ref);
837
+ return quoted(`${ pathId(arg.ref[0]) }:${ arg.ref.slice(1).map(pathId).join('.') }`);
838
+ return quoted(pathId(arg.ref[0]));
838
839
  }
839
840
  if (!arg.name)
840
841
  return quoted( arg.name );
@@ -848,6 +849,10 @@ function transformArg( arg, r, args, texts ) {
848
849
  return artName( arg, prop );
849
850
  }
850
851
 
852
+ function pathId( item ) {
853
+ return (typeof item === 'string') ? item : item.id;
854
+ }
855
+
851
856
  // TODO: very likely delete this function
852
857
  function searchName( art, id, variant ) {
853
858
  if (!variant) {
@@ -1479,7 +1484,7 @@ function constructSemanticLocationFromCsnPath( model, options, csnPath ) {
1479
1484
 
1480
1485
  let result = '';
1481
1486
  const csnDictionaries = [
1482
- 'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions',
1487
+ 'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions', 'vocabularies',
1483
1488
  ];
1484
1489
  // Properties that (currently) end the semantic location.
1485
1490
  const queryPropsLast = [ 'where', 'groupBy', 'having', 'orderBy', 'limit', 'offset' ];
package/lib/base/model.js CHANGED
@@ -27,6 +27,7 @@ const queryOps = {
27
27
  const availableBetaFlags = {
28
28
  // enabled by --beta-mode
29
29
  annotationExpressions: true,
30
+ odataPathsInAnnotationExpressions: true,
30
31
  odataAnnotationExpressions: true,
31
32
  assocsWithParams: true, // beta, because runtimes don't support it, yet.
32
33
  hanaAssocRealCardinality: true,
@@ -39,6 +40,7 @@ const availableBetaFlags = {
39
40
  tenantVariable: true,
40
41
  calcAssoc: true,
41
42
  vectorType: true,
43
+ v5preview: true,
42
44
  // disabled by --beta-mode
43
45
  nestedServices: false,
44
46
  };
@@ -0,0 +1,28 @@
1
+ 'use strict';
2
+
3
+ const { setProp } = require('../base/model');
4
+ const { featureFlags } = require('../transform/db/featureFlags');
5
+
6
+ /**
7
+ *
8
+ * @param {string} flag
9
+ *
10
+ * @returns {Function} Function to correctly set the given flag
11
+ */
12
+ function setFeatureFlag( flag ) {
13
+ return function setFlag() {
14
+ if (!this.csn.meta)
15
+ setProp(this.csn, 'meta', {});
16
+ if (!this.csn.meta[featureFlags])
17
+ this.csn.meta[featureFlags] = {};
18
+
19
+ this.csn.meta[featureFlags][flag] = true;
20
+ };
21
+ }
22
+
23
+ // Export a applyTransformations callback object that sets the feature flags if certain properties are present
24
+ module.exports = {
25
+ value: setFeatureFlag('$calculatedElements'),
26
+ expand: setFeatureFlag('$expandInline'),
27
+ inline: setFeatureFlag('$expandInline'),
28
+ };