@sap/cds-compiler 3.1.2 → 3.3.2

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 (100) hide show
  1. package/CHANGELOG.md +80 -3
  2. package/bin/cdsc.js +1 -1
  3. package/doc/CHANGELOG_BETA.md +18 -0
  4. package/lib/api/main.js +8 -13
  5. package/lib/base/error.js +2 -2
  6. package/lib/base/keywords.js +2 -24
  7. package/lib/base/message-registry.js +43 -14
  8. package/lib/base/messages.js +20 -10
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/actionsFunctions.js +1 -1
  11. package/lib/checks/annotationsOData.js +2 -2
  12. package/lib/checks/arrayOfs.js +15 -7
  13. package/lib/checks/cdsPersistence.js +1 -1
  14. package/lib/checks/checkForTypes.js +48 -0
  15. package/lib/checks/defaultValues.js +2 -2
  16. package/lib/checks/elements.js +81 -6
  17. package/lib/checks/foreignKeys.js +12 -13
  18. package/lib/checks/invalidTarget.js +10 -11
  19. package/lib/checks/managedInType.js +21 -15
  20. package/lib/checks/nullableKeys.js +1 -1
  21. package/lib/checks/onConditions.js +9 -9
  22. package/lib/checks/parameters.js +21 -0
  23. package/lib/checks/selectItems.js +1 -1
  24. package/lib/checks/types.js +2 -2
  25. package/lib/checks/utils.js +17 -7
  26. package/lib/checks/validator.js +26 -14
  27. package/lib/compiler/assert-consistency.js +13 -6
  28. package/lib/compiler/builtins.js +8 -0
  29. package/lib/compiler/checks.js +40 -33
  30. package/lib/compiler/define.js +50 -44
  31. package/lib/compiler/extend.js +303 -37
  32. package/lib/compiler/kick-start.js +2 -35
  33. package/lib/compiler/populate.js +83 -62
  34. package/lib/compiler/propagator.js +1 -1
  35. package/lib/compiler/resolve.js +61 -104
  36. package/lib/compiler/shared.js +16 -6
  37. package/lib/compiler/tweak-assocs.js +25 -12
  38. package/lib/compiler/utils.js +2 -2
  39. package/lib/edm/annotations/genericTranslation.js +3 -3
  40. package/lib/edm/csn2edm.js +10 -10
  41. package/lib/edm/edm.js +17 -9
  42. package/lib/edm/edmPreprocessor.js +53 -30
  43. package/lib/edm/edmUtils.js +7 -2
  44. package/lib/gen/Dictionary.json +14 -0
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +3 -2
  47. package/lib/gen/languageParser.js +4205 -4100
  48. package/lib/inspect/inspectModelStatistics.js +1 -1
  49. package/lib/inspect/inspectPropagation.js +23 -9
  50. package/lib/json/csnVersion.js +1 -1
  51. package/lib/json/from-csn.js +26 -19
  52. package/lib/json/to-csn.js +47 -5
  53. package/lib/language/antlrParser.js +1 -1
  54. package/lib/language/genericAntlrParser.js +29 -13
  55. package/lib/language/language.g4 +28 -8
  56. package/lib/main.d.ts +3 -6
  57. package/lib/model/.eslintrc.json +13 -0
  58. package/lib/model/api.js +4 -2
  59. package/lib/model/csnRefs.js +74 -47
  60. package/lib/model/csnUtils.js +236 -218
  61. package/lib/model/enrichCsn.js +41 -31
  62. package/lib/model/revealInternalProperties.js +61 -57
  63. package/lib/model/sortViews.js +31 -31
  64. package/lib/modelCompare/compare.js +6 -6
  65. package/lib/optionProcessor.js +5 -0
  66. package/lib/render/manageConstraints.js +2 -2
  67. package/lib/render/toCdl.js +31 -44
  68. package/lib/render/toHdbcds.js +7 -5
  69. package/lib/render/toRename.js +4 -4
  70. package/lib/render/toSql.js +11 -5
  71. package/lib/render/utils/common.js +20 -9
  72. package/lib/render/utils/sql.js +5 -5
  73. package/lib/transform/db/applyTransformations.js +32 -3
  74. package/lib/transform/db/expansion.js +81 -37
  75. package/lib/transform/db/flattening.js +1 -1
  76. package/lib/transform/db/temporal.js +1 -1
  77. package/lib/transform/db/transformExists.js +1 -1
  78. package/lib/transform/forOdataNew.js +10 -7
  79. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +7 -7
  80. package/lib/transform/localized.js +28 -19
  81. package/lib/transform/odata/toFinalBaseType.js +8 -11
  82. package/lib/transform/odata/typesExposure.js +1 -1
  83. package/lib/transform/transformUtilsNew.js +101 -39
  84. package/lib/transform/translateAssocsToJoins.js +5 -4
  85. package/lib/utils/moduleResolve.js +5 -5
  86. package/lib/utils/objectUtils.js +3 -3
  87. package/package.json +2 -2
  88. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  89. package/share/messages/check-proper-type-of.md +4 -4
  90. package/share/messages/check-proper-type.md +2 -2
  91. package/share/messages/duplicate-autoexposed.md +4 -4
  92. package/share/messages/extend-repeated-intralayer.md +4 -5
  93. package/share/messages/extend-unrelated-layer.md +4 -4
  94. package/share/messages/message-explanations.json +3 -1
  95. package/share/messages/redirected-to-ambiguous.md +7 -6
  96. package/share/messages/redirected-to-complex.md +63 -0
  97. package/share/messages/redirected-to-unrelated.md +6 -5
  98. package/share/messages/rewrite-not-supported.md +4 -4
  99. package/share/messages/syntax-expected-integer.md +3 -3
  100. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -59,6 +59,13 @@
59
59
  // delete `s` and then still expects inspectRef() to be able to resolve a
60
60
  // reference `['s', 'x']`.
61
61
 
62
+ // There are currently 3 (SQL) backend issues for which we provide a workaround:
63
+ //
64
+ // - function `resolvePath`: issue with argument `arg` being falsy
65
+ // - function `artifactRef`: issue with non-string ref without definition
66
+ // - function `initColumnElement`: issue with column which is neither `*` nor
67
+ // a `ref` with sibling `inline`, but still has no corresponding element
68
+
62
69
  // The functions in this module also use an internal cache. The second call of
63
70
  // inspectRef() in the following example might lead to a wrong result or an
64
71
  // exception if the assignment to `inspectRef` is not uncommented:
@@ -164,8 +171,8 @@
164
171
  // - _effectiveType on def/member/items: cached result of effectiveType()
165
172
  // - _origin on def/member/items: the "prototype"
166
173
  // - $origin on def/member/items: whether implicit _origin refs have been set for direct members
167
- // - _parent: not yet used (for debugging)
168
- //
174
+ // - _parent: currently just use to allow ref to `up_` in anonymous aspect
175
+ // for managed compositions
169
176
  // - _env on non-string path item: environment provided by the ref so far,
170
177
  // next path item is element in it
171
178
  // - _ref on non-string `type` or `from` ref, or on alias: the referred def/elem
@@ -181,7 +188,7 @@
181
188
 
182
189
  const BUILTIN_TYPE = {};
183
190
  const { locationString } = require('../base/location');
184
- const { ModelError, CompilerAssertion } = require("../base/error");
191
+ const { ModelError, CompilerAssertion } = require('../base/error');
185
192
 
186
193
  // Properties in which artifact or members are defined - next property in the
187
194
  // "csnPath" is the name or index of that property; 'args' (its value can be a
@@ -207,7 +214,7 @@ const referenceSemantics = {
207
214
  excluding: { lexical: false, dynamic: 'source' },
208
215
  expand: { lexical: justDollar, dynamic: 'expand' }, // ...using baseEnv
209
216
  inline: { lexical: justDollar, dynamic: 'inline' }, // ...using baseEnv
210
- ref_where: { lexical: justDollar , dynamic: 'ref-target'}, // ...using baseEnv
217
+ ref_where: { lexical: justDollar, dynamic: 'ref-target' }, // ...using baseEnv
211
218
  on: { lexical: justDollar, dynamic: 'query' }, // assoc defs, redirected to
212
219
  // there are also 'on_join' and 'on_mixin' with default semantics
213
220
  orderBy_ref: { lexical: query => query, dynamic: 'query' },
@@ -216,7 +223,7 @@ const referenceSemantics = {
216
223
  // refs in ORDER BY expr in UNION not really allowed - only with table alias (of outer queries) or $self
217
224
  orderBy_set_expr: { lexical: query => query.$next, dynamic: false },
218
225
  // default: { lexical: query => query, dynamic: 'source' }
219
- }
226
+ };
220
227
 
221
228
  function justDollar() {
222
229
  return null;
@@ -234,23 +241,23 @@ function csnRefs( csn, universalReady ) {
234
241
  initDefinition( art );
235
242
  }
236
243
  // Functions which set the new `baseEnv`:
237
- resolveRef.expandInline = function resolve_expandInline( ref, ...args ) {
244
+ resolveRef.expandInline = function resolveExpandInline( ref, ...args ) {
238
245
  return cached( ref, '_env', () => navigationEnv( resolveRef( ref, ...args ).art ) );
239
- }
240
- resolveRef.ref_where = function resolve_ref_where( pathItem, baseRef, ...args ) {
246
+ };
247
+ resolveRef.ref_where = function resolveRefWhere( pathItem, baseRef, ...args ) {
241
248
  return cached( pathItem, '_env', () => {
242
249
  resolveRef( baseRef, ...args ); // sets _env cache for non-string ref items
243
250
  return getCache( pathItem, '_env' );
244
251
  } );
245
- }
252
+ };
246
253
  return {
247
254
  effectiveType,
248
255
  artifactRef,
249
256
  getOrigin,
250
257
  inspectRef,
251
258
  queryOrMain,
252
- getColumn: ( elem ) => getCache( elem, '_column' ),
253
- getElement: ( col ) => getCache( col, '_element' ),
259
+ getColumn: elem => getCache( elem, '_column' ),
260
+ getElement: col => getCache( col, '_element' ),
254
261
  initDefinition,
255
262
  targetAspect,
256
263
  __getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
@@ -330,7 +337,7 @@ function csnRefs( csn, universalReady ) {
330
337
  // Backend bug workaround, TODO: delete next 2 lines
331
338
  if (notFound !== undefined)
332
339
  return notFound;
333
- throw new ModelError( `Unknown artifact reference: ${typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref}` );
340
+ throw new ModelError( `Unknown artifact reference: ${ typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref }` );
334
341
  }
335
342
 
336
343
  function artifactPathRef( ref ) {
@@ -364,9 +371,9 @@ function csnRefs( csn, universalReady ) {
364
371
  // 3. user-induced redirection (in 'cast') with explicit foreign keys
365
372
  // 4. original provided association def inside $origin with explicit foreign keys
366
373
  // (outside $origin like 2)
367
- const targetName = refCtx !== 'keys_origin' && art.target
368
- || art.$origin && art.$origin.target
369
- || art.cast.target;
374
+ const targetName = refCtx !== 'keys_origin' && art.target ||
375
+ art.$origin && art.$origin.target ||
376
+ art.cast.target;
370
377
  const target = csn.definitions[targetName];
371
378
  initDefinition( target );
372
379
  return target;
@@ -387,8 +394,8 @@ function csnRefs( csn, universalReady ) {
387
394
  const { $location } = art;
388
395
  const location = $location &&
389
396
  (typeof $location === 'string' ? $location : locationString( $location ));
390
- const def = Object.keys( art ).join('+') + (location ? ':' + location : '');
391
- throw new Error( `Inspecting non-initialized CSN node {${def}}` );
397
+ const def = Object.keys( art ).join('+') + (location ? `:${ location }` : '');
398
+ throw new Error( `Inspecting non-initialized CSN node {${ def }}` );
392
399
  }
393
400
  const step = getCache( art, '$origin$step' );
394
401
  if (!step)
@@ -428,7 +435,7 @@ function csnRefs( csn, universalReady ) {
428
435
  return effectiveType( art ).items;
429
436
  if (step.target)
430
437
  return targetAspect( effectiveType( art ) );
431
- throw Error( `Illegal navigation step ${ Object.keys(step)[0]}` );
438
+ throw Error( `Illegal navigation step ${ Object.keys(step)[0] }` );
432
439
  }
433
440
 
434
441
  function effectiveArtFor( art, property ) {
@@ -529,16 +536,17 @@ function csnRefs( csn, universalReady ) {
529
536
  return resolvePath( path, csn.definitions[head], null, 'global', semantics.assoc );
530
537
 
531
538
 
532
- let qcache = query && cache.get( query.projection || query );
539
+ const qcache = query && cache.get( query.projection || query );
533
540
  // first the lexical scopes (due to query hierarchy) and $magic: ---------
534
541
  if (semantics.lexical !== false) {
535
542
  const tryAlias = path.length > 1 || ref.expand || ref.inline;
536
543
  let ncache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
537
544
  while (ncache) {
538
545
  const alias = tryAlias && ncache.$aliases[head];
539
- if (alias)
546
+ if (alias) {
540
547
  return resolvePath( path, alias._select || alias._ref, null,
541
548
  'alias', ncache.$queryNumber );
549
+ }
542
550
  const mixin = ncache._select.mixin && ncache._select.mixin[head];
543
551
  if (mixin && {}.hasOwnProperty.call( ncache._select.mixin, head )) {
544
552
  setCache( mixin, '_parent', qcache._select );
@@ -559,8 +567,14 @@ function csnRefs( csn, universalReady ) {
559
567
  const target = assocTarget( parent, refCtx );
560
568
  return resolvePath( path, target.elements[head], target, 'target' );
561
569
  }
562
- if (baseEnv) // ref-target (filter condition), expand, inline
563
- return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
570
+ if (baseEnv) { // ref-target (filter condition), expand, inline
571
+ if (semantics.dynamic !== 'query')
572
+ return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
573
+ // in an ON condition of an association inside inner expand/inline:
574
+ const elemParent = getCache( parent, '_element' );
575
+ if (elemParent) // expand in expand
576
+ return resolvePath( path, elemParent.elements[head], null, 'query' );
577
+ }
564
578
  if (!query) { // outside queries - TODO: items?
565
579
  let art = parent.elements[head];
566
580
  // Ref to up_ in anonymous aspect
@@ -582,11 +596,11 @@ function csnRefs( csn, universalReady ) {
582
596
  const alias = qcache.$aliases[name];
583
597
  const found = alias.elements[head];
584
598
  if (found)
585
- return resolvePath( path, found, alias._ref, 'source', name )
599
+ return resolvePath( path, found, alias._ref, 'source', name );
586
600
  }
587
601
  }
588
602
  // console.log(query.SELECT,qcache,qcache.$next,main)
589
- throw new ModelError ( `Path item 0=${ head } refers to nothing, refCtx: ${ refCtx }` );
603
+ throw new ModelError( `Path item 0=${ head } refers to nothing, refCtx: ${ refCtx }` );
590
604
  }
591
605
 
592
606
  /**
@@ -601,7 +615,7 @@ function csnRefs( csn, universalReady ) {
601
615
  const links = path.map( (_v, idx) => ({ idx }) );
602
616
  // TODO: backends should be changed to enable uncommenting:
603
617
  // if (!art) // does not work with test3/Associations/KeylessManagedAssociation/
604
- // throw new ModelError ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
618
+ // throw new ModelError( `Path item 0=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
605
619
  links[0].art = art;
606
620
  for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above
607
621
  parent = navigationEnv( art, staticAssoc );
@@ -610,9 +624,9 @@ function csnRefs( csn, universalReady ) {
610
624
  setCache( path[i - 1], '_env', parent );
611
625
  art = parent.elements[pathId( path[i] )];
612
626
  if (!art) {
613
- const env = links[i - 1].env;
627
+ const { env } = links[i - 1];
614
628
  const loc = env.name && env.name.$location || env.$location;
615
- throw new ModelError ( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
629
+ throw new ModelError( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
616
630
  }
617
631
  links[i].art = art;
618
632
  }
@@ -626,11 +640,15 @@ function csnRefs( csn, universalReady ) {
626
640
  parent = null;
627
641
  }
628
642
  if (typeof last !== 'string')
629
- setCache( last, '_env', env )
643
+ setCache( last, '_env', env );
630
644
  }
631
645
  return (extraInfo && scope !== 'global')
632
- ? { links, art, parent, scope, $env: extraInfo }
633
- : { links, art, parent, scope };
646
+ ? {
647
+ links, art, parent, scope, $env: extraInfo,
648
+ }
649
+ : {
650
+ links, art, parent, scope,
651
+ };
634
652
  }
635
653
 
636
654
  /**
@@ -649,7 +667,7 @@ function csnRefs( csn, universalReady ) {
649
667
  if (query.ref) { // ref in from
650
668
  // console.log('SQ:',query,cache.get(query))
651
669
  const as = query.as || implicitAs( query.ref );
652
- const _ref = cached( query, '_from', artifactFromRef )
670
+ const _ref = cached( query, '_from', artifactFromRef );
653
671
  getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements, _parent: query };
654
672
  }
655
673
  else {
@@ -749,8 +767,9 @@ function csnRefs( csn, universalReady ) {
749
767
  hidden = {};
750
768
  cache.set( obj, hidden );
751
769
  }
752
- else if (hidden[prop] !== undefined)
770
+ else if (hidden[prop] !== undefined) {
753
771
  return hidden[prop];
772
+ }
754
773
  const val = calc( obj );
755
774
  hidden[prop] = val;
756
775
  return val;
@@ -759,7 +778,7 @@ function csnRefs( csn, universalReady ) {
759
778
 
760
779
  // Return value of a query SELECT for the query node, or the main artifact,
761
780
  // i.e. a value with an `elements` property.
762
- // TODO: only used in forHanaNew - move somewhere else
781
+ // TODO: only used in forRelationalDB - move somewhere else
763
782
  /**
764
783
  * @param {object} query node (object with SET or SELECT property)
765
784
  * @param {object} main definition
@@ -830,7 +849,7 @@ function traverseFrom( from, fromSelect, parentQuery, callback ) {
830
849
 
831
850
  function traverseExpr( expr, parentQuery, callback ) {
832
851
  if (expr.SELECT || expr.SET)
833
- traverseQuery( expr, null, parentQuery, callback )
852
+ traverseQuery( expr, null, parentQuery, callback );
834
853
  for (const prop of [ 'args', 'xpr' ]) {
835
854
  // all properties which could have sub queries (directly or indirectly),
836
855
  const val = expr[prop];
@@ -842,7 +861,7 @@ function traverseExpr( expr, parentQuery, callback ) {
842
861
  }
843
862
 
844
863
  function traverseDef( node, parent, kind, name, callback ) {
845
- callback ( node, parent, kind, name );
864
+ callback( node, parent, kind, name );
846
865
  if (node.params) {
847
866
  for (const n of Object.keys( node.params ))
848
867
  traverseType( node.params[n], node, 'param', n, callback );
@@ -852,32 +871,32 @@ function traverseDef( node, parent, kind, name, callback ) {
852
871
  traverseType( node, true, kind, name, callback );
853
872
  if (node.actions) {
854
873
  for (const n of Object.keys( node.actions ))
855
- traverseDef( node.actions[n], node, 'action', n, callback )
874
+ traverseDef( node.actions[n], node, 'action', n, callback );
856
875
  }
857
876
  }
858
877
 
859
878
  function traverseType( node, parent, kind, name, callback ) {
860
879
  if (parent !== true)
861
- callback ( node, parent, kind, name );
880
+ callback( node, parent, kind, name );
862
881
  const target = targetAspect( node );
863
882
  if (target && typeof target === 'object' && target.elements) {
864
- callback ( target, node, 'target', true );
883
+ callback( target, node, 'target', true );
865
884
  node = target;
866
885
  }
867
886
  else if (node.items) {
868
887
  let items = 0;
869
888
  while (node.items) {
870
- callback ( node.items, node, 'items', ++items );
889
+ callback( node.items, node, 'items', ++items );
871
890
  node = node.items;
872
891
  }
873
892
  }
874
893
  if (node.elements) {
875
894
  for (const n of Object.keys( node.elements ))
876
- traverseDef( node.elements[n], node, 'element', n, callback )
895
+ traverseDef( node.elements[n], node, 'element', n, callback );
877
896
  }
878
897
  if (node.enum) {
879
898
  for (const n of Object.keys( node.enum ))
880
- traverseDef( node.enum[n], node, 'enum', n, callback )
899
+ traverseDef( node.enum[n], node, 'enum', n, callback );
881
900
  }
882
901
  }
883
902
 
@@ -901,12 +920,16 @@ function startCsnPath( csnPath, csn ) {
901
920
  const head = csnPath[0];
902
921
  if (typeof head !== 'string') {
903
922
  const { main, parent, art } = head;
904
- return { index: 1, main, parent, art };
923
+ return {
924
+ index: 1, main, parent, art,
925
+ };
905
926
  }
906
927
  if (csnPath.length < 2 || csnPath[0] !== 'definitions')
907
928
  throw new CompilerAssertion( 'References outside definitions not supported yet');
908
929
  const art = csn.definitions[csnPath[1]];
909
- return { index: 2, main: art, parent: null, art };
930
+ return {
931
+ index: 2, main: art, parent: null, art,
932
+ };
910
933
  }
911
934
 
912
935
  /**
@@ -917,12 +940,15 @@ function startCsnPath( csnPath, csn ) {
917
940
  function analyseCsnPath( csnPath, csn, resolve ) {
918
941
  /** @type {object} */
919
942
  let query = null;
943
+ /** @type {any} */
920
944
  let refCtx = null;
921
945
  /** @type {boolean|string|number} */
922
946
  let isName = false;
923
947
  let baseRef = null;
924
948
  let baseEnv = null;
925
- let { index, main, parent, art } = startCsnPath( csnPath, csn );
949
+ let {
950
+ index, main, parent, art,
951
+ } = startCsnPath( csnPath, csn );
926
952
  let obj = art;
927
953
 
928
954
  for (; index < csnPath.length; index++) {
@@ -951,7 +977,9 @@ function analyseCsnPath( csnPath, csn, resolve ) {
951
977
  }
952
978
  else if (artifactProperties.includes( String(prop) )) {
953
979
  if (refCtx === 'target' || refCtx === 'targetAspect') { // with 'elements'
954
- main = art = obj; // $self refers to the anonymous aspect
980
+ // $self refers to the anonymous aspect
981
+ main = obj;
982
+ art = obj;
955
983
  parent = null;
956
984
  }
957
985
  isName = prop;
@@ -981,8 +1009,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
981
1009
  baseEnv = resolve.expandInline( obj, refCtx, main, query, parent, baseEnv );
982
1010
  refCtx = prop;
983
1011
  }
984
- if (prop === 'expand')
985
- isName = prop;
1012
+ isName = prop;
986
1013
  }
987
1014
  else if (prop === 'on') {
988
1015
  if (refCtx === 'from')