@sap/cds-compiler 4.2.2 → 4.3.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 (66) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/bin/cdsc.js +8 -0
  3. package/bin/cdshi.js +3 -3
  4. package/doc/CHANGELOG_BETA.md +7 -0
  5. package/lib/api/main.js +19 -0
  6. package/lib/base/location.js +16 -0
  7. package/lib/base/message-registry.js +47 -16
  8. package/lib/base/messages.js +49 -38
  9. package/lib/base/model.js +2 -1
  10. package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
  11. package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
  12. package/lib/checks/existsMustEndInAssoc.js +27 -0
  13. package/lib/checks/onConditions.js +47 -1
  14. package/lib/checks/validator.js +10 -1
  15. package/lib/compiler/assert-consistency.js +23 -15
  16. package/lib/compiler/base.js +31 -14
  17. package/lib/compiler/builtins.js +21 -20
  18. package/lib/compiler/checks.js +36 -49
  19. package/lib/compiler/define.js +71 -91
  20. package/lib/compiler/extend.js +27 -25
  21. package/lib/compiler/finalize-parse-cdl.js +1 -1
  22. package/lib/compiler/generate.js +67 -87
  23. package/lib/compiler/kick-start.js +7 -5
  24. package/lib/compiler/populate.js +32 -30
  25. package/lib/compiler/propagator.js +2 -0
  26. package/lib/compiler/resolve.js +29 -25
  27. package/lib/compiler/shared.js +57 -31
  28. package/lib/compiler/tweak-assocs.js +203 -22
  29. package/lib/compiler/utils.js +0 -18
  30. package/lib/gen/Dictionary.json +10 -4
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/languageParser.js +3 -3
  33. package/lib/inspect/inspectPropagation.js +2 -1
  34. package/lib/json/from-csn.js +63 -28
  35. package/lib/json/to-csn.js +23 -13
  36. package/lib/language/antlrParser.js +1 -1
  37. package/lib/language/errorStrategy.js +5 -1
  38. package/lib/language/genericAntlrParser.js +67 -61
  39. package/lib/main.d.ts +26 -1
  40. package/lib/main.js +2 -1
  41. package/lib/model/csnRefs.js +1 -0
  42. package/lib/model/csnUtils.js +28 -0
  43. package/lib/model/revealInternalProperties.js +3 -9
  44. package/lib/optionProcessor.js +17 -1
  45. package/lib/render/toCdl.js +1 -1
  46. package/lib/transform/db/associations.js +3 -4
  47. package/lib/transform/db/backlinks.js +293 -0
  48. package/lib/transform/db/expansion.js +18 -7
  49. package/lib/transform/db/flattening.js +3 -2
  50. package/lib/transform/db/rewriteCalculatedElements.js +1 -67
  51. package/lib/transform/db/transformExists.js +3 -58
  52. package/lib/transform/db/views.js +8 -14
  53. package/lib/transform/effective/.eslintrc.json +4 -0
  54. package/lib/transform/effective/associations.js +101 -0
  55. package/lib/transform/effective/main.js +88 -0
  56. package/lib/transform/effective/misc.js +61 -0
  57. package/lib/transform/effective/queries.js +42 -0
  58. package/lib/transform/effective/types.js +121 -0
  59. package/lib/transform/forRelationalDB.js +12 -235
  60. package/lib/transform/localized.js +22 -3
  61. package/lib/transform/parseExpr.js +7 -3
  62. package/lib/transform/transformUtils.js +5 -22
  63. package/lib/transform/translateAssocsToJoins.js +42 -38
  64. package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
  65. package/package.json +1 -2
  66. package/lib/language/language.g4 +0 -3260
@@ -1036,7 +1036,7 @@ function originRef( art, user ) {
1036
1036
  while (isMember( parent ) && parent.kind !== 'select') {
1037
1037
  const nkind = normalizedKind[parent.kind];
1038
1038
  const name = parent.name || parent._outer.name;
1039
- if (name.id || !r.length)
1039
+ if (name.id && parent.kind !== '$inline' || !r.length)
1040
1040
  // Return parameter is in XSN - kind: 'param', name.id: ''
1041
1041
  // eslint-disable-next-line no-nested-ternary, max-len
1042
1042
  r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
@@ -1047,17 +1047,17 @@ function originRef( art, user ) {
1047
1047
  return false; // do not write, probably use $origin: {...}
1048
1048
  // for sub query in FROM in sub query in FROM, we could condense the info
1049
1049
 
1050
- r.push( parent.name.absolute );
1050
+ r.push( (parent._main || parent).name.id );
1051
1051
  r.reverse();
1052
1052
  return r;
1053
1053
  }
1054
1054
 
1055
1055
  function kind( k, csn, node ) {
1056
1056
  if (k === 'annotate' || k === 'extend') {
1057
- // We just use `name.absolute` because it is very likely a "constructed"
1057
+ // We just use `name.id` because it is very likely a "constructed"
1058
1058
  // extensions. The CSN parser must produce name.path like for other refs.
1059
1059
  if (!node._main)
1060
- csn[k] = node.name.absolute || artifactRef( node.name, true );
1060
+ csn[k] = node.name.id || artifactRef( node.name, true );
1061
1061
  else if (k === 'extend')
1062
1062
  csn.kind = k;
1063
1063
  }
@@ -1102,7 +1102,7 @@ function artifactRef( node, terse ) {
1102
1102
  // Works also on XSN directly coming from parser and with XSN from CDL->CSN transformation
1103
1103
  // Shortcut for many cases:
1104
1104
  if (terse && node._artifact && !node._artifact._main && terse !== '.path')
1105
- return node._artifact.name.absolute;
1105
+ return node._artifact.name.id;
1106
1106
  let { path } = node;
1107
1107
  if (!path)
1108
1108
  return undefined; // TODO: complain with strict
@@ -1111,25 +1111,35 @@ function artifactRef( node, terse ) {
1111
1111
 
1112
1112
  const head = path[0];
1113
1113
  const root = head._artifact;
1114
- const id = root?.name.absolute;
1114
+ const main = root?._main || root;
1115
+ const id = (main?.extern || main?.name)?.id;
1115
1116
  const scope = node.scope || path.length;
1116
1117
 
1117
1118
  if (typeof scope === 'number' && scope > 1) {
1118
1119
  const item = path[scope - 1];
1119
1120
  const name = item._artifact?.name;
1120
- const absolute = name?.absolute ||
1121
+ const absolute = name?.id ||
1121
1122
  `${ id || head.id }.${ path.slice( 1, scope ).map( i => i.id ).join('.') }`;
1122
1123
  path = [ Object.assign( {}, item, { id: absolute } ), ...path.slice( scope ) ];
1123
1124
  }
1124
1125
  else if (scope === 'typeOf') { // TYPE OF without ':' in path
1125
- if (root) {
1126
- const structs = root.name.element?.split('.').map( n => ({ id: n }) );
1127
- // TODO: change (follow parents) if we introduce sparse names
1128
- path = [ { id }, ...(structs || []), ...path.slice(1) ];
1129
- }
1130
- else if (strictMode) {
1126
+ if (!root) {
1131
1127
  throw new CompilerAssertion( `Unexpected TYPE OF in ${ locationString(node.location) }`);
1132
1128
  }
1129
+ else if (!root._main) {
1130
+ path = [ { id }, ...path.slice(1) ];
1131
+ }
1132
+ else {
1133
+ path = path.slice(1).reverse();
1134
+ let parent = root;
1135
+ while (parent._main) {
1136
+ path.push( { id: parent.name.id } );
1137
+ parent = parent._parent;
1138
+ parent = parent._outer || parent; // for anonymous aspect
1139
+ }
1140
+ path.push( { id } );
1141
+ path.reverse();
1142
+ }
1133
1143
  }
1134
1144
  else if (root && id !== head.id) {
1135
1145
  path = [ Object.assign( {}, head, { id } ), ...path.slice( 1 ) ];
@@ -50,7 +50,7 @@ class RewriteTypeTokenStream extends antlr4.CommonTokenStream {
50
50
  }
51
51
  else if (t.type === this.NEW) {
52
52
  const n = super.LT(k + 1);
53
- // TODO v4: rewrite token in grammar via `this.setLocalToken`
53
+ // TODO v5: rewrite token in grammar via `this.setLocalToken`
54
54
  if (n?.type === this.Identifier) {
55
55
  const o = super.LT(k + 2);
56
56
  if (o?.type === this.PAREN)
@@ -180,7 +180,7 @@ function sync( recognizer ) {
180
180
 
181
181
  case ATNState.PLUS_LOOP_BACK: // 11
182
182
  case ATNState.STAR_LOOP_BACK: { // 9
183
- // TODO: do not delete a '}'
183
+ // TODO: do not delete a '}', ')', ',', ';'
184
184
  this.reportUnwantedToken(recognizer);
185
185
  const expecting = new IntervalSet();
186
186
  expecting.addSet(recognizer.getExpectedTokens());
@@ -345,6 +345,10 @@ function recoverInline( recognizer ) {
345
345
  return this._super.recoverInline.call( this, recognizer );
346
346
 
347
347
  const token = recognizer.getCurrentToken();
348
+ // TODO: do not delete `)`, `}`,
349
+
350
+ // TODO: overwrite singleTokenDeletion do not delete parens etc for identifier
351
+ // or non-reserved keywords
348
352
  if (!keywordRegexp.test( token.text ))
349
353
  return this._super.recoverInline.call( this, recognizer );
350
354
 
@@ -22,7 +22,7 @@ const {
22
22
  XsnArtifact, XsnName, CsnLocation, XsnSource,
23
23
  } = require('../compiler/classes');
24
24
  const { isBetaEnabled } = require('../base/model');
25
- const { weakLocation } = require('../base/messages');
25
+ const { weakLocation } = require('../base/location');
26
26
  const { normalizeNewLine } = require('./textUtils');
27
27
 
28
28
  const $location = Symbol.for('cds.$location');
@@ -370,14 +370,15 @@ function assignAnnotation( art, anno, prefix = '' ) {
370
370
  absolute = `${ prefix }${ pathname }`;
371
371
  }
372
372
  else {
373
- absolute = prefix.slice( 0, -1 );
373
+ absolute = prefix.slice( 0, -1 ); // remove final dot
374
374
  }
375
+
375
376
  if ($flatten) {
376
377
  for (const a of $flatten)
377
378
  this.assignAnnotation( art, a, `${ absolute }.` );
378
379
  }
379
380
  else {
380
- name.absolute = absolute;
381
+ name.id = absolute;
381
382
  this.addAnnotation( art, `@${ absolute }`, anno );
382
383
  }
383
384
  if (!prefix) { // set deprecated $annotations for cds-lsp
@@ -706,6 +707,18 @@ function fragileAlias( ast, safe = false ) {
706
707
 
707
708
  // Return AST for identifier token `token`. Also check that identifier is not empty.
708
709
  function identAst( token, category, noTokenTypeCheck = false ) {
710
+ if (!token) { // for rule identAst
711
+ const { start, stop } = this._ctx; // token.tokenIndex
712
+ // - correct parsing: start = stop
713
+ // - singleTokenDeletion(), e.g. with `| Ident`: start < stop → stop
714
+ // - after recoverInline: start > stop (!) → stop = the previous token, if it is
715
+ // ident-like and the one before not in `.@#`, → start ('') otherwise
716
+ token = stop;
717
+ if (start.tokenIndex > stop.tokenIndex &&
718
+ (stop.type !== this.constructor.Identifier && !/^[a-zA-Z_]+$/.test( stop.text ) ||
719
+ [ '.', '@', '#' ].includes( this._input.LT(-2)?.text )))
720
+ token = start;
721
+ }
709
722
  token.isIdentifier = category;
710
723
  let id = token.text;
711
724
  if (!noTokenTypeCheck &&
@@ -776,12 +789,7 @@ function valuePathAst( ref ) {
776
789
  const { path } = ref;
777
790
  if (!path || path.broken)
778
791
  return ref;
779
- if (path.length !== 1) {
780
- const item = path.find( i => i.args && i.$syntax !== ':' );
781
- if (!item) // also covers empty paths
782
- return ref;
783
- }
784
- else if (path.length === 1) {
792
+ if (path.length === 1) {
785
793
  const { args, id, location } = path[0];
786
794
  if (args
787
795
  ? path[0].$syntax === ':'
@@ -799,40 +807,46 @@ function valuePathAst( ref ) {
799
807
  : { op, func: ref, location: ref.location };
800
808
  }
801
809
 
802
- // method call ---------------------------
803
810
 
804
- const args = [];
805
- const pathRest = [ ...path ];
806
- let pathHead = pathRest.shift();
811
+ // $syntax === ':' => path(P: 1)
812
+ // $syntax !== ':' => path(P => 1) or path(1) or path()
813
+ const firstFunc = path.findIndex( i => i.args && i.$syntax !== ':' );
814
+ if (firstFunc === -1) // also covers empty paths
815
+ return ref;
807
816
 
808
- if (pathHead.args) {
809
- args.push({
810
- op: { location: pathHead.location, val: 'call' },
811
- func: { path: [ pathHead ] },
812
- location: pathHead.location,
813
- args: pathHead.args || [],
814
- });
815
- pathHead = pathRest.shift();
816
- }
817
- else {
818
- const refPath = [];
819
- while (pathHead && !pathHead.args) {
820
- refPath.push(pathHead);
821
- pathHead = pathRest.shift();
817
+
818
+ // Method Call ---------------------------
819
+ // Transform the path into `.`-operators.
820
+ // Everything after the first function is also a function, and not a reference.
821
+
822
+ for (let i = firstFunc; i < path.length; ++i) {
823
+ if (path[i].args && path[i].$syntax === ':') {
824
+ // Error for `a(P => 1).b.c(P: 1)`: no ref after function.
825
+ this.$messageFunctions.error('syntax-invalid-ref', path[i].args[$location], {
826
+ code: '=>',
827
+ }, 'References after function calls can\'t be resolved. Use $(CODE) in function arguments');
828
+ break;
822
829
  }
823
- args.push({ path: refPath, location: refPath[0].location });
824
830
  }
825
831
 
826
- if (pathHead?.args)
827
- pathRest.unshift(pathHead);
828
-
829
- for (const method of pathRest) {
832
+ const args = [];
833
+ if (firstFunc > 0) {
830
834
  args.push({
831
- // TODO: Update parser to have proper location for `.`?
832
- location: weakLocation(method.location),
833
- val: '.',
834
- literal: 'token',
835
+ path: path.slice(0, firstFunc),
836
+ location: locUtils.combinedLocation(path[0].location, path[path.length - 1].location),
835
837
  });
838
+ }
839
+
840
+ const pathRest = path.slice(firstFunc);
841
+ for (const method of pathRest) {
842
+ if (method !== pathRest[0] || firstFunc > 0) {
843
+ args.push({
844
+ // TODO: Update parser to have proper location for `.`?
845
+ location: weakLocation(method.location),
846
+ val: '.',
847
+ literal: 'token',
848
+ });
849
+ }
836
850
  const func = {
837
851
  op: { location: method.location, val: 'call' },
838
852
  func: { path: [ method ] },
@@ -1026,46 +1040,38 @@ function reportUnexpectedSpace( prefix = this._input.LT(-1),
1026
1040
  //
1027
1041
  // If argument `kind` is provided, set `art.kind` to that value.
1028
1042
  // If argument `name` is provided, set `art.name`:
1029
- // - if `name` is an array, the name consist of the ID of the last path item
1043
+ // - if `name` is an array, `name.id` consist of the ID of the last array item
1030
1044
  // (for elements via columns, foreign keys, table aliases)
1031
- // - if `name` is an object, the name consist of the IDs of all path items
1032
- // (for main artifact definitions)
1045
+ // - if `name` is an object, `name.id` is either set, or the (local) name is calculated
1046
+ // from the IDs of all items in `name.path` (for main artifact definitions).
1033
1047
  function addDef( art, parent, env, kind, name ) {
1034
1048
  if (Array.isArray(name)) {
1035
- // XSN TODO: clearly say: definitions have name.path, members have name.id
1036
1049
  const last = name.length && name[name.length - 1];
1037
- if (last && last.id) { // // A.B.C -> 'C'
1038
- art.name = {
1039
- id: last.id, location: last.location, $inferred: 'as',
1040
- };
1041
- }
1050
+ art.name = { // A.B.C -> 'C'
1051
+ id: last?.id || '', location: last.location, $inferred: 'as',
1052
+ };
1042
1053
  }
1043
1054
  else if (name) {
1044
1055
  art.name = name;
1045
- if (name.id == null)
1046
- name.id = pathName( name.path ); // A.B.C -> 'A.B.C'
1047
- // TODO: get rid of setting `id`, only use for named values in structs
1056
+ if (!name.id && kind === null) // namedValue, fortunately no `variant` there
1057
+ art.name.id = pathName( art.name?.path );
1058
+ }
1059
+ else {
1060
+ art.name = { id: '' };
1048
1061
  }
1049
-
1050
1062
  if (kind)
1051
1063
  art.kind = kind;
1052
- if (!art.name || art.name.id == null) {
1053
- // no id was parsed, but with error recovery: no further error
1054
- // TODO: add to parent[env]['']
1055
- // which could be tested in name search (then no undefined-ref error)
1056
- // console.log( kind+': !!', art, parent, env, name )
1057
- return art;
1058
- }
1059
- // console.log( kind+':', art, parent, env, name )
1064
+
1065
+ const id = art.name?.id || pathName( art.name?.path ); // returns '' for corrupted name
1060
1066
 
1061
1067
  if (env === 'artifacts' || env === 'vocabularies') {
1062
- dictAddArray( parent[env], art.name.id, art );
1068
+ dictAddArray( parent[env], id, art );
1063
1069
  }
1064
- else if (kind || this.options.parseOnly) {
1065
- dictAdd( parent[env], art.name.id, art );
1070
+ else if (kind || this.options.parseOnly) { // TODO: do not check parseOnly
1071
+ dictAdd( parent[env], id, art );
1066
1072
  }
1067
1073
  else {
1068
- dictAdd( parent[env], art.name.id, art, ( duplicateName, loc ) => {
1074
+ dictAdd( parent[env], id, art, ( duplicateName, loc ) => {
1069
1075
  // do not use function(), otherwise `this` is wrong:
1070
1076
  if (kind === 0) {
1071
1077
  this.error( 'syntax-duplicate-argument', loc, { name: duplicateName },
package/lib/main.d.ts CHANGED
@@ -248,6 +248,18 @@ declare namespace compiler {
248
248
  serviceNames?: string[]
249
249
  }
250
250
 
251
+ /**
252
+ * Options used by the `for.effective()` CSN transformation.
253
+ *
254
+ * WORK IN PROGRESS
255
+ *
256
+ * @internal
257
+ * @see _for.effective()
258
+ */
259
+ export interface EffectiveCsnOptions extends SqlOptions {
260
+ // TODO
261
+ }
262
+
251
263
  /**
252
264
  * Options used by SQL `to.sql()` backend.
253
265
  *
@@ -818,11 +830,24 @@ declare namespace compiler {
818
830
  */
819
831
  export namespace _for {
820
832
  /**
821
- * Transform the given (generic) CSN into one that is used for OData.
833
+ * Transform the given (inferred/client) CSN into one that is used for OData.
822
834
  * Changes include flattening, type resolution and more, according to
823
835
  * the provided options.
824
836
  */
825
837
  function odata(csn: CSN, options?: ODataOptions): CSN;
838
+ /**
839
+ * Transform the given CSN into one that has these properties:
840
+ * - types are resolved
841
+ * - elements are flattened
842
+ * - …
843
+ *
844
+ * THIS IS HIGHLY EXPERIMENTAL
845
+ *
846
+ * Beta flag `effectiveCsn` is required.
847
+ *
848
+ * @internal
849
+ */
850
+ function effective(csn: CSN, options?: EffectiveCsnOptions): CSN;
826
851
  }
827
852
 
828
853
  export { _for as for };
package/lib/main.js CHANGED
@@ -115,7 +115,8 @@ module.exports = {
115
115
  },
116
116
  // SNAPI
117
117
  for: {
118
- odata: (...args) => snapi.odata(...args)
118
+ odata: (...args) => snapi.odata(...args),
119
+ effective: (...args) => snapi.for_effective(...args),
119
120
  },
120
121
  to: {
121
122
  cdl: Object.assign((...args) => snapi.cdl(...args), {
@@ -433,6 +433,7 @@ function csnRefs( csn, universalReady ) {
433
433
  return getOriginExplicit( $origin.$origin );
434
434
  const [ head, ...tail ] = $origin;
435
435
  const main = csn.definitions[head];
436
+ // if (!main) throw Error(JSON.stringify({$origin,csn}))
436
437
  initDefinition( main );
437
438
  return tail.reduce( originNavigation, main );
438
439
  }
@@ -1411,6 +1411,32 @@ function functionList( functions, thisArg ) {
1411
1411
  };
1412
1412
  }
1413
1413
 
1414
+ /**
1415
+ * Return true if 'arg' is an expression argument denoting "$self" || "$projection"
1416
+ * @param {object} arg
1417
+ * @returns {boolean}
1418
+ */
1419
+ function isDollarSelfOrProjectionOperand( arg ) {
1420
+ return arg.ref && arg.ref.length === 1 && (arg.ref[0] === '$self' || arg.ref[0] === '$projection');
1421
+ }
1422
+
1423
+ /**
1424
+ * Return true if 'arg' is an expression argument of type association or composition
1425
+ * @param {object} arg
1426
+ * @param {CSN.Path} path
1427
+ * @param {function} inspectRef
1428
+ * @returns {boolean}
1429
+ */
1430
+ function isAssociationOperand( arg, path, inspectRef ) {
1431
+ if (!arg.ref) {
1432
+ // Not a path, hence not an association (literal, expression, function, whatever ...)
1433
+ return false;
1434
+ }
1435
+ const { art } = inspectRef(path);
1436
+ // If it has a target, it is an association or composition
1437
+ return art && art.target !== undefined;
1438
+ }
1439
+
1414
1440
  module.exports = {
1415
1441
  getUtils,
1416
1442
  cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
@@ -1456,4 +1482,6 @@ module.exports = {
1456
1482
  isDeepEqual,
1457
1483
  functionList,
1458
1484
  cardinality2str,
1485
+ isAssociationOperand,
1486
+ isDollarSelfOrProjectionOperand,
1459
1487
  };
@@ -159,14 +159,8 @@ function revealInternalProperties( model, nameOrPath ) {
159
159
 
160
160
  function shortenName( node, parent ) {
161
161
  const name = reveal( node, parent );
162
- if (name && typeof name === 'object' && name.absolute && node.kind) {
162
+ if (name && typeof name === 'object' && parent.kind) {
163
163
  const text = artifactIdentifier( parent );
164
- delete name.absolute;
165
- delete name.select;
166
- delete name.action;
167
- delete name.parameter;
168
- delete name.alias;
169
- delete name.element;
170
164
  name['-->'] = text;
171
165
  }
172
166
  return name;
@@ -193,7 +187,7 @@ function revealInternalProperties( model, nameOrPath ) {
193
187
  const { name, $flatten } = anno.value || anno;
194
188
  const value = ($flatten)
195
189
  ? { name: reveal( name ), $flatten: $flatten.map( $annotation ) }
196
- : `@${ name?.absolute }`;
190
+ : `@${ name?.id }`;
197
191
  return { value, location: locationString( anno.location || anno.name.location ) };
198
192
  }
199
193
 
@@ -349,7 +343,7 @@ function artifactIdentifier( node, parent ) {
349
343
  ? artifactIdentifier( node._artifact )
350
344
  : JSON.stringify(node.name);
351
345
  case 'builtin':
352
- return `$magicVariables/${ msg.artName(node) }`;
346
+ return msg.artName(node);
353
347
  case 'source':
354
348
  case 'using':
355
349
  return `source:${ quoted( node.location && node.location.file )
@@ -105,8 +105,9 @@ optionProcessor
105
105
  --beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
106
106
  Valid values are:
107
107
  ${
108
- Object.keys(availableBetaFlags).sort()
108
+ Object.keys(availableBetaFlags)
109
109
  .filter(flag => availableBetaFlags[flag])
110
+ .sort()
110
111
  .join('\n' + ' '.repeat(30))
111
112
  }
112
113
  --deprecated <list> Comma separated list of deprecated options.
@@ -151,6 +152,7 @@ optionProcessor
151
152
  manageConstraints [options] <files...> (internal) Generate ALTER TABLE statements to
152
153
  add / modify referential constraints.
153
154
  inspect [options] <files...> (internal) Inspect the given CDS files.
155
+ toEffectiveCsn [options] <files...> (internal) Get an effective CSN for SEAL; requires beta mode
154
156
 
155
157
  Environment variables
156
158
  NO_COLOR If set, compiler messages (/output) will not be colored.
@@ -523,6 +525,20 @@ optionProcessor.command('inspect')
523
525
  --propagation <art> Show propagation sources for <art>
524
526
  `);
525
527
 
528
+ optionProcessor.command('toEffectiveCsn')
529
+ .option('-h, --help')
530
+ .positionalArgument('<files...>')
531
+ .help(`
532
+ Usage: cdsc toEffectiveCsn [options] <files...>
533
+
534
+ (internal): Get the effective CSN model compiled from the provided CDS files.
535
+ This command may change any time, including its name.
536
+ Beta mode is required.
537
+
538
+ Options
539
+ -h, --help Show this help text
540
+ `);
541
+
526
542
  module.exports = {
527
543
  optionProcessor
528
544
  };
@@ -593,7 +593,7 @@ function csnToCdl( csn, options ) {
593
593
  result += env.indent;
594
594
  result += element.virtual ? 'virtual ' : '';
595
595
  result += element.key ? 'key ' : '';
596
- // TODO(v4): Remove once deprecated flag for `masked` is removed.
596
+ // TODO(v5): Remove once deprecated flag for `masked` is removed.
597
597
  result += element.masked ? 'masked ' : '';
598
598
  result += quoteNonIdentifierOrKeyword(elementName, env);
599
599
  if (element.val !== undefined) { // enum value
@@ -3,6 +3,7 @@
3
3
  const {
4
4
  applyTransformationsOnNonDictionary,
5
5
  applyTransformations,
6
+ implicitAs,
6
7
  } = require('../../model/csnUtils');
7
8
 
8
9
  /**
@@ -15,9 +16,7 @@ const {
15
16
  * @returns {CSN.Model} Return the input csn, with the transformations applied
16
17
  */
17
18
  function attachOnConditions( csn, csnUtils, pathDelimiter ) {
18
- const {
19
- isManagedAssociation,
20
- } = csnUtils;
19
+ const { isManagedAssociation } = csnUtils;
21
20
 
22
21
  const alreadyHandled = new WeakMap();
23
22
  applyTransformations(csn, {
@@ -59,7 +58,7 @@ function attachOnConditions( csn, csnUtils, pathDelimiter ) {
59
58
  elemName,
60
59
  ].concat(foreignKey.ref),
61
60
  };
62
- const fkName = `${elemName}${pathDelimiter}${foreignKey.as}`;
61
+ const fkName = `${elemName}${pathDelimiter}${foreignKey.as || implicitAs(foreignKey.ref)}`;
63
62
  const fKeyArg = {
64
63
  ref: [
65
64
  fkName,