@sap/cds-compiler 3.8.2 → 3.9.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 (82) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/bin/cdsc.js +2 -2
  3. package/doc/CHANGELOG_BETA.md +26 -5
  4. package/lib/api/.eslintrc.json +3 -2
  5. package/lib/api/options.js +3 -1
  6. package/lib/api/validate.js +1 -1
  7. package/lib/base/message-registry.js +28 -19
  8. package/lib/base/messages.js +6 -1
  9. package/lib/base/model.js +2 -2
  10. package/lib/checks/.eslintrc.json +1 -0
  11. package/lib/checks/actionsFunctions.js +6 -6
  12. package/lib/checks/annotationsOData.js +1 -1
  13. package/lib/checks/elements.js +28 -17
  14. package/lib/checks/foreignKeys.js +1 -1
  15. package/lib/checks/invalidTarget.js +1 -1
  16. package/lib/checks/onConditions.js +11 -6
  17. package/lib/checks/queryNoDbArtifacts.js +1 -1
  18. package/lib/checks/types.js +1 -1
  19. package/lib/checks/utils.js +1 -1
  20. package/lib/checks/validator.js +3 -2
  21. package/lib/compiler/assert-consistency.js +7 -2
  22. package/lib/compiler/base.js +8 -4
  23. package/lib/compiler/builtins.js +7 -0
  24. package/lib/compiler/checks.js +73 -6
  25. package/lib/compiler/define.js +10 -5
  26. package/lib/compiler/extend.js +910 -1711
  27. package/lib/compiler/finalize-parse-cdl.js +1 -1
  28. package/lib/compiler/generate.js +838 -0
  29. package/lib/compiler/index.js +2 -0
  30. package/lib/compiler/populate.js +2 -2
  31. package/lib/compiler/propagator.js +20 -8
  32. package/lib/compiler/resolve.js +3 -3
  33. package/lib/compiler/shared.js +3 -1
  34. package/lib/edm/annotations/genericTranslation.js +18 -8
  35. package/lib/edm/csn2edm.js +14 -14
  36. package/lib/edm/edm.js +25 -11
  37. package/lib/edm/edmPreprocessor.js +47 -23
  38. package/lib/edm/edmUtils.js +37 -9
  39. package/lib/gen/Dictionary.json +5 -7
  40. package/lib/gen/language.checksum +1 -1
  41. package/lib/gen/language.interp +3 -1
  42. package/lib/gen/language.tokens +24 -23
  43. package/lib/gen/languageLexer.interp +4 -1
  44. package/lib/gen/languageLexer.js +792 -784
  45. package/lib/gen/languageLexer.tokens +12 -11
  46. package/lib/gen/languageParser.js +3564 -3493
  47. package/lib/json/from-csn.js +28 -6
  48. package/lib/json/to-csn.js +10 -6
  49. package/lib/language/antlrParser.js +11 -3
  50. package/lib/language/genericAntlrParser.js +2 -1
  51. package/lib/language/language.g4 +14 -3
  52. package/lib/model/csnRefs.js +10 -5
  53. package/lib/model/csnUtils.js +41 -76
  54. package/lib/modelCompare/utils/.eslintrc.json +1 -1
  55. package/lib/optionProcessor.js +7 -4
  56. package/lib/render/.eslintrc.json +1 -1
  57. package/lib/render/toCdl.js +244 -168
  58. package/lib/render/toHdbcds.js +18 -10
  59. package/lib/render/toSql.js +24 -2
  60. package/lib/transform/db/.eslintrc.json +4 -3
  61. package/lib/transform/db/cdsPersistence.js +1 -1
  62. package/lib/transform/db/expansion.js +11 -6
  63. package/lib/transform/db/flattening.js +22 -15
  64. package/lib/transform/db/rewriteCalculatedElements.js +50 -29
  65. package/lib/transform/db/temporal.js +1 -1
  66. package/lib/transform/db/views.js +1 -1
  67. package/lib/transform/draft/db.js +1 -1
  68. package/lib/transform/draft/odata.js +3 -4
  69. package/lib/transform/forOdataNew.js +5 -6
  70. package/lib/transform/forRelationalDB.js +7 -7
  71. package/lib/transform/localized.js +1 -1
  72. package/lib/transform/odata/toFinalBaseType.js +6 -6
  73. package/lib/transform/odata/typesExposure.js +12 -3
  74. package/lib/transform/odata/utils.js +3 -0
  75. package/lib/transform/transformUtilsNew.js +11 -26
  76. package/lib/transform/translateAssocsToJoins.js +9 -9
  77. package/lib/transform/universalCsn/.eslintrc.json +3 -2
  78. package/lib/transform/universalCsn/coreComputed.js +1 -1
  79. package/lib/transform/universalCsn/universalCsnEnricher.js +6 -4
  80. package/lib/utils/file.js +3 -3
  81. package/lib/utils/moduleResolve.js +1 -1
  82. package/package.json +1 -1
@@ -135,7 +135,18 @@ const xorGroups = {
135
135
  // quantifiers 'some' and 'any are 'xpr' token strings in CSN v1.0
136
136
  };
137
137
 
138
- // Functions reading properties which do no count for the message
138
+ /**
139
+ * Properties that are required next to `=` to make an annotation value an actual expression
140
+ * and not some foreign structure.
141
+ *
142
+ * @type {string[]}
143
+ */
144
+ const xprInAnnoProperties = [
145
+ 'ref', 'xpr', 'list', 'literal', 'val',
146
+ '#', 'func', 'args', 'SELECT', 'SET',
147
+ ];
148
+
149
+ // Functions reading properties which do not count for the message
139
150
  // 'Object in $(PROP) must have at least one property'
140
151
  const functionsOfIrrelevantProps = [ ignore, extra, explicitName ];
141
152
 
@@ -688,6 +699,10 @@ const schema = compileSchema( {
688
699
  vZeroFor: 'val', // CSN v0.1.0 property for `val` in enum def
689
700
  // type: annoValue,
690
701
  inKind: [ 'element' ],
702
+ optional: exprProperties.concat([ 'stored' ]),
703
+ },
704
+ stored: {
705
+ type: boolOrNull,
691
706
  },
692
707
  // ignored: ----------------------------------------------------------------
693
708
  $location: { // special
@@ -1354,8 +1369,15 @@ function annoValue( val, spec ) {
1354
1369
  return retval;
1355
1370
  }
1356
1371
  else if (typeof val['='] === 'string') {
1372
+ // An object with `=` is an expression if and only if:
1373
+ // - there is exactly one property ('=')
1374
+ // - there is at least one other expression property (e.g. "xpr")
1375
+ // TODO: Have xprInAnnoProperties centrally for other backends to use as well (toCdl)
1357
1376
  const valKeys = Object.keys(val);
1358
- if (valKeys.length > 1 && isBetaEnabled(userOptions, 'annotationExpressions')) {
1377
+ if (valKeys.length > 1 &&
1378
+ (isBetaEnabled(userOptions, 'annotationExpressions') ||
1379
+ isBetaEnabled(userOptions, 'v4preview')) &&
1380
+ xprInAnnoProperties.some(prop => val[prop] !== undefined)) {
1359
1381
  const s = schema['@'].schema['-expr'];
1360
1382
  const r = { location: location() };
1361
1383
  Object.assign(r, object(val, s));
@@ -1367,6 +1389,7 @@ function annoValue( val, spec ) {
1367
1389
  ++virtualLine;
1368
1390
  return r;
1369
1391
  }
1392
+ // fallthrough -> unchecked structure
1370
1393
  }
1371
1394
  if (typeof val['#'] === 'string') {
1372
1395
  if (Object.keys( val ).length === 1) {
@@ -1725,8 +1748,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
1725
1748
  {
1726
1749
  '#': variant,
1727
1750
  prop,
1728
- parentprop:
1729
- parentSpec.msgProp,
1751
+ parentprop: parentSpec.msgProp,
1730
1752
  kind,
1731
1753
  } );
1732
1754
  }
@@ -1859,10 +1881,10 @@ function refSplit( name, prop ) {
1859
1881
  return { path: path.map( id => ({ id, location: location() }) ), location: location() };
1860
1882
  }
1861
1883
 
1862
- function replaceZeroValue( spec, msgVariant, otherprop ) {
1884
+ function replaceZeroValue( spec, msgVariant, newValue ) {
1863
1885
  if (!csnVersionZero && !spec.vZeroFor) {
1864
1886
  warning( 'syntax-deprecated-value', location(true),
1865
- { '#': msgVariant, prop: spec.msgProp, otherprop } );
1887
+ { '#': msgVariant, prop: spec.msgProp, value: newValue } );
1866
1888
  }
1867
1889
  }
1868
1890
 
@@ -431,14 +431,15 @@ function i18n( i18nNode ) {
431
431
  return csn;
432
432
  }
433
433
 
434
- function sources( srcDict, csn ) {
435
- const names = Object.keys( srcDict );
434
+ function sources( srcDict, csn, model ) {
435
+ let names = model._sources || Object.keys( srcDict );
436
436
  const $sources = names.length && srcDict[names[0]].$sources;
437
437
  if ($sources) {
438
438
  setHidden( csn, '$sources', $sources );
439
439
  return undefined;
440
440
  }
441
- // TODO: sort according to some layering order, see #6368
441
+ if (model._sortedSources)
442
+ names = model._sortedSources.map( s => s.realname );
442
443
  setHidden( csn, '$sources', (!strictMode) ? names : names.map( relativeName ) );
443
444
  return undefined;
444
445
 
@@ -1242,10 +1243,13 @@ function enumValueOrCalc( v, csn, node ) {
1242
1243
  if (v.$inferred && (universalCsn || gensrcFlavor))
1243
1244
  return undefined;
1244
1245
  // Enums can have values but if enums are extended, their kind is 'element'
1245
- if (node.kind === 'enum' || node.$syntax === 'enum')
1246
+ if (node.kind === 'enum' || node.$syntax === 'enum') {
1246
1247
  Object.assign( csn, expression( v ) );
1247
- else if (node.$syntax === 'calc') // TODO: || node._parent?.kind === 'extend'
1248
- return expression( v );
1248
+ }
1249
+ else if (node.$syntax === 'calc') { // TODO: || node._parent?.kind === 'extend'
1250
+ const stored = v.stored ? { stored: value(v.stored) } : {};
1251
+ return Object.assign( stored, expression( v ) );
1252
+ }
1249
1253
  return undefined;
1250
1254
  }
1251
1255
 
@@ -11,6 +11,7 @@
11
11
  const antlr4 = require('antlr4');
12
12
 
13
13
  const { CompileMessage } = require('../base/messages');
14
+ const { isBetaEnabled } = require('../base/model');
14
15
  const errorStrategy = require('./errorStrategy');
15
16
 
16
17
  const Parser = require('../gen/languageParser').default;
@@ -29,6 +30,11 @@ class ErrorListener extends antlr4.error.ErrorListener {
29
30
  }
30
31
 
31
32
  class RewriteTypeTokenStream extends antlr4.CommonTokenStream {
33
+ constructor(lexer, v4newKeyword) {
34
+ super(lexer);
35
+ this.v4newKeyword = v4newKeyword;
36
+ }
37
+
32
38
  LT( k ) {
33
39
  const t = super.LT(k);
34
40
  if (!t || !t.type)
@@ -49,9 +55,10 @@ class RewriteTypeTokenStream extends antlr4.CommonTokenStream {
49
55
  }
50
56
  else if (t.type === this.NEW) {
51
57
  const n = super.LT(k + 1);
52
- if (n && n.type === this.Identifier && /^st_/i.test( n.text )) {
58
+ // TODO v4: rewrite token in grammar via `this.setLocalToken`
59
+ if (n?.type === this.Identifier && (this.v4newKeyword || /^st_/i.test( n.text ))) {
53
60
  const o = super.LT(k + 2);
54
- if (o && o.type === this.PAREN)
61
+ if (o?.type === this.PAREN)
55
62
  return t;
56
63
  }
57
64
  t.type = this.Identifier;
@@ -111,7 +118,8 @@ function parse( source, filename = '<undefined>.cds',
111
118
  options = {}, messageFunctions = null,
112
119
  rule = 'cdl' ) {
113
120
  const lexer = new Lexer( new antlr4.InputStream(source) );
114
- const tokenStream = new RewriteTypeTokenStream(lexer);
121
+ const v4newKeyword = isBetaEnabled(options, 'v4preview');
122
+ const tokenStream = new RewriteTypeTokenStream(lexer, v4newKeyword);
115
123
  /** @type {object} */
116
124
  const parser = new Parser( tokenStream );
117
125
  const errorListener = new ErrorListener();
@@ -1095,7 +1095,8 @@ function addExtension( ext, parent, kind, artName, elemPath ) {
1095
1095
  }
1096
1096
 
1097
1097
  function aspectWithoutElements( art ) {
1098
- // Empty dictionary to allow element extensions.
1098
+ // Empty dictionary to allow element extensions. NO, please NO empty dict.
1099
+ // TODO: Checking it here does not prevent aspect in CSN input having no elements!
1099
1100
  art.elements = this.createDict();
1100
1101
  if (!isBetaEnabled( this.options, 'aspectWithoutElements' )) {
1101
1102
  this.error( null, [ art.name.location ], {},
@@ -607,10 +607,14 @@ elementDefInner[ art, outer, mightBeEnum ]
607
607
  ':'
608
608
  elementType[ $art, $mightBeEnum ]
609
609
  |
610
- '=' e=expression // SQL has syntax variant using AS - we DO NOT
610
+ eq='=' e=expression // SQL has syntax variant using AS - we DO NOT
611
+ stored=STORED?
611
612
  { $art.value = $e.expr;
612
613
  // this.setIntroLocation( eq ); -- future
613
- if ($mightBeEnum && ($e.expr?.val !== undefined || $e.expr?.sym !== undefined) &&
614
+ if ($stored)
615
+ $art.value.stored = this.valueWithTokenLocation( true, $stored );
616
+ if ($mightBeEnum && !$stored &&
617
+ ($e.expr?.val !== undefined || $e.expr?.sym !== undefined) &&
614
618
  !$virtual && !$key && !$masked && !$art.elements && !$art.type)
615
619
  $art['$'+'syntax'] = 'enum';
616
620
  }
@@ -721,7 +725,12 @@ elementProperties[ elem ]
721
725
  :
722
726
  defaultAndNullablity[ $elem ]
723
727
  |
724
- '=' e=expression { $elem.value = $e.expr; }
728
+ '=' e=expression
729
+ stored=STORED?
730
+ { $elem.value = $e.expr;
731
+ if ($stored)
732
+ $elem.value.stored = this.valueWithTokenLocation( true, $stored );
733
+ }
725
734
  ;
726
735
 
727
736
  defaultAndNullablity[ elem ]
@@ -2956,6 +2965,7 @@ ident[ category ] returns[ id ]
2956
2965
  | RIGHT
2957
2966
  | ROW
2958
2967
  | ROWS
2968
+ | STORED
2959
2969
  | SERVICE
2960
2970
  | THEN
2961
2971
  | UNION
@@ -3155,6 +3165,7 @@ RIGHT : [rR][iI][gG][hH][tT] ;
3155
3165
  ROW : [rR][oO][wW] ;
3156
3166
  ROWS : [rR][oO][wW][sS] ;
3157
3167
  SERVICE : [sS][eE][rR][vV][iI][cC][eE] ;
3168
+ STORED : [sS][tT][oO][rR][eE][dD] ;
3158
3169
  THEN : [tT][hH][eE][nN] ;
3159
3170
  TO : [tT][oO] ; // or make reserved? (is in SQL-92)
3160
3171
  TYPE : [tT][yY][pP][eE] ;
@@ -66,8 +66,11 @@
66
66
  // - function `initColumnElement`: issue with column which is neither `*` nor
67
67
  // a `ref` with sibling `inline`, but still has no corresponding element
68
68
 
69
- // The functions in this module also use an internal cache. The second call of
70
- // inspectRef() in the following example might lead to a wrong result or an
69
+ // The functions in this module also use an internal cache, which can be dropped
70
+ // for a single definition (main artifact) with function dropDefinitionCache().
71
+
72
+ // When modifying the CSN, caches might need to be invalidated. In the following
73
+ // example, the second call of inspectRef() might lead to a wrong result or an
71
74
  // exception if the assignment to `inspectRef` is not uncommented:
72
75
  //
73
76
  // let { inspectRef } = csnRefs( csn );
@@ -517,7 +520,7 @@ function csnRefs( csn, universalReady ) {
517
520
  /**
518
521
  * @param {CSN.Path} csnPath
519
522
  *
520
- * - return value `art`: the “resulting” CSN of the reference
523
+ * - return value `art`: the “resulting” CSN node of the reference
521
524
  *
522
525
  * - return value `links`: array of { art, env } in length of ref.path where
523
526
  * art = the definition or element reached by the ref path so far
@@ -992,6 +995,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
992
995
  /** @type {boolean|string|number} */
993
996
  let isName = false;
994
997
  let baseRef = null;
998
+ let baseCtx = null;
995
999
  let baseEnv = null;
996
1000
  let {
997
1001
  index, main, parent, art,
@@ -1049,13 +1053,13 @@ function analyseCsnPath( csnPath, csn, resolve ) {
1049
1053
  }
1050
1054
  else if (prop === 'where' && refCtx === 'ref') {
1051
1055
  if (resolve)
1052
- baseEnv = resolve.ref_where( obj, baseRef, refCtx, main, query, parent, baseEnv );
1056
+ baseEnv = resolve.ref_where( obj, baseRef, baseCtx, main, query, parent, baseEnv );
1053
1057
  refCtx = 'ref_where';
1054
1058
  }
1055
1059
  else if (prop === 'expand' || prop === 'inline') {
1056
1060
  if (obj.ref) {
1057
1061
  if (resolve)
1058
- baseEnv = resolve.expandInline( obj, refCtx, main, query, parent, baseEnv );
1062
+ baseEnv = resolve.expandInline( obj, baseCtx, main, query, parent, baseEnv );
1059
1063
  refCtx = prop;
1060
1064
  }
1061
1065
  isName = prop;
@@ -1070,6 +1074,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
1070
1074
  }
1071
1075
  else if (prop === 'ref') {
1072
1076
  baseRef = obj; // needs to be inspected for filter conditions
1077
+ baseCtx = refCtx;
1073
1078
  refCtx = prop;
1074
1079
  }
1075
1080
  else if (prop === 'orderBy') {
@@ -47,13 +47,12 @@ function getUtils( model, universalReady ) {
47
47
  const special$self = !model?.definitions?.$self && '$self';
48
48
  const _csnRefs = csnRefs(model, universalReady);
49
49
  const { artifactRef } = _csnRefs;
50
- /** Cache for getFinalBaseTypeWithProps(). Specific to the current model. */
50
+ /** Cache for getFinalTypeInfo(). Specific to the current model. */
51
51
  const finalBaseTypeCache = Object.create(null);
52
52
 
53
53
  return {
54
54
  getCsnDef,
55
55
  isStructured,
56
- getFinalType,
57
56
  isManagedAssociation,
58
57
  isAssocOrComposition,
59
58
  isAssociation,
@@ -63,7 +62,7 @@ function getUtils( model, universalReady ) {
63
62
  addStringAnnotationTo,
64
63
  getServiceName,
65
64
  hasAnnotationValue,
66
- getFinalBaseTypeWithProps,
65
+ getFinalTypeInfo,
67
66
  get$combined,
68
67
  getQueryPrimarySource,
69
68
  ..._csnRefs,
@@ -136,7 +135,7 @@ function getUtils( model, universalReady ) {
136
135
  elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
137
136
  }
138
137
  else if (arg.SELECT || arg.SET) {
139
- elements = mergeElementMaps(elements, getSources(arg));
138
+ elements = mergeElementMaps(elements, getSources(arg, true));
140
139
  }
141
140
  }
142
141
 
@@ -224,18 +223,7 @@ function getUtils( model, universalReady ) {
224
223
  * @returns {boolean}
225
224
  */
226
225
  function isStructured( obj ) {
227
- return !!(obj.elements || (obj.type && getFinalBaseTypeWithProps(obj.type)?.elements));
228
- }
229
-
230
- /**
231
- * Resolves typedefs to its final type (name) which is returned.
232
- * @param {string|object} typeName Absolute type name or type ref ({ref: [...]}).
233
- * @returns {string|object}
234
- */
235
- function getFinalType( typeName ) {
236
- if (!typeName)
237
- return typeName;
238
- return getFinalBaseTypeWithProps(typeName)?.type || null;
226
+ return !!(obj.elements || (obj.type && getFinalTypeInfo(obj.type)?.elements));
239
227
  }
240
228
 
241
229
  // Return true if 'node' is a managed association element
@@ -247,30 +235,30 @@ function getUtils( model, universalReady ) {
247
235
  /**
248
236
  * Returns if a type is an association or a composition (possibly via type chain).
249
237
  *
250
- * @param {string} typeName Absolute type name
238
+ * @param {object} artifact Element or other artifact.
251
239
  */
252
- function isAssocOrComposition( typeName ) {
253
- const finalType = getFinalType( typeName );
240
+ function isAssocOrComposition( artifact ) {
241
+ const finalType = artifact && getFinalTypeInfo( artifact.type )?.type;
254
242
  return (finalType === 'cds.Association' || finalType === 'cds.Composition');
255
243
  }
256
244
 
257
245
  /**
258
246
  * Returns true if a type is an association (possibly via type chain).
259
247
  *
260
- * @param {string} typeName Absolute type name
248
+ * @param {object} artifact Element or other artifact.
261
249
  */
262
- function isAssociation( typeName ) {
263
- const finalType = getFinalType( typeName );
250
+ function isAssociation( artifact ) {
251
+ const finalType = artifact && getFinalTypeInfo( artifact.type )?.type;
264
252
  return (finalType === 'cds.Association');
265
253
  }
266
254
 
267
255
  /**
268
256
  * Returns if a type is a composition (possibly via type chain).
269
257
  *
270
- * @param {string} typeName Absolute type name
258
+ * @param {object} artifact Element or other artifact.
271
259
  */
272
- function isComposition( typeName ) {
273
- const finalType = getFinalType( typeName );
260
+ function isComposition( artifact ) {
261
+ const finalType = artifact && getFinalTypeInfo( artifact.type )?.type;
274
262
  return (finalType === 'cds.Composition');
275
263
  }
276
264
 
@@ -340,7 +328,7 @@ function getUtils( model, universalReady ) {
340
328
  *
341
329
  * Notes:
342
330
  * - Caches type lookups. If the CSN changes drastically, you will need to re-call
343
- * `getUtils()` and use the newly returned `getFinalBaseTypeWithProps()`.
331
+ * `getUtils()` and use the newly returned `getFinalTypeInfo()`.
344
332
  * - Does _not_ return the underlying type definition! It is an object with all relevant
345
333
  * type properties collected while traversing the type chain!
346
334
  *
@@ -349,7 +337,7 @@ function getUtils( model, universalReady ) {
349
337
  * @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
350
338
  * @returns {object|null}
351
339
  */
352
- function getFinalBaseTypeWithProps( type ) {
340
+ function getFinalTypeInfo( type ) {
353
341
  type = normalizeTypeRef(type);
354
342
  if (!type)
355
343
  return null;
@@ -392,7 +380,7 @@ function getUtils( model, universalReady ) {
392
380
  finalBaseTypeCache[resolvedKey] = true;
393
381
 
394
382
  // Continue the search
395
- const finalBase = getFinalBaseTypeWithProps(type);
383
+ const finalBase = getFinalTypeInfo(type);
396
384
  if (!finalBase) // Reference has no proper type, e.g. due to `type of View:calculated`.
397
385
  return _cacheResolved(null);
398
386
 
@@ -561,28 +549,6 @@ function forEachMember( construct, callback, path = [], ignoreIgnore = true, ite
561
549
  }
562
550
  }
563
551
 
564
- /**
565
- * Call `forEachMember` and then apply `forEachMember` on queries.
566
- *
567
- * @param {CSN.Artifact} construct
568
- * @param {genericCallback|genericCallback[]} callback
569
- * @param {CSN.Path} [path]
570
- * @param {boolean} [ignoreIgnore]
571
- * @param {object} iterateOptions can be used to skip certain kinds from being iterated
572
- * @param {constructCallback|constructCallback[]} callback
573
- */
574
- function forEachMemberWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
575
- constructCallback = (_construct, _prop, _path) => {} ) {
576
- forEachMember(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
577
- if (construct.query) {
578
- forAllQueries(construct.query, (q, p) => {
579
- const s = q.SELECT;
580
- if (s)
581
- forEachMember(s, callback, p, ignoreIgnore, iterateOptions);
582
- }, [ ...path, 'query' ]);
583
- }
584
- }
585
-
586
552
  /**
587
553
  * Apply function `callback(member, memberName)` to each member in `construct`,
588
554
  * recursively (i.e. also for sub-elements of elements).
@@ -606,30 +572,6 @@ function forEachMemberRecursively( construct, callback, path = [], ignoreIgnore
606
572
  }, path, ignoreIgnore, iterateOptions, constructCallback);
607
573
  }
608
574
 
609
- /**
610
- * Apply function `callback(member, memberName)` to each member in `construct`,
611
- * recursively (i.e. also for sub-elements of elements).
612
- * Recursively iterate over elements of `construct` query.
613
- *
614
- * @param {CSN.Artifact} construct
615
- * @param {genericCallback|genericCallback[]} callback
616
- * @param {CSN.Path} [path]
617
- * @param {boolean} [ignoreIgnore]
618
- * @param {object} iterateOptions can be used to skip certain kinds from being iterated
619
- * @param {constructCallback|constructCallback[]} callback
620
- */
621
- function forEachMemberRecursivelyWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
622
- constructCallback = (_construct, _prop, _path) => {} ) {
623
- forEachMemberRecursively(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
624
- if (construct.query) {
625
- forAllQueries(construct.query, (q, p) => {
626
- const s = q.SELECT;
627
- if (s)
628
- forEachMemberRecursively(s, callback, p, ignoreIgnore, iterateOptions);
629
- }, [ ...path, 'query' ]);
630
- }
631
- }
632
-
633
575
  /**
634
576
  * Apply function `callback` to all objects in dictionary `dict`, including all
635
577
  * duplicates (found under the same name). Function `callback` is called with
@@ -767,6 +709,8 @@ function isEdmPropertyRendered( elementCsn, options ) {
767
709
  // FKs are rendered in
768
710
  // V2/V4 flat: always on
769
711
  // V4 struct: on/off
712
+ if (elementCsn == null)
713
+ return false;
770
714
  const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
771
715
  const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
772
716
  const isNavigable = elementCsn.target
@@ -1398,6 +1342,28 @@ function isDeepEqual( obj, other, noExtendedProps ) {
1398
1342
  return true;
1399
1343
  }
1400
1344
 
1345
+ /**
1346
+ * convert a cardinality object to string representation
1347
+ * @param {object} node
1348
+ * @param {boolean} withSrc
1349
+ * @returns {string} cardinality as string
1350
+ */
1351
+ function cardinality2str( node, withSrc = true ) {
1352
+ const ofto = node.type === 'cds.Composition' ? 'of' : 'to';
1353
+ if (node.cardinality == null || (node.cardinality.src == null || !withSrc) && node.cardinality.min == null && node.cardinality.max === 1)
1354
+ return `${ ofto } one`;
1355
+ if ((node.cardinality.src == null || !withSrc) && node.cardinality.min == null && node.cardinality.max === '*')
1356
+ return `${ ofto } many`;
1357
+ let s = '[';
1358
+ if (node.cardinality.src != null && withSrc)
1359
+ s += `${ node.cardinality.src },`;
1360
+ if (node.cardinality.min != null)
1361
+ s += `${ node.cardinality.min }..`;
1362
+ if (node.cardinality.max != null)
1363
+ s += `${ node.cardinality.max }]`;
1364
+ return s;
1365
+ }
1366
+
1401
1367
  /**
1402
1368
  * Returns a function that, if called, calls all functions inside
1403
1369
  * the given `functions` array with the same arguments.
@@ -1422,9 +1388,7 @@ module.exports = {
1422
1388
  forEachGeneric,
1423
1389
  forEachDefinition,
1424
1390
  forEachMember,
1425
- forEachMemberWithQuery,
1426
1391
  forEachMemberRecursively,
1427
- forEachMemberRecursivelyWithQuery,
1428
1392
  forAllQueries,
1429
1393
  hasAnnotationValue,
1430
1394
  isEdmPropertyRendered,
@@ -1457,4 +1421,5 @@ module.exports = {
1457
1421
  implicitAs,
1458
1422
  isDeepEqual,
1459
1423
  functionList,
1424
+ cardinality2str,
1460
1425
  };
@@ -17,6 +17,6 @@
17
17
  "no-shadow": "warn"
18
18
  },
19
19
  "env": {
20
- "es2020": true
20
+ "es2022": true
21
21
  }
22
22
  }
@@ -108,7 +108,6 @@ optionProcessor
108
108
  enableUniversalCsn
109
109
  postgres
110
110
  aspectWithoutElements
111
- odataOpenType
112
111
  odataTerms
113
112
  optionalActionFunctionParameters
114
113
  --deprecated <list> Comma separated list of deprecated options.
@@ -222,11 +221,12 @@ optionProcessor.command('O, toOdata')
222
221
  .option('-j, --json')
223
222
  .option(' --odata-containment')
224
223
  .option(' --odata-capabilities-pullup')
224
+ .option(' --odata-openapi-hints')
225
225
  .option(' --odata-proxies')
226
226
  .option(' --odata-x-service-refs')
227
227
  .option(' --odata-foreign-keys')
228
228
  .option(' --odata-v2-partial-constr')
229
- .option(' --odata-voc-refs <list>')
229
+ .option(' --odata-vocabularies <list>')
230
230
  .option('-c, --csn')
231
231
  .option('-f, --odata-format <format>', ['flat', 'structured'])
232
232
  .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: [ '--names' ] })
@@ -250,13 +250,14 @@ optionProcessor.command('O, toOdata')
250
250
  structured : (V4 only) Render structured metadata
251
251
  --odata-containment Generate Containment Navigation Properties for compositions (V4 only)
252
252
  --odata-capabilities-pullup Rewrite @Capabilities annotations (V4 containment only).
253
+ --odata-openapi-hints Add various annotations to JSON API as input for OpenAPI generation.
253
254
  --odata-proxies Generate Proxies for out-of-service navigation targets (V4 only).
254
255
  --odata-x-service-refs Generate schema references (V4 only).
255
256
  --odata-foreign-keys Render foreign keys in structured format (V4 only)
256
257
  --odata-v2-partial-constr Render referential constraints also for partial principal key tuple
257
258
  (Not spec compliant and V2 only)
258
- --odata-voc-refs <list> JSON array of adhoc vocabulary definitions
259
- [ { alias, ns, uri }, ... ]
259
+ --odata-vocabularies <list> JSON array of adhoc vocabulary definitions
260
+ { prefix: { alias, ns, uri }, ... }
260
261
  -n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
261
262
  the corresponding database name (see "--sql-mapping" for "toHana or "toSql")
262
263
  plain : (default) Names in uppercase and flattened with underscores
@@ -297,6 +298,7 @@ optionProcessor.command('Q, toSql')
297
298
  .option(' --pre2134ReferentialConstraintNames')
298
299
  .option(' --disable-hana-comments')
299
300
  .option(' --generated-by-comment')
301
+ .option(' --better-sqlite-session-variables')
300
302
  .help(`
301
303
  Usage: cdsc toSql [options] <files...>
302
304
 
@@ -348,6 +350,7 @@ optionProcessor.command('Q, toSql')
348
350
  --pre2134ReferentialConstraintNames Do not prefix the constraint identifier with "c__"
349
351
  --disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
350
352
  --generated-by-comment Enable rendering of the initial SQL comment for HDI-based artifacts
353
+ --better-sqlite-session-variables Enable better-sqlite compatible rendering of $user. Only active if sqlDialect is \`sqlite\`
351
354
 
352
355
  `);
353
356
 
@@ -17,6 +17,6 @@
17
17
  "no-shadow": "warn"
18
18
  },
19
19
  "env": {
20
- "es2020": true
20
+ "es2022": true
21
21
  }
22
22
  }